From 4d576b07da81c34b1703a265e3ba24990f6511e1 Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Sat, 7 Dec 2024 13:32:08 +0100 Subject: [PATCH] Basic error validation in the form --- app/api/authentication.tsx | 29 ++++++++++++++++++++----- app/lib/definitions.ts | 6 ++++- app/lib/utils.ts | 5 +++++ app/ui/components/registration-form.tsx | 12 ++++++++-- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/app/api/authentication.tsx b/app/api/authentication.tsx index a7648f6..5390fd5 100644 --- a/app/api/authentication.tsx +++ b/app/api/authentication.tsx @@ -1,7 +1,7 @@ /* Copyright (C) 2024 Manuel Bustillo*/ -import { getCsrfToken, getSlug } from '@/app/lib/utils'; -import { Captcha, User } from '@/app/lib/definitions'; +import { asArray, getCsrfToken, getSlug } from '@/app/lib/utils'; +import { Captcha, StructuredErrors, User } from '@/app/lib/definitions'; export function login({ email, password, onLogin }: { email: string, password: string, onLogin: (user: User) => void }) { return fetch(`/api/${getSlug()}/users/sign_in`, { @@ -34,13 +34,23 @@ export function logout({ onLogout }: { onLogout: () => void }) { .catch((error) => console.error(error)); } -export function register({slug, email, password, passwordConfirmation, captcha, onRegister}: { +function flattenErrors(errors: StructuredErrors): string[] { + if(errors instanceof Array) { + return errors; + } + return Object.keys(errors).map((key) => { + return `${key}: ${asArray(errors[key]).join(', ')}`; + }); +} + +export function register({slug, email, password, passwordConfirmation, captcha, onRegister, onError}: { slug: string, email: string, password: string, passwordConfirmation: string, captcha: Captcha, - onRegister: () => void + onRegister: () => void, + onError: (errors: string[]) => void }){ fetch(`/api/${slug}/users`, { method: 'POST', @@ -55,6 +65,13 @@ export function register({slug, email, password, passwordConfirmation, captcha, 'Content-Type': 'application/json', 'X-CSRF-TOKEN': getCsrfToken(), } - }).then(onRegister) - .catch((error) => console.error(error)); + }).then((response) => { + if(response.ok) { + response.json().then(onRegister); + } else { + response.json().then((data: any) => { + onError(data.errors && flattenErrors(data.errors) || [data.error]); + }); + } + }) } diff --git a/app/lib/definitions.ts b/app/lib/definitions.ts index 8e161e4..aba529b 100644 --- a/app/lib/definitions.ts +++ b/app/lib/definitions.ts @@ -69,4 +69,8 @@ export type User = { export type Captcha = { id: string; answer: string; -} \ No newline at end of file +} + +export type StructuredErrors = { + [key: string]: string[]|string; +}; \ No newline at end of file diff --git a/app/lib/utils.ts b/app/lib/utils.ts index 069744a..bbd6700 100644 --- a/app/lib/utils.ts +++ b/app/lib/utils.ts @@ -13,3 +13,8 @@ export const getSlug = () => localStorage.getItem('slug') || 'default'; export const capitalize = (val:string) => { return String(val).charAt(0).toUpperCase() + String(val).slice(1); } + +// From https://stackoverflow.com/a/62118163/3607039 +export function asArray(value: T | T[]): T[] { + return ([] as T[]).concat(value) +} \ No newline at end of file diff --git a/app/ui/components/registration-form.tsx b/app/ui/components/registration-form.tsx index 2c2d304..5c4fb71 100644 --- a/app/ui/components/registration-form.tsx +++ b/app/ui/components/registration-form.tsx @@ -11,6 +11,8 @@ import { register } from '@/app/api/authentication'; import { getCaptchaChallenge } from '@/app/api/captcha'; export default function RegistrationForm() { + const [errors, setErrors] = useState([]); + const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [passwordConfirmation, setPasswordConfirmation] = useState(""); @@ -26,6 +28,7 @@ export default function RegistrationForm() { console.log(id, url); setCaptchaId(id); setCaptchaUrl(url); + setCaptchaAnswer(""); } }); } @@ -52,10 +55,14 @@ export default function RegistrationForm() { captcha - setCaptchaAnswer(e.target.value)} /> + setCaptchaAnswer(e.target.value)} /> + {errors.map((error, index) => ( +
{error}
+ ))} +