/** * useTablePreferences Hook * * Manages localStorage persistence for user table preferences: * - Column visibility (show/hide columns) * - Column sizing (width in pixels) * - Column order (reordering) * - Density mode (compact vs comfortable) * - Default page size * * Includes versioning for forward compatibility when adding new preferences. */ import { useState, useEffect, useCallback } from 'react'; import type { TablePreferences } from '@/lib/types/policy-table'; import type { VisibilityState, ColumnSizingState } from '@tanstack/react-table'; const STORAGE_KEY = 'policy-explorer-preferences'; const STORAGE_VERSION = 1; // Default preferences const DEFAULT_PREFERENCES: TablePreferences = { version: STORAGE_VERSION, columnVisibility: {}, columnSizing: {}, columnOrder: [], density: 'comfortable', defaultPageSize: 50, }; /** * Load preferences from localStorage with error handling */ function loadPreferences(): TablePreferences { if (typeof window === 'undefined') { return DEFAULT_PREFERENCES; } try { const stored = localStorage.getItem(STORAGE_KEY); if (!stored) { return DEFAULT_PREFERENCES; } const parsed = JSON.parse(stored) as TablePreferences; // Version migration logic if (parsed.version !== STORAGE_VERSION) { // Future: Handle migrations between versions console.log('Migrating preferences from version', parsed.version, 'to', STORAGE_VERSION); return { ...DEFAULT_PREFERENCES, ...parsed, version: STORAGE_VERSION }; } return parsed; } catch (error) { console.error('Failed to load table preferences:', error); return DEFAULT_PREFERENCES; } } /** * Save preferences to localStorage with error handling */ function savePreferences(preferences: TablePreferences): void { if (typeof window === 'undefined') { return; } try { localStorage.setItem(STORAGE_KEY, JSON.stringify(preferences)); } catch (error) { console.error('Failed to save table preferences:', error); // Handle quota exceeded if (error instanceof Error && error.name === 'QuotaExceededError') { console.warn('localStorage quota exceeded. Clearing old preferences.'); try { localStorage.removeItem(STORAGE_KEY); localStorage.setItem(STORAGE_KEY, JSON.stringify(preferences)); } catch (retryError) { console.error('Failed to clear and save preferences:', retryError); } } } } export function useTablePreferences() { const [preferences, setPreferences] = useState(DEFAULT_PREFERENCES); const [isLoaded, setIsLoaded] = useState(false); // Load preferences on mount useEffect(() => { const loaded = loadPreferences(); setPreferences(loaded); setIsLoaded(true); }, []); // Save preferences whenever they change useEffect(() => { if (isLoaded) { savePreferences(preferences); } }, [preferences, isLoaded]); // Update column visibility const updateColumnVisibility = useCallback((visibility: VisibilityState) => { setPreferences((prev) => ({ ...prev, columnVisibility: visibility, })); }, []); // Update column sizing const updateColumnSizing = useCallback((sizing: ColumnSizingState) => { setPreferences((prev) => ({ ...prev, columnSizing: sizing, })); }, []); // Update column order const updateColumnOrder = useCallback((order: string[]) => { setPreferences((prev) => ({ ...prev, columnOrder: order, })); }, []); // Update density mode const updateDensity = useCallback((density: 'compact' | 'comfortable') => { setPreferences((prev) => ({ ...prev, density, })); }, []); // Update default page size const updateDefaultPageSize = useCallback((pageSize: 10 | 25 | 50 | 100) => { setPreferences((prev) => ({ ...prev, defaultPageSize: pageSize, })); }, []); // Reset all preferences to defaults const resetPreferences = useCallback(() => { setPreferences(DEFAULT_PREFERENCES); if (typeof window !== 'undefined') { localStorage.removeItem(STORAGE_KEY); } }, []); return { preferences, isLoaded, updateColumnVisibility, updateColumnSizing, updateColumnOrder, updateDensity, updateDefaultPageSize, resetPreferences, }; }