/** * useURLState Hook * * Synchronizes table state with URL query parameters for shareable filtered/sorted views. * Uses `nuqs` library for type-safe URL state management with Next.js App Router. * * URL Parameters: * - p: page (0-based index) * - ps: pageSize (10, 25, 50, 100) * - sb: sortBy (settingName, policyName, policyType, lastSyncedAt) * - sd: sortDir (asc, desc) * - pt: policyTypes (comma-separated) * - q: searchQuery */ import { useQueryState, parseAsInteger, parseAsString, parseAsStringLiteral, parseAsArrayOf } from 'nuqs'; import { useCallback } from 'react'; const PAGE_SIZES = [10, 25, 50, 100] as const; const SORT_BY_OPTIONS = ['settingName', 'policyName', 'policyType', 'lastSyncedAt'] as const; const SORT_DIR_OPTIONS = ['asc', 'desc'] as const; export function useURLState() { // Page (0-based) const [page, setPage] = useQueryState( 'p', parseAsInteger.withDefault(0) ); // Page Size const [pageSize, setPageSize] = useQueryState( 'ps', parseAsInteger.withDefault(50) ); // Sort By const [sortBy, setSortBy] = useQueryState( 'sb', parseAsString.withDefault('settingName') ); // Sort Direction const [sortDir, setSortDir] = useQueryState( 'sd', parseAsStringLiteral(['asc', 'desc'] as const).withDefault('asc') ); // Policy Types (comma-separated) const [policyTypes, setPolicyTypes] = useQueryState( 'pt', parseAsArrayOf(parseAsString, ',').withDefault([]) ); // Search Query const [searchQuery, setSearchQuery] = useQueryState( 'q', parseAsString.withDefault('') ); // Update page with validation const updatePage = useCallback((newPage: number) => { const validPage = Math.max(0, newPage); setPage(validPage); }, [setPage]); // Update page size with validation const updatePageSize = useCallback((newPageSize: number) => { const validPageSize = PAGE_SIZES.includes(newPageSize as typeof PAGE_SIZES[number]) ? newPageSize : 50; setPageSize(validPageSize); // Reset to first page when changing page size setPage(0); }, [setPageSize, setPage]); // Update sorting const updateSorting = useCallback((newSortBy: string, newSortDir: 'asc' | 'desc') => { setSortBy(newSortBy); setSortDir(newSortDir); }, [setSortBy, setSortDir]); // Toggle sort direction const toggleSortDir = useCallback(() => { setSortDir(sortDir === 'asc' ? 'desc' : 'asc'); }, [sortDir, setSortDir]); // Update policy types filter const updatePolicyTypes = useCallback((types: string[]) => { setPolicyTypes(types); // Reset to first page when changing filters setPage(0); }, [setPolicyTypes, setPage]); // Update search query const updateSearchQuery = useCallback((query: string) => { setSearchQuery(query); // Reset to first page when searching setPage(0); }, [setSearchQuery, setPage]); // Clear all filters const clearFilters = useCallback(() => { setPolicyTypes([]); setSearchQuery(''); setPage(0); }, [setPolicyTypes, setSearchQuery, setPage]); // Reset all URL state const resetURLState = useCallback(() => { setPage(0); setPageSize(50); setSortBy('settingName'); setSortDir('asc'); setPolicyTypes([]); setSearchQuery(''); }, [setPage, setPageSize, setSortBy, setSortDir, setPolicyTypes, setSearchQuery]); return { // Current state page, pageSize, sortBy, sortDir, policyTypes, searchQuery, // Update functions updatePage, updatePageSize, updateSorting, toggleSortDir, updatePolicyTypes, updateSearchQuery, clearFilters, resetURLState, }; }