From 4ac90699b6ad681c784b84298659ee1df3241d6e Mon Sep 17 00:00:00 2001 From: Marco Pedone Date: Wed, 18 Mar 2026 14:54:33 +0100 Subject: [PATCH] feat: implement DataTableProvider for centralized data management and refactor related components --- .../src/components/custom_ui/data-table.tsx | 197 +++++++++--------- .../custom_ui/dataTable-ViewToggle.tsx | 13 +- .../custom_ui/dataTable-pagination.tsx | 15 +- .../custom_ui/dataTable-toolbar.tsx | 76 ++++--- .../src/components/tables/users-table.tsx | 4 +- .../src/providers/DataTableProvider.tsx | 57 +++++ 6 files changed, 204 insertions(+), 158 deletions(-) create mode 100644 apps/infoalloggi/src/providers/DataTableProvider.tsx diff --git a/apps/infoalloggi/src/components/custom_ui/data-table.tsx b/apps/infoalloggi/src/components/custom_ui/data-table.tsx index 66d690d..19dd467 100644 --- a/apps/infoalloggi/src/components/custom_ui/data-table.tsx +++ b/apps/infoalloggi/src/components/custom_ui/data-table.tsx @@ -31,9 +31,10 @@ import { TableRow, } from "~/components/ui/table"; import { cn } from "~/lib/utils"; +import { DataTableProvider } from "~/providers/DataTableProvider"; import { ContextMenu, ContextMenuTrigger } from "../ui/context-menu"; -type DataTableProps = { +export type DataTableProps = { columns: ColumnDef[]; data: TData[]; pinnedFiltri?: PinnedFiltro[]; @@ -143,104 +144,108 @@ export function DataTable({ }, [table, onTableInit]); return ( -
- -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext(), - )} - - ); - })} - - ))} - - - {virtualizer.getVirtualItems().length ? ( - virtualizer.getVirtualItems().map((virtualRow, index) => { - // biome-ignore lint/style/noNonNullAssertion: - const row = rows[virtualRow.index]!; + +
+ +
+
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {virtualizer.getVirtualItems().length ? ( + virtualizer.getVirtualItems().map((virtualRow, index) => { + // biome-ignore lint/style/noNonNullAssertion: + const row = rows[virtualRow.index]!; - const rowElem = ( - rowOnClick(row.original, row.id) - : undefined - } - style={{ - height: `${virtualRow.size}px`, - transform: `translateY(${ - virtualRow.start - index * virtualRow.size - }px)`, - }} + const rowElem = ( + rowOnClick(row.original, row.id) + : undefined + } + style={{ + height: `${virtualRow.size}px`, + transform: `translateY(${ + virtualRow.start - index * virtualRow.size + }px)`, + }} + > + {row.getVisibleCells().map((cell) => { + return ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ); + })} + + ); + + if (withContextMenu && contextMenuContent) { + return ( + + + {rowElem} + + + {contextMenuContent(row.original)} + + ); + } + return rowElem; + }) + ) : ( + + - {row.getVisibleCells().map((cell) => { - return ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ); - })} - - ); + No results. + + + )} + +
+
- if (withContextMenu && contextMenuContent) { - return ( - - {rowElem} - - {contextMenuContent(row.original)} - - ); - } - return rowElem; - }) - ) : ( - - - No results. - - - )} - - +
- - - + ); } diff --git a/apps/infoalloggi/src/components/custom_ui/dataTable-ViewToggle.tsx b/apps/infoalloggi/src/components/custom_ui/dataTable-ViewToggle.tsx index 679cada..71b60cd 100644 --- a/apps/infoalloggi/src/components/custom_ui/dataTable-ViewToggle.tsx +++ b/apps/infoalloggi/src/components/custom_ui/dataTable-ViewToggle.tsx @@ -1,4 +1,3 @@ -import type { Table } from "@tanstack/react-table"; import { Filter } from "lucide-react"; import { Button } from "~/components/ui/button"; import { @@ -9,16 +8,10 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "~/components/ui/dropdown-menu"; +import { useDataTable } from "~/providers/DataTableProvider"; -interface DataTableViewOptionsProps { - table: Table; - columns_titles: Record; -} - -export function DataTableViewOptions({ - table, - columns_titles, -}: DataTableViewOptionsProps) { +export function DataTableViewOptions() { + const { table, columns_titles } = useDataTable(); const hidableColumns = table .getAllColumns() .filter( diff --git a/apps/infoalloggi/src/components/custom_ui/dataTable-pagination.tsx b/apps/infoalloggi/src/components/custom_ui/dataTable-pagination.tsx index b16e840..6ea7871 100644 --- a/apps/infoalloggi/src/components/custom_ui/dataTable-pagination.tsx +++ b/apps/infoalloggi/src/components/custom_ui/dataTable-pagination.tsx @@ -1,4 +1,3 @@ -import type { Table } from "@tanstack/react-table"; import { ChevronFirst, ChevronLast, @@ -13,16 +12,10 @@ import { SelectTrigger, SelectValue, } from "~/components/ui/select"; +import { useDataTable } from "~/providers/DataTableProvider"; -interface DataTablePaginationProps { - table: Table; - hasSelectRow?: boolean; -} - -export function DataTablePagination({ - table, - hasSelectRow, -}: DataTablePaginationProps) { +export function DataTablePagination() { + const { table, hasSelectRow } = useDataTable(); return (
@@ -40,7 +33,7 @@ export function DataTablePagination({ }} value={`${table.getState().pagination.pageSize}`} > - + diff --git a/apps/infoalloggi/src/components/custom_ui/dataTable-toolbar.tsx b/apps/infoalloggi/src/components/custom_ui/dataTable-toolbar.tsx index b920045..7005eb3 100644 --- a/apps/infoalloggi/src/components/custom_ui/dataTable-toolbar.tsx +++ b/apps/infoalloggi/src/components/custom_ui/dataTable-toolbar.tsx @@ -1,10 +1,10 @@ -import type { Table } from "@tanstack/react-table"; import { RotateCcw } from "lucide-react"; import type { ComponentType } from "react"; import { DataTableFacetedFilter } from "~/components/custom_ui/dataTable-FacetedFilter"; import { DataTableViewOptions } from "~/components/custom_ui/dataTable-ViewToggle"; import Input from "~/components/custom_ui/input"; import { Button } from "~/components/ui/button"; +import { useDataTable } from "~/providers/DataTableProvider"; export type PinnedFiltro = { title: string; @@ -15,51 +15,22 @@ export type PinnedFiltro = { icon?: ComponentType<{ className?: string }>; }[]; }; +/** + * columnName: null per ricerca globale + */ export type SearchFiltro = { - columnName: string; + columnName: string | null; placeholder: string; }; -export function DataTableToolbar(props: { - table: Table; - pinnedColumns?: PinnedFiltro[]; - searchColumn?: SearchFiltro; - columns_titles: Record; - globalFilter: string; -}) { - const { table, pinnedColumns, searchColumn, columns_titles, globalFilter } = - props; +export function DataTableToolbar() { + const { table, pinnedFiltri } = useDataTable(); const isFiltered = table.getState().columnFilters.length > 0; return (
- {searchColumn && - (searchColumn.columnName ? ( - - table - .getColumn(searchColumn.columnName) - ?.setFilterValue(event.target.value) - } - placeholder={searchColumn.placeholder} - value={ - (table - .getColumn(searchColumn.columnName) - ?.getFilterValue() as string) ?? "" - } - /> - ) : ( - - table.setGlobalFilter(String(event.target.value)) - } - placeholder={searchColumn.placeholder} - value={globalFilter ?? ""} - /> - ))} - {pinnedColumns?.map( + + {pinnedFiltri?.map( (column) => table.getColumn(column.columnName) && ( (props: { )}
- +
); } + +function SearchInput() { + const { table, searchColumn } = useDataTable(); + const { globalFilter } = table.getState(); + if (!searchColumn) return null; + const { columnName, placeholder } = searchColumn; + if (columnName) { + return ( + + table.getColumn(columnName)?.setFilterValue(event.target.value) + } + placeholder={placeholder} + value={(table.getColumn(columnName)?.getFilterValue() as string) ?? ""} + /> + ); + } + return ( + table.setGlobalFilter(String(event.target.value))} + placeholder={searchColumn.placeholder} + value={globalFilter ?? ""} + /> + ); +} diff --git a/apps/infoalloggi/src/components/tables/users-table.tsx b/apps/infoalloggi/src/components/tables/users-table.tsx index 3b07c32..79578ac 100644 --- a/apps/infoalloggi/src/components/tables/users-table.tsx +++ b/apps/infoalloggi/src/components/tables/users-table.tsx @@ -248,8 +248,8 @@ export const UsersTable = (props: { data: UserWChatInfo[] }) => { }, ]; const searchFiltro: SearchFiltro = { - columnName: "username", - placeholder: "Filtra utenti...", + columnName: null, + placeholder: "Cerca...", }; return (
diff --git a/apps/infoalloggi/src/providers/DataTableProvider.tsx b/apps/infoalloggi/src/providers/DataTableProvider.tsx new file mode 100644 index 0000000..78ac6d2 --- /dev/null +++ b/apps/infoalloggi/src/providers/DataTableProvider.tsx @@ -0,0 +1,57 @@ +import type { Table } from "@tanstack/react-table"; +import { createContext, type ReactNode, useContext } from "react"; +import type { + PinnedFiltro, + SearchFiltro, +} from "~/components/custom_ui/dataTable-toolbar"; + +type DataTableContextType = { + table: Table; + searchColumn?: SearchFiltro; + columns_titles: Record; + pinnedFiltri?: PinnedFiltro[]; + hasSelectRow?: boolean; +}; + +// biome-ignore lint/suspicious/noExplicitAny: +const DataTableContext = createContext | null>(null); + +export function DataTableProvider({ + children, + table, + searchColumn, + columns_titles, + pinnedFiltri, + hasSelectRow, +}: { + children: ReactNode; + table: Table; + searchColumn?: SearchFiltro; + columns_titles: Record; + pinnedFiltri?: PinnedFiltro[]; + hasSelectRow?: boolean; +}) { + return ( + + {children} + + ); +} + +export const useDataTable = () => { + const value = useContext( + DataTableContext, + ) as DataTableContextType | null; + if (!value) { + throw new Error("useDataTable must be used within a DataTableProvider"); + } + return value; +};