11import React , { useEffect , useState } from 'react' ;
2- import { Dialog , DialogActions , DialogContent , DialogTitle , TextField , MenuItem , Select , InputLabel , FormControl , Button , SelectChangeEvent } from '@mui/material' ;
2+ import { Dialog , DialogActions , DialogContent , DialogTitle , TextField , MenuItem , Select , InputLabel , FormControl , Button , SelectChangeEvent , InputAdornment , IconButton } from '@mui/material' ;
33import { UserRequest } from './UserManagement' ;
44import { useUserList } from 'services/user' ;
55import { User } from './UserManagement' ;
66import { useAlert } from 'contexts/AlertContextProvider' ;
77import Alert from '@mui/material/Alert' ;
8+ import { Visibility , VisibilityOff } from '@mui/icons-material' ;
89
910interface AddUserProps {
1011 open : boolean ;
@@ -37,12 +38,20 @@ const AddUser: React.FC<AddUserProps> = ({ open, onClose, onSubmit, currentUser
3738 const { data : users } = useUserList ( ) ;
3839 const { showAlert } = useAlert ( ) ;
3940 const [ error , setError ] = useState < boolean | null > ( null ) ;
41+ const [ passwordRequirements , setPasswordRequirements ] = useState ( {
42+ length : false ,
43+ uppercase : false ,
44+ lowercase : false ,
45+ number : false ,
46+ specialChar : false ,
47+ } ) ;
48+ const [ showPassword , setShowPassword ] = useState < boolean > ( false ) ;
4049
4150 useEffect ( ( ) => {
4251 const userName = newUser ?. user_name . replace ( / \s + / g, '_' ) ;
4352 const emailAddress = newUser ?. email_address ;
4453 if ( userName || emailAddress ) {
45- const usernameExists = users ?. data ?. some ( ( user : { user_name : string ; } ) => user . user_name === userName ) ;
54+ const usernameExists = users ?. data ?. some ( ( user : { user_name : string ; } ) => user . user_name . toLowerCase ( ) === userName . toLowerCase ( ) ) ;
4655 setIsUsernameTaken ( usernameExists || false ) ;
4756 } else {
4857 setIsUsernameTaken ( null ) ;
@@ -58,10 +67,53 @@ const AddUser: React.FC<AddUserProps> = ({ open, onClose, onSubmit, currentUser
5867
5968 const handleChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
6069 const { name, value } = e . target ;
61- setNewUser ( {
62- ...newUser ,
63- [ name ] : value ,
64- } ) ;
70+
71+ setNewUser ( prev => ( {
72+ ...prev ,
73+ [ name ] : name === 'user_name' ? value . toLowerCase ( ) : value
74+ } ) ) ;
75+
76+ if ( name === 'password' ) {
77+ validatePassword ( value ) ;
78+ }
79+ } ;
80+
81+ const validatePassword = ( password : string ) => {
82+ const updatedRequirements = {
83+ length : password . length >= 8 && password . length <= 15 ,
84+ uppercase : / [ A - Z ] / . test ( password ) ,
85+ lowercase : / [ a - z ] / . test ( password ) ,
86+ number : / \d / . test ( password ) ,
87+ specialChar : / [ ! @ # $ % ^ & * ( ) , . ? " : { } | < > ] / . test ( password ) ,
88+ } ;
89+
90+ setPasswordRequirements ( updatedRequirements ) ;
91+ } ;
92+
93+ const getPasswordHelperText = ( ) => {
94+ const requirements = [ ] ;
95+
96+ if ( ! passwordRequirements . length ) {
97+ requirements . push ( '8-15 characters' ) ;
98+ }
99+ if ( ! passwordRequirements . uppercase ) {
100+ requirements . push ( 'at least one uppercase letter' ) ;
101+ }
102+ if ( ! passwordRequirements . lowercase ) {
103+ requirements . push ( 'at least one lowercase letter' ) ;
104+ }
105+ if ( ! passwordRequirements . number ) {
106+ requirements . push ( 'at least one number' ) ;
107+ }
108+ if ( ! passwordRequirements . specialChar ) {
109+ requirements . push ( 'at least one special character' ) ;
110+ }
111+
112+ if ( requirements . length > 0 ) {
113+ return `Password must contain: ${ requirements . join ( ', ' ) } ` ;
114+ } else {
115+ return '' ;
116+ }
65117 } ;
66118
67119 const handleRoleChange = ( e : SelectChangeEvent < string | string [ ] > ) => {
@@ -74,15 +126,14 @@ const AddUser: React.FC<AddUserProps> = ({ open, onClose, onSubmit, currentUser
74126 const handleSubmit = ( ) => {
75127 onSubmit ( newUser )
76128 . then ( ( ) => {
77- onClose ( ) ;
78- resetForm ( ) ;
129+ onClose ( ) ;
130+ resetForm ( ) ;
79131 } )
80132 . catch ( ( ) => {
81133 showAlert ( 'Failed to create user' , 'error' ) ;
82134 setError ( true ) ;
83135 } ) ;
84136 } ;
85-
86137
87138 const resetForm = ( ) => {
88139 setNewUser ( {
@@ -93,18 +144,20 @@ const AddUser: React.FC<AddUserProps> = ({ open, onClose, onSubmit, currentUser
93144 } ) ;
94145 } ;
95146
147+ const isUserNameValid = newUser ?. user_name && newUser ?. user_name . length >= 3 ;
96148 const isEmailValid = newUser ?. email_address ? emailRegex . test ( newUser ?. email_address ) : true ;
97- const isFirstNameValid = ! newUser . first_name || newUser . first_name . length >= 3 ;
98- const isLastNameValid = ! newUser . last_name || newUser . last_name . length >= 3 ;
149+ const isFirstNameValid = ! newUser ?. first_name || newUser ?. first_name . length >= 3 ;
150+ const isLastNameValid = ! newUser ?. last_name || newUser ?. last_name . length >= 3 ;
151+ const isPasswordValid = newUser ?. password && getPasswordHelperText ( ) === '' ;
99152 const isFormValid =
100- newUser ?. user_name &&
101153 newUser ?. email_address &&
102- newUser ?. password &&
154+ isPasswordValid &&
103155 newUser ?. roles . length > 0 &&
104156 isUsernameTaken === false &&
105157 isEmailValid &&
106158 isFirstNameValid &&
107- isLastNameValid ;
159+ isLastNameValid &&
160+ isUserNameValid ;
108161
109162 const availableRoles = currentUser ?. is_owner ? rolesOptions : rolesOptions . filter ( role => role . value !== 'admin' ) ;
110163
@@ -137,8 +190,8 @@ const AddUser: React.FC<AddUserProps> = ({ open, onClose, onSubmit, currentUser
137190 onChange = { handleChange }
138191 required
139192 margin = "normal"
140- error = { isUsernameTaken === true }
141- helperText = { isUsernameTaken ? 'Username already exists' : '' }
193+ error = { isUsernameTaken === true || isUserNameValid === false }
194+ helperText = { isUsernameTaken ? 'Username already exists' : ( isUserNameValid === false ) ? 'Username must be at least 3 characters' : '' }
142195 />
143196 < TextField
144197 label = "First Name"
@@ -148,7 +201,7 @@ const AddUser: React.FC<AddUserProps> = ({ open, onClose, onSubmit, currentUser
148201 value = { newUser . first_name }
149202 onChange = { handleChange }
150203 margin = "normal"
151- error = { ! isFirstNameValid }
204+ error = { ! isFirstNameValid }
152205 helperText = { ! isFirstNameValid ? 'If provided, first name must be at least 3 characters' : '' }
153206 />
154207 < TextField
@@ -178,14 +231,28 @@ const AddUser: React.FC<AddUserProps> = ({ open, onClose, onSubmit, currentUser
178231 < TextField
179232 label = "Password"
180233 name = "password"
181- type = " password"
234+ type = { showPassword ? 'text' : ' password' }
182235 fullWidth
183236 variant = "outlined"
184237 value = { newUser . password }
185238 onChange = { handleChange }
186239 required
187240 margin = "normal"
241+ error = { isPasswordValid === false }
242+ helperText = { isPasswordValid === false && getPasswordHelperText ( ) }
188243 autoComplete = "new-password"
244+ InputProps = { {
245+ endAdornment : (
246+ < InputAdornment position = "end" >
247+ < IconButton
248+ onClick = { ( ) => setShowPassword ( ! showPassword ) }
249+ edge = "end"
250+ >
251+ { showPassword ? < Visibility /> : < VisibilityOff /> }
252+ </ IconButton >
253+ </ InputAdornment >
254+ ) ,
255+ } }
189256 />
190257 < FormControl fullWidth margin = "normal" >
191258 < InputLabel > Role</ InputLabel >
0 commit comments