FesaComponents

DataTable

Tabla con paginación, orden, búsqueda y filtros en servidor.

La tabla pide cada página a una Server Action tuya y guarda su estado en la URL (page, pageSize, q, sortBy, sortDir y una clave por filtro).

actions.ts
"use server";

import type { DataTableFetchParams, DataTableFetchResult } from "@fesa/components/datatable";

export async function getInvoicesPageAction(
  params: DataTableFetchParams
): Promise<DataTableFetchResult<InvoiceRow>> {
  const sort = params.sorting[0];
  const [rows, total] = await Promise.all([
    db.invoice.findMany({
      orderBy: sort ? { [sort.id]: sort.desc ? "desc" : "asc" } : undefined,
      skip: params.pageIndex * params.pageSize,
      take: params.pageSize,
    }),
    db.invoice.count(),
  ]);
  return { data: rows, totalRows: total };
}
view.tsx
import { parseTableSearchParams, type PageSearchParams } from "@fesa/components/datatable/server";

export async function InvoicesView({ searchParams }: { searchParams?: PageSearchParams }) {
  const tableParams = parseTableSearchParams((await searchParams) ?? {}, { defaultPageSize: 20 });
  const first = await getInvoicesPageAction(tableParams);
  return <InvoicesClient initialData={first.data} initialTotalRows={first.totalRows} />;
}
client.tsx
"use client";

import { SmartDataTable, useSmartTable } from "@fesa/components/datatable";

export function InvoicesClient({ initialData, initialTotalRows }: Props) {
  const table = useSmartTable<InvoiceRow>({
    fetchPage: (params) => getInvoicesPageAction(params),
    defaultPageSize: 20,
  });

  return (
    <SmartDataTable
      {...table.smartProps}
      initialData={initialData}
      initialTotalRows={initialTotalRows}
      columns={columns}
    />
  );
}