edit validation messages, saving formData

This commit is contained in:
smanylov
2025-12-07 12:53:56 +07:00
parent e469bbe031
commit 29c139001d
4 changed files with 180 additions and 50 deletions
+90 -16
View File
@@ -29,16 +29,41 @@ export async function logout() {
} }
export async function authorization( export async function authorization(
state: string | undefined, state: {
previousState: {
email: string;
}
error: Record<string, string>
} | undefined,
formData: FormData, formData: FormData,
) { ) {
const email = formData.get('email') as string || '';
const password = formData.get('password');
const validatedFields = loginFormSchema.safeParse({ const validatedFields = loginFormSchema.safeParse({
email: formData.get('email'), email,
password: formData.get('password'), password
}) })
if (!validatedFields.success) { if (!validatedFields.success) {
return 'error'; const errors: Record<string, string> = {}
JSON.parse(validatedFields.error.message).forEach((obj: {
message: string,
path: string
}) => {
if (errors[obj.path[0]]) {
errors[obj.path[0]] = `${errors[obj.path[0]]} ${obj.message}`;
} else {
errors[obj.path[0]] = obj.message;
}
});
return {
previousState: {
email
},
error: errors
};
} }
try { try {
@@ -58,12 +83,21 @@ export async function authorization(
let parsed = await response.json(); let parsed = await response.json();
await createSession(parsed.token, parsed.email); await createSession(parsed.token, parsed.email);
} else { } else {
throw new Error('Something went wrong. from server'); throw new Error(`Something went wrong. Status from server ${response.status}`);
} }
} catch (error) { } catch (error) {
const errors: Record<string, string> = {}
errors['server'] = `${error}`;
if (error) { if (error) {
return 'Something went wrong. from server'; return {
previousState: {
email,
password
},
error: errors
};
} }
throw error; throw error;
} }
@@ -71,16 +105,35 @@ export async function authorization(
redirect('/pages/dashboard'); redirect('/pages/dashboard');
} }
type FormState = {
previousState: {
full_name: string;
email: string;
password: string;
confirm_password: string;
phone: string;
company: string;
}
error: Record<string, string>
};
export async function registration( export async function registration(
state: Record<string, string> | undefined, state: FormState | undefined,
formData: FormData, formData: FormData,
) { ): Promise<FormState> {
const full_name = formData.get('full_name') as string || '';
const email = formData.get('email') as string || '';
const password = formData.get('password') as string || '';
const confirm_password = formData.get('confirm_password') as string || '';
const phone = formData.get('phone') as string || '';
const company = formData.get('company') as string || '';
const validatedFields = SignupFormSchema.safeParse({ const validatedFields = SignupFormSchema.safeParse({
full_name: formData.get('full_name'), full_name,
email: formData.get('email'), email,
password: formData.get('password'), password,
confirm_password: formData.get('confirm_password'), confirm_password,
}) });
if (!validatedFields.success) { if (!validatedFields.success) {
const errors: Record<string, string> = {} const errors: Record<string, string> = {}
@@ -95,14 +148,24 @@ export async function registration(
} }
}); });
return errors; return {
previousState: {
full_name,
email,
password,
confirm_password,
phone,
company
},
error: errors
};
} }
try { try {
const { full_name, email, password } = validatedFields.data; const { full_name, email, password } = validatedFields.data;
const response = await fetch(`${API_BASE_URL}/v1/api/auth/register`, { const response = await fetch(`${API_BASE_URL}/v1/api/auth/register`, {
method: 'POST', method: 'POST',
body: JSON.stringify({ fullName: full_name, email, password }), body: JSON.stringify({ fullName: full_name, email, password, phone, company }),
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"Accept": "application/json" "Accept": "application/json"
@@ -121,7 +184,18 @@ export async function registration(
if (error) { if (error) {
const errors: Record<string, string> = {} const errors: Record<string, string> = {}
errors['server'] = `-error-2 -> ${error}` errors['server'] = `-error-2 -> ${error}`
return errors;
return {
previousState: {
full_name,
email,
password,
confirm_password,
phone,
company
},
error: errors
};
} }
throw error; throw error;
} }
+8 -8
View File
@@ -20,20 +20,20 @@ export const SignupFormSchema = z
.object({ .object({
full_name: z full_name: z
.string() .string()
.min(3, { error: 'Name must be at least 3 characters long.' }) .min(3, { error: 'Имя должно содержать не менее 3 символов.' })
.trim(), .trim(),
email: z.email({ error: 'Please enter a valid email.' }).trim(), email: z.email({ error: 'Пожалуйста, введите правильный адрес электронной почты.' }).trim(),
password: z password: z
.string() .string()
.min(8, { error: 'Be at least 8 characters long' }) .min(8, { error: 'Длина пароля должна быть не менее 8 символов.' })
.regex(/[a-zA-Z]/, { error: 'Contain at least one letter.' }) .regex(/[a-zA-Z]/, { error: 'Пароль должен содержать хотя бы одну букву.' })
.regex(/[0-9]/, { error: 'Contain at least one number.' }) .regex(/[0-9]/, { error: 'Пароль должен содержать хотя бы одну цифру.' })
.trim(), .trim(),
confirm_password: z confirm_password: z
.string() .string()
}) })
.refine((data) => data.password === data.confirm_password, { .refine((data) => data.password === data.confirm_password, {
message: 'passwordMismatchErrorMessage', message: 'Повторите пароль',
path: ['confirm_password'], path: ['confirm_password'],
}); });
@@ -41,11 +41,11 @@ export const loginFormSchema = z
.object({ .object({
email: z email: z
.string() .string()
.min(1, { error: 'Email must be not empty' }) .min(1, { error: 'Адрес электронной почты не должен быть пустым' })
.trim(), .trim(),
password: z password: z
.string() .string()
.min(1, { error: 'Password must be not empty' }) .min(1, { error: 'Пароль не должен быть пустым' })
.trim(), .trim(),
}) })
+24 -5
View File
@@ -5,7 +5,7 @@ import { useActionState } from 'react';
import { authorization } from '@/app/actions/auth'; import { authorization } from '@/app/actions/auth';
export default function LoginForm() { export default function LoginForm() {
const [errorMessage, formAction, isPending] = useActionState( const [state, formAction, isPending] = useActionState(
authorization, authorization,
undefined, undefined,
); );
@@ -14,14 +14,33 @@ export default function LoginForm() {
<form action={formAction}> <form action={formAction}>
<div className={`${styles['form-group']}`}> <div className={`${styles['form-group']}`}>
<label className={`${styles['form-label']}`}>Email адрес</label> <label className={`${styles['form-label']}`}>Email адрес</label>
<input type="readOnly" id="email" name="email" className={`${styles['form-input']}`} placeholder="Введите ваш email" /> <input
type="readOnly"
id="email"
name="email"
className={`${styles['form-input']}`}
placeholder="Введите ваш email"
defaultValue={state?.previousState?.email ? state?.previousState?.email : ''}
/>
{state?.error?.email && (
<p className="text-sm text-red-500">{state?.error?.email}</p>
)}
</div> </div>
<div className={`${styles['form-group']}`}> <div className={`${styles['form-group']}`}>
<label className={`${styles['form-label']}`}>Пароль</label> <label className={`${styles['form-label']}`}>Пароль</label>
<input type="password" id="password" name="password" className={`${styles['form-input']}`} placeholder="Введите пароль" /> <input
type="password"
id="password"
name="password"
className={`${styles['form-input']}`}
placeholder="Введите пароль"
/>
{state?.error?.password && (
<p className="text-sm text-red-500">{state?.error?.password}</p>
)}
</div> </div>
{errorMessage && ( {state?.error.server && (
<p className="text-sm text-red-500">{errorMessage}</p> <p className="text-sm text-red-500">{state?.error.server}</p>
)} )}
<div className={`${styles['form-options']}`}> <div className={`${styles['form-options']}`}>
<div className={`${styles['form-checkbox']}`}> <div className={`${styles['form-checkbox']}`}>
+57 -20
View File
@@ -1,19 +1,15 @@
'use client' 'use client'
import styles from '@/app/styles/login.module.scss'; import styles from '@/app/styles/login.module.scss';
import { useActionState, useEffect } from 'react'; import { useActionState } from 'react';
import { registration } from '@/app/actions/auth'; import { registration } from '@/app/actions/auth';
export default function RegisterForm() { export default function RegisterForm() {
const [errorMessage, formAction, isPending] = useActionState( const [state, formAction, isPending] = useActionState(
registration, registration,
undefined, undefined,
); );
useEffect(() => {
console.log(errorMessage);
}, [errorMessage])
return ( return (
<form action={(e) => { <form action={(e) => {
formAction(e); formAction(e);
@@ -24,22 +20,43 @@ export default function RegisterForm() {
> >
Полное имя Полное имя
</label> </label>
<input type="text" id="full_name" name="full_name" className={`${styles['form-input']}`} placeholder="Иванов Иван Иванович" /> <input
{errorMessage?.full_name && ( type="text"
<p className="text-sm text-red-500">{errorMessage!.full_name}</p> id="full_name"
name="full_name"
className={`${styles['form-input']}`}
placeholder="Иванов Иван Иванович"
defaultValue={state?.previousState?.full_name ? state?.previousState?.full_name : ''}
/>
{state?.error?.full_name && (
<p className="text-sm text-red-500">{state?.error?.full_name}</p>
)} )}
</div> </div>
<div className={`${styles['form-row']}`}> <div className={`${styles['form-row']}`}>
<div className={`${styles['form-group']}`}> <div className={`${styles['form-group']}`}>
<label className={`${styles['form-label']} ${styles['required']}`}>Email адрес</label> <label className={`${styles['form-label']} ${styles['required']}`}>Email адрес</label>
<input type="text" id="email" name="email" className={`${styles['form-input']}`} placeholder="ivan@example.com" /> <input
{errorMessage?.email && ( type="text"
<p className="text-sm text-red-500">{errorMessage!.email}</p> id="email"
name="email"
className={`${styles['form-input']}`}
placeholder="ivan@example.com"
defaultValue={state?.previousState?.email ? state?.previousState?.email : ''}
/>
{state?.error?.email && (
<p className="text-sm text-red-500">{state?.error?.email}</p>
)} )}
</div> </div>
<div className={`${styles['form-group']}`}> <div className={`${styles['form-group']}`}>
<label className={`${styles['form-label']}`}>Телефон</label> <label className={`${styles['form-label']}`}>Телефон</label>
<input type="phone" id="phone" name="phone" className={`${styles['form-input']}`} placeholder="+7 (999) 123-45-67" /> <input
type="phone"
id="phone"
name="phone"
className={`${styles['form-input']}`}
placeholder="+7 (999) 123-45-67"
defaultValue={state?.previousState?.phone ? state?.previousState?.phone : ''}
/>
</div> </div>
</div> </div>
<div className={`${styles['form-group']}`}> <div className={`${styles['form-group']}`}>
@@ -48,21 +65,41 @@ export default function RegisterForm() {
> >
Компания Компания
</label> </label>
<input type="text" id="company" name="company" className={`${styles['form-input']}`} placeholder="ООО «Ваша компания»" /> <input
type="text"
id="company"
name="company"
className={`${styles['form-input']}`}
placeholder="ООО «Ваша компания»"
defaultValue={state?.previousState?.company ? state?.previousState?.company : ''}
/>
</div> </div>
<div className={`${styles['form-row']}`}> <div className={`${styles['form-row']}`}>
<div className={`${styles['form-group']}`}> <div className={`${styles['form-group']}`}>
<label className={`${styles['form-label']} ${styles['required']}`}>Пароль</label> <label className={`${styles['form-label']} ${styles['required']}`}>Пароль</label>
<input type="password" id="password" name="password" className={`${styles['form-input']}`} placeholder="Минимум 6 символов" /> <input
{errorMessage?.password && ( type="password"
<p className="text-sm text-red-500">{errorMessage!.password}</p> id="password" name="password"
className={`${styles['form-input']}`}
placeholder="Минимум 8 символов"
defaultValue={state?.previousState?.password ? state?.previousState?.password : ''}
/>
{state?.error?.password && (
<p className="text-sm text-red-500">{state?.error?.password}</p>
)} )}
</div> </div>
<div className={`${styles['form-group']}`}> <div className={`${styles['form-group']}`}>
<label className={`${styles['form-label']} ${styles['required']}`}>Подтвердите пароль</label> <label className={`${styles['form-label']} ${styles['required']}`}>Подтвердите пароль</label>
<input type="password" id="confirm_password" name="confirm_password" className={`${styles['form-input']}`} placeholder="Повторите пароль" /> <input
{errorMessage?.confirm_password && ( type="password"
<p className="text-sm text-red-500">{errorMessage!.confirm_password}</p> id="confirm_password"
name="confirm_password"
className={`${styles['form-input']}`}
placeholder="Повторите пароль"
defaultValue={state?.previousState?.confirm_password ? state?.previousState?.confirm_password : ''}
/>
{state?.error?.confirm_password && (
<p className="text-sm text-red-500">{state?.error?.confirm_password}</p>
)} )}
</div> </div>
</div> </div>