| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
 | 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<T> = {
  /**
   * A callback to handle submit validation.
   */
  validator: DataValidator<T>;
  /**
   * The messages to use on failure or success.
   */
  messages: Partial<FormSubmitMessages>;
};
export type FormSubmitHandler<T> = (
  data: T
) => Maybe<FormSubmitValidation<T>> | Promise<Maybe<FormSubmitValidation<T>>>;
export type FormSubmitStatus = 'IDLE' | 'PENDING' | 'FAILED' | 'SUCCEEDED';
export type FormHandlers<T extends Record<string, unknown>> = {
  /**
   * 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<T>;
};
export type UseFormSubmitReturn = {
  /**
   * The message to use on submit failure or success.
   */
  messages: Nullable<Partial<FormSubmitMessages>>;
  /**
   * A method to handle form submit.
   *
   * @param {FormEvent<HTMLFormElement>} e - The event.
   * @returns {Promise<void>}
   */
  submit: (e: FormEvent<HTMLFormElement>) => Promise<void>;
  /**
   * 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<FormHandlers<T>>} handlers - The submit handlers.
 * @returns {UseFormSubmitReturn} A submit method, the status and messages.
 */
export const useFormSubmit = <T extends Record<string, unknown>>(
  data: T,
  handlers?: Partial<FormHandlers<T>>
): UseFormSubmitReturn => {
  const { onFailure, onSuccess, submit: submitHandler } = handlers ?? {};
  const [messages, setMessages] =
    useState<Nullable<Partial<FormSubmitMessages>>>(null);
  const [submitStatus, setSubmitStatus] = useState<FormSubmitStatus>('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<HTMLFormElement>) => {
      e.preventDefault();
      setMessages(null);
      setSubmitStatus('PENDING');
      return handleSubmit();
    },
    [handleSubmit]
  );
  return { messages, submit, submitStatus };
};
 |