All checks were successful
Trigger Cloudarix Deploy / call-webhook (push) Successful in 2s
✨ New Features - Advanced data table with TanStack Table v8 + Server Actions - Server-side pagination (10/25/50/100 rows per page) - Multi-column sorting with visual indicators - Column management (show/hide, resize) persisted to localStorage - URL state synchronization for shareable filtered views - Sticky header with compact/comfortable density modes 📦 Components Added - PolicyTableV2.tsx - Main table with TanStack integration - PolicyTableColumns.tsx - 7 column definitions with sorting - PolicyTablePagination.tsx - Pagination controls - PolicyTableToolbar.tsx - Density toggle + column visibility menu - ColumnVisibilityMenu.tsx - Show/hide columns dropdown 🔧 Hooks Added - usePolicyTable.ts - TanStack Table initialization - useURLState.ts - URL query param sync with nuqs - useTablePreferences.ts - localStorage persistence 🎨 Server Actions Updated - getPolicySettingsV2 - Pagination + sorting + filtering + Zod validation - exportPolicySettingsCSV - Server-side CSV generation (max 5000 rows) 📚 Documentation Added - Intune Migration Guide (1400+ lines) - Reverse engineering strategy - Intune Reference Version tracking - Tasks completed: 22/62 (Phase 1-3) ✅ Zero TypeScript compilation errors ✅ All MVP success criteria met (pagination, sorting, column management) ✅ Ready for Phase 4-7 (filtering, export, detail view, polish) Refs: specs/004-policy-explorer-v2/tasks.md
110 lines
3.0 KiB
TypeScript
110 lines
3.0 KiB
TypeScript
/**
|
|
* usePolicyTable Hook
|
|
*
|
|
* Initializes TanStack Table with manual pagination mode for server-side data fetching.
|
|
* Integrates with URL state and localStorage preferences.
|
|
*
|
|
* Key Features:
|
|
* - Manual pagination (data fetched via Server Actions)
|
|
* - Column visibility persistence
|
|
* - Column sizing with drag resize
|
|
* - Row selection for CSV export
|
|
* - Sorting with URL sync
|
|
*/
|
|
|
|
import { useEffect, useMemo, useState } from 'react';
|
|
import {
|
|
getCoreRowModel,
|
|
useReactTable,
|
|
type ColumnDef,
|
|
type SortingState,
|
|
type VisibilityState,
|
|
type ColumnSizingState,
|
|
type RowSelectionState,
|
|
type PaginationState,
|
|
} from '@tanstack/react-table';
|
|
import type { PolicySettingRow, PaginationMeta } from '@/lib/types/policy-table';
|
|
|
|
interface UsePolicyTableProps {
|
|
data: PolicySettingRow[];
|
|
columns: ColumnDef<PolicySettingRow>[];
|
|
pagination: PaginationState;
|
|
onPaginationChange: (updater: PaginationState | ((old: PaginationState) => PaginationState)) => void;
|
|
sorting: SortingState;
|
|
onSortingChange: (updater: SortingState | ((old: SortingState) => SortingState)) => void;
|
|
columnVisibility?: VisibilityState;
|
|
onColumnVisibilityChange?: (updater: VisibilityState | ((old: VisibilityState) => VisibilityState)) => void;
|
|
columnSizing?: ColumnSizingState;
|
|
onColumnSizingChange?: (updater: ColumnSizingState | ((old: ColumnSizingState) => ColumnSizingState)) => void;
|
|
meta?: PaginationMeta;
|
|
enableRowSelection?: boolean;
|
|
}
|
|
|
|
export function usePolicyTable({
|
|
data,
|
|
columns,
|
|
pagination,
|
|
onPaginationChange,
|
|
sorting,
|
|
onSortingChange,
|
|
columnVisibility = {},
|
|
onColumnVisibilityChange,
|
|
columnSizing = {},
|
|
onColumnSizingChange,
|
|
meta,
|
|
enableRowSelection = false,
|
|
}: UsePolicyTableProps) {
|
|
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
|
|
|
// Reset row selection when page changes
|
|
useEffect(() => {
|
|
setRowSelection({});
|
|
}, [pagination.pageIndex]);
|
|
|
|
// Initialize TanStack Table
|
|
const table = useReactTable({
|
|
data,
|
|
columns,
|
|
pageCount: meta?.pageCount ?? -1,
|
|
state: {
|
|
pagination,
|
|
sorting,
|
|
columnVisibility,
|
|
columnSizing,
|
|
rowSelection,
|
|
},
|
|
onPaginationChange,
|
|
onSortingChange,
|
|
onColumnVisibilityChange,
|
|
onColumnSizingChange,
|
|
onRowSelectionChange: setRowSelection,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
manualPagination: true, // Server-side pagination
|
|
manualSorting: true, // Server-side sorting
|
|
manualFiltering: true, // Server-side filtering
|
|
enableRowSelection,
|
|
enableColumnResizing: true,
|
|
columnResizeMode: 'onChange',
|
|
});
|
|
|
|
// Get selected rows
|
|
const selectedRows = useMemo(() => {
|
|
return table.getSelectedRowModel().rows.map(row => row.original);
|
|
}, [table, rowSelection]);
|
|
|
|
// Selection helpers
|
|
const selectedCount = selectedRows.length;
|
|
const totalCount = meta?.totalCount ?? 0;
|
|
const hasSelection = selectedCount > 0;
|
|
const allRowsSelected = table.getIsAllPageRowsSelected();
|
|
|
|
return {
|
|
table,
|
|
selectedRows,
|
|
selectedCount,
|
|
totalCount,
|
|
hasSelection,
|
|
allRowsSelected,
|
|
};
|
|
}
|