179 lines
5.2 KiB
TypeScript
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;
|