/* eslint-disable react-hooks/exhaustive-deps, react-hooks/set-state-in-effect, react-hooks/immutability, react-refresh/only-export-components */ import { useState, useEffect, useRef } from 'react'; import { NavLink, useNavigate, useLocation } from 'react-router-dom'; import { useAuth } from './AuthContext'; export const getInitials = (userData) => { if (!userData) return 'DP'; const firstName = (userData.first_name || '').trim(); const lastName = (userData.last_name || '').trim(); if (firstName || lastName) { const f = firstName.charAt(0); const l = lastName.charAt(0); if (f && l) return `${f}${l}`.toUpperCase(); if (f) return f.toUpperCase(); if (l) return l.toUpperCase(); } const name = (userData.name || userData.full_name || '').trim(); if (name) { const parts = name.split(/\s+/); if (parts.length >= 2) { return `${parts[0].charAt(0)}${parts[parts.length - 1].charAt(0)}`.toUpperCase(); } return name.substring(0, 2).toUpperCase(); } const email = (userData.email || '').trim(); if (email) { const username = email.split('@')[0]; const parts = username.split(/[._-]/); if (parts.length >= 2 && parts[0] && parts[1]) { return `${parts[0].charAt(0)}${parts[1].charAt(0)}`.toUpperCase(); } if (username.length >= 2) { return username.substring(0, 2).toUpperCase(); } return username.charAt(0).toUpperCase(); } return 'DP'; }; export const Layout = ({ children }) => { const { user, logout } = useAuth(); const navigate = useNavigate(); const location = useLocation(); const searchParams = new URLSearchParams(location.search); const urlQuery = searchParams.get('q') || ''; const [globalSearchQuery, setGlobalSearchQuery] = useState(urlQuery); useEffect(() => { setGlobalSearchQuery(urlQuery); }, [urlQuery]); // Always scroll to top of page on route change useEffect(() => { window.scrollTo(0, 0); const mainElement = document.querySelector('main'); if (mainElement) mainElement.scrollTop = 0; }, [location.pathname]); const [isDemoMode, setIsDemoMode] = useState(false); const [isProfileOpen, setIsProfileOpen] = useState(false); const [isNotificationsOpen, setIsNotificationsOpen] = useState(false); const [notifications, setNotifications] = useState([]); const [loadingNotifications, setLoadingNotifications] = useState(false); const [hasUnreadNotifications, setHasUnreadNotifications] = useState(false); const notificationsRef = useRef(null); const profileRef = useRef(null); useEffect(() => { const handleClickOutside = (event) => { if (notificationsRef.current && !notificationsRef.current.contains(event.target)) { setIsNotificationsOpen(false); } if (profileRef.current && !profileRef.current.contains(event.target)) { setIsProfileOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, []); const fetchNotifications = async () => { if (!user) return; setLoadingNotifications(true); try { const token = localStorage.getItem('wss_token') || sessionStorage.getItem('wss_token'); const res = await fetch('/api/auth/notifications', { headers: { 'Authorization': `Bearer ${token}` } }); if (res.ok) { const data = await res.json(); const fetched = data.notifications || []; const lastSeenId = localStorage.getItem('last_seen_notification_id'); if (fetched.length > 0 && String(fetched[0].id) !== lastSeenId) { setHasUnreadNotifications(true); } setNotifications(fetched); } } catch (err) { console.error("Failed to fetch notifications:", err); } finally { setLoadingNotifications(false); } }; useEffect(() => { if (user) { fetchNotifications(); const interval = setInterval(fetchNotifications, 60000); // refresh every minute return () => clearInterval(interval); } }, [user]); useEffect(() => { setIsDemoMode(!!window.WSS_DEMO_MODE); const checkInterval = setInterval(() => { setIsDemoMode(!!window.WSS_DEMO_MODE); }, 1000); return () => clearInterval(checkInterval); }, []); // Force removal of dark theme and clear localStorage keys useEffect(() => { document.documentElement.classList.remove('dark'); localStorage.removeItem('color-theme'); }, []); const [impersonationToken, setImpersonationToken] = useState(null); const [organizations, setOrganizations] = useState([]); useEffect(() => { setImpersonationToken(localStorage.getItem('original_admin_token')); }, [location.pathname]); useEffect(() => { const fetchOrgs = async () => { const adminToken = localStorage.getItem('original_admin_token') || localStorage.getItem('wss_token') || sessionStorage.getItem('wss_token'); // Only attempt if they might be an admin if (!adminToken) return; try { const res = await fetch('/api/auth/organizations', { headers: { 'Authorization': `Bearer ${adminToken}` } }); if (res.ok) { const data = await res.json(); setOrganizations(data.organizations || []); } } catch (err) { console.error("Failed to fetch organizations for dropdown", err); } }; const isSuperAdmin = user?.role === 'super_admin' || user?.role === 'admin' || localStorage.getItem('original_admin_token'); if (isSuperAdmin) { fetchOrgs(); } }, [user]); const handleReturnToAdmin = () => { const orig = localStorage.getItem('original_admin_token'); if (orig) { localStorage.removeItem('original_admin_token'); localStorage.setItem('wss_token', orig); window.location.href = location.pathname; } }; const handleLogout = () => { const isSuperAdmin = user?.role === 'super_admin' || sessionStorage.getItem('superAdminAuth') === 'true'; logout(); sessionStorage.removeItem('superAdminAuth'); localStorage.removeItem('original_admin_token'); navigate(isSuperAdmin ? '/' : '/login'); }; const navItems = [ ...(user?.role === 'executive_user' ? [ { to: '/scans/history', label: 'Organization Reports', icon: 'analytics' }, { to: '/settings', label: 'Settings', icon: 'settings' }, ] : [ { to: '/dashboard', label: 'Dashboard', icon: 'dashboard' }, { to: '/scans/new', label: 'New Scan', icon: 'security' }, { to: '/scans/history', label: 'Reports', icon: 'analytics' }, { to: '/scans/results', label: 'Vulnerabilities', icon: 'bug_report' }, { to: '/settings', label: 'Settings', icon: 'settings' }, ]), ...(user?.role === 'super_admin' ? [{ to: '/super-admin', label: 'Global Management', icon: 'admin_panel_settings' }] : user?.role === 'admin' ? [{ to: '/admin', label: 'Global Management', icon: 'admin_panel_settings' }] : user?.role === 'support_engineer' ? [{ to: '/support', label: 'Global Management', icon: 'support_agent' }] : []), ]; return (
{/* SideNavBar (Stitch Layout) */} {/* TopNavBar (Stitch Layout) */}
{/* Mobile Hamburger menu */}
menu
LarShield Logo
Lar Shield {user?.role === 'super_admin' ? 'SUPER ADMIN' : user?.role === 'admin' ? 'ADMIN' : user?.role === 'support_engineer' ? 'SUPPORT ENGINEER' : user?.role === 'executive_user' ? 'EXECUTIVE USER' : user?.role === 'soc_analyst' ? 'SOC ANALYST' : 'ORG ADMIN'}
{/* Search Bar Utility */}
search setGlobalSearchQuery(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { if (globalSearchQuery.trim()) { navigate(`/scans/history?q=${encodeURIComponent(globalSearchQuery.trim())}`); } else { navigate(`/scans/history`); } } }} />
{/* Right Nav Icons / Mode Selector */}
{/* Organization Display / Admin Dropdown */} {(user?.role === 'admin' || user?.role === 'super_admin' || impersonationToken) && organizations.length > 0 ? (
) : user?.org_name ? (
domain {user.org_name}
) : null} {/* Active Status Badge */} {isDemoMode ? (
Sandbox Mode
) : (
Connected
)} {/* Profile & Controls */}
{isNotificationsOpen && (

notifications Notifications

{loadingNotifications && notifications.length === 0 ? (
sync Loading notifications...
) : notifications.length === 0 ? (
notifications_off
All Caught Up! You have no new notifications right now.
) : ( notifications.slice(0, 3).map(n => (
{n.icon}
{n.title} {n.message} {new Date(n.timestamp).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })}
)) )}
)}
{!user && ( )} {user && (
{isProfileOpen && (
{getInitials(user)}

{user.first_name || user.last_name ? `${user.first_name || ''} ${user.last_name || ''}`.trim() : user.email}

{(user.role || 'User').replace(/_/g, ' ')}

)}
)}
{/* Main Content Render Area */}
{impersonationToken && (
vpn_key Impersonation Mode Active You are currently viewing data for {user?.org_name || organizations.find(o => String(o.id) === String(user?.org_id))?.name || 'this organization'}.
)}
{children}
); };