Uecko_ERP/modules/customers/src/web/pages/update/customer-update.tsx

171 lines
5.2 KiB
TypeScript
Raw Normal View History

2025-09-23 16:38:20 +00:00
import { AppBreadcrumb, AppContent, BackHistoryButton } from "@repo/rdx-ui/components";
2025-09-16 17:29:37 +00:00
import { useNavigate } from "react-router-dom";
2025-09-24 10:34:04 +00:00
import { formHasAnyDirty, pickFormDirtyValues } from "@erp/core/client";
2025-09-23 16:38:20 +00:00
import { FormCommitButtonGroup, UnsavedChangesProvider, useUrlParamId } from "@erp/core/hooks";
2025-09-24 10:34:04 +00:00
import { showErrorToast, showSuccessToast, showWarningToast } from "@repo/rdx-ui/helpers";
import { FieldErrors, FormProvider } from "react-hook-form";
2025-09-22 17:43:55 +00:00
import {
CustomerEditForm,
CustomerEditorSkeleton,
ErrorAlert,
NotFoundCard,
} from "../../components";
2025-09-23 17:21:16 +00:00
import { useCustomerForm, useCustomerQuery, useUpdateCustomerMutation } from "../../hooks";
2025-09-16 17:29:37 +00:00
import { useTranslation } from "../../i18n";
2025-09-24 10:34:04 +00:00
import { CustomerFormData, defaultCustomerFormData } from "../../schemas";
2025-09-16 17:29:37 +00:00
export const CustomerUpdate = () => {
const customerId = useUrlParamId();
2025-09-22 17:43:55 +00:00
const { t } = useTranslation();
2025-09-16 17:29:37 +00:00
const navigate = useNavigate();
// 1) Estado de carga del cliente (query)
const {
data: customerData,
isLoading: isLoadingCustomer,
isError: isLoadError,
error: loadError,
} = useCustomerQuery(customerId, { enabled: !!customerId });
// 2) Estado de actualización (mutación)
const {
2025-09-23 16:38:20 +00:00
mutate,
2025-09-16 17:29:37 +00:00
isPending: isUpdating,
isError: isUpdateError,
error: updateError,
2025-09-17 17:37:41 +00:00
} = useUpdateCustomerMutation();
2025-09-16 17:29:37 +00:00
2025-09-23 17:21:16 +00:00
// 3) Form hook
const form = useCustomerForm({
2025-09-24 10:34:04 +00:00
initialValues: customerData ?? defaultCustomerFormData,
2025-09-23 17:21:16 +00:00
});
2025-09-24 10:34:04 +00:00
// 4) Submit con navegación condicionada por éxito
2025-09-23 17:21:16 +00:00
const handleSubmit = (formData: CustomerFormData) => {
2025-09-24 10:34:04 +00:00
const { dirtyFields } = form.formState;
if (!formHasAnyDirty(dirtyFields)) {
showWarningToast("No hay cambios para guardar");
return;
}
const patchData = pickFormDirtyValues(formData, dirtyFields);
2025-09-23 16:38:20 +00:00
mutate(
2025-09-24 10:34:04 +00:00
{ id: customerId!, data: patchData },
2025-09-23 16:38:20 +00:00
{
onSuccess(data) {
showSuccessToast(t("pages.update.successTitle"), t("pages.update.successMsg"));
2025-09-17 17:37:41 +00:00
2025-09-24 10:34:04 +00:00
// 🔹 limpiar el form e isDirty pasa a false
form.reset(data);
2025-09-23 16:38:20 +00:00
},
onError(error) {
showErrorToast(t("pages.update.errorTitle"), error.message);
},
2025-09-17 17:37:41 +00:00
}
2025-09-23 16:38:20 +00:00
);
2025-09-16 17:29:37 +00:00
};
2025-09-24 11:49:17 +00:00
const handleReset = () => form.reset(customerData ?? defaultCustomerFormData);
const handleBack = () => {
navigate(-1);
};
2025-09-23 17:21:16 +00:00
const handleError = (errors: FieldErrors<CustomerFormData>) => {
2025-09-19 09:29:49 +00:00
console.error("Errores en el formulario:", errors);
// Aquí puedes manejar los errores, por ejemplo, mostrar un mensaje al usuario
};
2025-09-16 17:29:37 +00:00
if (isLoadingCustomer) {
2025-09-17 17:37:41 +00:00
return <CustomerEditorSkeleton />;
2025-09-16 17:29:37 +00:00
}
if (isLoadError) {
return (
<>
<AppBreadcrumb />
<AppContent>
2025-09-17 17:37:41 +00:00
<ErrorAlert
title={t("pages.update.loadErrorTitle", "No se pudo cargar el cliente")}
message={
(loadError as Error)?.message ??
t("pages.update.loadErrorMsg", "Inténtalo de nuevo más tarde.")
}
/>
2025-09-16 17:29:37 +00:00
<div className='flex items-center justify-end'>
<BackHistoryButton />
</div>
</AppContent>
</>
);
}
2025-09-17 17:37:41 +00:00
if (!customerData)
2025-09-16 17:29:37 +00:00
return (
<>
<AppBreadcrumb />
<AppContent>
2025-09-17 17:37:41 +00:00
<NotFoundCard
title={t("pages.update.notFoundTitle", "Cliente no encontrado")}
message={t("pages.update.notFoundMsg", "Revisa el identificador o vuelve al listado.")}
/>
2025-09-16 17:29:37 +00:00
</AppContent>
</>
);
return (
<>
<AppBreadcrumb />
<AppContent>
2025-09-24 10:34:04 +00:00
<UnsavedChangesProvider isDirty={form.formState.isDirty}>
2025-09-24 15:09:37 +00:00
<div className='flex items-center justify-between space-y-6'>
2025-09-23 16:38:20 +00:00
<div className='space-y-2'>
<h2 className='text-2xl font-bold tracking-tight text-balance scroll-m-2'>
{t("pages.update.title")}
</h2>
<p className='text-muted-foreground scroll-m-20 tracking-tight text-balance'>
{t("pages.update.description")}
</p>
</div>
<FormCommitButtonGroup
2025-09-24 11:49:17 +00:00
isLoading={isUpdating}
disabled={isUpdating}
2025-09-23 16:38:20 +00:00
cancel={{
to: "/customers/list",
2025-09-24 11:49:17 +00:00
disabled: isUpdating,
2025-09-20 10:43:37 +00:00
}}
2025-09-23 16:38:20 +00:00
submit={{
2025-09-24 10:34:04 +00:00
formId: "customer-update-form",
2025-09-23 16:38:20 +00:00
disabled: isUpdating,
}}
2025-09-24 11:49:17 +00:00
onBack={() => handleBack()}
onReset={() => handleReset()}
2025-09-23 16:38:20 +00:00
/>
</div>
{/* Alerta de error de actualización (si ha fallado el último intento) */}
{isUpdateError && (
<ErrorAlert
title={t("pages.update.errorTitle", "No se pudo guardar los cambios")}
message={
(updateError as Error)?.message ??
t("pages.update.errorMsg", "Revisa los datos e inténtalo de nuevo.")
}
/>
)}
2025-09-16 17:29:37 +00:00
2025-09-24 15:09:37 +00:00
<FormProvider {...form}>
<CustomerEditForm
formId={"customer-update-form"} // para que el botón del header pueda hacer submit
onSubmit={handleSubmit}
onError={handleError}
/>
</FormProvider>
2025-09-23 16:38:20 +00:00
</UnsavedChangesProvider>
2025-09-16 17:29:37 +00:00
</AppContent>
</>
);
};