diff --git a/src/components/ClientsTable.tsx b/src/components/ClientsTable.tsx index 0a4c769f..51b3788e 100644 --- a/src/components/ClientsTable.tsx +++ b/src/components/ClientsTable.tsx @@ -89,8 +89,13 @@ type ClientTableProps = { const STORAGE_KEYS = { PAGE_SIZE: "datatable-page-size", + COLUMN_VISIBILITY: "datatable-column-visibility", getTablePageSize: (tableId?: string) => - tableId ? `datatable-${tableId}-page-size` : STORAGE_KEYS.PAGE_SIZE + tableId ? `datatable-${tableId}-page-size` : STORAGE_KEYS.PAGE_SIZE, + getTableColumnVisibility: (tableId?: string) => + tableId + ? `datatable-${tableId}-column-visibility` + : STORAGE_KEYS.COLUMN_VISIBILITY }; const getStoredPageSize = (tableId?: string, defaultSize = 20): number => { @@ -122,6 +127,48 @@ const setStoredPageSize = (pageSize: number, tableId?: string): void => { } }; +const getStoredColumnVisibility = ( + tableId?: string, + defaultVisibility?: Record +): Record => { + if (typeof window === "undefined") return defaultVisibility || {}; + + try { + const key = STORAGE_KEYS.getTableColumnVisibility(tableId); + const stored = localStorage.getItem(key); + if (stored) { + const parsed = JSON.parse(stored); + // Validate that it's an object + if (typeof parsed === "object" && parsed !== null) { + return parsed; + } + } + } catch (error) { + console.warn( + "Failed to read column visibility from localStorage:", + error + ); + } + return defaultVisibility || {}; +}; + +const setStoredColumnVisibility = ( + visibility: Record, + tableId?: string +): void => { + if (typeof window === "undefined") return; + + try { + const key = STORAGE_KEYS.getTableColumnVisibility(tableId); + localStorage.setItem(key, JSON.stringify(visibility)); + } catch (error) { + console.warn( + "Failed to save column visibility to localStorage:", + error + ); + } +}; + export default function ClientsTable({ userClients, machineClients, @@ -157,15 +204,22 @@ export default function ClientsTable({ useState([]); const [machineGlobalFilter, setMachineGlobalFilter] = useState([]); - const [userColumnVisibility, setUserColumnVisibility] = useState({ + const defaultUserColumnVisibility = { client: false, subnet: false - }); - const [machineColumnVisibility, setMachineColumnVisibility] = useState({ + }; + const defaultMachineColumnVisibility = { client: false, subnet: false, userId: false - }); + }; + + const [userColumnVisibility, setUserColumnVisibility] = useState( + () => getStoredColumnVisibility("user-clients", defaultUserColumnVisibility) + ); + const [machineColumnVisibility, setMachineColumnVisibility] = useState( + () => getStoredColumnVisibility("machine-clients", defaultMachineColumnVisibility) + ); const currentView = searchParams.get("view") || defaultView; @@ -522,10 +576,7 @@ export default function ClientsTable({ pageSize: userPageSize, pageIndex: 0 }, - columnVisibility: { - client: false, - subnet: false - } + columnVisibility: userColumnVisibility }, state: { sorting: userSorting, @@ -551,11 +602,7 @@ export default function ClientsTable({ pageSize: machinePageSize, pageIndex: 0 }, - columnVisibility: { - client: false, - subnet: false, - userId: false - } + columnVisibility: machineColumnVisibility }, state: { sorting: machineSorting, @@ -575,6 +622,15 @@ export default function ClientsTable({ setStoredPageSize(newPageSize, "machine-clients"); }; + // Persist column visibility changes to localStorage + useEffect(() => { + setStoredColumnVisibility(userColumnVisibility, "user-clients"); + }, [userColumnVisibility]); + + useEffect(() => { + setStoredColumnVisibility(machineColumnVisibility, "machine-clients"); + }, [machineColumnVisibility]); + return ( <> {selectedClient && ( diff --git a/src/components/ResourcesTable.tsx b/src/components/ResourcesTable.tsx index 68aa4f49..a00749c9 100644 --- a/src/components/ResourcesTable.tsx +++ b/src/components/ResourcesTable.tsx @@ -116,8 +116,13 @@ type ResourcesTableProps = { const STORAGE_KEYS = { PAGE_SIZE: "datatable-page-size", + COLUMN_VISIBILITY: "datatable-column-visibility", getTablePageSize: (tableId?: string) => - tableId ? `datatable-${tableId}-page-size` : STORAGE_KEYS.PAGE_SIZE + tableId ? `datatable-${tableId}-page-size` : STORAGE_KEYS.PAGE_SIZE, + getTableColumnVisibility: (tableId?: string) => + tableId + ? `datatable-${tableId}-column-visibility` + : STORAGE_KEYS.COLUMN_VISIBILITY }; const getStoredPageSize = (tableId?: string, defaultSize = 20): number => { @@ -149,6 +154,48 @@ const setStoredPageSize = (pageSize: number, tableId?: string): void => { } }; +const getStoredColumnVisibility = ( + tableId?: string, + defaultVisibility?: Record +): Record => { + if (typeof window === "undefined") return defaultVisibility || {}; + + try { + const key = STORAGE_KEYS.getTableColumnVisibility(tableId); + const stored = localStorage.getItem(key); + if (stored) { + const parsed = JSON.parse(stored); + // Validate that it's an object + if (typeof parsed === "object" && parsed !== null) { + return parsed; + } + } + } catch (error) { + console.warn( + "Failed to read column visibility from localStorage:", + error + ); + } + return defaultVisibility || {}; +}; + +const setStoredColumnVisibility = ( + visibility: Record, + tableId?: string +): void => { + if (typeof window === "undefined") return; + + try { + const key = STORAGE_KEYS.getTableColumnVisibility(tableId); + localStorage.setItem(key, JSON.stringify(visibility)); + } catch (error) { + console.warn( + "Failed to save column visibility to localStorage:", + error + ); + } +}; + export default function ResourcesTable({ resources, internalResources, @@ -196,8 +243,12 @@ export default function ResourcesTable({ useState([]); const [internalGlobalFilter, setInternalGlobalFilter] = useState([]); const [isRefreshing, setIsRefreshing] = useState(false); - const [proxyColumnVisibility, setProxyColumnVisibility] = useState({}); - const [internalColumnVisibility, setInternalColumnVisibility] = useState({}); + const [proxyColumnVisibility, setProxyColumnVisibility] = useState( + () => getStoredColumnVisibility("proxy-resources", {}) + ); + const [internalColumnVisibility, setInternalColumnVisibility] = useState( + () => getStoredColumnVisibility("internal-resources", {}) + ); const currentView = searchParams.get("view") || defaultView; @@ -684,7 +735,8 @@ export default function ResourcesTable({ pagination: { pageSize: proxyPageSize, pageIndex: 0 - } + }, + columnVisibility: proxyColumnVisibility }, state: { sorting: proxySorting, @@ -709,7 +761,8 @@ export default function ResourcesTable({ pagination: { pageSize: internalPageSize, pageIndex: 0 - } + }, + columnVisibility: internalColumnVisibility }, state: { sorting: internalSorting, @@ -729,6 +782,15 @@ export default function ResourcesTable({ setStoredPageSize(newPageSize, "internal-resources"); }; + // Persist column visibility changes to localStorage + useEffect(() => { + setStoredColumnVisibility(proxyColumnVisibility, "proxy-resources"); + }, [proxyColumnVisibility]); + + useEffect(() => { + setStoredColumnVisibility(internalColumnVisibility, "internal-resources"); + }, [internalColumnVisibility]); + return ( <> {selectedResource && ( diff --git a/src/components/ui/data-table.tsx b/src/components/ui/data-table.tsx index 0a09c679..5faa64b2 100644 --- a/src/components/ui/data-table.tsx +++ b/src/components/ui/data-table.tsx @@ -44,8 +44,13 @@ import { const STORAGE_KEYS = { PAGE_SIZE: "datatable-page-size", + COLUMN_VISIBILITY: "datatable-column-visibility", getTablePageSize: (tableId?: string) => - tableId ? `${tableId}-size` : STORAGE_KEYS.PAGE_SIZE + tableId ? `${tableId}-size` : STORAGE_KEYS.PAGE_SIZE, + getTableColumnVisibility: (tableId?: string) => + tableId + ? `${tableId}-column-visibility` + : STORAGE_KEYS.COLUMN_VISIBILITY }; const getStoredPageSize = (tableId?: string, defaultSize = 20): number => { @@ -78,6 +83,48 @@ const setStoredPageSize = (pageSize: number, tableId?: string): void => { } }; +const getStoredColumnVisibility = ( + tableId?: string, + defaultVisibility?: Record +): Record => { + if (typeof window === "undefined") return defaultVisibility || {}; + + try { + const key = STORAGE_KEYS.getTableColumnVisibility(tableId); + const stored = localStorage.getItem(key); + if (stored) { + const parsed = JSON.parse(stored); + // Validate that it's an object + if (typeof parsed === "object" && parsed !== null) { + return parsed; + } + } + } catch (error) { + console.warn( + "Failed to read column visibility from localStorage:", + error + ); + } + return defaultVisibility || {}; +}; + +const setStoredColumnVisibility = ( + visibility: Record, + tableId?: string +): void => { + if (typeof window === "undefined") return; + + try { + const key = STORAGE_KEYS.getTableColumnVisibility(tableId); + localStorage.setItem(key, JSON.stringify(visibility)); + } catch (error) { + console.warn( + "Failed to save column visibility to localStorage:", + error + ); + } +}; + type TabFilter = { id: string; label: string; @@ -104,6 +151,7 @@ type DataTableProps = { defaultPageSize?: number; columnVisibility?: Record; enableColumnVisibility?: boolean; + persistColumnVisibility?: boolean | string; }; export function DataTable({ @@ -122,13 +170,30 @@ export function DataTable({ persistPageSize = false, defaultPageSize = 20, columnVisibility: defaultColumnVisibility, - enableColumnVisibility = false + enableColumnVisibility = false, + persistColumnVisibility = false }: DataTableProps) { const t = useTranslations(); // Determine table identifier for storage + // Use persistPageSize string if provided, otherwise use persistColumnVisibility string, otherwise undefined const tableId = - typeof persistPageSize === "string" ? persistPageSize : undefined; + typeof persistPageSize === "string" + ? persistPageSize + : typeof persistColumnVisibility === "string" + ? persistColumnVisibility + : undefined; + + // Compute initial column visibility (from localStorage if enabled, otherwise from prop/default) + const initialColumnVisibility = (() => { + if (persistColumnVisibility) { + return getStoredColumnVisibility( + tableId, + defaultColumnVisibility + ); + } + return defaultColumnVisibility || {}; + })(); // Initialize page size from storage or default const [pageSize, setPageSize] = useState(() => { @@ -143,9 +208,8 @@ export function DataTable({ ); const [columnFilters, setColumnFilters] = useState([]); const [globalFilter, setGlobalFilter] = useState([]); - const [columnVisibility, setColumnVisibility] = useState( - defaultColumnVisibility || {} - ); + const [columnVisibility, setColumnVisibility] = + useState(initialColumnVisibility); const [activeTab, setActiveTab] = useState( defaultTab || tabs?.[0]?.id || "" ); @@ -180,7 +244,7 @@ export function DataTable({ pageSize: pageSize, pageIndex: 0 }, - columnVisibility: defaultColumnVisibility || {} + columnVisibility: initialColumnVisibility }, state: { sorting, @@ -202,6 +266,13 @@ export function DataTable({ } }, [pageSize, table, persistPageSize, tableId]); + useEffect(() => { + // Persist column visibility to localStorage when it changes + if (persistColumnVisibility) { + setStoredColumnVisibility(columnVisibility, tableId); + } + }, [columnVisibility, persistColumnVisibility, tableId]); + const handleTabChange = (value: string) => { setActiveTab(value); // Reset to first page when changing tabs