feat(expenses): add import functionality for expenses and total pending calculation

This commit is contained in:
Jose Selesan
2026-05-29 14:30:10 -03:00
parent 4b17940134
commit b1968cece9
11 changed files with 600 additions and 19 deletions

View File

@@ -7,7 +7,7 @@ import {
} from "@tanstack/react-table";
import type { Expense } from "@/lib/api";
import { Button } from "@/components/ui/button";
import { CircleDollarSign } from "lucide-react";
import { CircleDollarSign, ChevronsLeftIcon, ChevronsRightIcon, SkipBackIcon, SkipForwardIcon } from "lucide-react";
import {
Pagination,
PaginationContent,
@@ -114,6 +114,10 @@ export function ExpensesTable({ data, onPay, page, total, pageSize, onPageChange
});
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);
return (
<div className="space-y-4">
@@ -157,6 +161,16 @@ export function ExpensesTable({ data, onPay, page, total, pageSize, onPageChange
{totalPages > 1 && (
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationLink
onClick={(e) => { e.preventDefault(); onPageChange(1); }}
href="#"
aria-label="Ir a la primera página"
className={page <= 1 ? "pointer-events-none opacity-50" : ""}
>
<SkipBackIcon className="size-4" />
</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationPrevious
onClick={(e) => { e.preventDefault(); if (page > 1) onPageChange(page - 1); }}
@@ -164,7 +178,18 @@ export function ExpensesTable({ data, onPay, page, total, pageSize, onPageChange
className={page <= 1 ? "pointer-events-none opacity-50" : ""}
/>
</PaginationItem>
{Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (
{currentGroup > 0 && (
<PaginationItem>
<PaginationLink
onClick={(e) => { e.preventDefault(); onPageChange(startPage - 1); }}
href="#"
aria-label="Ir al grupo anterior"
>
<ChevronsLeftIcon className="size-4" />
</PaginationLink>
</PaginationItem>
)}
{Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i).map((p) => (
<PaginationItem key={p}>
<PaginationLink
isActive={p === page}
@@ -175,6 +200,17 @@ export function ExpensesTable({ data, onPay, page, total, pageSize, onPageChange
</PaginationLink>
</PaginationItem>
))}
{(currentGroup + 1) * GROUP_SIZE < totalPages && (
<PaginationItem>
<PaginationLink
onClick={(e) => { e.preventDefault(); onPageChange(endPage + 1); }}
href="#"
aria-label="Ir al siguiente grupo"
>
<ChevronsRightIcon className="size-4" />
</PaginationLink>
</PaginationItem>
)}
<PaginationItem>
<PaginationNext
onClick={(e) => { e.preventDefault(); if (page < totalPages) onPageChange(page + 1); }}
@@ -182,6 +218,16 @@ export function ExpensesTable({ data, onPay, page, total, pageSize, onPageChange
className={page >= totalPages ? "pointer-events-none opacity-50" : ""}
/>
</PaginationItem>
<PaginationItem>
<PaginationLink
onClick={(e) => { e.preventDefault(); onPageChange(totalPages); }}
href="#"
aria-label="Ir a la última página"
className={page >= totalPages ? "pointer-events-none opacity-50" : ""}
>
<SkipForwardIcon className="size-4" />
</PaginationLink>
</PaginationItem>
</PaginationContent>
</Pagination>
)}