import { type ReactNode } from 'react'; import { type ColumnDef } from '@tanstack/react-table'; import type { GetDynamicColumns, RowActionPredicate } from './dynamic-columns-shim'; import type { ActionDefinition } from './types'; export interface DynamicTableProps { model: string; endpoint?: string; /** * Endpoint base for WRITES (row delete, bulk delete) when it differs from * the list endpoint — e.g. listing from a role-scoped `/dynamic//me` * while deleting against `/dynamic//`. Falls back to `endpoint`. */ mutationEndpoint?: string; enableUrlSync?: boolean; /** * Initial sort applied when there is no user/URL sorting yet. It seeds the * `sorting` state, so a `?sortBy=` deep-link wins over it (when * `enableUrlSync`), and the user can still change or clear the sort via * the column headers. Only read on mount. */ defaultSort?: { id: string; desc?: boolean; }; /** * Hide the import action on THIS view even when the model supports it. * A role-scoped view (e.g. a rep seeing only their own records) usually * wants the table without a bulk-import entry point, while the same model * keeps it on the admin view. Absent → the model's metadata decides. */ hideImport?: boolean; /** Hide the export action on this view. See `hideImport`. */ hideExport?: boolean; hiddenColumns?: string[]; /** * Row-action ALLOWLIST for this table instance, by action key (manifest * v3 NavItem.actions, carried through the host's nav item for this route). * When set, only these row actions render — even though the model may * declare more. Lets two views of the SAME model differ: a generic list * keeps every action, while a purpose-built screen (e.g. a credit-approval * queue) shows only authorize_credit/reject_credit instead of every action * SalesOrder declares (Generar factura, Cancelar, etc. included). * Undefined → every row action the model declares (unchanged default). */ allowedActionKeys?: string[]; onAction?: (action: string, row: any) => void; /** * Called when the user clicks anywhere on a data row (not on a checkbox, * action button, or interactive element inside the cell). When provided, * each row becomes focusable (cursor-pointer). Absent → rows are not * clickable and the behaviour is unchanged. */ onRowClick?: (row: any) => void; /** * Consumer-side per-row gate for row actions, AND-ed with the metadata * gates (`requiresState` + `condition`): it can only hide more. Receives the * full action definition (adapt an `(actionKey, row)` predicate with * `(a, row) => legacy(a.key, row)`). A throw hides the action (fail-closed, * `console.error`). Memoize it (module-level fn or `useCallback`). * Omitted → behaviour unchanged. Not a substitute for backend authorization. */ isRowActionVisible?: RowActionPredicate; refreshTrigger?: any; defaultFilters?: Record; extraColumns?: ColumnDef[]; /** * Host-provided factory that turns metadata into TanStack column defs. * Lives in the host because the rendered cells depend on the host's * design system (Badge, Avatar, MediaGallery, phone flags, etc.). * Optional — a sensible default maps each column to { accessorKey, header }. */ getDynamicColumns?: GetDynamicColumns; /** * IANA timezone (e.g. the org's `America/Mexico_City`) used to render * datetime/timestamp cells. When provided, instants are displayed in this * zone instead of the viewer's browser zone, so the day/time never shifts. * Optional — omitting it preserves the legacy browser-local formatting. */ timeZone?: string; /** * ISO 4217 currency code (e.g. the org's `MXN`) used as the fallback for * money cells (`type:'number'` + `cellStyle:'currency'`) that don't carry * an explicit per-column currency. Optional — defaults to 'USD'. */ currency?: string; /** * Pagination mode. * - 'pages' (default): classic pager footer (DataTablePagination — rows * per page selector, "página X de Y", first/prev/next/last). Each page * change fetches and REPLACES the visible rows (page/per_page against * the same server params the infinite mode uses; pageCount derives * from meta.total). The chosen page size persists per table in * localStorage (keyed by model). Changing any filter/sort/search * resets to page 1. * - 'infinite': rows accumulate as the user scrolls (a sentinel at the * bottom fetches + appends the next page, deduped by id, respecting * the active filters/search). Changing any filter/sort/search resets * to page 1. */ pagination?: 'pages' | 'infinite'; /** * @deprecated Use `pagination="infinite"`. Kept for back-compat: when the * new `pagination` prop is not provided, `infiniteScroll` still selects the * mode exactly as before. */ infiniteScroll?: boolean; /** * Row virtualization. When the visible row model has at least N rows, * only viewport rows are mounted (helps infinite scroll and large page * sizes). `true` / omitted → default threshold (40). `false` → always * render every row. A positive number overrides the threshold. */ virtualizeRows?: boolean | number; /** * Host-provided actions rendered in the floating bar that appears while * rows are selected, next to the built-in "Eliminar". Pass a node, or a * function that receives the current selection: * - `selectedRows`: the selected row objects (current page only); * - `selectedIds`: their `id`s; * - `clearSelection()`: unselect everything (call after a successful action); * - `refresh()`: reload the list. * The selection is cleared whenever the page, page size, sort, search or * column filters change (classic pagination), so a bulk action can never * hit rows the user no longer sees. The host owns the write call and its * authorization — the table only supplies the selection. */ extraBulkActions?: ReactNode | ((ctx: DynamicTableBulkContext) => ReactNode); /** Hide the built-in bulk "Eliminar" (e.g. a model whose rows must not be deleted in batch). */ hideBulkDelete?: boolean; /** * Called once when a built-in bulk delete finishes, with the ids that were * deleted and the ones refused (`message` / `status` come from the server * response when available; a cancelled reason prompt has no message). Lets a * host persist or log the outcome. The table's own toasts are unaffected. */ onBulkDeleteResult?: (result: BulkDeleteResult) => void; /** * First-use content shown instead of "No se encontraron resultados" when the * list loaded fine, is empty, and no search/filter is active. Never shown * after a load error (a retry is offered) or a 403. */ emptyState?: ReactNode; } /** Context handed to `DynamicTableProps.extraBulkActions` when it is a function. */ export interface DynamicTableBulkContext { selectedRows: any[]; selectedIds: Array; clearSelection: () => void; refresh: () => void; /** Metadata actions visible to the current user (capability-gated when a PermissionsProvider is mounted). */ actions: ActionDefinition[]; /** `true` when the user may run `actionKey` on this model; always `true` without a PermissionsProvider. */ can: (actionKey: string) => boolean; } /** Outcome of a built-in bulk delete (see `onBulkDeleteResult`). */ export interface BulkDeleteResult { succeeded: Array; failed: Array<{ id: string | number; message?: string; status?: number; }>; } export declare function DynamicTable({ model, endpoint, mutationEndpoint, enableUrlSync, defaultSort, hideImport, hideExport, hiddenColumns, allowedActionKeys, onAction, onRowClick, isRowActionVisible: rowActionPredicate, refreshTrigger, defaultFilters, extraColumns, getDynamicColumns, timeZone: timeZoneProp, currency: currencyProp, pagination: paginationMode, infiniteScroll: infiniteScrollProp, virtualizeRows, extraBulkActions, hideBulkDelete, onBulkDeleteResult, emptyState, }: DynamicTableProps): import("react").JSX.Element; //# sourceMappingURL=dynamic-table.d.ts.map