import { useState, useEffect, useCallback, useRef } from 'react' import { Link, useLocation, useNavigate, Outlet } from 'react-router-dom' import { useAuthStore } from '@/store/authStore' import { usePermissions } from '@/hooks/usePermissions' import { BrandLogo } from '@/components/common/BrandLogo' import { Menu, X, LogOut, User, Shield, ChevronDown, FolderTree, ListOrdered, Layers } from 'lucide-react' import { cn } from '@/lib/utils' interface NavItem { path: string label: string children?: { path: string; label: string; icon: React.ReactNode }[] } export function AppLayout() { const location = useLocation() const navigate = useNavigate() const { user, logout } = useAuthStore() const { effectiveRole, isSuperAdmin } = usePermissions() const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const [flowsDropdownOpen, setFlowsDropdownOpen] = useState(false) const flowsDropdownRef = useRef(null) const handleLogout = async () => { setMobileMenuOpen(false) await logout() navigate('/login') } // Close mobile menu on route change const [prevPath, setPrevPath] = useState(location.pathname) if (prevPath !== location.pathname) { setPrevPath(location.pathname) if (mobileMenuOpen) setMobileMenuOpen(false) setFlowsDropdownOpen(false) } // Close on Escape const handleKeyDown = useCallback((e: KeyboardEvent) => { if (e.key === 'Escape') { setMobileMenuOpen(false) setFlowsDropdownOpen(false) } }, []) // Close dropdown on outside click useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (flowsDropdownRef.current && !flowsDropdownRef.current.contains(e.target as Node)) { setFlowsDropdownOpen(false) } } if (flowsDropdownOpen) { document.addEventListener('mousedown', handleClickOutside) } return () => document.removeEventListener('mousedown', handleClickOutside) }, [flowsDropdownOpen]) useEffect(() => { if (mobileMenuOpen) { document.addEventListener('keydown', handleKeyDown) document.body.style.overflow = 'hidden' } else { document.body.style.overflow = '' } return () => { document.removeEventListener('keydown', handleKeyDown) document.body.style.overflow = '' } }, [mobileMenuOpen, handleKeyDown]) const isFlowsActive = location.pathname.startsWith('/trees') || location.pathname.startsWith('/flows') const navItems: NavItem[] = [ { path: '/', label: 'Home' }, { path: '/trees', label: 'Flows', children: [ { path: '/trees', label: 'All Flows', icon: }, { path: '/trees?type=troubleshooting', label: 'Troubleshooting', icon: }, { path: '/trees?type=procedural', label: 'Procedures', icon: }, ], }, { path: '/my-trees', label: 'My Flows' }, { path: '/sessions', label: 'Sessions' }, { path: '/shares', label: 'My Shares' }, { path: '/account', label: 'Account' }, ...(isSuperAdmin ? [{ path: '/admin', label: 'Admin Panel' }] : []), ] return (
{/* Subtle radial overlay for depth */}
{/* Header */}
{/* Mobile hamburger */} {/* Logo */}
ResolutionFlow {/* Desktop Navigation */}
{/* Right side controls */}
{/* User info */}
{user?.name || user?.email}
{/* Role badge */} {effectiveRole && effectiveRole !== 'engineer' && (
{effectiveRole === 'super_admin' ? 'Super Admin' : effectiveRole === 'owner' ? 'Owner' : 'Viewer'}
)}
{/* Logout button */}
{/* Mobile Nav Drawer */} {mobileMenuOpen && (
{/* Backdrop */}
setMobileMenuOpen(false)} aria-hidden="true" /> {/* Drawer */}
)} {/* Main Content */}
) } export default AppLayout