11import React , { useCallback , useEffect , useRef , useState } from 'react' ;
22import { useTranslation } from 'react-i18next' ;
3- import { Button , ModalBody , ModalFooter , ModalHeader , InlineNotification , Tile , PasswordInput } from '@carbon/react' ;
4- import { navigate , showToast , useConfig } from '@openmrs/esm-framework' ;
3+ import { Button , ModalBody , ModalFooter , ModalHeader , Tile , PasswordInput , Form , Layer } from '@carbon/react' ;
4+ import { navigate , showSnackbar } from '@openmrs/esm-framework' ;
55import styles from './change-password-modal.scss' ;
66import { performPasswordChange } from './change-password-model.resource' ;
77
@@ -11,8 +11,6 @@ interface ChangePasswordModalProps {
1111
1212export default function ChangePasswordModal ( { close } : ChangePasswordModalProps ) {
1313 const { t } = useTranslation ( ) ;
14- const config = useConfig ( ) ;
15- const [ errorMessage , setErrorMessage ] = useState ( '' ) ;
1614 const [ isSavingPassword , setIsSavingPassword ] = useState ( false ) ;
1715 const oldPasswordInputRef = useRef < HTMLInputElement > ( null ) ;
1816 const newPasswordInputRef = useRef < HTMLInputElement > ( null ) ;
@@ -30,6 +28,58 @@ export default function ChangePasswordModal({ close }: ChangePasswordModalProps)
3028 confirmPassword : '' ,
3129 } ) ;
3230
31+ const handleValidation = useCallback (
32+ ( passwordInputValue , passwordInputFieldName ) => {
33+ if ( passwordInputFieldName === 'newPassword' ) {
34+ const uppercaseRegExp = / (? = .* ?[ A - Z ] ) / ;
35+ const lowercaseRegExp = / (? = .* ?[ a - z ] ) / ;
36+ const digitsRegExp = / (? = .* ?[ 0 - 9 ] ) / ;
37+ const minLengthRegExp = / .{ 8 , } / ;
38+ const passwordLength = passwordInputValue . length ;
39+ const uppercasePassword = uppercaseRegExp . test ( passwordInputValue ) ;
40+ const lowercasePassword = lowercaseRegExp . test ( passwordInputValue ) ;
41+ const digitsPassword = digitsRegExp . test ( passwordInputValue ) ;
42+ const minLengthPassword = minLengthRegExp . test ( passwordInputValue ) ;
43+ let errMsg = '' ;
44+ if ( passwordLength === 0 ) {
45+ errMsg = t ( 'passwordIsEmpty' , 'Password is empty' ) ;
46+ } else if ( ! uppercasePassword ) {
47+ errMsg = t ( 'atLeastOneUppercase' , 'At least one Uppercase' ) ;
48+ } else if ( ! lowercasePassword ) {
49+ errMsg = t ( 'atLeastOneLowercase' , 'At least one Lowercase' ) ;
50+ } else if ( ! digitsPassword ) {
51+ errMsg = t ( 'atLeastOneDigit' , 'At least one digit' ) ;
52+ } else if ( ! minLengthPassword ) {
53+ errMsg = t ( 'minimum8Characters' , 'Minimum 8 characters' ) ;
54+ } else if ( passwordInput . oldPassword . length > 0 && passwordInput . newPassword === passwordInput . oldPassword ) {
55+ errMsg = t ( 'newPasswordMustNotBeTheSameAsOld' , 'New password must not be the same as password' ) ;
56+ } else {
57+ errMsg = '' ;
58+ setIsNewPasswordInvalid ( false ) ;
59+ }
60+ setNewPasswordErr ( errMsg ) ;
61+ } else if (
62+ passwordInputFieldName === 'confirmPassword' ||
63+ ( passwordInputFieldName === 'newPassword' && passwordInput . confirmPassword . length > 0 )
64+ ) {
65+ if ( passwordInput . confirmPassword !== passwordInput . newPassword ) {
66+ setConfirmPasswordError ( t ( 'confirmPasswordMustBeTheSameAsNew' , 'Confirm password must be the same as new' ) ) ;
67+ } else {
68+ setConfirmPasswordError ( '' ) ;
69+ setIsConfirmPasswordInvalid ( false ) ;
70+ }
71+ } else {
72+ if ( passwordInput . newPassword . length > 0 && passwordInput . newPassword === passwordInput . oldPassword ) {
73+ setOldPasswordErr ( t ( 'oldPasswordMustNotBeTheSameAsNew' , 'Old password must not be the same as new' ) ) ;
74+ } else {
75+ setOldPasswordErr ( '' ) ;
76+ setIsOldPasswordInvalid ( false ) ;
77+ }
78+ }
79+ } ,
80+ [ passwordInput . confirmPassword , passwordInput . newPassword , passwordInput . oldPassword , t ] ,
81+ ) ;
82+
3383 useEffect ( ( ) => {
3484 if ( passwordInput . oldPassword !== '' ) {
3585 handleValidation ( passwordInput . oldPassword , 'oldPassword' ) ;
@@ -40,7 +90,7 @@ export default function ChangePasswordModal({ close }: ChangePasswordModalProps)
4090 if ( passwordInput . confirmPassword !== '' ) {
4191 handleValidation ( passwordInput . confirmPassword , 'confirmPassword' ) ;
4292 }
43- } , [ passwordInput ] ) ;
93+ } , [ handleValidation , passwordInput ] ) ;
4494
4595 const handlePasswordChange = ( event ) => {
4696 const passwordInputValue = event . target . value . trim ( ) ;
@@ -49,77 +99,30 @@ export default function ChangePasswordModal({ close }: ChangePasswordModalProps)
4999 setPasswordInput ( NewPasswordInput ) ;
50100 } ;
51101
52- const handleValidation = ( passwordInputValue , passwordInputFieldName ) => {
53- if ( passwordInputFieldName === 'newPassword' ) {
54- const uppercaseRegExp = / (? = .* ?[ A - Z ] ) / ;
55- const lowercaseRegExp = / (? = .* ?[ a - z ] ) / ;
56- const digitsRegExp = / (? = .* ?[ 0 - 9 ] ) / ;
57- const minLengthRegExp = / .{ 8 , } / ;
58- const passwordLength = passwordInputValue . length ;
59- const uppercasePassword = uppercaseRegExp . test ( passwordInputValue ) ;
60- const lowercasePassword = lowercaseRegExp . test ( passwordInputValue ) ;
61- const digitsPassword = digitsRegExp . test ( passwordInputValue ) ;
62- const minLengthPassword = minLengthRegExp . test ( passwordInputValue ) ;
63- let errMsg = '' ;
64- if ( passwordLength === 0 ) {
65- errMsg = t ( 'passwordIsEmpty' , 'Password is empty' ) ;
66- } else if ( ! uppercasePassword ) {
67- errMsg = t ( 'atLeastOneUppercase' , 'At least one Uppercase' ) ;
68- } else if ( ! lowercasePassword ) {
69- errMsg = t ( 'atLeastOneLowercase' , 'At least one Lowercase' ) ;
70- } else if ( ! digitsPassword ) {
71- errMsg = t ( 'atLeastOneDigit' , 'At least one digit' ) ;
72- } else if ( ! minLengthPassword ) {
73- errMsg = t ( 'minimum8Characters' , 'Minimum 8 characters' ) ;
74- } else if ( passwordInput . oldPassword . length > 0 && passwordInput . newPassword === passwordInput . oldPassword ) {
75- errMsg = t ( 'newPasswordMustNotBeTheSameAsOld' , 'New password must not be the same as password' ) ;
76- } else {
77- errMsg = '' ;
78- setIsNewPasswordInvalid ( false ) ;
79- }
80- setNewPasswordErr ( errMsg ) ;
81- } else if (
82- passwordInputFieldName === 'confirmPassword' ||
83- ( passwordInputFieldName === 'newPassword' && passwordInput . confirmPassword . length > 0 )
84- ) {
85- if ( passwordInput . confirmPassword !== passwordInput . newPassword ) {
86- setConfirmPasswordError ( t ( 'confirmPasswordMustBeTheSameAsNew' , 'Confirm password must be the same as new' ) ) ;
87- } else {
88- setConfirmPasswordError ( '' ) ;
89- setIsConfirmPasswordInvalid ( false ) ;
90- }
91- } else {
92- if ( passwordInput . newPassword . length > 0 && passwordInput . newPassword === passwordInput . oldPassword ) {
93- setOldPasswordErr ( t ( 'oldPasswordMustNotBeTheSameAsNew' , 'Old password must not be the same as new' ) ) ;
94- } else {
95- setOldPasswordErr ( '' ) ;
96- setIsOldPasswordInvalid ( false ) ;
97- }
98- }
99- } ;
100-
101102 const handleSubmit = useCallback (
102103 async ( evt : React . FormEvent < HTMLFormElement > ) => {
103104 evt . preventDefault ( ) ;
104- evt . stopPropagation ( ) ;
105-
106- try {
107- setIsSavingPassword ( true ) ;
108- await performPasswordChange ( passwordInput . oldPassword , passwordInput . confirmPassword ) . then ( ( ) => {
105+ setIsSavingPassword ( true ) ;
106+ performPasswordChange ( passwordInput . oldPassword , passwordInput . confirmPassword )
107+ . then ( ( ) => {
109108 close ( ) ;
110109 navigate ( { to : `\${openmrsSpaBase}/logout` } ) ;
111- showToast ( {
112- title : t ( 'userPassword' , 'User password' ) ,
113- description : t ( 'userPasswordUpdated' , 'User password updated successfully' ) ,
110+ showSnackbar ( {
111+ isLowContrast : true ,
114112 kind : 'success' ,
113+ subtitle : t ( 'userPasswordUpdated' , 'User password updated successfully' ) ,
114+ title : t ( 'userPassword' , 'User password' ) ,
115+ } ) ;
116+ } )
117+ . catch ( ( error ) => {
118+ setIsSavingPassword ( false ) ;
119+ showSnackbar ( {
120+ title : t ( 'invalidPasswordCredentials' , 'Invalid password provided' ) ,
121+ kind : 'error' ,
122+ isLowContrast : false ,
123+ subtitle : error ?. message ,
115124 } ) ;
116125 } ) ;
117- } catch ( error ) {
118- setIsSavingPassword ( false ) ;
119- setErrorMessage ( `${ t ( 'invalidPasswordCredentials' , 'Invalid password provided' ) } : ${ error . message } ` ) ;
120- }
121-
122- return false ;
123126 } ,
124127 [ close , passwordInput . confirmPassword , passwordInput . oldPassword , t ] ,
125128 ) ;
@@ -128,23 +131,10 @@ export default function ChangePasswordModal({ close }: ChangePasswordModalProps)
128131 < >
129132 < ModalHeader closeModal = { close } title = { t ( 'changePassword' , 'Change Password' ) } />
130133 < ModalBody >
131- < div className = { styles [ 'input-group' ] } >
132- { errorMessage && (
133- < InlineNotification
134- className = { styles . errorMessage }
135- kind = "error"
136- /**
137- * This comment tells i18n to still keep the following translation keys (used as value for: errorMessage):
138- * t('invalidPasswordCredentials')
139- */
140- subtitle = { t ( errorMessage ) }
141- title = { t ( 'error' , 'Error' ) }
142- onClick = { ( ) => setErrorMessage ( '' ) }
143- />
144- ) }
145- < Tile >
146- < form onSubmit = { handleSubmit } ref = { formRef } >
147- < div className = { styles [ 'input-group' ] } >
134+ < Tile >
135+ < Form onSubmit = { handleSubmit } ref = { formRef } >
136+ < div className = { styles [ 'input-group' ] } >
137+ < Layer >
148138 < PasswordInput
149139 id = "oldPassword"
150140 invalid = { oldPasswordError . length > 0 }
@@ -157,6 +147,8 @@ export default function ChangePasswordModal({ close }: ChangePasswordModalProps)
157147 required
158148 showPasswordLabel = "Show old password"
159149 />
150+ </ Layer >
151+ < Layer >
160152 < PasswordInput
161153 id = "newPassword"
162154 invalid = { newPasswordError . length > 0 }
@@ -169,6 +161,8 @@ export default function ChangePasswordModal({ close }: ChangePasswordModalProps)
169161 required
170162 showPasswordLabel = "Show new password"
171163 />
164+ </ Layer >
165+ < Layer >
172166 < PasswordInput
173167 id = "confirmPassword"
174168 invalid = { confirmPasswordError . length > 0 }
@@ -181,10 +175,10 @@ export default function ChangePasswordModal({ close }: ChangePasswordModalProps)
181175 required
182176 showPasswordLabel = "Show confirm password"
183177 />
184- </ div >
185- </ form >
186- </ Tile >
187- </ div >
178+ </ Layer >
179+ </ div >
180+ </ Form >
181+ </ Tile >
188182 </ ModalBody >
189183 < ModalFooter >
190184 < Button kind = "secondary" onClick = { close } >
@@ -195,7 +189,7 @@ export default function ChangePasswordModal({ close }: ChangePasswordModalProps)
195189 onClick = { handleSubmit }
196190 disabled = { isSavingPassword || isNewPasswordInvalid || isConfirmPasswordInvalid || isOldPasswordInvalid }
197191 >
198- { t ( 'apply ' , 'Apply ' ) }
192+ { t ( 'updatePassword ' , 'Update Password ' ) }
199193 </ Button >
200194 </ ModalFooter >
201195 </ >
0 commit comments