diff --git a/frontend/src/store/columnVisibilityStore.ts b/frontend/src/store/columnVisibilityStore.ts new file mode 100644 index 00000000..dd796106 --- /dev/null +++ b/frontend/src/store/columnVisibilityStore.ts @@ -0,0 +1,70 @@ +/** + * Column Visibility Store (Zustand) + * Manages column visibility settings per page with localStorage persistence + * Uses the same pattern as siteStore and sectorStore + */ +import { create } from 'zustand'; +import { persist } from 'zustand/middleware'; + +interface ColumnVisibilityState { + // Map of page pathname to Set of visible column keys + pageColumns: Record; + + // Actions + setPageColumns: (pathname: string, columnKeys: string[]) => void; + getPageColumns: (pathname: string) => string[]; + toggleColumn: (pathname: string, columnKey: string) => void; + resetPageColumns: (pathname: string) => void; +} + +export const useColumnVisibilityStore = create()( + persist( + (set, get) => ({ + pageColumns: {}, + + setPageColumns: (pathname: string, columnKeys: string[]) => { + set((state) => ({ + pageColumns: { + ...state.pageColumns, + [pathname]: columnKeys, + }, + })); + }, + + getPageColumns: (pathname: string) => { + return get().pageColumns[pathname] || []; + }, + + toggleColumn: (pathname: string, columnKey: string) => { + set((state) => { + const currentColumns = state.pageColumns[pathname] || []; + const newColumns = currentColumns.includes(columnKey) + ? currentColumns.filter((key) => key !== columnKey) + : [...currentColumns, columnKey]; + + return { + pageColumns: { + ...state.pageColumns, + [pathname]: newColumns, + }, + }; + }); + }, + + resetPageColumns: (pathname: string) => { + set((state) => { + const newPageColumns = { ...state.pageColumns }; + delete newPageColumns[pathname]; + return { pageColumns: newPageColumns }; + }); + }, + }), + { + name: 'igny8-column-visibility', + partialize: (state) => ({ + pageColumns: state.pageColumns, + }), + } + ) +); + diff --git a/frontend/src/templates/TablePageTemplate.tsx b/frontend/src/templates/TablePageTemplate.tsx index 15d56db1..29962284 100644 --- a/frontend/src/templates/TablePageTemplate.tsx +++ b/frontend/src/templates/TablePageTemplate.tsx @@ -39,6 +39,7 @@ import BulkExportModal from '../components/common/BulkExportModal'; import BulkStatusUpdateModal from '../components/common/BulkStatusUpdateModal'; import { CompactPagination } from '../components/ui/pagination'; import { usePageSizeStore } from '../store/pageSizeStore'; +import { useColumnVisibilityStore } from '../store/columnVisibilityStore'; import ToggleTableRow, { ToggleButton } from '../components/common/ToggleTableRow'; import ColumnSelector from '../components/common/ColumnSelector'; @@ -224,81 +225,72 @@ export default function TablePageTemplate({ const { setMetrics } = useHeaderMetrics(); const toast = useToast(); const { pageSize, setPageSize } = usePageSizeStore(); + const { pageColumns, setPageColumns, getPageColumns } = useColumnVisibilityStore(); - // Column visibility state management with localStorage persistence - const getStorageKey = () => { - // Use pathname to create unique storage key per page - return `table-columns-${location.pathname}`; - }; - - // Initialize visible columns from localStorage or defaults + // Column visibility state management with Zustand store (same pattern as site/sector stores) + // Initialize visible columns from store or defaults const initializeVisibleColumns = useMemo(() => { - const storageKey = getStorageKey(); - try { - const saved = localStorage.getItem(storageKey); - if (saved) { - const savedSet = new Set(JSON.parse(saved)); - // Validate that all saved columns still exist - const validColumns = columns.filter(col => savedSet.has(col.key)); - if (validColumns.length > 0) { - // Add any new columns with defaultVisible !== false - const newColumns = columns - .filter(col => !savedSet.has(col.key) && col.defaultVisible !== false) - .map(col => col.key); - return new Set([...Array.from(validColumns.map(col => col.key)), ...newColumns]); - } + const savedColumnKeys = getPageColumns(location.pathname); + + if (savedColumnKeys.length > 0) { + const savedSet = new Set(savedColumnKeys); + // Validate that all saved columns still exist + const validColumns = columns.filter(col => savedSet.has(col.key)); + if (validColumns.length > 0) { + // Add any new columns with defaultVisible !== false + const newColumns = columns + .filter(col => !savedSet.has(col.key) && col.defaultVisible !== false) + .map(col => col.key); + return new Set([...Array.from(validColumns.map(col => col.key)), ...newColumns]); } - } catch (e) { - // Ignore parse errors } + // Default: show all columns that have defaultVisible !== false return new Set( columns .filter(col => col.defaultVisible !== false) .map(col => col.key) ); - }, [columns, location.pathname]); + }, [columns, location.pathname, getPageColumns]); const [visibleColumns, setVisibleColumns] = useState>(initializeVisibleColumns); // Update visible columns when columns prop or pathname changes useEffect(() => { - const storageKey = getStorageKey(); - try { - const saved = localStorage.getItem(storageKey); - if (saved) { - const savedSet = new Set(JSON.parse(saved)); - // Validate that all saved columns still exist - const validColumns = columns.filter(col => savedSet.has(col.key)); - if (validColumns.length > 0) { - // Add any new columns with defaultVisible !== false - const newColumns = columns - .filter(col => !savedSet.has(col.key) && col.defaultVisible !== false) - .map(col => col.key); - setVisibleColumns(new Set([...Array.from(validColumns.map(col => col.key)), ...newColumns])); - return; - } + const savedColumnKeys = getPageColumns(location.pathname); + + if (savedColumnKeys.length > 0) { + const savedSet = new Set(savedColumnKeys); + // Validate that all saved columns still exist + const validColumns = columns.filter(col => savedSet.has(col.key)); + if (validColumns.length > 0) { + // Add any new columns with defaultVisible !== false + const newColumns = columns + .filter(col => !savedSet.has(col.key) && col.defaultVisible !== false) + .map(col => col.key); + const newSet = new Set([...Array.from(validColumns.map(col => col.key)), ...newColumns]); + setVisibleColumns(newSet); + // Update store with validated columns + setPageColumns(location.pathname, Array.from(newSet)); + return; } - } catch (e) { - // Ignore parse errors } + // Default: show all columns that have defaultVisible !== false - setVisibleColumns(new Set( + const defaultSet = new Set( columns .filter(col => col.defaultVisible !== false) .map(col => col.key) - )); - }, [columns, location.pathname]); // Re-initialize when columns or pathname changes + ); + setVisibleColumns(defaultSet); + // Save defaults to store + setPageColumns(location.pathname, Array.from(defaultSet)); + }, [columns, location.pathname, getPageColumns, setPageColumns]); - // Save to localStorage whenever visibleColumns changes + // Save to store whenever visibleColumns changes (Zustand persist middleware handles localStorage) useEffect(() => { - const storageKey = getStorageKey(); - try { - localStorage.setItem(storageKey, JSON.stringify(Array.from(visibleColumns))); - } catch (e) { - // Ignore storage errors - } - }, [visibleColumns, location.pathname]); + setPageColumns(location.pathname, Array.from(visibleColumns)); + }, [visibleColumns, location.pathname, setPageColumns]); // Filter columns based on visibility const visibleColumnsList = useMemo(() => {