import { formHasAnyDirty } from "@erp/core/client"; import { useHookForm } from "@erp/core/hooks"; import { showErrorToast, showSuccessToast, showWarningToast } from "@repo/rdx-ui/helpers"; import { useEffect, useId, useMemo } from "react"; import type { FieldErrors } from "react-hook-form"; import { useTranslation } from "../../i18n"; import { type Customer, type UpdateCustomerByIdParams, useCustomerGetQuery, useCustomerUpdateMutation, } from "../../shared"; import { mapCustomerToCustomerUpdateForm } from "../adapters"; import { type CustomerUpdateForm, CustomerUpdateFormSchema, defaultCustomerUpdateForm, } from "../entities"; import { buildCustomerUpdatePatch, buildUpdateCustomerByIdParams } from "../utils"; export interface UseCustomerUpdateControllerOptions { onUpdated?(updated: Customer): void; successToasts?: boolean; // mostrar o no toast automáticamente onError?(error: Error, params: UpdateCustomerByIdParams): void; errorToasts?: boolean; // mostrar o no toast automáticamente } export const useCustomerUpdateController = ( customerId?: string, options?: UseCustomerUpdateControllerOptions ) => { const { t } = useTranslation(); const formId = useId(); // id único por instancia // 1) Estado de carga del cliente (query) const { data: customerData, isLoading, isError: isLoadError, error: loadError, } = useCustomerGetQuery(customerId, { enabled: Boolean(customerId) }); // 2) Estado de creación (mutación) const { mutateAsync, isPending: isUpdating, isError: isUpdateError, error: updateError, } = useCustomerUpdateMutation(); const initialValues = useMemo(() => { if (!customerData) return defaultCustomerUpdateForm; return mapCustomerToCustomerUpdateForm(customerData); }, [customerData]); // 3) Form hook const form = useHookForm({ resolverSchema: CustomerUpdateFormSchema, initialValues, disabled: isLoading || isUpdating, }); /** Reiniciar el form al recibir datos */ useEffect(() => { if (!customerData) return; console.log("Reseteando form con datos del cliente:", customerData); form.reset(mapCustomerToCustomerUpdateForm(customerData), { keepDirty: false, // <-- importante: no marca el form como "dirty" al cargar los datos reales }); }, [customerData, form]); /** Handlers */ const resetForm = () => { const initialData = customerData ? mapCustomerToCustomerUpdateForm(customerData) : defaultCustomerUpdateForm; form.reset(initialData, { keepDirty: false }); }; const submitHandler = form.handleSubmit( async (formData) => { if (!customerId) { showErrorToast(t("pages.update.error.title"), "Falta el ID del cliente"); return; } const { dirtyFields } = form.formState; if (!formHasAnyDirty(dirtyFields)) { showWarningToast(t("pages.update.error.no_changes"), "No hay cambios para guardar"); return; } const previousData = customerData; const patchData = buildCustomerUpdatePatch(formData, dirtyFields); const params: UpdateCustomerByIdParams = buildUpdateCustomerByIdParams(customerId, patchData); try { // Enviamos cambios al servidor const updated = await mutateAsync(params); // Ha ido bien -> actualizamos form con datos reales // keepDirty = false -> deja el formulario sin cambios sin tener que esperar al siguiente render. form.reset(mapCustomerToCustomerUpdateForm(updated), { keepDirty: false, }); if (options?.successToasts !== false) { showSuccessToast( t("pages.update.success.title", "Cliente modificado"), t("pages.update.success.message", "Se ha modificado correctamente.") ); } options?.onUpdated?.(updated); } catch (error: unknown) { const normalizedError = error instanceof Error ? error : new Error(t("pages.update.error.unknown")); form.reset( previousData ? mapCustomerToCustomerUpdateForm(previousData) : defaultCustomerUpdateForm, { keepDirty: false } ); if (options?.errorToasts !== false) { showErrorToast(t("pages.update.error.title"), normalizedError.message); } options?.onError?.(normalizedError, params); } }, (errors: FieldErrors) => { const firstKey = Object.keys(errors)[0] as keyof CustomerUpdateForm | undefined; if (firstKey) { document.querySelector(`[name="${String(firstKey)}"]`)?.focus(); } showWarningToast( t("forms.validation.title", "Revisa los campos"), t("forms.validation.message", "Hay errores de validación en el formulario.") ); } ); // Evento onSubmit ya preparado para el
const onSubmit = (event: React.FormEvent) => { event.stopPropagation(); // <-- evita que el submit se propage por los padre en el árbol DOM submitHandler(event); }; return { // form form, formId, // handlers del form onSubmit, resetForm, // carga de datos customerData, isLoading, isLoadError, loadError, // mutation isUpdating, isUpdateError, updateError, // No devolver FormProvider, así el controller es más // flexible y reusable (p.ej. para un modal) // FormProvider, }; };