import { useState, useMemo } from 'react'; import { Box, Collapse, Drawer, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Toolbar, Tooltip, } from '@mui/material'; import { Dashboard as DashboardIcon, DirectionsCar, Build, People, Air, CalendarMonth, MenuBook, AdminPanelSettings, Settings, Menu as MenuIcon, ExpandMore, ExpandLess, } from '@mui/icons-material'; import { useNavigate, useLocation } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { useLayout, DRAWER_WIDTH, DRAWER_WIDTH_COLLAPSED } from '../../contexts/LayoutContext'; import { useAuth } from '../../contexts/AuthContext'; import { vehiclesApi } from '../../services/vehicles'; export { DRAWER_WIDTH, DRAWER_WIDTH_COLLAPSED }; interface SubItem { text: string; path: string; } interface NavigationItem { text: string; icon: JSX.Element; path: string; subItems?: SubItem[]; } const kalenderSubItems: SubItem[] = [ { text: 'Veranstaltungen', path: '/kalender?tab=0' }, { text: 'Fahrzeugbuchungen', path: '/kalender?tab=1' }, ]; const adminSubItems: SubItem[] = [ { text: 'Services', path: '/admin?tab=0' }, { text: 'System', path: '/admin?tab=1' }, { text: 'Benutzer', path: '/admin?tab=2' }, { text: 'Broadcast', path: '/admin?tab=3' }, { text: 'Banner', path: '/admin?tab=4' }, { text: 'Wartung', path: '/admin?tab=5' }, ]; const baseNavigationItems: NavigationItem[] = [ { text: 'Dashboard', icon: , path: '/dashboard', }, { text: 'Kalender', icon: , path: '/kalender', subItems: kalenderSubItems, }, { text: 'Fahrzeuge', icon: , path: '/fahrzeuge', }, { text: 'Ausrüstung', icon: , path: '/ausruestung', }, { text: 'Mitglieder', icon: , path: '/mitglieder', }, { text: 'Atemschutz', icon: , path: '/atemschutz', }, { text: 'Wissen', icon: , path: '/wissen', }, ]; const adminItem: NavigationItem = { text: 'Admin', icon: , path: '/admin', subItems: adminSubItems, }; const adminSettingsItem: NavigationItem = { text: 'Einstellungen', icon: , path: '/admin/settings', }; interface SidebarProps { mobileOpen: boolean; onMobileClose: () => void; } function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) { const navigate = useNavigate(); const location = useLocation(); const { sidebarCollapsed, toggleSidebar } = useLayout(); const { user } = useAuth(); const isAdmin = user?.groups?.includes('dashboard_admin') ?? false; // Fetch vehicle list for dynamic dropdown sub-items const { data: vehicleList } = useQuery({ queryKey: ['vehicles', 'sidebar'], queryFn: () => vehiclesApi.getAll(), staleTime: 2 * 60 * 1000, }); const vehicleSubItems: SubItem[] = useMemo( () => (vehicleList ?? []).map((v) => ({ text: v.bezeichnung ?? v.kurzname, path: `/fahrzeuge/${v.id}`, })), [vehicleList], ); const navigationItems = useMemo((): NavigationItem[] => { const fahrzeugeItem: NavigationItem = { text: 'Fahrzeuge', icon: , path: '/fahrzeuge', subItems: vehicleSubItems.length > 0 ? vehicleSubItems : undefined, }; const items = baseNavigationItems.map((item) => item.path === '/fahrzeuge' ? fahrzeugeItem : item, ); return isAdmin ? [...items, adminItem, adminSettingsItem] : items; }, [isAdmin, vehicleSubItems]); // Expand state for items with sub-items — auto-expand when route matches const [expandedItems, setExpandedItems] = useState>({}); const isExpanded = (item: NavigationItem) => { if (expandedItems[item.path] !== undefined) return expandedItems[item.path]; // Auto-expand when the current route matches return location.pathname === item.path || location.pathname.startsWith(item.path + '/'); }; const toggleExpand = (path: string) => { setExpandedItems((prev) => ({ ...prev, [path]: !isExpanded({ path } as NavigationItem) })); }; const handleNavigation = (path: string) => { navigate(path); onMobileClose(); }; const drawerContent = ( {navigationItems.map((item) => { const isActive = location.pathname === item.path; const hasSubItems = item.subItems && item.subItems.length > 0; const expanded = hasSubItems && isExpanded(item); return ( handleNavigation(item.path)} aria-label={`Zu ${item.text} navigieren`} sx={{ justifyContent: sidebarCollapsed ? 'center' : 'initial', '&.Mui-selected': { backgroundColor: 'primary.light', color: 'primary.contrastText', '&:hover': { backgroundColor: 'primary.main', }, '& .MuiListItemIcon-root': { color: 'primary.contrastText', }, }, }} > {item.icon} {hasSubItems && !sidebarCollapsed && ( { e.stopPropagation(); toggleExpand(item.path); }} sx={{ color: isActive ? 'inherit' : 'text.secondary' }} > {expanded ? : } )} {hasSubItems && !sidebarCollapsed && ( {item.subItems!.map((sub) => { const isSubActive = location.pathname + location.search === sub.path; return ( handleNavigation(sub.path)} selected={isSubActive} sx={{ pl: 4, py: 0.5, '&.Mui-selected': { backgroundColor: 'primary.light', color: 'primary.contrastText', '&:hover': { backgroundColor: 'primary.main', }, }, }} > ); })} )} ); })} ); return ( <> {/* Mobile drawer */} {drawerContent} {/* Desktop drawer */} {drawerContent} ); } export default Sidebar;