diff --git a/src/api.ts b/src/api.ts index adc6891..3f80ee9 100644 --- a/src/api.ts +++ b/src/api.ts @@ -79,7 +79,8 @@ export function requestUserData(authToken: string) { }; } -export function requestRegisterUser({ firstName, lastName, email, phoneNumber, password }: UserRegisterFormValues) { +// We use snake_case in order to fetch and display field errors from the API without any additional formatting +export function requestRegisterUser({ first_name, last_name, email, phone_number, password }: UserRegisterFormValues) { return (dispatch: AppDispatch) => { dispatch(registerUserRequest()); @@ -90,10 +91,10 @@ export function requestRegisterUser({ firstName, lastName, email, phoneNumber, p 'Content-Type': 'application/json', }, body: JSON.stringify({ - first_name: firstName, - last_name: lastName, + first_name: first_name, + last_name: last_name, email, - phone_number: phoneNumber, + phone_number: phone_number, password, }), }) diff --git a/src/components/FormErrors.tsx b/src/components/FormErrors.tsx index f12681d..0b0dca7 100644 --- a/src/components/FormErrors.tsx +++ b/src/components/FormErrors.tsx @@ -4,17 +4,24 @@ import { Text, StyleSheet, View } from 'react-native'; // eslint-disable-next-line @typescript-eslint/no-explicit-any const FormErrors = ({ errors }: { errors: Record }) => { - if (!Object.keys(errors).length || !['non_field_errors', 'detail'].some((errorField) => errorField in errors)) { - return null; + if (!errors) { + return; } let errorFormatted; - if ('non_field_errors' in errors) { + if (errors instanceof Error) { + // This check might backfire on us as it's too general. We do not wish to display the error message to the user + // Hence we display this generic one + errorFormatted = 'Kunne ikke sende inn skjemaet! Prøv igjen senere...'; + } else if ('non_field_errors' in errors) { errorFormatted = errors.non_field_errors?.[0]; } else if ('detail' in errors) { errorFormatted = errors.detail; } - if (!errorFormatted) errorFormatted = 'Kunne ikke sende inn skjemaet, prøv igjen...'; + + if (!errorFormatted) { + return; + } return ( diff --git a/src/modules/login/Login.tsx b/src/modules/login/Login.tsx index 55571ce..8975b3b 100644 --- a/src/modules/login/Login.tsx +++ b/src/modules/login/Login.tsx @@ -9,7 +9,7 @@ import theme from 'src/theme'; import useLogin from './useLogin'; const Login = () => { - const { isLoggingIn, errors, control, setFocus, onSubmit } = useLogin(); + const { isLoggingIn, isSubmitted, errors, loginError, control, setFocus, onSubmit } = useLogin(); return ( @@ -50,6 +50,7 @@ const Login = () => { > Glemt passord? + {loginError && isSubmitted && } {isLoggingIn && } diff --git a/src/modules/login/useLogin.tsx b/src/modules/login/useLogin.tsx index c084014..7064011 100644 --- a/src/modules/login/useLogin.tsx +++ b/src/modules/login/useLogin.tsx @@ -22,7 +22,7 @@ const useLogin = () => { const { control, handleSubmit, - formState: { errors }, + formState: { errors, isSubmitted }, setFocus, } = useForm(); @@ -37,7 +37,7 @@ const useLogin = () => { } }, [isAuthenticated, navigation]); - return { onSubmit, isLoggingIn, setFocus, control, errors: { ...errors, ...loginError } }; + return { onSubmit, isLoggingIn, isSubmitted, setFocus, control, errors: { ...errors }, loginError }; }; export default useLogin; diff --git a/src/modules/register/UserRegister.tsx b/src/modules/register/UserRegister.tsx index 1d46de6..14de836 100644 --- a/src/modules/register/UserRegister.tsx +++ b/src/modules/register/UserRegister.tsx @@ -13,8 +13,18 @@ import useUserRegister from './useUserRegister'; const { Toast } = Incubator; const UserRegister = () => { - const { isRegisteringUser, onSubmit, isValid, control, errors, registerError, setFocus, toast, setToast } = - useUserRegister(); + const { + isRegisteringUser, + onSubmit, + isValid, + isSubmitted, + control, + errors, + registerError, + setFocus, + toast, + setToast, + } = useUserRegister(); const isDisabled = isRegisteringUser || !isValid; return ( @@ -22,15 +32,15 @@ const UserRegister = () => { setFocus('lastName')} + onSubmitEditing={() => setFocus('last_name')} /> { required: 'Kan ikke være tomt!', validate: (v) => EmailValidator.validate(v) || 'er ikke en gyldig e-post', }} - onSubmitEditing={() => setFocus('phoneNumber')} + onSubmitEditing={() => setFocus('phone_number')} /> { onSubmitEditing={onSubmit} /> - - + {registerError && isSubmitted && } - {isRegisteringUser && } diff --git a/src/modules/register/useUserRegister.tsx b/src/modules/register/useUserRegister.tsx index c1c0732..9cbade9 100644 --- a/src/modules/register/useUserRegister.tsx +++ b/src/modules/register/useUserRegister.tsx @@ -7,10 +7,10 @@ import { useAppDispatch, useAppSelector, usePrevious } from 'src/hooks'; import { ToastProps } from 'src/types/toast'; export interface UserRegisterFormValues { - firstName: string; - lastName: string; + first_name: string; + last_name: string; email: string; - phoneNumber: string; + phone_number: string; password: string; } @@ -19,7 +19,7 @@ const useUserRegister = () => { const navigation = useNavigation(); const isAuthenticated = useAppSelector((store) => store.isAuthenticated); const isRegisteringUser = useAppSelector((store) => store.isRegisteringUser); - const registerError = useAppSelector((store) => store.registerError) || {}; + const registerError = useAppSelector((store) => store.registerError); const [toast, setToast] = useState(null); const isAuthenticatedPrev = usePrevious(isAuthenticated); @@ -27,11 +27,13 @@ const useUserRegister = () => { const { control, handleSubmit, - formState: { errors, isValid }, + formState: { errors, isValid, isSubmitted }, setFocus, setError, + clearErrors, } = useForm(); const onSubmit = handleSubmit((values) => { + clearErrors(); if (!isValid) { setToast({ message: 'Noen av feltene er ikke fylt ut riktig', @@ -46,7 +48,7 @@ const useUserRegister = () => { // Display error messages from API on form fields Object.keys(registerError).forEach((key) => { - setError(key as keyof UserRegisterFormValues, { type: 'manual', message: registerError[key] }); + setError(key as keyof UserRegisterFormValues, { type: 'server', message: registerError[key].join('\n') }); }); }); @@ -56,6 +58,25 @@ const useUserRegister = () => { } }, [isAuthenticatedPrev, isAuthenticated, navigation]); - return { isRegisteringUser, onSubmit, isValid, registerError, control, errors, setFocus, toast, setToast }; + useEffect(() => { + clearErrors(); + if (!isSubmitted || !registerError) return; + Object.keys(registerError).forEach((key) => { + setError(key as keyof UserRegisterFormValues, { type: 'server', message: registerError[key].join('\n') }); + }); + }, [registerError]); + + return { + isRegisteringUser, + onSubmit, + isValid, + isSubmitted, + registerError, + control, + errors, + setFocus, + toast, + setToast, + }; }; export default useUserRegister; diff --git a/src/reducers.ts b/src/reducers.ts index 9f53614..740c9fe 100644 --- a/src/reducers.ts +++ b/src/reducers.ts @@ -77,7 +77,7 @@ export default function appReducers(state = initialState, action: any) { return { ...state, isRegisteringUser: true, registerError: null }; case REGISTER_USER_FAILURE: - return { ...state, isRegisteringUser: false, registerError: formatErrors(action.registerError) }; + return { ...state, isRegisteringUser: false, registerError: action.registerError }; case REGISTER_USER_SUCCESS: // eslint-disable-next-line no-case-declarations