import React, { useMemo, useState } from "react"; import { AppBar, Avatar, Badge, Box, Breadcrumbs, Divider, Drawer, IconButton, List, ListItemButton, ListItemIcon, ListItemText, Menu, MenuItem, Toolbar, Typography, } from "@mui/material"; import MenuIcon from "@mui/icons-material/Menu"; import NotificationsNoneIcon from "@mui/icons-material/NotificationsNone"; import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined"; import { ReactComponent as JobbjaktMark } from "../assets/jobbbjakt-mark.svg"; export type NavItem = { to: string; label: string; icon: React.ReactNode; hidden?: boolean; section?: string; }; function initialsFrom(s?: string) { const v = (s ?? "").trim(); if (!v) return "?"; const parts = v.split(/[\s@._-]+/).filter(Boolean); if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase(); return (parts[0][0] + parts[1][0]).toUpperCase(); } export default function AppShell({ pageTitle, breadcrumbs, pathname, nav, navBottom, onNavigate, onToggleDrawer, drawerOpen, user, notificationsCount, onOpenNotifications, onOpenSettings, onOpenProfile, onSignOut, rightActions, children, }: { pageTitle: string; breadcrumbs: string[]; pathname: string; nav: NavItem[]; navBottom: NavItem[]; onNavigate: (to: string) => void; onToggleDrawer: (open: boolean) => void; drawerOpen: boolean; user?: { email?: string; userName?: string; roleLabel?: string }; notificationsCount?: number; onOpenNotifications?: () => void; onOpenSettings?: () => void; onOpenProfile?: () => void; onSignOut?: () => void; rightActions?: React.ReactNode; children: React.ReactNode; }) { const drawerWidth = 254; const grouped = useMemo(() => { const groupItems = (items: NavItem[]) => { const map = new Map(); for (const it of items.filter((x) => !x.hidden)) { const key = it.section || ""; map.set(key, [...(map.get(key) ?? []), it]); } return Array.from(map.entries()); }; return { top: groupItems(nav), bottom: groupItems(navBottom), }; }, [nav, navBottom]); const renderNavList = (groups: Array<[string, NavItem[]]>) => ( {groups.map(([section, rows]) => ( {section ? ( {section} ) : null} {rows.map((item) => { const selected = pathname === item.to || pathname.startsWith(item.to + "/"); return ( onNavigate(item.to)} sx={(theme: any) => ({ borderRadius: 2, mb: 0.5, border: "1px solid transparent", "&.Mui-selected": { backgroundColor: theme.vars.palette.action.hover, borderColor: theme.vars.palette.divider, }, })} > {item.badgeCount && item.badgeCount > 0 ? ( 99 ? "99+" : item.badgeCount}> {item.icon} ) : item.icon} ); })} ))} ); const drawerContent = ( Jobbjakt Track your hunt {renderNavList(grouped.top)} {renderNavList(grouped.bottom)} ); const initials = initialsFrom(user?.userName || user?.email); const [userMenuAnchor, setUserMenuAnchor] = useState(null); const userMenuOpen = Boolean(userMenuAnchor); return ( ({ borderBottom: `1px solid ${theme.vars.palette.grey[300]}`, backgroundColor: theme.vars.palette.background.default, backgroundImage: "none", zIndex: theme.zIndex.drawer + 1, })} > onToggleDrawer(true)} sx={{ display: { xs: "inline-flex", md: "none" }, border: "1px solid", borderColor: "divider", borderRadius: 2 }} > {/* Top-bar search removed (unused). */} {rightActions} {user ? ( setUserMenuAnchor(e.currentTarget)} sx={{ borderRadius: 2, border: "1px solid", borderColor: "divider" }} > {initials} {user.userName || user.email || "User"} {user.roleLabel || ""} ) : null} setUserMenuAnchor(null)} anchorOrigin={{ vertical: "bottom", horizontal: "right" }} transformOrigin={{ vertical: "top", horizontal: "right" }} > { setUserMenuAnchor(null); onOpenProfile?.(); }} > Profile { setUserMenuAnchor(null); onOpenSettings?.(); }} > Settings { setUserMenuAnchor(null); onSignOut?.(); }} > Sign out ({ display: { xs: "none", md: "block" }, width: drawerWidth, flexShrink: 0, [`& .MuiDrawer-paper`]: { width: drawerWidth, boxSizing: "border-box", borderRight: `1px solid ${theme.vars.palette.grey[300]}`, backgroundColor: theme.vars.palette.background.default, backgroundImage: "none", boxShadow: "none", }, })} open > {drawerContent} onToggleDrawer(false)} ModalProps={{ keepMounted: true }} sx={(theme: any) => ({ display: { xs: "block", md: "none" }, [`& .MuiDrawer-paper`]: { width: drawerWidth, borderRight: `1px solid ${theme.vars.palette.grey[300]}`, backgroundColor: theme.vars.palette.background.default, backgroundImage: "none", }, })} > {drawerContent} {breadcrumbs.map((c) => ( {c} ))} {pageTitle} {children} ); }