Files
dashfront/src/components/AppShell.jsx
2026-07-19 19:39:07 +02:00

91 lines
2.8 KiB
JavaScript

import Box from '@mui/material/Box';
import List from '@mui/material/List';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import UserMenu from './UserMenu';
import { NAV_ITEMS } from '../nav';
import { artikelQueryPath } from '../pages/artikel/artikelQuery';
const SIDEBAR_WIDTH = 144;
export default function AppShell() {
const navigate = useNavigate();
const { pathname } = useLocation();
return (
<Box sx={{ display: 'flex', minHeight: '100vh', bgcolor: 'background.default' }}>
<Box
component="nav"
sx={(theme) => ({
width: SIDEBAR_WIDTH,
flexShrink: 0,
display: 'flex',
flexDirection: 'column',
bgcolor: theme.nav.background,
})}
>
<List disablePadding sx={{ flexGrow: 1 }}>
{NAV_ITEMS.map(({ path, label, icon: Icon }) => {
const active = pathname === path || pathname.startsWith(`${path}/`);
return (
<ListItemButton
key={path}
type="button"
selected={active}
onMouseDown={(e) => e.preventDefault()}
onClick={() => {
navigate(path === '/artikel' ? artikelQueryPath() : path);
}}
sx={(theme) => ({
borderRadius: 0,
px: 1.5,
py: 1.25,
borderLeft: '3px solid transparent',
color: theme.nav.item,
'& .MuiListItemIcon-root': { color: 'inherit' },
'&:hover': {
bgcolor: theme.nav.itemHover,
color: theme.nav.itemHoverText,
},
'&.Mui-selected': {
bgcolor: theme.nav.itemActive,
color: theme.nav.itemActiveText,
fontWeight: 600,
borderLeftColor: theme.nav.itemActiveAccent,
'&:hover': {
bgcolor: theme.nav.itemActive,
color: theme.nav.itemActiveText,
},
},
})}
>
<ListItemIcon sx={{ minWidth: 36 }}>
<Icon />
</ListItemIcon>
<ListItemText primary={label} />
</ListItemButton>
);
})}
</List>
<UserMenu />
</Box>
<Box
component="main"
sx={(theme) => ({
flexGrow: 1,
py: 4,
px: 3,
bgcolor: theme.nav.mainBackground,
})}
>
<Outlet />
</Box>
</Box>
);
}