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;