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

Password management forms refactoring: Refactored `ChangePwdForm` (#4318)

* AuthN Forms: Changed hook forms subtree structure matching components tree.

* Password Mgmt Forms: Refactored ChangePwdForm.

* Forms: Added reset API.
	+ Updated the hook forms accordingly.
parent 2dea72cb
No related branches found
No related tags found
No related merge requests found
Showing
with 178 additions and 123 deletions
......@@ -452,6 +452,27 @@
"label": "Remember me"
}
}
},
"change_password": {
"fields": {
"old_password": {
"label": "Current Password",
"placeholder": "Enter your password"
},
"new_password": {
"label": "New Password",
"placeholder": "Enter your new password"
},
"password_confirmation": {
"label": "Confirm password",
"placeholder": "Confirm your new password"
}
},
"validations": {
"old_password": {
"required": "Please enter your current password"
}
}
}
},
"admin": {
......
......@@ -6,11 +6,11 @@ import FormControl from '../../../shared_components/forms/FormControl';
import Form from '../../../shared_components/forms/Form';
import Spinner from '../../../shared_components/utilities/Spinner';
import useAdminCreateUser from '../../../../hooks/mutations/admin/manage_users/useAdminCreateUser';
import useCreateUserForm from '../../../../hooks/forms/admin/create_user/useCreateUserForm';
import useUserSignupForm from '../../../../hooks/forms/admin/manage_users/useUserSignupForm';
export default function UserSignupForm({ handleClose }) {
const { t } = useTranslation();
const { fields, methods } = useCreateUserForm();
const { fields, methods } = useUserSignupForm();
const createUserAPI = useAdminCreateUser({ onSettled: handleClose });
return (
......
......@@ -10,7 +10,7 @@ import FormControl from '../../../shared_components/forms/FormControl';
import Form from '../../../shared_components/forms/Form';
import Spinner from '../../../shared_components/utilities/Spinner';
import useCreateSession from '../../../../hooks/mutations/sessions/useCreateSession';
import useSignInForm from '../../../../hooks/forms/authentication/useSignInForm';
import useSignInForm from '../../../../hooks/forms/users/authentication/useSignInForm';
import HCaptcha from '../../../shared_components/utilities/HCaptcha';
import FormCheckBox from '../../../shared_components/forms/controls/FormCheckBox';
......
......@@ -5,7 +5,7 @@ import FormControl from '../../../shared_components/forms/FormControl';
import Form from '../../../shared_components/forms/Form';
import Spinner from '../../../shared_components/utilities/Spinner';
import useCreateUser from '../../../../hooks/mutations/users/useCreateUser';
import useSignUpForm from '../../../../hooks/forms/authentication/useSignUpForm';
import useSignUpForm from '../../../../hooks/forms/users/authentication/useSignUpForm';
import HCaptcha from '../../../shared_components/utilities/HCaptcha';
export default function SignupForm() {
......
import React from 'react';
import { Button, Stack } from 'react-bootstrap';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import FormControl from '../../../shared_components/forms/FormControl';
import Form from '../../../shared_components/forms/Form';
import { changePwdFormConfig, changePwdFormFields } from '../../../../helpers/forms/ChangePwdFormHelpers';
import Spinner from '../../../shared_components/utilities/Spinner';
import useChangePwd from '../../../../hooks/mutations/users/useChangePwd';
import useChangePwdForm from '../../../../hooks/forms/users/password_management/useChangePwdForm';
export default function ChangePwdForm() {
const { t } = useTranslation();
const methods = useForm(changePwdFormConfig);
const fields = changePwdFormFields;
const changePwd = useChangePwd();
const { methods, fields, reset } = useChangePwdForm();
const changePwdAPI = useChangePwd();
return (
<Form
methods={methods}
onSubmit={changePwd.mutate}
>
<FormControl field={fields.old_password} type="password" />
<Form methods={methods} onSubmit={changePwdAPI.mutate}>
<FormControl field={fields.old_password} type="password" autoFocus />
<FormControl field={fields.new_password} type="password" />
<FormControl field={fields.password_confirmation} type="password" />
<Stack direction="horizontal" gap={2} className="float-end">
<Button
variant="neutral"
onClick={() => methods.reset({
old_password: '',
new_password: '',
password_confirmation: '',
})}
>
{ t('cancel') }
</Button>
<Button variant="brand" type="submit" disabled={changePwd.isLoading}>
{changePwd.isLoading && <Spinner className="me-2" />}
<Button variant="neutral" onClick={reset}> { t('cancel') } </Button>
<Button variant="brand" type="submit" disabled={changePwdAPI.isLoading}>
{changePwdAPI.isLoading && <Spinner className="me-2" />}
{ t('user.account.change_password') }
</Button>
</Stack>
......
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
const validationSchema = yup.object({
// TODO: amir - Revisit validations.
old_password: yup.string().required('Please enter your current password.'),
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 changePwdFormConfig = {
mode: 'onChange',
criteriaMode: 'all',
defaultValues: {
old_password: '',
new_password: '',
password_confirmation: '',
},
resolver: yupResolver(validationSchema),
};
export const changePwdFormFields = {
old_password: {
label: 'Current Password',
placeHolder: 'Enter your password',
controlId: 'changePwdFormOldPwd',
hookForm: {
id: 'old_password',
},
},
new_password: {
label: 'New Password',
placeHolder: 'Enter your new password',
controlId: 'changePwdFormNewPwd',
hookForm: {
id: 'new_password',
validations: {
deps: ['password_confirmation'],
},
},
},
password_confirmation: {
label: 'Confirm password',
placeHolder: 'Confirm your new password',
controlId: 'changePwdFormPwdConf',
hookForm: {
id: 'password_confirmation',
validations: {
deps: ['new_password'],
},
},
},
};
......@@ -2,9 +2,9 @@ import { useMemo } from 'react';
import { yupResolver } from '@hookform/resolvers/yup';
import { useTranslation } from 'react-i18next';
import { useForm } from 'react-hook-form';
import { useSignUpFormValidation } from '../../authentication/useSignUpForm';
import { useSignUpFormValidation } from '../../users/authentication/useSignUpForm';
export default function useCreateUserForm(_config = {}) {
export default function useUserSignupForm({ defaultValues: _defaultValues, ..._config } = {}) {
const { t, i18n } = useTranslation();
const fields = useMemo(() => ({
......@@ -51,16 +51,22 @@ export default function useCreateUserForm(_config = {}) {
const validationSchema = useSignUpFormValidation();
const config = useMemo(() => ({
...{
mode: 'onChange',
criteriaMode: 'all',
defaultValues: {
...{
name: '',
email: '',
password: '',
password_confirmation: '',
},
..._defaultValues,
},
resolver: yupResolver(validationSchema),
}), [validationSchema]);
},
..._config,
}), [validationSchema, _defaultValues]);
return { methods: useForm({ ...config, ..._config }), fields };
}
......@@ -2,7 +2,7 @@ import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { useTranslation } from 'react-i18next';
import { useForm } from 'react-hook-form';
import { useMemo } from 'react';
import { useCallback, useMemo } from 'react';
export function useSignInFormValidation() {
return useMemo(() => (yup.object({
......@@ -12,7 +12,7 @@ export function useSignInFormValidation() {
})), []);
}
export default function useSignInForm(_config = {}) {
export default function useSignInForm({ defaultValues: _defaultValues, ..._config } = {}) {
const { t, i18n } = useTranslation();
const fields = useMemo(() => ({
......@@ -44,15 +44,25 @@ export default function useSignInForm(_config = {}) {
const validationSchema = useSignInFormValidation();
const config = useMemo(() => ({
...{
mode: 'onSubmit',
criteriaMode: 'firstError',
defaultValues: {
...{
email: '',
password: '',
extend_session: false,
},
..._defaultValues,
},
resolver: yupResolver(validationSchema),
}), [validationSchema]);
},
..._config,
}), [validationSchema, _defaultValues]);
const methods = useForm(config);
const reset = useCallback(() => methods.reset(config.defaultValues), [methods.reset, config.defaultValues]);
return { methods: useForm({ ...config, ..._config }), fields };
return { methods, fields, reset };
}
......@@ -2,7 +2,7 @@ import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { useTranslation } from 'react-i18next';
import { useForm } from 'react-hook-form';
import { useMemo } from 'react';
import { useCallback, useMemo } from 'react';
export function useSignUpFormValidation() {
return useMemo(() => (yup.object({
......@@ -29,7 +29,7 @@ export function useSignUpFormValidation() {
})), []);
}
export default function useSignUpForm(_config = {}) {
export default function useSignUpForm({ defaultValues: _defaultValues, ..._config } = {}) {
const { t, i18n } = useTranslation();
const fields = useMemo(() => ({
......@@ -76,16 +76,26 @@ export default function useSignUpForm(_config = {}) {
const validationSchema = useSignUpFormValidation();
const config = useMemo(() => ({
...{
mode: 'onChange',
criteriaMode: 'all',
defaultValues: {
...{
name: '',
email: '',
password: '',
password_confirmation: '',
},
..._defaultValues,
},
resolver: yupResolver(validationSchema),
}), [validationSchema]);
},
..._config,
}), [validationSchema, _defaultValues]);
const methods = useForm(config);
const reset = useCallback(() => methods.reset(config.defaultValues), [methods.reset, config.defaultValues]);
return { methods: useForm({ ...config, ..._config }), fields };
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 useChangePwdFormValidation() {
return useMemo(() => (yup.object({
old_password: yup.string().required('forms.user.change_password.validations.old_password.required'),
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 useChangePwdForm({ defaultValues: _defaultValues, ..._config } = {}) {
const { t, i18n } = useTranslation();
const fields = useMemo(() => ({
old_password: {
label: t('forms.user.change_password.fields.old_password.label'),
placeHolder: t('forms.user.change_password.fields.old_password.placeholder'),
controlId: 'changePwdFormOldPwd',
hookForm: {
id: 'old_password',
},
},
new_password: {
label: t('forms.user.change_password.fields.new_password.label'),
placeHolder: t('forms.user.change_password.fields.new_password.placeholder'),
controlId: 'changePwdFormNewPwd',
hookForm: {
id: 'new_password',
validations: {
deps: ['password_confirmation'],
},
},
},
password_confirmation: {
label: t('forms.user.change_password.fields.password_confirmation.label'),
placeHolder: t('forms.user.change_password.fields.password_confirmation.placeholder'),
controlId: 'changePwdFormPwdConf',
hookForm: {
id: 'password_confirmation',
validations: {
deps: ['new_password'],
},
},
},
}), [i18n.resolvedLanguage]);
const validationSchema = useChangePwdFormValidation();
const config = useMemo(() => ({
...{
mode: 'onChange',
criteriaMode: 'all',
defaultValues: {
...{
old_password: '',
new_password: '',
password_confirmation: '',
},
..._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 };
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment