"use client"; import { ColumnDef, flexRender, getCoreRowModel, useReactTable, getPaginationRowModel, SortingState, getSortedRowModel, ColumnFiltersState, getFilteredRowModel } from "@tanstack/react-table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Button } from "@app/components/ui/button"; import { useEffect, useMemo, useState } from "react"; import { Input } from "@app/components/ui/input"; import { DataTablePagination } from "@app/components/DataTablePagination"; import { Plus, Search, RefreshCw, Filter, X, Download } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@app/components/ui/card"; import { Tabs, TabsList, TabsTrigger } from "@app/components/ui/tabs"; import { useTranslations } from "next-intl"; import { DateRangePicker, DateTimeValue } from "@app/components/DateTimePicker"; const STORAGE_KEYS = { PAGE_SIZE: "datatable-page-size", getTablePageSize: (tableId?: string) => tableId ? `${tableId}-size` : STORAGE_KEYS.PAGE_SIZE }; const getStoredPageSize = (tableId?: string, defaultSize = 20): number => { if (typeof window === "undefined") return defaultSize; try { const key = STORAGE_KEYS.getTablePageSize(tableId); const stored = localStorage.getItem(key); if (stored) { const parsed = parseInt(stored, 10); // Validate that it's a reasonable page size if (parsed > 0 && parsed <= 1000) { return parsed; } } } catch (error) { console.warn("Failed to read page size from localStorage:", error); } return defaultSize; }; const setStoredPageSize = (pageSize: number, tableId?: string): void => { if (typeof window === "undefined") return; try { const key = STORAGE_KEYS.getTablePageSize(tableId); localStorage.setItem(key, pageSize.toString()); } catch (error) { console.warn("Failed to save page size to localStorage:", error); } }; type TabFilter = { id: string; label: string; filterFn: (row: any) => boolean; }; type DataTableProps = { columns: ColumnDef[]; data: TData[]; title?: string; addButtonText?: string; onRefresh?: () => void; onExport?: () => void; isExporting?: boolean; isRefreshing?: boolean; searchPlaceholder?: string; searchColumn?: string; defaultSort?: { id: string; desc: boolean; }; tabs?: TabFilter[]; defaultTab?: string; persistPageSize?: boolean | string; defaultPageSize?: number; onDateRangeChange?: ( startDate: DateTimeValue, endDate: DateTimeValue ) => void; dateRange?: { start: DateTimeValue; end: DateTimeValue; }; }; export function LogDataTable({ columns, data, title, onRefresh, isRefreshing, onExport, isExporting, searchPlaceholder = "Search...", searchColumn = "name", defaultSort, tabs, defaultTab, persistPageSize = false, defaultPageSize = 20, onDateRangeChange, dateRange }: DataTableProps) { const t = useTranslations(); // Determine table identifier for storage const tableId = typeof persistPageSize === "string" ? persistPageSize : undefined; // Initialize page size from storage or default const [pageSize, setPageSize] = useState(() => { if (persistPageSize) { return getStoredPageSize(tableId, defaultPageSize); } return defaultPageSize; }); const [sorting, setSorting] = useState( defaultSort ? [defaultSort] : [] ); const [columnFilters, setColumnFilters] = useState([]); const [globalFilter, setGlobalFilter] = useState([]); const [activeTab, setActiveTab] = useState( defaultTab || tabs?.[0]?.id || "" ); const [startDate, setStartDate] = useState( dateRange?.start || {} ); const [endDate, setEndDate] = useState(dateRange?.end || {}); // Sync internal date state with external dateRange prop useEffect(() => { if (dateRange?.start) { setStartDate(dateRange.start); } if (dateRange?.end) { setEndDate(dateRange.end); } }, [dateRange?.start, dateRange?.end]); // Apply tab filter to data const filteredData = useMemo(() => { if (!tabs || activeTab === "") { return data; } const activeTabFilter = tabs.find((tab) => tab.id === activeTab); if (!activeTabFilter) { return data; } return data.filter(activeTabFilter.filterFn); }, [data, tabs, activeTab]); const table = useReactTable({ data: filteredData, columns, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), onSortingChange: setSorting, getSortedRowModel: getSortedRowModel(), onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), onGlobalFilterChange: setGlobalFilter, initialState: { pagination: { pageSize: pageSize, pageIndex: 0 } }, state: { sorting, columnFilters, globalFilter } }); useEffect(() => { const currentPageSize = table.getState().pagination.pageSize; if (currentPageSize !== pageSize) { table.setPageSize(pageSize); // Persist to localStorage if enabled if (persistPageSize) { setStoredPageSize(pageSize, tableId); } } }, [pageSize, table, persistPageSize, tableId]); const handleTabChange = (value: string) => { setActiveTab(value); // Reset to first page when changing tabs table.setPageIndex(0); }; // Enhanced pagination component that updates our local state const handlePageSizeChange = (newPageSize: number) => { setPageSize(newPageSize); table.setPageSize(newPageSize); // Persist immediately when changed if (persistPageSize) { setStoredPageSize(newPageSize, tableId); } }; const handleDateRangeChange = ( start: DateTimeValue, end: DateTimeValue ) => { setStartDate(start); setEndDate(end); onDateRangeChange?.(start, end); }; return (
table.setGlobalFilter( String(e.target.value) ) } className="w-full pl-8 m-0" />
{onRefresh && ( )} {onExport && ( )}
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( {header.isPlaceholder ? null : flexRender( header.column.columnDef .header, header.getContext() )} ))} ))} {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender( cell.column.columnDef.cell, cell.getContext() )} ))} )) ) : ( No results found. )}
); }