diff --git a/modules/customer-invoices/src/web/hooks/use-customer-invoices-query.tsx b/modules/customer-invoices/src/web/hooks/use-customer-invoices-query.tsx index eb48902d..8f9360cd 100644 --- a/modules/customer-invoices/src/web/hooks/use-customer-invoices-query.tsx +++ b/modules/customer-invoices/src/web/hooks/use-customer-invoices-query.tsx @@ -21,10 +21,11 @@ export const useInvoicesQuery = (options?: InvoicesQueryOptions) => { queryKey: CUSTOMER_INVOICES_QUERY_KEY(criteria), queryFn: async ({ signal }) => { return await dataSource.getList("customer-invoices", { - params: criteria, + ...criteria, signal, }); }, - enabled + enabled, + placeholderData: (previousData, previousQuery) => previousData, // Mantener datos previos mientras se carga nueva datos (antiguo `keepPreviousData`) }); }; diff --git a/modules/customer-invoices/src/web/pages/list/invoices-list-grid.tsx b/modules/customer-invoices/src/web/pages/list/invoices-list-grid.tsx index 73c5e6a0..08410c4f 100644 --- a/modules/customer-invoices/src/web/pages/list/invoices-list-grid.tsx +++ b/modules/customer-invoices/src/web/pages/list/invoices-list-grid.tsx @@ -2,22 +2,30 @@ import type { CellKeyDownEvent, RowClickedEvent } from "ag-grid-community"; import { useCallback, useState } from "react"; -import { DataTable } from '@repo/rdx-ui/components'; +import { DataTable, LoadingOverlay } from '@repo/rdx-ui/components'; import { Button, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@repo/shadcn-ui/components'; import { FileDownIcon, FilterIcon, SearchIcon } from 'lucide-react'; import { useNavigate } from "react-router-dom"; import { useTranslation } from "../../i18n"; -import { CustomerInvoicesPage } from '../../schemas'; +import { InvoiceSummaryFormData, InvoicesPageFormData } from '../../schemas'; import { useInvoicesListColumns } from './use-invoices-list-columns'; export type InvoiceUpdateCompProps = { - invoicesPage: CustomerInvoicesPage; + invoicesPage: InvoicesPageFormData; loading?: boolean; + + onPageChange?: (pageIndex: number) => void; + onPageSizeChange?: (pageSize: number) => void; } // Create new GridExample component -export const InvoicesListGrid = ({ invoicesPage, loading }: InvoiceUpdateCompProps) => { +export const InvoicesListGrid = ({ + invoicesPage, + loading, + onPageChange, + onPageSizeChange, +}: InvoiceUpdateCompProps) => { const { t } = useTranslation(); const navigate = useNavigate(); @@ -25,8 +33,7 @@ export const InvoicesListGrid = ({ invoicesPage, loading }: InvoiceUpdateCompPro const [statusFilter, setStatusFilter] = useState("todas"); const columns = useInvoicesListColumns(); - - const { items, page, total_pages, total_items } = invoicesPage; + const { items, page, per_page, total_pages, total_items } = invoicesPage; // Navegación accesible (click o teclado) @@ -71,10 +78,20 @@ export const InvoicesListGrid = ({ invoicesPage, loading }: InvoiceUpdateCompPro ); + const handleRowClick = useCallback( + (invoice: InvoiceSummaryFormData, _index: number, e: React.MouseEvent) => { + const url = `/customer-invoices/${invoice.id}/edit`; + if (e.metaKey || e.ctrlKey) window.open(url, "_blank", "noopener,noreferrer"); + else navigate(url); + }, + [navigate] + ); + + // Render principal return ( - <> - {/* Filters and Actions */} +
+ {/* Barra de filtros */}
@@ -103,8 +120,28 @@ export const InvoicesListGrid = ({ invoicesPage, loading }: InvoiceUpdateCompPro
- + + + {loading && ( +
+ +
+ )} +
- +
); }; \ No newline at end of file diff --git a/modules/customer-invoices/src/web/pages/list/invoices-list-page.tsx b/modules/customer-invoices/src/web/pages/list/invoices-list-page.tsx index f75cc4d0..ea52003e 100644 --- a/modules/customer-invoices/src/web/pages/list/invoices-list-page.tsx +++ b/modules/customer-invoices/src/web/pages/list/invoices-list-page.tsx @@ -2,7 +2,7 @@ import { ErrorAlert } from '@erp/customers/components'; import { AppBreadcrumb, AppContent, AppHeader, BackHistoryButton } from "@repo/rdx-ui/components"; import { Button } from "@repo/shadcn-ui/components"; import { FilePenIcon, PlusIcon } from "lucide-react"; -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { useNavigate } from "react-router-dom"; import { InvoicesListGrid, PageHeader } from '../../components'; import { useInvoicesQuery } from '../../hooks'; @@ -13,6 +13,9 @@ export const InvoiceListPage = () => { const { t } = useTranslation(); const navigate = useNavigate(); + const [pageIndex, setOageIndex] = useState(0); + const [pageSize, setPageSize] = useState(10); + const { data, isLoading, @@ -20,7 +23,8 @@ export const InvoiceListPage = () => { error, } = useInvoicesQuery({ criteria: { - pageSize: 999, + pageSize, + pageNumber: pageIndex } }); @@ -32,6 +36,15 @@ export const InvoiceListPage = () => { } }, [data]); + const handlePageChange = (newPageIndex: number) => { + // TanStack usa pageIndex 0-based → API usa 0-based también + setOageIndex(newPageIndex); + }; + + const handlePageSizeChange = (newSize: number) => { + setPageSize(newSize); + setOageIndex(0); + }; if (isError || !invoicesPageData) { return ( @@ -92,7 +105,12 @@ export const InvoiceListPage = () => {
- +
diff --git a/modules/customer-invoices/src/web/pages/list/use-invoices-list-columns.tsx b/modules/customer-invoices/src/web/pages/list/use-invoices-list-columns.tsx index eea9e1ea..59575ee1 100644 --- a/modules/customer-invoices/src/web/pages/list/use-invoices-list-columns.tsx +++ b/modules/customer-invoices/src/web/pages/list/use-invoices-list-columns.tsx @@ -4,16 +4,16 @@ import type { ColumnDef } from "@tanstack/react-table"; import * as React from "react"; import { CustomerInvoiceStatusBadge } from '../../components'; import { useTranslation } from '../../i18n'; -import { InvoicesPageFormData } from '../../schemas/invoice-resume.form.schema'; +import { InvoiceSummaryFormData } from '../../schemas/invoice-resume.form.schema'; -export function useInvoicesListColumns(): ColumnDef[] { +export function useInvoicesListColumns(): ColumnDef[] { //const { t, readOnly, currency_code, language_code } = useInvoiceContext(); const { t } = useTranslation(); // Atención: Memoizar siempre para evitar reconstrucciones y resets de estado de tabla - return React.useMemo[]>(() => [ + return React.useMemo[]>(() => [ { accessorKey: "invoice_number", header: ({ column }) => ( diff --git a/packages/rdx-ui/src/components/datatable/data-table-pagination.tsx b/packages/rdx-ui/src/components/datatable/data-table-pagination.tsx index 3cde5d36..3d3624b7 100644 --- a/packages/rdx-ui/src/components/datatable/data-table-pagination.tsx +++ b/packages/rdx-ui/src/components/datatable/data-table-pagination.tsx @@ -27,14 +27,19 @@ interface DataTablePaginationProps { export function DataTablePagination({ table, className }: DataTablePaginationProps) { const { t } = useTranslation(); - const { pageIndex, pageSize } = table.getState().pagination; - const pageCount = table.getPageCount() || 1; + const { pageIndex: rawIndex, pageSize: rawSize } = table.getState().pagination; const totalRows = (table.options.meta as DataTableMeta)?.totalItems ?? table.getFilteredRowModel().rows.length; + + // Normalización segura + const pageIndex = Number.isFinite(rawIndex) && rawIndex >= 0 ? rawIndex : 0; + const pageSize = Number.isFinite(rawSize) && rawSize > 0 ? rawSize : 10; + + const pageCount = table.getPageCount() || Math.max(1, Math.ceil((totalRows || 0) / pageSize)); const hasSelected = table.getFilteredSelectedRowModel().rows.length > 0; - // Calcula rango visible (inicio-fin) - const start = totalRows === 0 ? 0 : pageIndex * pageSize + 1; - const end = Math.min((pageIndex + 1) * pageSize, totalRows); + // Rango visible (1-based en UI) + const start = totalRows > 0 ? pageIndex * pageSize + 1 : 0; + const end = totalRows > 0 ? Math.min(start + pageSize - 1, totalRows) : 0; return (