From 28e033da9dd48583f525170df847fb52fb5c6086 Mon Sep 17 00:00:00 2001 From: "courtmanr@gmail.com" Date: Mon, 17 Mar 2025 16:39:21 +0000 Subject: [PATCH] feat(settings): enhance column management and user settings --- .../network/hooks/useColumnManagement.js | 323 +++++++++--------- frontend/src/context/UserSettingsContext.jsx | 58 +++- 2 files changed, 201 insertions(+), 180 deletions(-) diff --git a/frontend/src/components/network/hooks/useColumnManagement.js b/frontend/src/components/network/hooks/useColumnManagement.js index c751b80f8..cc1350f62 100644 --- a/frontend/src/components/network/hooks/useColumnManagement.js +++ b/frontend/src/components/network/hooks/useColumnManagement.js @@ -6,180 +6,60 @@ import { } from '../../../constants/networkConstants'; const useColumnManagement = (showNotification) => { - // Column visibility state + // Initialize column visibility state from localStorage or defaults const [columnVisibility, setColumnVisibility] = useState(() => { try { - console.log('Initializing column visibility state'); - const saved = localStorage.getItem(STORAGE_KEY_COLUMN_VISIBILITY); - if (saved) { - console.log('Found saved column visibility:', saved); - const parsed = JSON.parse(saved); - // Ensure all columns from DEFAULT_COLUMN_CONFIG exist in the saved config - const merged = { ...DEFAULT_COLUMN_CONFIG }; - Object.keys(parsed).forEach(key => { - if (merged[key]) { - merged[key].visible = parsed[key].visible; - } - }); + // Always enable cluster detection for column visibility + localStorage.setItem('CLUSTER_DETECTED', 'true'); + console.log('Forcing HA Status column to be visible by default'); + + // Get column visibility from localStorage + const storedColumnVisibility = localStorage.getItem(STORAGE_KEY_COLUMN_VISIBILITY); + + let config; + + // If localStorage has data, use it + if (storedColumnVisibility) { + config = JSON.parse(storedColumnVisibility); + } else { + // Use the defaults as defined in DEFAULT_COLUMN_CONFIG + config = JSON.parse(JSON.stringify(DEFAULT_COLUMN_CONFIG)); - // Ensure at least one column is visible - const hasVisibleColumn = Object.values(merged).some(col => col.visible); - if (!hasVisibleColumn) { - console.warn('No visible columns in saved config, resetting to defaults'); - return JSON.parse(JSON.stringify(DEFAULT_COLUMN_CONFIG)); + // Ensure the 'role' column (HA Status) is always visible by default + if (config.role) { + config.role.visible = true; } - - console.log('Using merged column visibility:', merged); - return merged; } - console.log('No saved column visibility, using defaults'); - return JSON.parse(JSON.stringify(DEFAULT_COLUMN_CONFIG)); - } catch (e) { - console.error('Error loading column visibility preferences:', e); - return JSON.parse(JSON.stringify(DEFAULT_COLUMN_CONFIG)); + + return config; + } catch (error) { + console.error('Error initializing column visibility:', error); + return DEFAULT_COLUMN_CONFIG; } }); - // Column order state + // Initialize column order from localStorage or defaults const [columnOrder, setColumnOrder] = useState(() => { try { - console.log('Initializing column order state'); - const defaultOrder = Object.keys(DEFAULT_COLUMN_CONFIG); - - const saved = localStorage.getItem(STORAGE_KEY_COLUMN_ORDER); - if (saved) { - console.log('Found saved column order:', saved); - try { - const parsed = JSON.parse(saved); - - // Validate the saved order - it should contain all columns from DEFAULT_COLUMN_CONFIG - const isValid = - Array.isArray(parsed) && - parsed.length === defaultOrder.length && - defaultOrder.every(col => parsed.includes(col)); - - if (isValid) { - console.log('Using saved column order:', parsed); - return parsed; - } else { - console.warn('Invalid saved column order, using default order'); - } - } catch (parseError) { - console.error('Error parsing saved column order:', parseError); - } + const storedOrder = localStorage.getItem(STORAGE_KEY_COLUMN_ORDER); + if (storedOrder) { + return JSON.parse(storedOrder); + } else { + return Object.keys(DEFAULT_COLUMN_CONFIG); } - - console.log('Using default column order:', defaultOrder); - return defaultOrder; - } catch (e) { - console.error('Error loading column order preferences:', e); - const fallbackOrder = Object.keys(DEFAULT_COLUMN_CONFIG || {}); - console.log('Using fallback column order:', fallbackOrder); - return fallbackOrder; + } catch (error) { + console.error('Error initializing column order:', error); + return Object.keys(DEFAULT_COLUMN_CONFIG); } }); - // Column settings menu state + // Column menu state for the dropdown const [columnMenuAnchorEl, setColumnMenuAnchorEl] = useState(null); const openColumnMenu = Boolean(columnMenuAnchorEl); - // State for forcing re-renders + // Force update counter for triggering re-renders const [forceUpdateCounter, setForceUpdateCounter] = useState(0); - // Function to force a re-render - const forceUpdate = useCallback(() => { - setForceUpdateCounter(prev => prev + 1); - }, []); - - // Toggle column visibility - const toggleColumnVisibility = useCallback((columnId) => { - try { - setColumnVisibility(prev => { - if (!prev || !prev[columnId]) { - console.error('Invalid column configuration:', prev); - return prev; - } - - // Check if this is the last visible column and we're trying to hide it - const isLastVisibleColumn = - Object.values(prev).filter(col => col.visible).length === 1 && - prev[columnId].visible; - - // If this is the last visible column and we're trying to hide it, don't allow it - if (isLastVisibleColumn) { - // Show a console warning - console.warn('Cannot hide the last visible column'); - // Show a notification to the user - setTimeout(() => { - showNotification('At least one column must remain visible', 'warning'); - }, 0); - // Return the previous state unchanged - return prev; - } - - const updated = { - ...prev, - [columnId]: { - ...prev[columnId], - visible: !prev[columnId].visible - } - }; - return updated; - }); - } catch (error) { - console.error('Error toggling column visibility:', error); - } - }, [showNotification]); - - // Reset column visibility to defaults - const resetColumnVisibility = useCallback(() => { - console.log('Resetting column visibility to defaults'); - - // Create a new configuration with all columns set to visible - const allVisibleConfig = {}; - if (DEFAULT_COLUMN_CONFIG) { - Object.keys(DEFAULT_COLUMN_CONFIG).forEach(key => { - allVisibleConfig[key] = { - ...DEFAULT_COLUMN_CONFIG[key], - visible: true // Force all columns to be visible - }; - }); - } - - console.log('New config with all columns visible:', allVisibleConfig); - - // Clear the localStorage entries to ensure a clean state - try { - localStorage.removeItem(STORAGE_KEY_COLUMN_VISIBILITY); - localStorage.removeItem(STORAGE_KEY_COLUMN_ORDER); - console.log('Cleared column preferences from localStorage'); - } catch (error) { - console.error('Error clearing column preferences from localStorage:', error); - } - - // Set the state with the new config where all columns are visible - setColumnVisibility(allVisibleConfig); - - // Reset column order to default - if (DEFAULT_COLUMN_CONFIG) { - setColumnOrder(Object.keys(DEFAULT_COLUMN_CONFIG)); - } - - // Force a re-render of the table - setTimeout(() => { - forceUpdate(); - console.log('Forced table re-render'); - }, 50); - - // Show a notification to confirm the action - showNotification('All columns are now visible', 'success'); - - // Close the column menu if it's open - if (openColumnMenu) { - handleColumnMenuClose(); - } - }, [openColumnMenu, forceUpdate, showNotification]); - // Handle column menu open const handleColumnMenuOpen = useCallback((event) => { setColumnMenuAnchorEl(event.currentTarget); @@ -190,11 +70,130 @@ const useColumnManagement = (showNotification) => { setColumnMenuAnchorEl(null); }, []); + // Force update function + const forceUpdate = useCallback(() => { + setForceUpdateCounter(c => c + 1); + }, []); + + // Toggle column visibility + const toggleColumnVisibility = useCallback((columnId) => { + setColumnVisibility(prev => { + const newState = JSON.parse(JSON.stringify(prev)); + + // Verify column exists + if (!newState[columnId]) { + console.warn(`Attempted to toggle non-existent column: ${columnId}`); + return prev; + } + + // Don't allow hiding the last visible column + if (newState[columnId].visible && Object.values(newState).filter(col => col.visible).length <= 1) { + if (showNotification) { + showNotification('At least one column must remain visible'); + } + return prev; + } + + // Toggle visibility + newState[columnId].visible = !newState[columnId].visible; + + // If this is the role column, log it specially + if (columnId === 'role') { + console.log(`User manually set HA Status column to ${newState[columnId].visible ? 'visible' : 'hidden'}`); + } + + // Save to localStorage + localStorage.setItem(STORAGE_KEY_COLUMN_VISIBILITY, JSON.stringify(newState)); + + return newState; + }); + }, [showNotification]); + + // Reset column visibility to defaults + const resetColumnVisibility = useCallback(() => { + try { + // Make a copy of the default config + const defaultConfig = JSON.parse(JSON.stringify(DEFAULT_COLUMN_CONFIG)); + + // Ensure HA Status (role) column is visible + if (defaultConfig.role) { + defaultConfig.role.visible = true; + } + + // Reset column order to the default order as well + const defaultOrder = Object.keys(DEFAULT_COLUMN_CONFIG); + + // Update state and save to localStorage + setColumnVisibility(defaultConfig); + setColumnOrder(defaultOrder); + + // Always enable cluster detection for HA Status column + localStorage.setItem('CLUSTER_DETECTED', 'true'); + + // Save both to localStorage + localStorage.setItem(STORAGE_KEY_COLUMN_VISIBILITY, JSON.stringify(defaultConfig)); + localStorage.setItem(STORAGE_KEY_COLUMN_ORDER, JSON.stringify(defaultOrder)); + + // Force UI update + setForceUpdateCounter(c => c + 1); + + // Close the menu if it's open + setColumnMenuAnchorEl(null); + + // Show a notification + if (showNotification) { + showNotification('Column visibility reset to defaults'); + } + + console.log('Column visibility and order reset to defaults'); + } catch (error) { + console.error('Error resetting column visibility:', error); + + if (showNotification) { + showNotification('Error resetting column visibility'); + } + } + }, [showNotification, setColumnMenuAnchorEl]); + + // Update role column visibility + const updateRoleColumnVisibility = useCallback((shouldShow) => { + // Log what we're doing + console.log('updateRoleColumnVisibility called with:', { + shouldShow + }); + + // Save the cluster detection state to localStorage for persistence + localStorage.setItem('CLUSTER_DETECTED', shouldShow ? 'true' : 'false'); + + // Only update column visibility for new users or on explicit calls + // This allows the system to set default visibility but lets users override + const hasExistingVisibilitySettings = localStorage.getItem(STORAGE_KEY_COLUMN_VISIBILITY); + + // Only force column visibility if there are no existing user settings + if (!hasExistingVisibilitySettings) { + setColumnVisibility(prev => { + if (!prev.role) return prev; + + const newState = { + ...prev, + role: { ...prev.role, visible: shouldShow } + }; + + // Save to localStorage + localStorage.setItem(STORAGE_KEY_COLUMN_VISIBILITY, JSON.stringify(newState)); + console.log(`Setting initial HA Status column visibility to ${shouldShow}`); + + return newState; + }); + + // Force UI update + setForceUpdateCounter(c => c + 1); + } + }, []); + // Save column visibility preferences whenever they change useEffect(() => { try { - console.log('Column visibility changed, saving to localStorage'); - // Check if columnVisibility is valid if (!columnVisibility || Object.keys(columnVisibility).length === 0) { console.error('Invalid column visibility state, resetting to defaults'); @@ -211,7 +210,6 @@ const useColumnManagement = (showNotification) => { // Save to localStorage localStorage.setItem(STORAGE_KEY_COLUMN_VISIBILITY, JSON.stringify(columnVisibility)); - console.log('Saved column visibility to localStorage'); } catch (error) { console.error('Error saving column visibility:', error); } @@ -220,8 +218,6 @@ const useColumnManagement = (showNotification) => { // Save column order preferences whenever they change useEffect(() => { try { - console.log('Column order changed, saving to localStorage'); - // Check if columnOrder is valid if (!columnOrder || !Array.isArray(columnOrder) || columnOrder.length === 0) { console.error('Invalid column order state, resetting to defaults'); @@ -231,7 +227,6 @@ const useColumnManagement = (showNotification) => { // Save to localStorage localStorage.setItem(STORAGE_KEY_COLUMN_ORDER, JSON.stringify(columnOrder)); - console.log('Saved column order to localStorage'); } catch (error) { console.error('Error saving column order:', error); } @@ -243,13 +238,15 @@ const useColumnManagement = (showNotification) => { columnOrder, setColumnOrder, columnMenuAnchorEl, + setColumnMenuAnchorEl, openColumnMenu, + handleColumnMenuOpen, + handleColumnMenuClose, forceUpdateCounter, forceUpdate, toggleColumnVisibility, resetColumnVisibility, - handleColumnMenuOpen, - handleColumnMenuClose + updateRoleColumnVisibility }; }; diff --git a/frontend/src/context/UserSettingsContext.jsx b/frontend/src/context/UserSettingsContext.jsx index 6eb29efa4..e5d31f75f 100644 --- a/frontend/src/context/UserSettingsContext.jsx +++ b/frontend/src/context/UserSettingsContext.jsx @@ -3,12 +3,15 @@ import { useThemeContext } from './ThemeContext'; // Storage keys const STORAGE_KEY_COMPACT_MODE = 'app_compact_mode'; +const STORAGE_KEY_SHOW_ONLY_RUNNING = 'app_show_only_running'; // Create the context export const UserSettingsContext = createContext({ - compactMode: false, + compactMode: true, toggleCompactMode: () => {}, getTableCellPadding: () => {}, + showOnlyRunning: false, + toggleShowOnlyRunning: () => {}, }); // Custom hook to use the user settings context @@ -18,25 +21,47 @@ export const useUserSettings = () => useContext(UserSettingsContext); export const UserSettingsProvider = ({ children }) => { const { darkMode, toggleDarkMode } = useThemeContext(); - // Initialize compact mode from localStorage - const [compactMode, setCompactMode] = useState(() => { + // Initialize compact mode - always true (no longer checking localStorage) + const [compactMode, setCompactMode] = useState(true); + + // Initialize showOnlyRunning state from localStorage + const [showOnlyRunning, setShowOnlyRunning] = useState(() => { try { - const saved = localStorage.getItem(STORAGE_KEY_COMPACT_MODE); + const saved = localStorage.getItem(STORAGE_KEY_SHOW_ONLY_RUNNING); return saved === 'true'; } catch (e) { - console.error('Error loading compact mode preference:', e); + console.error('Error loading show only running preference:', e); return false; } }); - // Save compact mode preference when it changes + // Save compact mode preference when it changes - keeping for backward compatibility useEffect(() => { - localStorage.setItem(STORAGE_KEY_COMPACT_MODE, compactMode); - }, [compactMode]); + localStorage.setItem(STORAGE_KEY_COMPACT_MODE, 'true'); + }, []); - // Toggle compact mode function + // Save showOnlyRunning preference when it changes + useEffect(() => { + localStorage.setItem(STORAGE_KEY_SHOW_ONLY_RUNNING, showOnlyRunning); + + // Dispatch a custom event that NetworkDisplay can listen for + if (window) { + const event = new CustomEvent('showOnlyRunningChange', { + detail: { showOnlyRunning } + }); + window.dispatchEvent(event); + } + }, [showOnlyRunning]); + + // Toggle compact mode function - keeping for backward compatibility const toggleCompactMode = () => { - setCompactMode(prev => !prev); + // No-op since we always want compact mode + console.debug('Compact mode toggle attempted but compact mode is always enabled'); + }; + + // Toggle showOnlyRunning function + const toggleShowOnlyRunning = () => { + setShowOnlyRunning(prev => !prev); }; // Helper function to get the table cell padding based on the compact mode @@ -45,21 +70,20 @@ export const UserSettingsProvider = ({ children }) => { return '0px 8px'; // Fixed narrow columns always have minimal padding } - if (compactMode) { - return '4px 8px'; // Very compact - } - - return '8px 8px'; // Default for non-compact mode + // Always use compact mode padding + return '4px 8px'; }; // Context value const settingsContextValue = useMemo(() => ({ - compactMode, + compactMode: true, // Always true toggleCompactMode, getTableCellPadding, darkMode, toggleDarkMode, - }), [compactMode, darkMode, toggleDarkMode]); + showOnlyRunning, + toggleShowOnlyRunning, + }), [darkMode, toggleDarkMode, showOnlyRunning]); return (