1import { useDialog } from "@marzneshin/common/hooks";
2import { zodResolver } from "@hookform/resolvers/zod";
3import { UseMutationResult } from "@tanstack/react-query";
4import { useEffect } from "react";
5import { FieldValues, SubmitHandler, useForm } from "react-hook-form";
6import { ZodSchema } from "zod";
7
8export interface MutationDialogProps<TData> {
9 entity: TData | null;
10 onClose: () => void;
11}
12
13interface IMutationDialog<TData, TError = unknown, TVariables = unknown> extends MutationDialogProps<TData> {
14 createMutation: UseMutationResult<TData, TError, TVariables>
15 updateMutation: UseMutationResult<TData, TError, TVariables>
16 schema: ZodSchema
17 defaultValue: FieldValues
18 loadFormtter?: (d: FieldValues) => FieldValues
19}
20
21/** useMutationDialog - Dialog mutation hook
22 *
23 * @param entity - Entity value for update, leave empty for creation
24 * @param createMutation - react-query mutation hook for entity creation
25 * @param updateMutation - react-query mutation hook for entity edition
26 * @param entity - Entity value for update, leave empty for creation
27 * @param schema - Entity schema validated by zodResolver
28 * @param onClose - Handler for when the dialog is closed
29 * @param getDefaultValue - Default value function return the default value for function
30 * - Must be wrapped in useCallback to avoid infinite useEffect loop
31 *
32 * @returns react-hook-form and submit handler plus the dialog open state
33 *
34 * @template TData - Mutation Type of Schema
35 * @template TError = unknown
36 * @template TVariables = unknown
37 */
38
39export const useMutationDialog = <TData, TError = unknown, TVariables = unknown>({
40 onClose,
41 entity,
42 createMutation,
43 updateMutation,
44 schema,
45 defaultValue,
46 loadFormtter = (s) => s,
47}: IMutationDialog<TData, TError, TVariables>) => {
48 const [open, onOpenChange] = useDialog(true);
49
50 const form = useForm({
51 defaultValues: entity ? loadFormtter(entity) : defaultValue,
52 resolver: zodResolver(schema)
53 });
54
55 const submit: SubmitHandler<FieldValues> = async (values) => {
56 if (entity) {
57 updateMutation.mutate(values as TVariables);
58 } else {
59 createMutation.mutate(values as TVariables);
60 }
61 onOpenChange(false);
62 };
63
64 useEffect(() => {
65 if (entity) {
66 form.reset(loadFormtter(entity))
67 } else {
68 form.reset(defaultValue);
69 }
70 // eslint-disable-next-line
71 }, [entity, defaultValue]);
72
73 useEffect(() => {
74 if (!open) onClose();
75 }, [open, onClose]);
76
77 const handleSubmit = form.handleSubmit(submit as SubmitHandler<FieldValues>);
78
79 return {
80 form,
81 open, onOpenChange,
82 handleSubmit
83 };
84};