/** * Form Builder * Builds form state with multiple fields */ import { useCallback, useRef } from 'react'; import type { FormConfig } from '../types/FormTypes'; export interface UseFormOptions> extends FormConfig { initialValues: T; onFieldChange?: (field: keyof T, value: string) => void; onErrorsClear?: () => void; } export interface UseFormResult> { values: T; errors: Record; touched: Record; handleChange: (field: keyof T) => (value: string) => void; setFieldValue: (field: keyof T, value: string) => void; setFieldError: (field: keyof T, error: string | null) => void; setFieldTouched: (field: keyof T, touched: boolean) => void; resetForm: () => void; clearErrors: () => void; clearFieldError: (field: keyof T) => void; isDirty: boolean; } /** * Hook for managing form with multiple fields */ export function useForm>( options: UseFormOptions ): UseFormResult { const { initialValues, onFieldChange, onErrorsClear, } = options; type FieldKey = keyof T; const [values, setValues] = React.useState(initialValues); const [errors, setErrors] = React.useState>( {} as Record ); const [touched, setTouched] = React.useState>( {} as Record ); // Store initial values in ref const initialValuesRef = useRef(initialValues); const onFieldChangeRef = useRef(onFieldChange); const onErrorsClearRef = useRef(onErrorsClear); // Update refs onFieldChangeRef.current = onFieldChange; onErrorsClearRef.current = onErrorsClear; const handleChange = useCallback( (field: FieldKey) => (value: string) => { setValues((prev) => ({ ...prev, [field]: value })); // Clear error when field changes (optional) if (errors[field]) { setErrors((prev) => ({ ...prev, [field]: null })); } // Call external callback onFieldChangeRef.current?.(field, value); }, [errors] ); const setFieldValue = useCallback((field: FieldKey, value: string) => { setValues((prev) => ({ ...prev, [field]: value })); onFieldChangeRef.current?.(field, value); }, []); const setFieldError = useCallback((field: FieldKey, error: string | null) => { setErrors((prev) => ({ ...prev, [field]: error })); }, []); const setFieldTouched = useCallback((field: FieldKey, isTouched: boolean) => { setTouched((prev) => ({ ...prev, [field]: isTouched })); }, []); const resetForm = useCallback(() => { setValues(initialValuesRef.current); setErrors({} as Record); setTouched({} as Record); }, []); const clearErrors = useCallback(() => { setErrors({} as Record); onErrorsClearRef.current?.(); }, []); const clearFieldError = useCallback((field: FieldKey) => { setErrors((prev) => ({ ...prev, [field]: null })); }, []); // Check if form is dirty (has changes) const isDirty = Object.keys(values).some( (key) => values[key as FieldKey] !== initialValuesRef.current[key as FieldKey] ); return { values, errors, touched, handleChange, setFieldValue, setFieldError, setFieldTouched, resetForm, clearErrors, clearFieldError, isDirty, }; } // Import React for useState import React from 'react';