Uecko_ERP/modules/customers/src/web/create/controllers/use-customer-create.controller.ts
2026-07-14 20:39:31 +02:00

179 lines
5.2 KiB
TypeScript

import { applyValidationErrorCollection } from "@erp/core";
import { formHasAnyDirty } from "@erp/core/client";
import { useHookForm } from "@erp/core/hooks";
import {
UniqueID,
type ValidationErrorCollection,
isValidationErrorCollection,
} from "@repo/rdx-ddd";
import {
focusFirstInputFormError,
showErrorToast,
showSuccessToast,
showWarningToast,
} from "@repo/rdx-ui/helpers";
import { useId } from "react";
import type { FieldErrors } from "react-hook-form";
import { useTranslation } from "../../i18n";
import { type CreateCustomerParams, type Customer, useCustomerCreateMutation } from "../../shared";
import { type CustomerCreateForm, CustomerCreateFormSchema } from "../entities";
import { buildCreateCustomerParams, buildCustomerCreateDefault } from "../utils";
import { buildCustomerCreateData } from "../utils/build-customer-create-data";
import { useCustomerCreateFiscalController } from "./use-customer-create-fiscal-controller";
export interface UseCustomerCreateControllerOptions {
onCreated?(created: Customer): void;
successToasts?: boolean; // mostrar o no toast automáticamente
onError?(error: Error, params: CreateCustomerParams): void;
errorToasts?: boolean; // mostrar o no toast automáticamente
}
const normalizeSubmitError = (error: unknown): Error | ValidationErrorCollection => {
if (isValidationErrorCollection(error)) {
return error;
}
if (error instanceof Error) {
return error;
}
return new Error("Unknown error");
};
export const useCreateCustomerController = (options?: UseCustomerCreateControllerOptions) => {
const { t } = useTranslation();
const formId = useId(); // id único por instancia
// 1) Estado de creación (mutación)
const {
mutateAsync,
isPending: isCreating,
isError: isCreateError,
error: createError,
} = useCustomerCreateMutation();
// 2) Form hook
const form = useHookForm<CustomerCreateForm>({
resolverSchema: CustomerCreateFormSchema,
initialValues: buildCustomerCreateDefault(),
disabled: isCreating,
});
/** Handlers */
const resetForm = () => {
form.reset(buildCustomerCreateDefault(), { keepDirty: false });
};
const submitHandler = form.handleSubmit(
async (formData: CustomerCreateForm) => {
if (!formHasAnyDirty(form.formState.dirtyFields)) {
showWarningToast(
t("create.feedback.no_changes.title"),
t("create.feedback.no_changes.message")
);
return;
}
const data = buildCustomerCreateData(formData);
const customerId = UniqueID.generateNewID().toString();
const params = buildCreateCustomerParams(customerId, data);
console.log("Enviando creación con params:", params);
try {
const created = await mutateAsync(params);
// Ha ido bien -> actualizamos form con datos por defecto
// keepDirty = false -> deja el formulario sin cambios sin tener que esperar al siguiente render.
resetForm();
if (options?.successToasts !== false) {
showSuccessToast(
t("create.feedback.success.title"),
t("create.feedback.success.message")
);
}
options?.onCreated?.(created);
} catch (error: unknown) {
const normalizedError = normalizeSubmitError(error);
// No revierto el form para que no se pierdan los cambios que
// ha hecho el usuario y no han sido guardados.
/*resetForm()*/
if (isValidationErrorCollection(normalizedError)) {
applyValidationErrorCollection(form, normalizedError);
console.log("Errores de validación aplicados al form:", form.formState.errors);
focusFirstInputFormError(form);
if (options?.errorToasts !== false) {
showWarningToast(
t("create.feedback.validation.title"),
t("create.feedback.validation.message")
);
}
options?.onError?.(normalizedError, params);
return;
}
if (options?.errorToasts !== false) {
showErrorToast(
t("create.errors.title"),
normalizedError.message || t("create.errors.unknown")
);
}
options?.onError?.(normalizedError, params);
}
},
(errors: FieldErrors<CustomerCreateForm>) => {
console.error(errors);
focusFirstInputFormError(form);
showWarningToast(
t("common.feedback.validation.title"),
t("common.feedback.validation.message")
);
}
);
// Evento onSubmit ya preparado para el <form>
const onSubmit = (event: React.SubmitEvent<HTMLFormElement>) => {
event.stopPropagation(); // <-- evita que el submit se propage por los padre en el árbol DOM
submitHandler(event);
};
const fiscalCtrl = useCustomerCreateFiscalController();
return {
// form
form,
formId,
// handlers del form
onSubmit,
resetForm,
// mutation
isCreating,
isCreateError,
createError,
// Otros controladores
fiscalCtrl,
// No devolver FormProvider, así el controller es más
// flexible y reusable (p.ej. para un modal)
// FormProvider,
};
};
export const useCustomerCreateController = useCreateCustomerController;