mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 14:00:29 +00:00
feat(settings): enhance column management and user settings
This commit is contained in:
@@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<UserSettingsContext.Provider value={settingsContextValue}>
|
||||
|
||||
Reference in New Issue
Block a user