// components/layout/Header.tsx import React, { useEffect, useState } from 'react'; import { NavigateFunction, useLocation } from 'react-router-dom'; import { AppBar, Toolbar, Tooltip, Typography, Button, IconButton, Box, Drawer, Divider, Avatar, Tabs, Tab, Container, Fade, Popover, Paper, Menu, MenuItem, ListItemIcon, ListItemText, Collapse, List, ListItem, ListItemButton, SxProps, } from '@mui/material'; import { styled, useTheme } from '@mui/material/styles'; import { Menu as MenuIcon, Dashboard, Person, Logout, Settings, ExpandMore, ExpandLess, KeyboardArrowDown, } from '@mui/icons-material'; import FaceRetouchingNaturalIcon from '@mui/icons-material/FaceRetouchingNatural'; import { getUserMenuItemsByGroup } from 'config/navigationConfig'; import { NavigationItem } from 'types/navigation'; import { Beta } from 'components/ui/Beta'; import { Candidate, Employer } from 'types/types'; import { SetSnackType } from 'components/Snack'; import { CopyBubble } from 'components/CopyBubble'; import 'components/layout/Header.css'; import { useAuth } from 'hooks/AuthContext'; import { useAppState } from 'hooks/GlobalContext'; // Styled components const StyledAppBar = styled(AppBar, { shouldForwardProp: (prop) => prop !== 'transparent', })<{ transparent?: boolean }>(({ theme, transparent }) => ({ backgroundColor: transparent ? 'transparent' : theme.palette.primary.main, boxShadow: transparent ? 'none' : '', transition: 'background-color 0.3s ease', borderRadius: 0, padding: 0, })); const NavLinksContainer = styled(Box)(({ theme }) => ({ display: 'flex', justifyContent: 'center', flex: 1, [theme.breakpoints.down('md')]: { display: 'none', }, })); const UserActionsContainer = styled(Box)(({ theme }) => ({ display: 'flex', alignItems: 'center', gap: theme.spacing(1), })); const UserButton = styled(Button)(({ theme }) => ({ color: theme.palette.primary.contrastText, textTransform: 'none', display: 'flex', alignItems: 'center', gap: theme.spacing(1), padding: theme.spacing(0.5, 1.5), borderRadius: theme.shape.borderRadius, '&:hover': { backgroundColor: theme.palette.action.hover, }, })); const MobileDrawer = styled(Drawer)(({ theme }) => ({ '& .MuiDrawer-paper': { width: 320, backgroundColor: theme.palette.background.paper, }, })); const DropdownButton = styled(Button)(({ theme }) => ({ color: theme.palette.primary.contrastText, textTransform: 'none', minHeight: 48, '&:hover': { backgroundColor: theme.palette.action.hover, }, })); const UserMenuContainer = styled(Paper)(({ theme }) => ({ backgroundColor: theme.palette.background.paper, borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[8], overflow: 'hidden', minWidth: 200, })); interface HeaderProps { transparent?: boolean; className?: string; navigate: NavigateFunction; navigationItems: NavigationItem[]; currentPath: string; sessionId?: string | null; } const Header: React.FC = (props: HeaderProps) => { const { user, logout } = useAuth(); const { setSnack } = useAppState(); const { transparent = false, className, navigate, navigationItems, sessionId, } = props; const theme = useTheme(); const location = useLocation(); const name = (user?.firstName || user?.email || ''); // State for desktop dropdown menus const [dropdownAnchors, setDropdownAnchors] = useState<{ [key: string]: HTMLElement | null }>({}); // State for mobile drawer const [mobileOpen, setMobileOpen] = useState(false); const [mobileExpanded, setMobileExpanded] = useState<{ [key: string]: boolean }>({}); // State for user menu const [userMenuAnchor, setUserMenuAnchor] = useState(null); const userMenuOpen = Boolean(userMenuAnchor); const isAdmin = user?.isAdmin || false; // Get user menu items from navigation config const userMenuGroups = getUserMenuItemsByGroup(user?.userType || null, isAdmin); // Create user menu items array with proper actions const createUserMenuItems = () => { const items: Array<{ id: string; label: string; icon: React.ReactElement | null; action: () => void; group?: string; }> = []; // Add profile group items userMenuGroups.profile.forEach(item => { if (!item.divider) { items.push({ id: item.id, label: item.label as string, icon: item.icon || null, action: () => item.path && navigate(item.path), group: 'profile' }); } }); // Add divider if we have items before system group if (items.length > 0 && userMenuGroups.system.length > 0) { items.push({ id: 'divider', label: '', icon: null, action: () => { }, group: 'divider' }); } // Add account group items userMenuGroups.account.forEach(item => { if (!item.divider) { items.push({ id: item.id, label: item.label as string, icon: item.icon || null, action: () => item.path && navigate(item.path), group: 'account' }); } }); // Add divider if we have items before system group if (items.length > 0 && userMenuGroups.system.length > 0) { items.push({ id: 'divider', label: '', icon: null, action: () => { }, group: 'divider' }); } // Add admin group items userMenuGroups.admin.forEach(item => { if (!item.divider) { items.push({ id: item.id, label: item.label as string, icon: item.icon || null, action: () => item.path && navigate(item.path), group: 'admin' }); } }); // Add divider if we have items before system group if (items.length > 0 && userMenuGroups.admin.length > 0) { items.push({ id: 'divider', label: '', icon: null, action: () => { }, group: 'divider' }); } // Add system group items with special handling for logout userMenuGroups.system.forEach(item => { if (item.id === 'logout') { items.push({ id: 'logout', label: 'Logout', icon: , action: () => { logout(); navigate('/'); }, group: 'system' }); } else if (!item.divider) { items.push({ id: item.id, label: item.label as string, icon: item.icon || null, action: () => item.path && navigate(item.path), group: 'system' }); } }); // Add other group items userMenuGroups.other.forEach(item => { if (!item.divider) { items.push({ id: item.id, label: item.label as string, icon: item.icon || null, action: () => item.path && navigate(item.path), group: 'other' }); } }); return items; }; const userMenuItems = createUserMenuItems(); // Helper function to check if current path matches navigation item const isCurrentPath = (item: NavigationItem): boolean => { if (!item.path) return false; if (item.exact) { return location.pathname === item.path; } return location.pathname.startsWith(item.path); }; // Helper function to check if any child is current path const hasActiveChild = (item: NavigationItem): boolean => { if (!item.children) return false; return item.children.some(child => isCurrentPath(child) || hasActiveChild(child)); }; // Desktop dropdown handlers const handleDropdownOpen = (event: React.MouseEvent, itemId: string) => { setDropdownAnchors(prev => ({ ...prev, [itemId]: event.currentTarget })); }; const handleDropdownClose = (itemId: string) => { setDropdownAnchors(prev => ({ ...prev, [itemId]: null })); }; // Mobile accordion handlers const handleMobileToggle = (itemId: string) => { setMobileExpanded(prev => ({ ...prev, [itemId]: !prev[itemId] })); }; const handleDrawerToggle = () => { setMobileOpen(!mobileOpen); }; const handleUserMenuOpen = (event: React.MouseEvent) => { setUserMenuAnchor(event.currentTarget); }; const handleUserMenuClose = () => { setUserMenuAnchor(null); }; const handleUserMenuAction = (item: { id: string; label: string; icon: React.ReactElement | null; action: () => void; group?: string }) => { if (item.group !== 'divider') { item.action(); handleUserMenuClose(); } }; // Navigation handlers const handleNavigate = (path: string) => { navigate(path); setMobileOpen(false); // Close all dropdowns setDropdownAnchors({}); }; // Render desktop navigation with dropdowns const renderDesktopNavigation = () => { return ( {navigationItems.map((item, index) => { const hasChildren = item.children && item.children.length > 0; const isActive = isCurrentPath(item) || hasActiveChild(item); if (hasChildren) { return ( handleDropdownOpen(e, item.id)} endIcon={} sx={{ backgroundColor: isActive ? 'action.selected' : 'transparent', color: isActive ? 'secondary.main' : 'primary.contrastText', }} > {item.icon && {item.icon}} {item.label} handleDropdownClose(item.id)} anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }} transformOrigin={{ vertical: 'top', horizontal: 'left' }} TransitionComponent={Fade} > {item.children?.map(child => ( child.path && handleNavigate(child.path)} selected={isCurrentPath(child)} disabled={!child.path} > {child.icon && {child.icon}} {child.label} ))} ); } else { return ( item.path && handleNavigate(item.path)} sx={{ backgroundColor: isActive ? 'action.selected' : 'transparent', color: isActive ? 'secondary.main' : 'primary.contrastText', mr: (index === 0 || index === navigationItems.length - 1) ? "auto" : "unset", }} > {item.icon && {item.icon}} {item.label} ); } })} ); }; // Render mobile accordion navigation const renderMobileNavigation = () => { const renderNavigationItem = (item: NavigationItem, depth: number = 0) => { const hasChildren = item.children && item.children.length > 0; const isActive = isCurrentPath(item) || hasActiveChild(item); const isExpanded = mobileExpanded[item.id]; return ( { if (hasChildren) { handleMobileToggle(item.id); } else if (item.path) { handleNavigate(item.path); } }} selected={isActive} sx={{ backgroundColor: isActive ? 'action.selected' : 'transparent', '&.Mui-selected': { backgroundColor: 'primary.main', color: 'primary.contrastText', '& .MuiListItemIcon-root': { color: 'primary.contrastText', }, }, }} > {item.icon && ( {item.icon} )} 0 ? '0.875rem' : '1rem', fontWeight: depth === 0 ? 500 : 400, } }} /> {hasChildren && ( {isExpanded ? : } )} {hasChildren && ( {item.children?.map(child => renderNavigationItem(child, depth + 1))} )} ); }; return ( {navigationItems.map((item) => renderNavigationItem(item))} {!user && ( handleNavigate('/login')}> )} ); }; // Render user menu content const renderUserMenu = () => { return ( {userMenuItems.map((item, index) => ( item.group === 'divider' ? ( ) : ( handleUserMenuAction(item)}> {item.icon && {item.icon}} ) ))} ); }; // Render user account section const renderUserSection = () => { if (!user) { return ( ); } return ( <> {name.charAt(0).toUpperCase()} {name} {renderUserMenu()} ); }; return ( {/* Navigation Links - Desktop */} {renderDesktopNavigation()} {/* User Actions Section */} {renderUserSection()} {/* Mobile Menu Button */} {sessionId && ( { navigate(`${window.location.pathname}?id=${sessionId}`); setSnack("Link copied!"); }} size="large" /> )} {/* Mobile Navigation Drawer */} {renderMobileNavigation()} { navigate('/docs/beta'); }} /> ); }; export { Header };