diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index d577d2c14..bd96277d8 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, Suspense, lazy } from 'react'; import { Container, Box, @@ -12,172 +12,72 @@ import { Button, Link, Chip, - Alert + Alert, + Menu, + MenuItem, + ListItemIcon, + ListItemText, + Switch, + Divider } from '@mui/material'; import { useTheme } from '@mui/material/styles'; import Brightness4Icon from '@mui/icons-material/Brightness4'; import Brightness7Icon from '@mui/icons-material/Brightness7'; import GitHubIcon from '@mui/icons-material/GitHub'; +import SettingsIcon from '@mui/icons-material/Settings'; +import ViewCompactIcon from '@mui/icons-material/ViewCompact'; import NetworkDisplay from './components/NetworkDisplay'; import { AppThemeProvider, useThemeContext } from './context/ThemeContext'; +import { SearchProvider } from './context/SearchContext'; +import { UserSettingsProvider, useUserSettings } from './context/UserSettingsContext'; +import SearchField from './components/SearchField'; import useSocket from './hooks/useSocket'; import { VERSION } from './utils/version'; import { AnimatedLogoWithText } from './components/network/UIComponents'; import { initializeStorage } from './utils/storageUtils'; +import { SnackbarProvider } from 'notistack'; function AppContent() { - const { darkMode, toggleDarkMode } = useThemeContext(); - const [selectedNode, setSelectedNode] = useState('all'); - const [hasError, setHasError] = useState(false); - const [errorMessage, setErrorMessage] = useState(''); - - // Check if we're in development mode - moved up to fix initialization order - const isDevelopment = import.meta.env.DEV; - - // Error boundary effect + // Initialize local storage with default values if needed useEffect(() => { - const handleError = (event) => { - console.error('Global error caught:', event.error); - setHasError(true); - setErrorMessage(event.error?.message || 'An unknown error occurred'); - }; - - window.addEventListener('error', handleError); - - return () => { - window.removeEventListener('error', handleError); - }; + initializeStorage(); }, []); - // Initialize storage based on environment - useEffect(() => { - // Only initialize mock data flags if in development mode and not explicitly disabled - if (isDevelopment && import.meta.env.DISABLE_MOCK_DATA !== 'true') { - console.log('Development mode detected, mock data can be enabled if needed'); - } else { - // In production or when explicitly disabled, ensure mock data is disabled - if (localStorage.getItem('use_mock_data') === 'true' || localStorage.getItem('MOCK_DATA_ENABLED') === 'true') { - console.log('Resetting mock data flags to match production environment'); - localStorage.removeItem('use_mock_data'); - localStorage.removeItem('MOCK_DATA_ENABLED'); - } - } - }, [isDevelopment]); - - const { - nodeData, - guestData, - isConnected, - isMockData, - processedMetricsData, - forceUpdateCounter - } = useSocket(); const theme = useTheme(); + const { status, connected } = useSocket(); - // Force a re-render when the forceUpdateCounter changes - useEffect(() => { - if (forceUpdateCounter % 10 === 0 && forceUpdateCounter > 0) { - console.log('Force update triggered by counter:', forceUpdateCounter); - } - }, [forceUpdateCounter]); + // Get theme context functions + const { darkMode, toggleDarkMode } = useUserSettings(); - // Check if mock data is enabled - using both localStorage values and socket status - const isMockDataEnabled = isMockData || - localStorage.getItem('use_mock_data') === 'true' || - localStorage.getItem('MOCK_DATA_ENABLED') === 'true'; + // Get user settings + const { compactMode, toggleCompactMode } = useUserSettings(); - // Initialize storage and check for environment changes - useEffect(() => { - const wasCleared = initializeStorage(); - if (wasCleared) { - console.log('Application data was cleared due to environment change'); - } - }, []); + // State for settings menu + const [settingsAnchorEl, setSettingsAnchorEl] = useState(null); + const isSettingsMenuOpen = Boolean(settingsAnchorEl); - // Add a listener for node change events from the NetworkDisplay component - useEffect(() => { - const handleNodeChangeEvent = (event) => { - if (event.detail && event.detail.node) { - setSelectedNode(event.detail.node); - } - }; - - window.addEventListener('nodeChange', handleNodeChangeEvent); - - // Check URL parameters on initial load - const urlParams = new URLSearchParams(window.location.search); - const nodeParam = urlParams.get('node'); - if (nodeParam) { - setSelectedNode(nodeParam); - } - - return () => { - window.removeEventListener('nodeChange', handleNodeChangeEvent); - }; - }, []); + // Settings menu handlers + const handleSettingsClick = (event) => { + setSettingsAnchorEl(event.currentTarget); + }; - // If there's an error, show an error message - if (hasError) { - return ( - - - Something went wrong - - - {errorMessage} - - - Try refreshing the page or check the console for more details. - - - - ); - } + const handleSettingsClose = () => { + setSettingsAnchorEl(null); + }; + const [selectedNode, setSelectedNode] = useState('all'); + + // Generate content based on connection status return ( - { - // Only blur if clicking the container itself, not its children - if (e.target === e.currentTarget) { - document.activeElement?.blur(); - } - }}> - {/* DEMO MODE Banner - Only visible when mock data is enabled */} - {(isMockDataEnabled) && ( - - DEMO MODE - Viewing simulated data. To view real Proxmox data, disable mock data in your .env file. - - )} - + @@ -223,12 +123,23 @@ function AppContent() { - {/* Dark Mode Toggle Button */} - + {/* Search Field */} + + + {/* Column Visibility Button - Add reference to NetworkDisplay to access this */} + + {/* This Box will be populated by NetworkDisplay with the column visibility button */} + + + {/* Settings Button */} + - {darkMode ? : } + + + {/* Settings Menu */} + + + + {darkMode ? : } + + + {darkMode ? "Switch to Light Mode" : "Switch to Dark Mode"} + + { + e.stopPropagation(); + toggleDarkMode(); + }} + /> + + + + + + + + + + + { + e.stopPropagation(); + toggleCompactMode(); + }} + color="primary" + /> + + + { if (e.target === e.currentTarget) { @@ -259,7 +240,7 @@ function AppContent() { } }} > - + {/* Network Display Component - pass the selected node as a prop */} @@ -288,7 +269,13 @@ function AppContent() { function App() { return ( - + + + + + + + ); }