Uecko_ERP/modules/customers/src/web/pages/view/customer-view-page.tsx

333 lines
13 KiB
TypeScript

import { PageHeader } from "@erp/core/components";
import { useUrlParamId } from "@erp/core/hooks";
import { AppContent, AppHeader, BackHistoryButton } from "@repo/rdx-ui/components";
import {
Badge,
Button,
Card,
CardContent,
CardHeader,
CardTitle,
} from "@repo/shadcn-ui/components";
import {
Banknote,
EditIcon,
FileText,
Globe,
Languages,
Mail,
MapPin,
MoreVertical,
Phone,
Smartphone,
} from "lucide-react";
import { useNavigate } from "react-router-dom";
import { CustomerEditorSkeleton, ErrorAlert } from "../../components";
import { useCustomerQuery } from "../../hooks";
import { useTranslation } from "../../i18n";
export const CustomerViewPage = () => {
const customerId = useUrlParamId();
const { t } = useTranslation();
const navigate = useNavigate();
// 1) Estado de carga del cliente (query)
const {
data: customer,
isLoading: isLoadingCustomer,
isError: isLoadError,
error: loadError,
} = useCustomerQuery(customerId, { enabled: !!customerId });
if (isLoadingCustomer) {
return <CustomerEditorSkeleton />;
}
if (isLoadError) {
return (
<>
<AppContent>
<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.")
}
/>
<div className='flex items-center justify-end'>
<BackHistoryButton />
</div>
</AppContent>
</>
);
}
return (
<>
<AppHeader>
<PageHeader
backIcon
title={
<div className='flex flex-wrap items-center gap-2'>
{customer?.name}{" "}
{customer?.trade_name && (
<span className='text-muted-foreground'>({customer.trade_name})</span>
)}
</div>
}
description={
<div className='mt-2 flex items-center gap-3'>
<Badge variant='secondary' className='font-mono'>
{customer?.tin}
</Badge>
<Badge variant='outline'>{customer?.is_company ? "Empresa" : "Persona"}</Badge>
</div>
}
rightSlot={
<div className='flex gap-2'>
<Button variant='outline' size='icon' onClick={() => navigate("/customers/list")}>
<MoreVertical className='h-4 w-4' />
</Button>
<Button>
<EditIcon className='mr-2 h-4 w-4' />
Editar
</Button>
</div>
}
/>
</AppHeader>
<AppContent>
{/* Main Content Grid */}
<div className='grid gap-6 md:grid-cols-2'>
{/* Información Básica */}
<Card>
<CardHeader>
<CardTitle className='flex items-center gap-2 text-lg'>
<FileText className='size-5 text-primary' />
Información Básica
</CardTitle>
</CardHeader>
<CardContent className='space-y-4'>
<div>
<dt className='text-sm font-medium text-muted-foreground'>Nombre</dt>
<dd className='mt-1 text-base text-foreground'>{customer?.name}</dd>
</div>
<div>
<dt className='text-sm font-medium text-muted-foreground'>Referencia</dt>
<dd className='mt-1 font-mono text-base text-foreground'>{customer?.reference}</dd>
</div>
<div>
<dt className='text-sm font-medium text-muted-foreground'>Registro Legal</dt>
<dd className='mt-1 text-base text-foreground'>{customer?.legal_record}</dd>
</div>
<div>
<dt className='text-sm font-medium text-muted-foreground'>Impuestos por Defecto</dt>
<dd className='mt-1'>
{customer?.default_taxes.map((tax) => (
<Badge key={tax} variant={"secondary"}>
{tax}
</Badge>
))}
</dd>
</div>
</CardContent>
</Card>
{/* Dirección */}
<Card>
<CardHeader>
<CardTitle className='flex items-center gap-2 text-lg'>
<MapPin className='size-5 text-primary' />
Dirección
</CardTitle>
</CardHeader>
<CardContent className='space-y-4'>
<div>
<dt className='text-sm font-medium text-muted-foreground'>Calle</dt>
<dd className='mt-1 text-base text-foreground'>
{customer?.street}
{customer?.street2 && (
<>
<br />
{customer?.street2}
</>
)}
</dd>
</div>
<div className='grid grid-cols-2 gap-4'>
<div>
<dt className='text-sm font-medium text-muted-foreground'>Ciudad</dt>
<dd className='mt-1 text-base text-foreground'>{customer?.city}</dd>
</div>
<div>
<dt className='text-sm font-medium text-muted-foreground'>Código Postal</dt>
<dd className='mt-1 text-base text-foreground'>{customer?.postal_code}</dd>
</div>
</div>
<div className='grid grid-cols-2 gap-4'>
<div>
<dt className='text-sm font-medium text-muted-foreground'>Provincia</dt>
<dd className='mt-1 text-base text-foreground'>{customer?.province}</dd>
</div>
<div>
<dt className='text-sm font-medium text-muted-foreground'>País</dt>
<dd className='mt-1 text-base text-foreground'>{customer?.country}</dd>
</div>
</div>
</CardContent>
</Card>
{/* Información de Contacto */}
<Card className='md:col-span-2'>
<CardHeader>
<CardTitle className='flex items-center gap-2 text-lg'>
<Mail className='size-5 text-primary' />
Información de Contacto
</CardTitle>
</CardHeader>
<CardContent>
<div className='grid gap-6 md:grid-cols-2'>
{/* Contacto Principal */}
<div className='space-y-4'>
<h3 className='font-semibold text-foreground'>Contacto Principal</h3>
{customer?.email_primary && (
<div className='flex items-start gap-3'>
<Mail className='mt-0.5 h-4 w-4 text-muted-foreground' />
<div className='flex-1'>
<dt className='text-sm font-medium text-muted-foreground'>Email</dt>
<dd className='mt-1 text-base text-foreground'>
{customer?.email_primary}
</dd>
</div>
</div>
)}
{customer?.mobile_primary && (
<div className='flex items-start gap-3'>
<Smartphone className='mt-0.5 h-4 w-4 text-muted-foreground' />
<div className='flex-1'>
<dt className='text-sm font-medium text-muted-foreground'>Móvil</dt>
<dd className='mt-1 text-base text-foreground'>
{customer?.mobile_primary}
</dd>
</div>
</div>
)}
{customer?.phone_primary && (
<div className='flex items-start gap-3'>
<Phone className='mt-0.5 h-4 w-4 text-muted-foreground' />
<div className='flex-1'>
<dt className='text-sm font-medium text-muted-foreground'>Teléfono</dt>
<dd className='mt-1 text-base text-foreground'>
{customer?.phone_primary}
</dd>
</div>
</div>
)}
</div>
{/* Contacto Secundario */}
<div className='space-y-4'>
<h3 className='font-semibold text-foreground'>Contacto Secundario</h3>
{customer?.email_secondary && (
<div className='flex items-start gap-3'>
<Mail className='mt-0.5 h-4 w-4 text-muted-foreground' />
<div className='flex-1'>
<dt className='text-sm font-medium text-muted-foreground'>Email</dt>
<dd className='mt-1 text-base text-foreground'>
{customer?.email_secondary}
</dd>
</div>
</div>
)}
{customer?.mobile_secondary && (
<div className='flex items-start gap-3'>
<Smartphone className='mt-0.5 h-4 w-4 text-muted-foreground' />
<div className='flex-1'>
<dt className='text-sm font-medium text-muted-foreground'>Móvil</dt>
<dd className='mt-1 text-base text-foreground'>
{customer?.mobile_secondary}
</dd>
</div>
</div>
)}
{customer?.phone_secondary && (
<div className='flex items-start gap-3'>
<Phone className='mt-0.5 h-4 w-4 text-muted-foreground' />
<div className='flex-1'>
<dt className='text-sm font-medium text-muted-foreground'>Teléfono</dt>
<dd className='mt-1 text-base text-foreground'>
{customer?.phone_secondary}
</dd>
</div>
</div>
)}
</div>
{/* Otros Contactos */}
{(customer?.website || customer?.fax) && (
<div className='space-y-4 md:col-span-2'>
<h3 className='font-semibold text-foreground'>Otros</h3>
<div className='grid gap-4 md:grid-cols-2'>
{customer?.website && (
<div className='flex items-start gap-3'>
<Globe className='mt-0.5 h-4 w-4 text-muted-foreground' />
<div className='flex-1'>
<dt className='text-sm font-medium text-muted-foreground'>Sitio Web</dt>
<dd className='mt-1 text-base text-primary hover:underline'>
<a href={customer?.website} target='_blank' rel='noopener noreferrer'>
{customer?.website}
</a>
</dd>
</div>
</div>
)}
{customer?.fax && (
<div className='flex items-start gap-3'>
<Phone className='mt-0.5 h-4 w-4 text-muted-foreground' />
<div className='flex-1'>
<dt className='text-sm font-medium text-muted-foreground'>Fax</dt>
<dd className='mt-1 text-base text-foreground'>{customer?.fax}</dd>
</div>
</div>
)}
</div>
</div>
)}
</div>
</CardContent>
</Card>
{/* Preferencias */}
<Card className='md:col-span-2'>
<CardHeader>
<CardTitle className='flex items-center gap-2 text-lg'>
<Languages className='size-5 text-primary' />
Preferencias
</CardTitle>
</CardHeader>
<CardContent>
<div className='grid gap-6 md:grid-cols-2'>
<div className='flex items-start gap-3'>
<Languages className='mt-0.5 h-4 w-4 text-muted-foreground' />
<div className='flex-1'>
<dt className='text-sm font-medium text-muted-foreground'>Idioma Preferido</dt>
<dd className='mt-1 text-base text-foreground'>{customer?.language_code}</dd>
</div>
</div>
<div className='flex items-start gap-3'>
<Banknote className='mt-0.5 h-4 w-4 text-muted-foreground' />
<div className='flex-1'>
<dt className='text-sm font-medium text-muted-foreground'>Moneda Preferida</dt>
<dd className='mt-1 text-base text-foreground'>{customer?.currency_code}</dd>
</div>
</div>
</div>
</CardContent>
</Card>
</div>
</AppContent>
</>
);
};