169 lines
5.6 KiB
TypeScript
169 lines
5.6 KiB
TypeScript
import {
|
|
Button,
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
Tabs,
|
|
TabsContent,
|
|
TabsList,
|
|
TabsTrigger,
|
|
} from "@repo/shadcn-ui/components";
|
|
import { X } from "lucide-react";
|
|
|
|
import { FieldErrors, FormProvider } from "react-hook-form";
|
|
import { CustomerAdditionalConfigFields } from "../../components/editor/customer-additional-config-fields";
|
|
import { CustomerAddressFields } from "../../components/editor/customer-address-fields";
|
|
import { CustomerBasicInfoFields } from "../../components/editor/customer-basic-info-fields";
|
|
|
|
import { useNavigate } from "react-router-dom";
|
|
|
|
import { formHasAnyDirty, pickFormDirtyValues } from "@erp/core/client";
|
|
import { UnsavedChangesProvider, useHookForm } from "@erp/core/hooks";
|
|
import { showErrorToast, showSuccessToast, showWarningToast } from "@repo/rdx-ui/helpers";
|
|
import { CustomerEditorSkeleton } from "../../components";
|
|
import { useCustomerQuery, useUpdateCustomer } from "../../hooks";
|
|
import { useTranslation } from "../../i18n";
|
|
import { CustomerFormData, CustomerFormSchema, defaultCustomerFormData } from "../../schemas";
|
|
|
|
interface CustomerEditModalProps {
|
|
customerId: string;
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
}
|
|
|
|
export function CustomerEditModal({ customerId, open, onOpenChange }: CustomerEditModalProps) {
|
|
const { t } = useTranslation();
|
|
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 {
|
|
mutate,
|
|
isPending: isUpdating,
|
|
isError: isUpdateError,
|
|
error: updateError,
|
|
} = useUpdateCustomer();
|
|
|
|
// 3) Form hook
|
|
const form = useHookForm<CustomerFormData>({
|
|
resolverSchema: CustomerFormSchema,
|
|
initialValues: customerData ?? defaultCustomerFormData,
|
|
disabled: isUpdating,
|
|
});
|
|
|
|
// 4) Submit con navegación condicionada por éxito
|
|
const handleSubmit = (formData: CustomerFormData) => {
|
|
const { dirtyFields } = form.formState;
|
|
|
|
if (!formHasAnyDirty(dirtyFields)) {
|
|
showWarningToast("No hay cambios para guardar");
|
|
return;
|
|
}
|
|
|
|
const patchData = pickFormDirtyValues(formData, dirtyFields);
|
|
mutate(
|
|
{ id: customerId!, data: patchData },
|
|
{
|
|
onSuccess(data) {
|
|
showSuccessToast(t("pages.update.successTitle"), t("pages.update.successMsg"));
|
|
|
|
// 🔹 limpiar el form e isDirty pasa a false
|
|
form.reset(data);
|
|
},
|
|
onError(error) {
|
|
showErrorToast(t("pages.update.errorTitle"), error.message);
|
|
},
|
|
}
|
|
);
|
|
};
|
|
|
|
const handleReset = () => form.reset(customerData ?? defaultCustomerFormData);
|
|
|
|
const handleBack = () => {
|
|
navigate(-1);
|
|
};
|
|
|
|
const handleError = (errors: FieldErrors<CustomerFormData>) => {
|
|
console.error("Errores en el formulario:", errors);
|
|
// Aquí puedes manejar los errores, por ejemplo, mostrar un mensaje al usuario
|
|
};
|
|
|
|
if (isLoadingCustomer || isLoadError) {
|
|
return <CustomerEditorSkeleton />;
|
|
}
|
|
|
|
return (
|
|
<UnsavedChangesProvider isDirty={form.formState.isDirty}>
|
|
<FormProvider {...form}>
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className='max-h-[90vh] max-w-3xl overflow-hidden p-0'>
|
|
<DialogHeader className='border-b px-6 py-4'>
|
|
<div className='flex items-center justify-between'>
|
|
<div>
|
|
<DialogTitle className='text-xl'>Editar Cliente</DialogTitle>
|
|
<DialogDescription className='mt-1'>
|
|
Modifica la información del cliente
|
|
</DialogDescription>
|
|
</div>
|
|
<Button
|
|
variant='ghost'
|
|
size='icon'
|
|
onClick={() => onOpenChange(false)}
|
|
className='size-8'
|
|
>
|
|
<X className='h-4 w-4' />
|
|
</Button>
|
|
</div>
|
|
</DialogHeader>
|
|
|
|
<Tabs defaultValue='basic' className='flex h-full flex-col'>
|
|
<TabsList className='mx-6 mt-4 grid w-auto grid-cols-4 gap-2'>
|
|
<TabsTrigger value='basic'>Información Básica</TabsTrigger>
|
|
<TabsTrigger value='address'>Dirección</TabsTrigger>
|
|
<TabsTrigger value='contact'>Contacto</TabsTrigger>
|
|
<TabsTrigger value='preferences'>Preferencias</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<div className='flex-1 overflow-y-auto px-6 py-4'>
|
|
<TabsContent value='basic' className='mt-0'>
|
|
<CustomerBasicInfoFields />
|
|
</TabsContent>
|
|
|
|
<TabsContent value='address' className='mt-0'>
|
|
<CustomerAddressFields />
|
|
</TabsContent>
|
|
|
|
<TabsContent value='contact' className='mt-0'>
|
|
<CustomerAddressFields />
|
|
</TabsContent>
|
|
|
|
<TabsContent value='preferences' className='mt-0'>
|
|
<CustomerAdditionalConfigFields />
|
|
</TabsContent>
|
|
</div>
|
|
|
|
<div className='border-t px-6 py-4'>
|
|
<div className='flex justify-end gap-3'>
|
|
<Button variant='outline' onClick={() => onOpenChange(false)}>
|
|
Cancelar
|
|
</Button>
|
|
<Button onClick={handleSubmit}>Guardar</Button>
|
|
</div>
|
|
</div>
|
|
</Tabs>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</FormProvider>
|
|
</UnsavedChangesProvider>
|
|
);
|
|
}
|