This commit is contained in:
David Arranz 2026-04-12 21:20:31 +02:00
parent f80eb9acab
commit 430ae8e5ea
4 changed files with 92 additions and 65 deletions

View File

@ -1 +1,2 @@
export * from "./issued-invoices-grid-empty";
export * from "./verifactu-status-badge";

View File

@ -0,0 +1,17 @@
import { Card, CardContent } from "@repo/shadcn-ui/components";
import { cn } from "@repo/shadcn-ui/lib/utils";
import { ChartNoAxesColumnIncreasingIcon } from "lucide-react";
export const IssuedInvoicesEmptyGrid = ({ className }: React.ComponentProps<"div">) => {
return (
<Card className={cn("w-full max-w-lg", className)}>
<CardContent>
<div className="rounded-md border border-dashed p-6 text-center">
<ChartNoAxesColumnIncreasingIcon className="text-muted-foreground mx-auto size-12" />
<p className="mt-2 text-sm font-medium">No data to show</p>
<p className="text-muted-foreground mt-1 text-sm">May take 15 minutes for data to load</p>
</div>
</CardContent>
</Card>
);
};

View File

@ -17,6 +17,7 @@ import { useNavigate } from "react-router-dom";
import { useTranslation } from "../../../../i18n";
import { useIssuedInvoiceListPageController } from "../../controllers";
import { IssuedInvoicesGrid, useIssuedInvoicesGridColumns } from "../blocks";
import { IssuedInvoicesEmptyGrid } from "../components";
export const ListIssuedInvoicesPage = () => {
const { t } = useTranslation();
@ -88,63 +89,71 @@ export const ListIssuedInvoicesPage = () => {
</Alert>
{/* Search and filters */}
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<SimpleSearchInput
loading={listCtrl.isLoading}
onSearchChange={listCtrl.setSearchValue}
value={listCtrl.search}
/>
{listCtrl.data.totalItems === 0 && <IssuedInvoicesEmptyGrid className="mx-auto" />}
<Select
onValueChange={(value) => listCtrl.setStatusFilter(value ?? "all")}
value={listCtrl.statusFilter}
>
<SelectTrigger className="w-full sm:w-48">
<FilterIcon aria-hidden className="mr-2 size-4" />
<SelectValue placeholder={t("filters.status")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">{t("catalog.issued_invoices.status.all.label")}</SelectItem>
<SelectItem value="Correcto">
{t("catalog.issued_invoices.status.correcto.label")}
</SelectItem>
<SelectItem value="Pendiente">
{t("catalog.issued_invoices.status.pendiente.label")}
</SelectItem>
<SelectItem value="aceptado_con_error">
{t("catalog.issued_invoices.status.aceptado_con_error.label")}
</SelectItem>
<SelectItem value="incorrecto">
{t("catalog.issued_invoices.status.incorrecto.label")}
</SelectItem>
<SelectItem value="duplicado">
{t("catalog.issued_invoices.status.duplicado.label")}
</SelectItem>
<SelectItem value="anulado">
{t("catalog.issued_invoices.status.anulado.label")}
</SelectItem>
<SelectItem value="factura_inexistente">
{t("catalog.issued_invoices.status.duplicado.label")}
</SelectItem>
<SelectItem value="rechazado">
{t("catalog.issued_invoices.status.rechazado.label")}
</SelectItem>
</SelectContent>
</Select>
</div>
<div className="min-h-0 flex-1 overflow-auto">
<IssuedInvoicesGrid
columns={columns}
data={listCtrl.data}
loading={listCtrl.isLoading}
onPageChange={listCtrl.setPageIndex}
onPageSizeChange={listCtrl.setPageSize}
// acciones rápidas del grid → page controller
//onRowClick={(id) => navigate(`/issuedInvoices/${id}`)}
pageIndex={listCtrl.pageIndex}
pageSize={listCtrl.pageSize}
/>
</div>
{listCtrl.data.totalItems !== 0 && (
<>
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<SimpleSearchInput
loading={listCtrl.isLoading}
onSearchChange={listCtrl.setSearchValue}
value={listCtrl.search}
/>
<Select
onValueChange={(value) => listCtrl.setStatusFilter(value ?? "all")}
value={listCtrl.statusFilter}
>
<SelectTrigger className="w-full sm:w-48">
<FilterIcon aria-hidden className="mr-2 size-4" />
<SelectValue placeholder={t("filters.status")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">
{t("catalog.issued_invoices.status.all.label")}
</SelectItem>
<SelectItem value="Correcto">
{t("catalog.issued_invoices.status.correcto.label")}
</SelectItem>
<SelectItem value="Pendiente">
{t("catalog.issued_invoices.status.pendiente.label")}
</SelectItem>
<SelectItem value="aceptado_con_error">
{t("catalog.issued_invoices.status.aceptado_con_error.label")}
</SelectItem>
<SelectItem value="incorrecto">
{t("catalog.issued_invoices.status.incorrecto.label")}
</SelectItem>
<SelectItem value="duplicado">
{t("catalog.issued_invoices.status.duplicado.label")}
</SelectItem>
<SelectItem value="anulado">
{t("catalog.issued_invoices.status.anulado.label")}
</SelectItem>
<SelectItem value="factura_inexistente">
{t("catalog.issued_invoices.status.duplicado.label")}
</SelectItem>
<SelectItem value="rechazado">
{t("catalog.issued_invoices.status.rechazado.label")}
</SelectItem>
</SelectContent>
</Select>
</div>
<div className="min-h-0 flex-1 overflow-auto">
<IssuedInvoicesGrid
columns={columns}
data={listCtrl.data}
loading={listCtrl.isLoading}
onPageChange={listCtrl.setPageIndex}
onPageSizeChange={listCtrl.setPageSize}
// acciones rápidas del grid → page controller
//onRowClick={(id) => navigate(`/issuedInvoices/${id}`)}
pageIndex={listCtrl.pageIndex}
pageSize={listCtrl.pageSize}
/>
</div>
</>
)}
</AppContent>
</section>
);

View File

@ -3,21 +3,21 @@ import { UserSearchIcon } from "lucide-react";
export const CustomerEmptyCard = (props: React.ComponentProps<"button">) => {
return (
<button
aria-label="Seleccionar cliente"
className="group w-full cursor-pointer rounded-lg border border-border bg-card p-4 transition hover:bg-accent/50 hover:border-primary"
tabIndex={0}
type='button'
className='group w-full cursor-pointer rounded-lg border border-border bg-card p-4 transition hover:bg-accent/50 hover:border-primary'
aria-label='Seleccionar cliente'
type="button"
{...props}
>
<div className='flex items-center gap-4 group-hover:text-primary'>
<div className='flex size-12 items-center justify-center rounded-full bg-muted group-hover:bg-primary/15'>
<UserSearchIcon className='size-6 text-muted-foreground group-hover:text-primary' />
<div className="flex items-center gap-4 group-hover:text-primary">
<div className="flex size-12 items-center justify-center rounded-full bg-muted group-hover:bg-primary/15">
<UserSearchIcon className="size-6 text-muted-foreground group-hover:text-primary" />
</div>
<div className='flex-1 text-left'>
<h3 className='font-medium text-muted-foreground mb-1 group-hover:text-primary'>
<div className="flex-1 text-left">
<h3 className="font-medium text-muted-foreground mb-1 group-hover:text-primary">
Seleccionar Cliente
</h3>
<p className='text-sm text-muted-foreground/70 group-hover:text-primary'>
<p className="text-sm text-muted-foreground/70 group-hover:text-primary">
Haz clic para buscar un cliente existente o crear uno nuevo
</p>
</div>