Uecko_ERP/modules/customers/src/web/pages/update/update.tsx
2025-09-16 19:29:37 +02:00

176 lines
5.9 KiB
TypeScript

import { AppBreadcrumb, AppContent, BackHistoryButton, ButtonGroup } from "@repo/rdx-ui/components";
import { Button } from "@repo/shadcn-ui/components";
import { useNavigate } from "react-router-dom";
import { useUrlParamId } from "@erp/core/hooks";
import { useCustomerQuery, useUpdateCustomerMutation } from "../../hooks";
import { useTranslation } from "../../i18n";
import { CustomerEditForm } from "./customer-edit-form";
export const CustomerUpdate = () => {
const { t } = useTranslation();
const customerId = useUrlParamId();
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 {
mutateAsync: updateAsync,
isPending: isUpdating,
isError: isUpdateError,
error: updateError,
} = useUpdateCustomerMutation(customerId || "");
// 3) Submit con navegación condicionada por éxito
const handleSubmit = async (formData: any) => {
try {
await updateAsync(formData); // solo navegamos si no lanza
// toast?.({ title: t('pages.update.successTitle'), description: t('pages.update.successMsg') });
navigate("/customers/list");
} catch (e) {
// toast?.({ variant: 'destructive', title: t('pages.update.errorTitle'), description: (e as Error).message });
// No navegamos en caso de error
}
};
if (isLoadingCustomer) {
return (
<>
<AppBreadcrumb />
<AppContent>
<div className='flex items-center justify-between'>
<div className='space-y-2'>
<div className='h-7 w-64 rounded-md bg-muted animate-pulse' />
<div className='h-5 w-96 rounded-md bg-muted animate-pulse' />
</div>
<div className='flex items-center gap-2'>
<BackHistoryButton />
<Button disabled aria-busy>
{t("pages.update.submit")}
</Button>
</div>
</div>
<div className='mt-6 grid gap-4'>
{/* Skeleton simple para el formulario */}
<div className='h-10 w-full rounded-md bg-muted animate-pulse' />
<div className='h-10 w-full rounded-md bg-muted animate-pulse' />
<div className='h-28 w-full rounded-md bg-muted animate-pulse' />
</div>
</AppContent>
</>
);
}
if (isLoadError) {
return (
<>
<AppBreadcrumb />
<AppContent>
<div
className='mb-4 rounded-lg border border-destructive/50 bg-destructive/10 p-4'
role='alert'
aria-live='assertive'
>
<p className='font-semibold text-destructive-foreground'>
{t("pages.update.loadErrorTitle", "No se pudo cargar el cliente")}
</p>
<p className='text-sm text-destructive-foreground/90'>
{(loadError as Error)?.message ??
t("pages.update.loadErrorMsg", "Inténtalo de nuevo más tarde.")}
</p>
</div>
<div className='flex items-center justify-end'>
<BackHistoryButton />
</div>
</AppContent>
</>
);
}
if (!customerData) {
return (
<>
<AppBreadcrumb />
<AppContent>
<div className='rounded-lg border bg-card p-6'>
<h3 className='text-lg font-semibold'>
{t("pages.update.notFoundTitle", "Cliente no encontrado")}
</h3>
<p className='text-sm text-muted-foreground'>
{t("pages.update.notFoundMsg", "Revisa el identificador o vuelve al listado.")}
</p>
</div>
<div className='mt-4 flex items-center justify-end'>
<BackHistoryButton />
</div>
</AppContent>
</>
);
}
return (
<>
<AppBreadcrumb />
<AppContent>
<div className='flex items-center justify-between space-y-2'>
<div>
<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>
<ButtonGroup>
<BackHistoryButton />
<Button
type='submit'
form='customer-edit-form'
className='cursor-pointer'
disabled={isUpdating || isLoadingCustomer}
aria-busy={isUpdating}
aria-disabled={isUpdating || isLoadingCustomer}
data-state={isUpdating ? "loading" : "idle"}
>
{t("pages.update.submit")}
</Button>
</ButtonGroup>
</div>
{/* Alerta de error de actualización (si ha fallado el último intento) */}
{isUpdateError && (
<div
className='mb-2 rounded-lg border border-destructive/50 bg-destructive/10 p-3'
role='alert'
aria-live='assertive'
>
<p className='text-sm font-medium text-destructive-foreground'>
{t("pages.update.errorTitle", "No se pudo guardar los cambios")}
</p>
<p className='text-xs text-destructive-foreground/90'>
{(updateError as Error)?.message ??
t("pages.update.errorMsg", "Revisa los datos e inténtalo de nuevo.")}
</p>
</div>
)}
<div className='flex flex-1 flex-col gap-4 p-4'>
{/* Importante: proveemos un formId para que el botón del header pueda hacer submit */}
<CustomerEditForm
formId='customer-edit-form'
data={customerData}
onSubmit={handleSubmit}
isPending={isUpdating}
/>
</div>
</AppContent>
</>
);
};