FactuGES_ERP/modules/customers/src/web/create/controllers/use-customer-create-page.controller.ts

121 lines
3.8 KiB
TypeScript
Raw Normal View History

2026-04-03 19:49:59 +00:00
import { formHasAnyDirty, pickFormDirtyValues } from "@erp/core/client";
import { useHookForm } from "@erp/core/hooks";
import { showErrorToast, showSuccessToast, showWarningToast } from "@repo/rdx-ui/helpers";
import { useId } from "react";
import { type FieldErrors, FormProvider } from "react-hook-form";
import { useTranslation } from "../../i18n";
import type { Customer } from "../../shared";
import { type CustomerFormData, CustomerFormSchema, defaultCustomerFormData } from "../types";
export interface UseCustomerCreateControllerOptions {
onCreated?(created: Customer): void;
successToasts?: boolean; // mostrar o no toast automáticcamente
onError?(error: Error, patchData: ReturnType<typeof pickFormDirtyValues>): void;
errorToasts?: boolean; // mostrar o no toast automáticcamente
}
export const useCustomerCreateController = (
customerId?: string,
options?: UseCustomerCreateControllerOptions
) => {
const { t } = useTranslation();
const formId = useId(); // id único por instancia
// 1) Estado de creación (mutación)
const {
mutateAsync,
isPending,
isError: isCreateError,
error: createError,
} = useCustomerCreateMutation();
// 2) Form hook
const form = useHookForm<CustomerFormData>({
resolverSchema: CustomerFormSchema,
initialValues: defaultCustomerFormData,
disabled: isPending,
});
/** Handlers */
const resetForm = () => form.reset(defaultCustomerFormData);
// Versión sincronizada
const submitHandler = form.handleSubmit(
async (formData) => {
if (!customerId) {
showErrorToast(t("pages.create.error.title"), "Falta el ID del cliente");
return;
}
const { dirtyFields } = form.formState;
if (!formHasAnyDirty(dirtyFields)) {
showWarningToast(t("pages.create.error.no_changes"), "No hay cambios para guardar");
return;
}
const patchData = pickFormDirtyValues(formData, dirtyFields);
const previousData = customerData;
try {
// Enviamos cambios al servidor
const created = await mutateAsync({ id: customerId, data: patchData });
// Ha ido bien -> actualizamos form con datos reales
// keepDirty = false -> deja el formulario sin cambios sin tener que esperar al siguiente render.
form.reset(created, { keepDirty: false });
if (options?.successToasts !== false) {
showSuccessToast(
t("pages.create.success.title", "Cliente modificado"),
t("pages.create.success.message", "Se ha modificado correctamente.")
);
}
options?.onCreated?.(created);
} catch (error: any) {
// Algo ha fallado -> revertimos cambios
form.reset(previousData ?? defaultCustomerFormData);
if (options?.errorToasts !== false) {
showErrorToast(t("pages.create.error.title"), error.message);
}
options?.onError?.(error, patchData);
}
},
(errors: FieldErrors<CustomerFormData>) => {
const firstKey = Object.keys(errors)[0] as keyof CustomerFormData | undefined;
if (firstKey) document.querySelector<HTMLElement>(`[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 <form>
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
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,
// mutation
isPending,
isCreateError,
createError,
// Por comodidad
FormProvider,
};
};