diff --git a/packages/web/src/components/Form/index.jsx b/packages/web/src/components/Form/index.jsx index 614873f7..352b0a69 100644 --- a/packages/web/src/components/Form/index.jsx +++ b/packages/web/src/components/Form/index.jsx @@ -46,7 +46,12 @@ function Form(props) { return ( -
+ + onSubmit?.(data, event, methods.setError), + )} + {...formProps} + > {render ? render(methods) : children}
diff --git a/packages/web/src/components/InstallationForm/index.jsx b/packages/web/src/components/InstallationForm/index.jsx index 80d66b6c..6e443302 100644 --- a/packages/web/src/components/InstallationForm/index.jsx +++ b/packages/web/src/components/InstallationForm/index.jsx @@ -2,35 +2,55 @@ import * as React from 'react'; import { Link as RouterLink } from 'react-router-dom'; import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; -import { Alert } from '@mui/material'; +import Alert from '@mui/material/Alert'; import LoadingButton from '@mui/lab/LoadingButton'; import * as yup from 'yup'; import { yupResolver } from '@hookform/resolvers/yup'; -import { enqueueSnackbar } from 'notistack'; import { useQueryClient } from '@tanstack/react-query'; import Link from '@mui/material/Link'; +import { getGeneralErrorMessage } from 'helpers/errors'; import useFormatMessage from 'hooks/useFormatMessage'; import useInstallation from 'hooks/useInstallation'; import * as URLS from 'config/urls'; import Form from 'components/Form'; import TextField from 'components/TextField'; -const validationSchema = yup.object().shape({ - fullName: yup.string().trim().required('installationForm.mandatoryInput'), - email: yup - .string() - .trim() - .email('installationForm.validateEmail') - .required('installationForm.mandatoryInput'), - password: yup.string().required('installationForm.mandatoryInput'), - confirmPassword: yup - .string() - .required('installationForm.mandatoryInput') - .oneOf([yup.ref('password')], 'installationForm.passwordsMustMatch'), -}); +const getValidationSchema = (formatMessage) => { + const getMandatoryInputMessage = (inputNameId) => + formatMessage('installationForm.mandatoryInput', { + inputName: formatMessage(inputNameId), + }); -const initialValues = { + return yup.object().shape({ + fullName: yup + .string() + .trim() + .required( + getMandatoryInputMessage('installationForm.fullNameFieldLabel'), + ), + email: yup + .string() + .trim() + .required(getMandatoryInputMessage('installationForm.emailFieldLabel')) + .email(formatMessage('installationForm.validateEmail')), + password: yup + .string() + .required(getMandatoryInputMessage('installationForm.passwordFieldLabel')) + .min(6, formatMessage('installationForm.passwordMinLength')), + confirmPassword: yup + .string() + .required( + getMandatoryInputMessage('installationForm.confirmPasswordFieldLabel'), + ) + .oneOf( + [yup.ref('password')], + formatMessage('installationForm.passwordsMustMatch'), + ), + }); +}; + +const defaultValues = { fullName: '', email: '', password: '', @@ -39,7 +59,7 @@ const initialValues = { function InstallationForm() { const formatMessage = useFormatMessage(); - const install = useInstallation(); + const { mutateAsync: install, isSuccess, isPending } = useInstallation(); const queryClient = useQueryClient(); const handleOnRedirect = () => { @@ -48,21 +68,38 @@ function InstallationForm() { }); }; - const handleSubmit = async (values) => { - const { fullName, email, password } = values; + const handleSubmit = async ({ fullName, email, password }, e, setError) => { try { - await install.mutateAsync({ + await install({ fullName, email, password, }); } catch (error) { - enqueueSnackbar( - error?.message || formatMessage('installationForm.error'), - { - variant: 'error', - }, - ); + const errors = error?.response?.data?.errors; + if (errors) { + const fieldNames = Object.keys(defaultValues); + Object.entries(errors).forEach(([fieldName, fieldErrors]) => { + if (fieldNames.includes(fieldName) && Array.isArray(fieldErrors)) { + setError(fieldName, { + type: 'fieldRequestError', + message: fieldErrors.join(', '), + }); + } + }); + } + + const generalError = getGeneralErrorMessage({ + error, + fallbackMessage: formatMessage('installationForm.error'), + }); + + if (generalError) { + setError('root.general', { + type: 'requestError', + message: generalError, + }); + } } }; @@ -82,11 +119,13 @@ function InstallationForm() { {formatMessage('installationForm.title')}
( + render={({ formState: { errors } }) => ( <> + + + + {errors?.root?.general && ( + + {errors.root.general.message} + + )} + + {isSuccess && ( + + {formatMessage('installationForm.success', { + link: (str) => ( + + {str} + + ), + })} + + )} {formatMessage('installationForm.submit')} )} /> - {install.isSuccess && ( - - {formatMessage('installationForm.success', { - link: (str) => ( - - {str} - - ), - })} - - )} ); } diff --git a/packages/web/src/components/SignUpForm/index.ee.jsx b/packages/web/src/components/SignUpForm/index.ee.jsx index e931d394..3cbc1033 100644 --- a/packages/web/src/components/SignUpForm/index.ee.jsx +++ b/packages/web/src/components/SignUpForm/index.ee.jsx @@ -3,33 +3,52 @@ import { useNavigate } from 'react-router-dom'; import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; import LoadingButton from '@mui/lab/LoadingButton'; +import Alert from '@mui/material/Alert'; import * as yup from 'yup'; import { yupResolver } from '@hookform/resolvers/yup'; +import { getGeneralErrorMessage } from 'helpers/errors'; import useAuthentication from 'hooks/useAuthentication'; import * as URLS from 'config/urls'; import Form from 'components/Form'; import TextField from 'components/TextField'; import useFormatMessage from 'hooks/useFormatMessage'; import useCreateAccessToken from 'hooks/useCreateAccessToken'; -import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar'; import useRegisterUser from 'hooks/useRegisterUser'; -const validationSchema = yup.object().shape({ - fullName: yup.string().trim().required('signupForm.mandatoryInput'), - email: yup - .string() - .trim() - .email('signupForm.validateEmail') - .required('signupForm.mandatoryInput'), - password: yup.string().required('signupForm.mandatoryInput'), - confirmPassword: yup - .string() - .required('signupForm.mandatoryInput') - .oneOf([yup.ref('password')], 'signupForm.passwordsMustMatch'), -}); +const getValidationSchema = (formatMessage) => { + const getMandatoryInputMessage = (inputNameId) => + formatMessage('signupForm.mandatoryInput', { + inputName: formatMessage(inputNameId), + }); -const initialValues = { + return yup.object().shape({ + fullName: yup + .string() + .trim() + .required(getMandatoryInputMessage('signupForm.fullNameFieldLabel')), + email: yup + .string() + .trim() + .required(getMandatoryInputMessage('signupForm.emailFieldLabel')) + .email(formatMessage('signupForm.validateEmail')), + password: yup + .string() + .required(getMandatoryInputMessage('signupForm.passwordFieldLabel')) + .min(6, formatMessage('signupForm.passwordMinLength')), + confirmPassword: yup + .string() + .required( + getMandatoryInputMessage('signupForm.confirmPasswordFieldLabel'), + ) + .oneOf( + [yup.ref('password')], + formatMessage('signupForm.passwordsMustMatch'), + ), + }); +}; + +const defaultValues = { fullName: '', email: '', password: '', @@ -40,7 +59,6 @@ function SignUpForm() { const navigate = useNavigate(); const authentication = useAuthentication(); const formatMessage = useFormatMessage(); - const enqueueSnackbar = useEnqueueSnackbar(); const { mutateAsync: registerUser, isPending: isRegisterUserPending } = useRegisterUser(); const { mutateAsync: createAccessToken, isPending: loginLoading } = @@ -52,7 +70,7 @@ function SignUpForm() { } }, [authentication.isAuthenticated]); - const handleSubmit = async (values) => { + const handleSubmit = async (values, e, setError) => { try { const { fullName, email, password } = values; await registerUser({ @@ -67,25 +85,28 @@ function SignUpForm() { const { token } = data; authentication.updateToken(token); } catch (error) { - const errors = error?.response?.data?.errors - ? Object.values(error.response.data.errors) - : []; + const errors = error?.response?.data?.errors; + if (errors) { + const fieldNames = Object.keys(defaultValues); + Object.entries(errors).forEach(([fieldName, fieldErrors]) => { + if (fieldNames.includes(fieldName) && Array.isArray(fieldErrors)) { + setError(fieldName, { + type: 'fieldRequestError', + message: fieldErrors.join(', '), + }); + } + }); + } - if (errors.length) { - for (const [error] of errors) { - enqueueSnackbar(error, { - variant: 'error', - SnackbarProps: { - 'data-test': 'snackbar-sign-up-error', - }, - }); - } - } else { - enqueueSnackbar(error?.message || formatMessage('signupForm.error'), { - variant: 'error', - SnackbarProps: { - 'data-test': 'snackbar-sign-up-error', - }, + const generalError = getGeneralErrorMessage({ + error, + fallbackMessage: formatMessage('signupForm.error'), + }); + + if (generalError) { + setError('root.general', { + type: 'requestError', + message: generalError, }); } } @@ -108,11 +129,13 @@ function SignUpForm() { ( + render={({ formState: { errors } }) => ( <> + {errors?.root?.general && ( + + {errors.root.general.message} + + )} + { + if (!error) { + return; + } + + const errors = error?.response?.data?.errors; + const generalError = errors?.general; + + if (generalError && Array.isArray(generalError)) { + return generalError.join(' '); + } + + if (!errors) { + return error?.message || fallbackMessage; + } +}; diff --git a/packages/web/src/locales/en.json b/packages/web/src/locales/en.json index b121f5e2..941af1fa 100644 --- a/packages/web/src/locales/en.json +++ b/packages/web/src/locales/en.json @@ -138,6 +138,7 @@ "installationForm.submit": "Create admin", "installationForm.validateEmail": "Email must be valid.", "installationForm.passwordsMustMatch": "Passwords must match.", + "installationForm.passwordMinLength": "Password must be at least 6 characters long.", "installationForm.mandatoryInput": "{inputName} is required.", "installationForm.success": "The admin account has been created, and thus, the installation has been completed. You can now log in here.", "installationForm.error": "Something went wrong. Please try again.", @@ -149,6 +150,7 @@ "signupForm.submit": "Sign up", "signupForm.validateEmail": "Email must be valid.", "signupForm.passwordsMustMatch": "Passwords must match.", + "signupForm.passwordMinLength": "Password must be at least 6 characters long.", "signupForm.mandatoryInput": "{inputName} is required.", "signupForm.error": "Something went wrong. Please try again.", "loginForm.title": "Login",