tenantpilot/components/policy-explorer/ColumnVisibilityMenu.tsx
Ahmed Darrazi 41e80b6c0c
All checks were successful
Trigger Cloudarix Deploy / call-webhook (push) Successful in 2s
feat(policy-explorer-v2): implement MVP Phase 1-3
 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
2025-12-10 00:18:05 +01:00

82 lines
2.4 KiB
TypeScript

/**
* ColumnVisibilityMenu
*
* Dropdown menu to show/hide table columns.
* Integrates with TanStack Table column visibility state.
*
* Features:
* - Checkbox list of all columns
* - Hide/show individual columns
* - "Reset to default" button
* - Persisted via localStorage
*/
'use client';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Columns3 } from 'lucide-react';
import type { Table } from '@tanstack/react-table';
import type { PolicySettingRow } from '@/lib/types/policy-table';
interface ColumnVisibilityMenuProps {
table: Table<PolicySettingRow>;
}
export function ColumnVisibilityMenu({ table }: ColumnVisibilityMenuProps) {
const columns = table
.getAllColumns()
.filter((column) => column.getCanHide());
const hiddenCount = columns.filter((column) => !column.getIsVisible()).length;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="ml-auto h-8 lg:flex">
<Columns3 className="mr-2 h-4 w-4" />
Columns
{hiddenCount > 0 && (
<span className="ml-1 rounded-full bg-primary px-2 py-0.5 text-xs text-primary-foreground">
{hiddenCount}
</span>
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[180px]">
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
<DropdownMenuSeparator />
{columns.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{/* Format column ID to human-readable label */}
{column.id.replace(/([A-Z])/g, ' $1').trim()}
</DropdownMenuCheckboxItem>
);
})}
<DropdownMenuSeparator />
<Button
variant="ghost"
size="sm"
className="w-full justify-start"
onClick={() => table.resetColumnVisibility()}
>
Reset to default
</Button>
</DropdownMenuContent>
</DropdownMenu>
);
}