import { useCallback, useState, type FormEvent } from 'react'; import type { DataValidator, Maybe, Nullable } from '../../../../types'; export type FormSubmitMessages = { /** * The message to use on error. */ error: string; /** * The message to use on success. */ success: string; }; export type FormSubmitValidation = { /** * A callback to handle submit validation. */ validator: DataValidator; /** * The messages to use on failure or success. */ messages: Partial; }; export type FormSubmitHandler = ( data: T ) => Maybe> | Promise>>; export type FormSubmitStatus = 'IDLE' | 'PENDING' | 'FAILED' | 'SUCCEEDED'; export type FormHandlers> = { /** * A callback function to handle submit failure. */ onFailure: () => void; /** * A callback function to handle submit success. */ onSuccess: () => void; /** * A callback function to handle submit. */ submit: FormSubmitHandler; }; export type UseFormSubmitReturn = { /** * The message to use on submit failure or success. */ messages: Nullable>; /** * A method to handle form submit. * * @param {FormEvent} e - The event. * @returns {Promise} */ submit: (e: FormEvent) => Promise; /** * The submit status. */ submitStatus: FormSubmitStatus; }; /** * React hook to handle form submit. * * @template {object} T - The object keys should match the fields name. * @param {T} data - The form values. * @param {Partial>} handlers - The submit handlers. * @returns {UseFormSubmitReturn} A submit method, the status and messages. */ export const useFormSubmit = >( data: T, handlers?: Partial> ): UseFormSubmitReturn => { const { onFailure, onSuccess, submit: submitHandler } = handlers ?? {}; const [messages, setMessages] = useState>>(null); const [submitStatus, setSubmitStatus] = useState('IDLE'); const handleFailure = useCallback(() => { setSubmitStatus('FAILED'); if (onFailure) onFailure(); }, [onFailure]); const handleSuccess = useCallback(() => { setSubmitStatus('SUCCEEDED'); if (onSuccess) onSuccess(); }, [onSuccess]); const handleSubmit = useCallback(async () => { const submitResult = submitHandler ? await submitHandler(data) : undefined; if (!submitResult) { handleSuccess(); return; } setMessages(submitResult.messages); const isSuccess = submitResult.validator(data); setSubmitStatus(isSuccess ? 'SUCCEEDED' : 'FAILED'); if (isSuccess) handleSuccess(); else handleFailure(); }, [data, handleFailure, handleSuccess, submitHandler]); const submit = useCallback( async (e: FormEvent) => { e.preventDefault(); setMessages(null); setSubmitStatus('PENDING'); return handleSubmit(); }, [handleSubmit] ); return { messages, submit, submitStatus }; };