diff --git a/packages/web/src/components/Form/index.jsx b/packages/web/src/components/Form/index.jsx index 352b0a69..d501e5a8 100644 --- a/packages/web/src/components/Form/index.jsx +++ b/packages/web/src/components/Form/index.jsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { FormProvider, useForm, useWatch } from 'react-hook-form'; import PropTypes from 'prop-types'; import isEqual from 'lodash/isEqual'; +import useFormatMessage from 'hooks/useFormatMessage'; const noop = () => null; @@ -18,6 +19,8 @@ function Form(props) { ...formProps } = props; + const formatMessage = useFormatMessage(); + const methods = useForm({ defaultValues, reValidateMode, @@ -25,6 +28,8 @@ function Form(props) { mode, }); + const { setError } = methods; + const form = useWatch({ control: methods.control }); const prevDefaultValues = React.useRef(defaultValues); @@ -44,12 +49,51 @@ function Form(props) { } }, [defaultValues]); + const handleErrors = React.useCallback( + function (errors) { + if (!errors) return; + + let shouldSetGenericGeneralError = true; + const fieldNames = Object.keys(defaultValues); + + Object.entries(errors).forEach(([fieldName, fieldErrors]) => { + if (fieldNames.includes(fieldName) && Array.isArray(fieldErrors)) { + shouldSetGenericGeneralError = false; + setError(fieldName, { + type: 'fieldRequestError', + message: fieldErrors.join(', '), + }); + } + }); + + // in case of general errors + if (Array.isArray(errors.general)) { + for (const error of errors.general) { + shouldSetGenericGeneralError = false; + setError('root.general', { type: 'requestError', message: error }); + } + } + + if (shouldSetGenericGeneralError) { + setError('root.general', { + type: 'requestError', + message: formatMessage('form.genericError'), + }); + } + }, + [defaultValues, formatMessage, setError], + ); + return (
- onSubmit?.(data, event, methods.setError), - )} + onSubmit={methods.handleSubmit(async (data, event) => { + try { + return await onSubmit?.(data); + } catch (errors) { + handleErrors(errors); + } + })} {...formProps} > {render ? render(methods) : children} diff --git a/packages/web/src/components/InstallationForm/index.jsx b/packages/web/src/components/InstallationForm/index.jsx index 6e443302..f1e30051 100644 --- a/packages/web/src/components/InstallationForm/index.jsx +++ b/packages/web/src/components/InstallationForm/index.jsx @@ -68,7 +68,7 @@ function InstallationForm() { }); }; - const handleSubmit = async ({ fullName, email, password }, e, setError) => { + const handleSubmit = async ({ fullName, email, password }) => { try { await install({ fullName, @@ -77,29 +77,8 @@ function InstallationForm() { }); } catch (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, - }); - } + throw errors; } }; diff --git a/packages/web/src/hooks/useFormatMessage.js b/packages/web/src/hooks/useFormatMessage.js index ef76fe0c..62e95e28 100644 --- a/packages/web/src/hooks/useFormatMessage.js +++ b/packages/web/src/hooks/useFormatMessage.js @@ -1,5 +1,13 @@ +import * as React from 'react'; import { useIntl } from 'react-intl'; + export default function useFormatMessage() { const { formatMessage } = useIntl(); - return (id, values = {}) => formatMessage({ id }, values); + + const customFormatMessage = React.useCallback( + (id, values = {}) => formatMessage({ id }, values), + [formatMessage], + ); + + return customFormatMessage; } diff --git a/packages/web/src/locales/en.json b/packages/web/src/locales/en.json index 941af1fa..19c8aec4 100644 --- a/packages/web/src/locales/en.json +++ b/packages/web/src/locales/en.json @@ -130,6 +130,7 @@ "webhookUrlInfo.description": "You'll need to configure your application with this webhook URL.", "webhookUrlInfo.helperText": "We've generated a custom webhook URL for you to send requests to. Learn more about webhooks.", "webhookUrlInfo.copy": "Copy", + "form.genericError": "Something went wrong. Please try again.", "installationForm.title": "Installation", "installationForm.fullNameFieldLabel": "Full name", "installationForm.emailFieldLabel": "Email", @@ -141,7 +142,6 @@ "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.", "signupForm.title": "Sign up", "signupForm.fullNameFieldLabel": "Full name", "signupForm.emailFieldLabel": "Email",