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

Forms: Refactored `UserSignupForm`. (#4300)

parent 32dcb86e
No related branches found
No related tags found
No related merge requests found
......@@ -177,8 +177,6 @@
"create_room": "Create Room",
"create_new_room": "Create New Room",
"user_created_at": "Created: {{user.created_at}}",
"enter_user_email": "Enter an email",
"enter_user_name": "Enter a name",
"are_you_sure_delete_account": "Are you sure you want to delete {{user.name}}'s account?",
"delete_account_warning": "If you choose to delete this account, it will NOT be recoverable.",
"invited": {
......@@ -377,6 +375,32 @@
"resend_btn_lbl": "Resend Verification"
},
"forms": {
"validations": {
"full_name": {
"required": "Please enter a full name",
"min": "Name must be at least 2 characters long",
"max": "Name must be at most 255 characters long"
},
"email": {
"required": "Please enter an email",
"email": "Entered value does not match email format",
"min": "Email must be at least 6 characters long",
"max": "Email must be at most 255 characters long"
},
"password": {
"match": "Password must have at least:",
"min": "- Eight characters",
"lower": "- One lowercase letter",
"upper": "- One uppercase letter",
"digit": "- One digit",
"symbol": "- One symbol",
"max": "Password must be at most 255 characters long"
},
"password_confirmation": {
"required": "Please enter a password confirmation",
"match": "The passwords do not match"
}
},
"user": {
"signup": {
"fields": {
......@@ -396,31 +420,27 @@
"label": "Confirm Password",
"placeholder": "Confirm password"
}
}
}
},
"validations": {
"admin": {
"createUser": {
"fields": {
"full_name": {
"required": "Please enter a full name",
"min": "Name must be at least 2 characters long",
"max": "Name must be at most 255 characters long"
"label": "Full Name",
"placeholder": "Enter the user full name"
},
"email": {
"required": "Please enter an email",
"email": "Entered value does not match email format",
"min": "Email must be at least 6 characters long",
"max": "Email must be at most 255 characters long"
"label": "Email",
"placeholder": "Enter the user email"
},
"password": {
"match": "Password must have at least:",
"min": "- Eight characters",
"lower": "- One lowercase letter",
"upper": "- One uppercase letter",
"digit": "- One digit",
"symbol": "- One symbol",
"max": "Password must be at most 255 characters long"
"label": "Password",
"placeholder": "Enter the user password"
},
"password_confirmation": {
"required": "Please enter a password confirmation",
"match": "The passwords do not match"
"label": "Confirm Password",
"placeholder": "Confirm the password"
}
}
}
......
......@@ -6,20 +6,16 @@ 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 useSignUpForm from '../../../../hooks/forms/authentication/useSignUpForm';
import useCreateUserForm from '../../../../hooks/forms/admin/create_user/useCreateUserForm';
export default function UserSignupForm({ handleClose }) {
const { t } = useTranslation();
const { fields, methods } = useSignUpForm();
const createUser = useAdminCreateUser({ onSettled: handleClose });
const { isSubmitting } = methods.formState;
fields.name.placeHolder = t('admin.manage_users.enter_user_name');
fields.email.placeHolder = t('admin.manage_users.enter_user_email');
const { fields, methods } = useCreateUserForm();
const createUserAPI = useAdminCreateUser({ onSettled: handleClose });
return (
<Form methods={methods} onSubmit={createUser.mutate}>
<FormControl field={fields.name} type="text" />
<Form methods={methods} onSubmit={createUserAPI.mutate}>
<FormControl field={fields.name} type="text" autoFocus />
<FormControl field={fields.email} type="email" />
<FormControl field={fields.password} type="password" />
<FormControl field={fields.password_confirmation} type="password" />
......@@ -28,8 +24,8 @@ export default function UserSignupForm({ handleClose }) {
<Button variant="neutral" className="ms-auto" onClick={handleClose}>
{t('close')}
</Button>
<Button variant="brand" type="submit" disabled={isSubmitting}>
{ isSubmitting && <Spinner className="me-2" /> }
<Button variant="brand" type="submit" disabled={createUserAPI.isLoading}>
{ createUserAPI.isLoading && <Spinner className="me-2" /> }
{ t('admin.manage_users.create_account') }
</Button>
</Stack>
......
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';
export default function useCreateUserForm(_config = {}) {
const { t, i18n } = useTranslation();
const fields = useMemo(() => ({
name: {
label: t('forms.admin.createUser.fields.full_name.label'),
placeHolder: t('forms.admin.createUser.fields.full_name.placeholder'),
controlId: 'createUserFormFullName',
hookForm: {
id: 'name',
},
},
email: {
label: t('forms.admin.createUser.fields.email.label'),
placeHolder: t('forms.admin.createUser.fields.email.placeholder'),
controlId: 'createUserFormEmail',
hookForm: {
id: 'email',
},
},
password: {
label: t('forms.admin.createUser.fields.password.label'),
placeHolder: t('forms.admin.createUser.fields.password.placeholder'),
controlId: 'createUserFormPwd',
hookForm: {
id: 'password',
validations: {
deps: ['password_confirmation'],
},
},
},
password_confirmation: {
label: t('forms.admin.createUser.fields.password_confirmation.label'),
placeHolder: t('forms.admin.createUser.fields.password_confirmation.placeholder'),
controlId: 'createUserFormPwdConfirm',
hookForm: {
id: 'password_confirmation',
validations: {
deps: ['password'],
},
},
},
}), [i18n.resolvedLanguage]);
const validationSchema = useSignUpFormValidation();
const config = useMemo(() => ({
mode: 'onChange',
criteriaMode: 'all',
defaultValues: {
name: '',
email: '',
password: '',
password_confirmation: '',
},
resolver: yupResolver(validationSchema),
}), [validationSchema]);
return { methods: useForm({ ...config, ..._config }), fields };
}
......@@ -2,11 +2,37 @@ 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';
export function useSignUpFormValidation() {
return useMemo(() => (yup.object({
name: yup.string().required('forms.validations.full_name.required')
.min(2, 'forms.validations.full_name.min')
.max(255, 'forms.validations.full_name.max'),
email: yup.string().required('forms.validations.email.required').email('forms.validations.email.email')
.min(6, 'forms.validations.email.min')
.max(255, 'forms.validations.email.max'),
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('password')], 'forms.validations.password_confirmation.match'),
})), []);
}
export default function useSignUpForm(_config = {}) {
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const fields = {
const fields = useMemo(() => ({
name: {
label: t('forms.user.signup.fields.full_name.label'),
placeHolder: t('forms.user.signup.fields.full_name.placeholder'),
......@@ -45,32 +71,11 @@ export default function useSignUpForm(_config = {}) {
},
},
},
};
const validationSchema = yup.object({
name: yup.string().required('forms.user.signup.validations.full_name.required')
.min(2, 'forms.user.signup.validations.full_name.min')
.max(255, 'forms.user.signup.validations.full_name.max'),
email: yup.string().required('forms.user.signup.validations.email.required').email('forms.user.signup.validations.email.email')
.min(6, 'forms.user.signup.validations.email.min')
.max(255, 'forms.user.signup.validations.email.max'),
}), [i18n.resolvedLanguage]);
password: yup.string().max(255, 'forms.user.signup.validations.password.max')
.matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[`@%~!#£$\\^&*()\][+={}/|:;"'<>\-,.?_ ]).{8,}$/,
'forms.user.signup.validations.password.match',
)
.min(8, 'forms.user.signup.validations.password.min')
.test('oneLower', 'forms.user.signup.validations.password.lower', (pwd) => pwd.match(/[a-z]/))
.test('oneUpper', 'forms.user.signup.validations.password.upper', (pwd) => pwd.match(/[A-Z]/))
.test('oneDigit', 'forms.user.signup.validations.password.digit', (pwd) => pwd.match(/\d/))
.test('oneSymbol', 'forms.user.signup.validations.password.symbol', (pwd) => pwd.match(/[`@%~!#£$\\^&*()\][+={}/|:;"'<>\-,.?_ ]/)),
password_confirmation: yup.string().required('forms.user.signup.validations.password_confirmation.required')
.oneOf([yup.ref('password')], 'forms.user.signup.validations.password_confirmation.match'),
});
const validationSchema = useSignUpFormValidation();
const config = {
const config = useMemo(() => ({
mode: 'onChange',
criteriaMode: 'all',
defaultValues: {
......@@ -80,7 +85,7 @@ export default function useSignUpForm(_config = {}) {
password_confirmation: '',
},
resolver: yupResolver(validationSchema),
};
}), [validationSchema]);
return { methods: useForm({ ...config, ..._config }), fields };
}
......@@ -7,18 +7,9 @@ export default function useAdminCreateUser({ onSettled }) {
const { t } = useTranslation();
const queryClient = useQueryClient();
const addInferredLanguage = (data) => {
const options = data;
const language = window.navigator.userLanguage || window.navigator.language;
options.language = language.match(/^[a-z]{2,}/)?.at(0);
return options;
};
return useMutation(
(user) => axios.post('/users.json', { user }),
{
onMutate: addInferredLanguage,
onSuccess: () => {
queryClient.invalidateQueries('getAdminUsers');
toast.success(t('toast.success.user.user_created'));
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment