Skip to content
Snippets Groups Projects
Unverified Commit b3d08821 authored by Khemissi Amir's avatar Khemissi Amir Committed by GitHub
Browse files

Password management forms refactoring: Refactored `ForgetPwdForm` & `ResetPwdForm`. (#4319)

* Password Mgmt Forms: Refactored ForgetPwdForm.

* Password Mgmt Forms: Refactored ResetPwdForm.
parent 26f0a7e8
No related branches found
No related tags found
No related merge requests found
...@@ -473,6 +473,31 @@ ...@@ -473,6 +473,31 @@
"required": "Please enter your current password" "required": "Please enter your current password"
} }
} }
},
"forget_password": {
"fields": {
"email": {
"label": "Email",
"placeholder": "Enter the account email"
}
},
"validations": {
"email": {
"required": "Please enter the account email"
}
}
},
"reset_password": {
"fields": {
"new_password": {
"label": "New Password",
"placeholder": "Enter your new password"
},
"password_confirmation": {
"label": "Confirm password",
"placeholder": "Confirm your new password"
}
}
} }
}, },
"admin": { "admin": {
......
import React from 'react'; import React from 'react';
import { Button, Stack } from 'react-bootstrap'; import { Button, Stack } from 'react-bootstrap';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import FormControl from '../../../shared_components/forms/FormControl'; import FormControl from '../../../shared_components/forms/FormControl';
import Form from '../../../shared_components/forms/Form'; import Form from '../../../shared_components/forms/Form';
import { forgetPwdFormConfig, forgetPwdFormFields } from '../../../../helpers/forms/ForgetPwdFormHelpers';
import Spinner from '../../../shared_components/utilities/Spinner'; import Spinner from '../../../shared_components/utilities/Spinner';
import useCreateResetPwd from '../../../../hooks/mutations/users/useCreateResetPwd'; import useCreateResetPwd from '../../../../hooks/mutations/users/useCreateResetPwd';
import useForgetPwdForm from '../../../../hooks/forms/users/password_management/useForgetPwdForm';
export default function ForgetPwdForm() { export default function ForgetPwdForm() {
const { t } = useTranslation(); const { t } = useTranslation();
const createResetPwd = useCreateResetPwd(); const createResetPwdAPI = useCreateResetPwd();
const methods = useForm(forgetPwdFormConfig); const { methods, fields } = useForgetPwdForm();
const { isSubmitting } = methods.formState;
const fields = forgetPwdFormFields;
return ( return (
<Form methods={methods} onSubmit={createResetPwd.mutate}> <Form methods={methods} onSubmit={createResetPwdAPI.mutate}>
<FormControl field={fields.email} type="email" /> <FormControl field={fields.email} type="email" autoFocus />
<Stack className="mt-1" gap={1}> <Stack className="mt-1" gap={1}>
<Button variant="brand" className="w-100 mb- mt-1" type="submit" disabled={isSubmitting}> <Button variant="brand" className="w-100 mb- mt-1" type="submit" disabled={createResetPwdAPI.isLoading}>
{isSubmitting && <Spinner className="me-2" />} {createResetPwdAPI.isLoading && <Spinner className="me-2" />}
{ t('user.account.reset_password') } { t('user.account.reset_password') }
</Button> </Button>
</Stack> </Stack>
......
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Button, Stack } from 'react-bootstrap'; import { Button, Stack } from 'react-bootstrap';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import FormControl from '../../../shared_components/forms/FormControl'; import FormControl from '../../../shared_components/forms/FormControl';
import Form from '../../../shared_components/forms/Form'; import Form from '../../../shared_components/forms/Form';
import { resetPwdFormConfig, resetPwdFormFields } from '../../../../helpers/forms/ResetPwdFormHelpers';
import Spinner from '../../../shared_components/utilities/Spinner'; import Spinner from '../../../shared_components/utilities/Spinner';
import useResetPwd from '../../../../hooks/mutations/users/useResetPwd'; import useResetPwd from '../../../../hooks/mutations/users/useResetPwd';
import useResetPwdForm from '../../../../hooks/forms/users/password_management/useResetPwdForm';
export default function ResetPwdForm({ token }) { export default function ResetPwdForm({ token }) {
const { t } = useTranslation(); const { t } = useTranslation();
const resetPwd = useResetPwd(); const resetPwdAPI = useResetPwd();
resetPwdFormConfig.defaultValues.token = token; const { methods, fields } = useResetPwdForm({ defaultValues: { token } });
const methods = useForm(resetPwdFormConfig);
const fields = resetPwdFormFields;
return ( return (
<Form methods={methods} onSubmit={resetPwd.mutate}> <Form methods={methods} onSubmit={resetPwdAPI.mutate}>
<FormControl field={fields.new_password} type="password" /> <FormControl field={fields.new_password} type="password" autoFocus />
<FormControl field={fields.password_confirmation} type="password" /> <FormControl field={fields.password_confirmation} type="password" />
<Stack className="mt-1" gap={1}> <Stack className="mt-1" gap={1}>
<Button variant="brand" className="w-100 mb- mt-1" type="submit" disabled={resetPwd.isLoading}> <Button variant="brand" className="w-100 mb- mt-1" type="submit" disabled={resetPwdAPI.isLoading}>
{resetPwd.isLoading && <Spinner className="me-2" />} {resetPwdAPI.isLoading && <Spinner className="me-2" />}
{ t('user.account.change_password') } { t('user.account.change_password') }
</Button> </Button>
</Stack> </Stack>
......
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
const validationSchema = yup.object({
// TODO: amir - Revisit validations.
email: yup.string().required('Please enter the account email.').email('Entered value does not match email format.'),
});
export const forgetPwdFormConfig = {
mode: 'onSubmit',
criteriaMode: 'firstError',
defaultValues: {
email: '',
},
resolver: yupResolver(validationSchema),
};
export const forgetPwdFormFields = {
email: {
label: 'Email',
placeHolder: 'Enter the account email',
controlId: 'forgetPwdFormEmail',
hookForm: {
id: 'email',
},
},
};
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
const validationSchema = yup.object({
// TODO: amir - Revisit validations.
new_password: yup.string()
.matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[`@%~!#£$\\^&*()\][+={}/|:;"'<>\-,.?_ ]).{8,}$/,
'Password must have at least:',
)
.min(8, '- Eight characters.')
.test('oneLower', '- One lowercase letter.', (pwd) => pwd.match(/[a-z]/))
.test('oneUpper', '- One uppercase letter.', (pwd) => pwd.match(/[A-Z]/))
.test('oneDigit', '- One digit.', (pwd) => pwd.match(/\d/))
.test('oneSymbol', '- One symbol.', (pwd) => pwd.match(/[`@%~!#£$\\^&*()\][+={}/|:;"'<>\-,.?_ ]/)),
password_confirmation: yup.string().required('').oneOf([yup.ref('new_password')], 'Your passwords do not match.'),
});
export const resetPwdFormConfig = {
mode: 'onChange',
criteriaMode: 'all',
defaultValues: {
new_password: '',
password_confirmation: '',
token: '',
},
resolver: yupResolver(validationSchema),
};
export const resetPwdFormFields = {
new_password: {
label: 'New Password',
placeHolder: 'Enter your new password',
controlId: 'resetPwdFormNewPwd',
hookForm: {
id: 'new_password',
validations: {
deps: ['password_confirmation'],
},
},
},
password_confirmation: {
label: 'Confirm Password',
placeHolder: 'Confirm password',
controlId: 'resetPwdFormPwdConfirm',
hookForm: {
id: 'password_confirmation',
validations: {
deps: ['new_password'],
},
},
},
};
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { useTranslation } from 'react-i18next';
import { useForm } from 'react-hook-form';
import { useCallback, useMemo } from 'react';
export function useForgetPwdFormValidation() {
return useMemo(() => (yup.object({
email: yup.string().required('forms.user.forget_password.validations.email.required').email('forms.validations.email.email'),
})), []);
}
export default function useForgetPwdForm({ defaultValues: _defaultValues, ..._config } = {}) {
const { t, i18n } = useTranslation();
const fields = useMemo(() => ({
email: {
label: t('forms.user.forget_password.fields.email.label'),
placeHolder: t('forms.user.forget_password.fields.email.placeholder'),
controlId: 'forgetPwdFormEmail',
hookForm: {
id: 'email',
},
},
}), [i18n.resolvedLanguage]);
const validationSchema = useForgetPwdFormValidation();
const config = useMemo(() => ({
...{
mode: 'onSubmit',
criteriaMode: 'firstError',
defaultValues: {
...{
email: '',
},
..._defaultValues,
},
resolver: yupResolver(validationSchema),
},
..._config,
}), [validationSchema, _defaultValues]);
const methods = useForm(config);
const reset = useCallback(() => methods.reset(config.defaultValues), [methods.reset, config.defaultValues]);
return { methods, fields, reset };
}
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { useTranslation } from 'react-i18next';
import { useForm } from 'react-hook-form';
import { useCallback, useMemo } from 'react';
export function useResetPwdFormValidation() {
return useMemo(() => (yup.object({
new_password: yup.string().max(255, 'forms.validations.password.max')
.matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[`@%~!#£$\\^&*()\][+={}/|:;"'<>\-,.?_ ]).{8,}$/,
'forms.validations.password.match',
)
.min(8, 'forms.validations.password.min')
.test('oneLower', 'forms.validations.password.lower', (pwd) => pwd.match(/[a-z]/))
.test('oneUpper', 'forms.validations.password.upper', (pwd) => pwd.match(/[A-Z]/))
.test('oneDigit', 'forms.validations.password.digit', (pwd) => pwd.match(/\d/))
.test('oneSymbol', 'forms.validations.password.symbol', (pwd) => pwd.match(/[`@%~!#£$\\^&*()\][+={}/|:;"'<>\-,.?_ ]/)),
password_confirmation: yup.string().required('forms.validations.password_confirmation.required')
.oneOf([yup.ref('new_password')], 'forms.validations.password_confirmation.match'),
})), []);
}
export default function useResetPwdForm({ defaultValues: _defaultValues, ..._config } = {}) {
const { t, i18n } = useTranslation();
const fields = useMemo(() => ({
new_password: {
label: t('forms.user.reset_password.fields.new_password.label'),
placeHolder: t('forms.user.reset_password.fields.new_password.placeholder'),
controlId: 'resetPwdFormNewPwd',
hookForm: {
id: 'new_password',
validations: {
deps: ['password_confirmation'],
},
},
},
password_confirmation: {
label: t('forms.user.reset_password.fields.password_confirmation.label'),
placeHolder: t('forms.user.reset_password.fields.password_confirmation.placeholder'),
controlId: 'resetPwdFormPwdConfirm',
hookForm: {
id: 'password_confirmation',
validations: {
deps: ['new_password'],
},
},
},
}), [i18n.resolvedLanguage]);
const validationSchema = useResetPwdFormValidation();
const config = useMemo(() => ({
...{
mode: 'onChange',
criteriaMode: 'all',
defaultValues: {
...{
new_password: '',
password_confirmation: '',
token: '',
},
..._defaultValues,
},
resolver: yupResolver(validationSchema),
},
..._config,
}), [validationSchema, _defaultValues]);
const methods = useForm(config);
const reset = useCallback(() => methods.reset(config.defaultValues), [methods.reset, config.defaultValues]);
return { methods, fields, reset };
}
...@@ -12,7 +12,7 @@ export default function useResetPwd() { ...@@ -12,7 +12,7 @@ export default function useResetPwd() {
(user) => axios.post('/reset_password/reset.json', { user }), (user) => axios.post('/reset_password/reset.json', { user }),
{ {
onSuccess: () => { onSuccess: () => {
toast.success(t('toast.success.password_updated')); toast.success(t('toast.success.user.password_updated'));
navigate('/signin'); navigate('/signin');
}, },
onError: () => { onError: () => {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment