'use client' import { useState, ChangeEvent, FormEvent } from 'react' import toast from 'react-hot-toast' import { ValidationRules, ValidationErrors } from '../types' type FormValue = string | number | boolean | string[] | number[] type CustomChangeEvent = { target: { id: string value: FormValue type?: string checked?: boolean } } export function useForm>( initialValues: T, validationRules?: { [K in keyof T]?: ValidationRules }, onSubmit?: (values: T) => void ) { const [values, setValues] = useState(initialValues) const [errors, setErrors] = useState({}) const [isVisible, setIsVisible] = useState(false) const handleChange = ( e: | ChangeEvent | CustomChangeEvent ) => { const { id, value, type } = e.target const isCheckbox = type === 'checkbox' && 'checked' in e.target setValues((prev) => ({ ...prev, [id]: isCheckbox ? (e.target as HTMLInputElement).checked : value, })) // скидываем ошибки if (errors[id]) { setErrors((prev) => { const newErrors = { ...prev } delete newErrors[id] return newErrors }) } } const resetField = (fieldName: keyof T, value: FormValue = '') => { setValues((prev) => ({ ...prev, [fieldName]: value, })) // очищаем ошибки для этого поля, если они есть if (errors[fieldName as string]) { setErrors((prev) => { const newErrors = { ...prev } delete newErrors[fieldName as string] return newErrors }) } } const fieldNames: { [key: string]: string } = { name: 'Имя', surmame: 'Фамилия', email: 'Email', phone_number: 'Номер телефона', password: 'Пароль', privacy_policy: 'Политика конфиденциальности', } const validate = () => { if (!validationRules) return true const newErrors: ValidationErrors = {} Object.keys(validationRules).forEach((key) => { const value = values[key] const rules = validationRules[key as keyof T] if (rules?.required && !value) { newErrors[key] = 'Это поле обязательно' toast.error( `Поле "${fieldNames[key] || key}" обязательно для заполнения`, { duration: 2000, position: 'top-right', style: { background: '#FEE2E2', color: '#991B1B', padding: '16px', borderRadius: '8px', }, } ) } if ( rules?.minLength && typeof value === 'string' && value.length < rules.minLength ) { newErrors[key] = `Минимальная длина ${rules.minLength} символов` toast.error( `Минимальная длина поля "${fieldNames[key] || key}" - ${ rules.minLength } символов`, { duration: 2000, position: 'top-right', style: { background: '#FEE2E2', color: '#991B1B', padding: '16px', borderRadius: '8px', }, } ) } if ( rules?.pattern && typeof value === 'string' && !rules.pattern.test(value) ) { newErrors[key] = 'Неверный формат' toast.error(`Поле "${fieldNames[key] || key}" заполнено некорректно`, { duration: 2000, position: 'top-right', style: { background: '#FEE2E2', color: '#991B1B', padding: '16px', borderRadius: '8px', }, }) } }) setErrors(newErrors) return Object.keys(newErrors).length === 0 } const handleSubmit = (e: FormEvent) => { e.preventDefault() if (validate() && onSubmit) { onSubmit(values) } } const togglePasswordVisibility = () => { setIsVisible(!isVisible) } return { values, errors, isVisible, setValues, handleChange, handleSubmit, togglePasswordVisibility, resetField, } }