import { type ColumnDef, flexRender, getCoreRowModel, useReactTable, } from "@tanstack/react-table"; import { ChevronsLeftIcon, ChevronsRightIcon, CircleDollarSign, Pencil, SkipBackIcon, SkipForwardIcon, } from "lucide-react"; import { useMemo } from "react"; import { Button } from "@/components/ui/button"; import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "@/components/ui/pagination"; import type { Expense } from "@/lib/api"; interface ExpensesTableProps { data: Expense[]; onPay: (expense: Expense) => void; onEdit: (expense: Expense) => void; page: number; total: number; pageSize: number; onPageChange: (page: number) => void; } function formatExpenseDate(value: string) { return new Date(value).toLocaleDateString("es-AR", { timeZone: "UTC", day: "2-digit", month: "2-digit", year: "numeric", }); } function formatExpenseAmount(expense: Expense) { const isPayed = expense.status === "PAYED"; const value = isPayed && expense.amountPayed ? Number(expense.amountPayed) : Number(expense.amount); return `$${value.toLocaleString("es-AR", { minimumFractionDigits: 2 })}`; } function ExpenseStatusBadge({ status }: { status: Expense["status"] }) { const isPayed = status === "PAYED"; return ( {isPayed ? "Pagado" : "Pendiente"} ); } export function ExpensesTable({ data, onPay, onEdit, page, total, pageSize, onPageChange, }: ExpensesTableProps) { const columns = useMemo[]>( () => [ { header: "Descripción", accessorKey: "description", cell: ({ row }) => ( {row.getValue("description")} ), }, { header: "Monto", accessorKey: "amount", cell: ({ row }) => { const expense = row.original; return ( {formatExpenseAmount(expense)} ); }, }, { header: "USDC", accessorKey: "usdcEquivalent", cell: ({ row }) => { const value = row.getValue("usdcEquivalent"); if (!value) { return --; } return ( {Number(value).toLocaleString("es-AR", { minimumFractionDigits: 2, maximumFractionDigits: 2, })}{" "} USDC ); }, }, { header: "Vencimiento", accessorKey: "dueDate", cell: ({ row }) => { return ( {formatExpenseDate(row.getValue("dueDate") as string)} ); }, }, { header: "Estado", accessorKey: "status", cell: ({ row }) => { return ( ); }, }, { header: "Fecha de pago", accessorKey: "paymentDate", cell: ({ row }) => { const paymentDate = row.getValue("paymentDate"); if (!paymentDate) { return --; } return ( {formatExpenseDate(paymentDate as string)} ); }, }, { id: "actions", cell: ({ row }) => { const expense = row.original; return (
{expense.status !== "PAYED" && ( )}
); }, }, ], [onPay, onEdit], ); const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), }); const totalPages = Math.max(1, Math.ceil(total / pageSize)); const GROUP_SIZE = 3; const currentGroup = Math.floor((page - 1) / GROUP_SIZE); const startPage = currentGroup * GROUP_SIZE + 1; const endPage = Math.min(startPage + GROUP_SIZE - 1, totalPages); const firstItem = total === 0 ? 0 : (page - 1) * pageSize + 1; const lastItem = Math.min(page * pageSize, total); const mobilePagination = total > 0 ? (
{firstItem}-{lastItem} de {total} · Página {page} de {totalPages}
) : null; return (
{mobilePagination}
{data.map((expense) => (

{expense.description}

Vence {formatExpenseDate(expense.dueDate)}

{formatExpenseAmount(expense)}

{expense.paymentDate ? `Pagado ${formatExpenseDate(expense.paymentDate)}` : "Sin fecha de pago"}

{expense.usdcEquivalent && (

{Number(expense.usdcEquivalent).toLocaleString("es-AR", { minimumFractionDigits: 2, maximumFractionDigits: 2, })}{" "} USDC

)}
{expense.status !== "PAYED" && ( )}
))} {data.length === 0 && (
No hay gastos para mostrar.
)}
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( ))} ))} {table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( ))} ))} {table.getRowModel().rows.length === 0 && ( )}
{flexRender( header.column.columnDef.header, header.getContext(), )}
{flexRender(cell.column.columnDef.cell, cell.getContext())}
No hay gastos para mostrar.
{mobilePagination} {totalPages > 1 && ( { e.preventDefault(); onPageChange(1); }} href="#" aria-label="Ir a la primera página" className={page <= 1 ? "pointer-events-none opacity-50" : ""} > { e.preventDefault(); if (page > 1) onPageChange(page - 1); }} href="#" className={page <= 1 ? "pointer-events-none opacity-50" : ""} /> {currentGroup > 0 && ( { e.preventDefault(); onPageChange(startPage - 1); }} href="#" aria-label="Ir al grupo anterior" > )} {Array.from( { length: endPage - startPage + 1 }, (_, i) => startPage + i, ).map((p) => ( { e.preventDefault(); onPageChange(p); }} href="#" > {p} ))} {(currentGroup + 1) * GROUP_SIZE < totalPages && ( { e.preventDefault(); onPageChange(endPage + 1); }} href="#" aria-label="Ir al siguiente grupo" > )} { e.preventDefault(); if (page < totalPages) onPageChange(page + 1); }} href="#" className={ page >= totalPages ? "pointer-events-none opacity-50" : "" } /> { e.preventDefault(); onPageChange(totalPages); }} href="#" aria-label="Ir a la última página" className={ page >= totalPages ? "pointer-events-none opacity-50" : "" } > )}
); }