From 2494a98a57dd3ac4dcbc1cc6baacdabc3572043c Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 21 Apr 2023 13:41:51 +0200 Subject: [PATCH] change(ui) - password validations --- .../Client/ProfileSettings/ChangePassword.js | 121 ------------- .../Client/ProfileSettings/ChangePassword.tsx | 171 ++++++++++++++++++ 2 files changed, 171 insertions(+), 121 deletions(-) delete mode 100644 frontend/app/components/Client/ProfileSettings/ChangePassword.js create mode 100644 frontend/app/components/Client/ProfileSettings/ChangePassword.tsx diff --git a/frontend/app/components/Client/ProfileSettings/ChangePassword.js b/frontend/app/components/Client/ProfileSettings/ChangePassword.js deleted file mode 100644 index 2640ab612..000000000 --- a/frontend/app/components/Client/ProfileSettings/ChangePassword.js +++ /dev/null @@ -1,121 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { Button, Message, Form, Input } from 'UI'; -import styles from './profileSettings.module.css'; -import { updatePassword } from 'Duck/user'; -import { toast } from 'react-toastify'; - -const ERROR_DOESNT_MATCH = "Passwords doesn't match"; -const MIN_LENGTH = 8; -const PASSWORD_POLICY = `Password should contain at least ${MIN_LENGTH} symbols`; -const checkDoesntMatch = (newPassword, newPasswordRepeat) => newPasswordRepeat.length > 0 && newPasswordRepeat !== newPassword; - -const defaultState = { - oldPassword: '', - newPassword: '', - newPasswordRepeat: '', - success: false, - show: false, -}; -@connect( - (state) => ({ - passwordErrors: state.getIn(['user', 'passwordErrors']), - loading: state.getIn(['user', 'updatePasswordRequest', 'loading']), - }), - { - updatePassword, - } -) -export default class ChangePassword extends React.PureComponent { - state = defaultState; - - write = ({ target: { name, value } }) => { - this.setState({ - [name]: value, - }); - }; - - handleSubmit = (e) => { - e.preventDefault(); - if (this.isSubmitDisabled()) return; - - const { oldPassword, newPassword } = this.state; - this.setState({ - success: false, - }); - - this.props - .updatePassword({ - oldPassword, - newPassword, - }) - .then((e) => { - const success = !e || !e.errors || e.errors.length === 0; - this.setState({ - ...defaultState, - success: success, - show: !success - }); - if (success) { - toast.success(`Successfully changed password`); - } - }); - }; - - isSubmitDisabled() { - const { oldPassword, newPassword, newPasswordRepeat } = this.state; - if (newPassword !== newPasswordRepeat || newPassword.length < MIN_LENGTH || oldPassword.length < MIN_LENGTH) return true; - return false; - } - - render() { - const { oldPassword, newPassword, newPasswordRepeat, success, show } = this.state; - const { loading, passwordErrors } = this.props; - - const doesntMatch = checkDoesntMatch(newPassword, newPasswordRepeat); - return show ? ( -
- - - - - - - -
{PASSWORD_POLICY}
-
- - - - - {passwordErrors.map((err) => ( - {err} - ))} - -
- - - -
-
- ) : ( -
this.setState({ show: true })}> - -
- ); - } -} diff --git a/frontend/app/components/Client/ProfileSettings/ChangePassword.tsx b/frontend/app/components/Client/ProfileSettings/ChangePassword.tsx new file mode 100644 index 000000000..c299cc5b1 --- /dev/null +++ b/frontend/app/components/Client/ProfileSettings/ChangePassword.tsx @@ -0,0 +1,171 @@ +import React, { useState, useCallback } from 'react'; +import { connect, ConnectedProps } from 'react-redux'; +import { Button, Message, Form, Input } from 'UI'; +import styles from './profileSettings.module.css'; +import { updatePassword } from 'Duck/user'; +import { toast } from 'react-toastify'; + +const ERROR_DOESNT_MATCH = "Passwords don't match"; +const MIN_LENGTH = 8; +const PASSWORD_POLICY = `Password should contain at least ${MIN_LENGTH} symbols`; + +type PropsFromRedux = ConnectedProps; + +const ChangePassword: React.FC = ({ passwordErrors, loading, updatePassword }) => { + const [oldPassword, setOldPassword] = useState(''); + const [newPassword, setNewPassword] = useState<{ value: string; error: boolean }>({ + value: '', + error: false, + }); + const [newPasswordRepeat, setNewPasswordRepeat] = useState<{ value: string; error: boolean }>({ + value: '', + error: false, + }); + const [success, setSuccess] = useState(false); + const [show, setShow] = useState(false); + + const checkDoesntMatch = useCallback((newPassword: string, newPasswordRepeat: string) => { + return newPasswordRepeat.length > 0 && newPasswordRepeat !== newPassword; + }, []); + + const isSubmitDisabled = useCallback(() => { + if ( + newPassword.value !== newPasswordRepeat.value || + newPassword.value.length < MIN_LENGTH || + oldPassword.length === 0 + ) { + return true; + } + return false; + }, [newPassword, newPasswordRepeat, oldPassword]); + + const validatePassword = (password: string) => { + const regex = + /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?])[A-Za-z\d!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]{8,}$/; + return regex.test(password); + }; + + const handleSubmit = useCallback( + (e: React.FormEvent) => { + e.preventDefault(); + if (isSubmitDisabled()) return; + + if (!validatePassword(newPassword.value)) { + setNewPassword({ ...newPassword, error: true }); + return; + } + + updatePassword({ + oldPassword, + newPassword: newPassword.value, + }).then((e: any) => { + const success = !e || !e.errors || e.errors.length === 0; + setSuccess(success); + setShow(!success); + if (success) { + toast.success(`Successfully changed password`); + setOldPassword(''); + setNewPassword({ value: '', error: false }); + setNewPasswordRepeat({ value: '', error: false }); + } + }); + }, + [isSubmitDisabled, oldPassword, newPassword, updatePassword] + ); + + return show ? ( +
+ + + ) => setOldPassword(e.target.value)} + /> + + + + ) => { + const newValue = e.target.value; + const isValid = validatePassword(newValue); + setNewPassword({ value: newValue, error: !isValid }); + }} + /> + + + + ) => { + const newValue = e.target.value; + const isValid = newValue === newPassword.value; + setNewPasswordRepeat({ value: newValue, error: !isValid }); + }} + /> + + {passwordErrors.map((err, i) => ( + + {err} + + ))} + + + {/* */} +
+ + +
+
+ ) : ( +
setShow(true)}> + +
+ ); +}; + +const mapStateToProps = (state: any) => ({ + passwordErrors: state.getIn(['user', 'passwordErrors']), + loading: state.getIn(['user', 'updatePasswordRequest', 'loading']), +}); + +const mapDispatchToProps = { + updatePassword, +}; + +const connector = connect(mapStateToProps, mapDispatchToProps); + +export default connector(ChangePassword);