add stuff table, add stuff remove and add interfaces
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
'use client'
|
||||
|
||||
import styles from '@/app/styles/module/login.module.scss'
|
||||
import { useActionState, useState, useRef, useEffect } from 'react';
|
||||
import { createStuff } from '@/app/actions/stuffActions';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { IconEye } from '@/app/ui/icons/icons';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
const createStuffSchema = z.object({
|
||||
fullName: z.string()
|
||||
.min(2, 'full-name-too-short')
|
||||
.max(100, 'full-name-too-long')
|
||||
.trim(),
|
||||
email: z.string()
|
||||
.email('invalid-email')
|
||||
.toLowerCase()
|
||||
.trim(),
|
||||
password: z.string()
|
||||
.min(6, 'password-too-short')
|
||||
.max(50, 'password-too-long'),
|
||||
})
|
||||
|
||||
export default function CreateStaffForm({ onSuccess }: { onSuccess?: () => void }) {
|
||||
const [state, formAction, isPending] = useActionState(
|
||||
createStuffAction,
|
||||
undefined,
|
||||
);
|
||||
const t = useTranslations('Login-register-form');
|
||||
const tStaff = useTranslations('Staff');
|
||||
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
function toggleShowPassword() {
|
||||
setShowPassword(!showPassword);
|
||||
}
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
useEffect(() => {
|
||||
if (state?.success) {
|
||||
toast.success(tStaff('staff-created-successfully'));
|
||||
queryClient.invalidateQueries({ queryKey: ['stuffList'] });
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
}
|
||||
if (state?.error?.server) {
|
||||
toast.error(t(state.error.server));
|
||||
}
|
||||
}, [state, queryClient, onSuccess, t, tStaff]);
|
||||
|
||||
return (
|
||||
<form
|
||||
className={`${styles['form-wrapper']}`}
|
||||
action={formAction}
|
||||
autoComplete="off"
|
||||
>
|
||||
{/* Поле Email */}
|
||||
<div className={`${styles['form-group']}`}>
|
||||
<label className={`${styles['form-label']}`}>
|
||||
{t('email-adress')}
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
className={`${styles['form-input']}`}
|
||||
placeholder={t('enter-email')}
|
||||
defaultValue={state?.previousState?.email || ''}
|
||||
autoComplete="off"
|
||||
onChange={(e) => {
|
||||
e.target.value = e.target.value.toLowerCase();
|
||||
}}
|
||||
/>
|
||||
{state?.error?.email && (
|
||||
<p className={`${styles['form-error']}`}>
|
||||
{t(state?.error?.email)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Поле Password */}
|
||||
<div className={`${styles['form-group']}`}>
|
||||
<label className={`${styles['form-label']}`}>
|
||||
{t('password')}
|
||||
</label>
|
||||
<div className={`${styles['password-wrapper']}`}>
|
||||
<input
|
||||
type={showPassword ? "text" : "password"}
|
||||
id="password"
|
||||
name="password"
|
||||
className={`${styles['form-input']} ${styles['password']}`}
|
||||
placeholder={t('enter-password')}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
<button
|
||||
onClick={toggleShowPassword}
|
||||
type="button"
|
||||
className={`show-password-button ${showPassword ? 'show' : ''}`}
|
||||
>
|
||||
<IconEye />
|
||||
</button>
|
||||
</div>
|
||||
{state?.error?.password && (
|
||||
<p className={`${styles['form-error']}`}>
|
||||
{t(state?.error?.password)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Поле Full Name */}
|
||||
<div className={`${styles['form-group']}`}>
|
||||
<label className={`${styles['form-label']}`}>
|
||||
{tStaff('full-name')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="fullName"
|
||||
name="fullName"
|
||||
className={`${styles['form-input']}`}
|
||||
placeholder={tStaff('enter-full-name')}
|
||||
defaultValue={state?.previousState?.fullName || ''}
|
||||
autoComplete="off"
|
||||
/>
|
||||
{state?.error?.fullName && (
|
||||
<p className={`${styles['form-error']}`}>
|
||||
{t(state?.error?.fullName)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Общая ошибка сервера */}
|
||||
{state?.error?.server && !state.success && (
|
||||
<p className={`${styles['form-error']}`}>
|
||||
{t(state?.error?.server)}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Кнопка отправки */}
|
||||
<button
|
||||
type="submit"
|
||||
className={`${styles['btn']}`}
|
||||
disabled={isPending}
|
||||
>
|
||||
{tStaff('create-staff')}
|
||||
{isPending && (
|
||||
<div className="loading-animation">
|
||||
<div className="global-spinner"></div>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
async function createStuffAction(
|
||||
state: {
|
||||
previousState: {
|
||||
fullName?: string;
|
||||
email?: string;
|
||||
password?: string;
|
||||
}
|
||||
error: Record<string, string>
|
||||
success?: boolean;
|
||||
} | undefined,
|
||||
formData: FormData,
|
||||
) {
|
||||
const fullName = formData.get('fullName') as string || '';
|
||||
const email = formData.get('email') as string || '';
|
||||
const password = formData.get('password') as string || '';
|
||||
|
||||
const validatedFields = createStuffSchema.safeParse({
|
||||
fullName,
|
||||
email,
|
||||
password
|
||||
});
|
||||
|
||||
if (!validatedFields.success) {
|
||||
const errors: Record<string, string> = {}
|
||||
JSON.parse(validatedFields.error.message).forEach((obj: {
|
||||
message: string,
|
||||
path: string[]
|
||||
}) => {
|
||||
const fieldName = obj.path[0];
|
||||
if (errors[fieldName]) {
|
||||
errors[fieldName] = `${errors[fieldName]} ${obj.message}`;
|
||||
} else {
|
||||
errors[fieldName] = obj.message;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
previousState: {
|
||||
fullName,
|
||||
email,
|
||||
password
|
||||
},
|
||||
error: errors,
|
||||
success: false
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const { fullName, email, password } = validatedFields.data;
|
||||
|
||||
// Вызов API для создания сотрудника
|
||||
const result = await createStuff(fullName, email, password);
|
||||
|
||||
if (result) {
|
||||
return {
|
||||
previousState: {
|
||||
fullName: '',
|
||||
email: '',
|
||||
password: ''
|
||||
},
|
||||
error: {},
|
||||
success: true
|
||||
};
|
||||
} else {
|
||||
throw new Error('creation-failed');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
const errors: Record<string, string> = {}
|
||||
|
||||
// Обработка различных ошибок от сервера
|
||||
switch (error) {
|
||||
case 'email-already-exists':
|
||||
errors['email'] = 'email-already-exists';
|
||||
break;
|
||||
case 'invalid-email':
|
||||
errors['email'] = 'invalid-email';
|
||||
break;
|
||||
default:
|
||||
errors['server'] = 'request-ended-with-an-error';
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
previousState: {
|
||||
fullName,
|
||||
email,
|
||||
password
|
||||
},
|
||||
error: errors,
|
||||
success: false
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user