seahaven-ap/src/layout/AppShell.tsx

108 lines
2.6 KiB
TypeScript
Raw Normal View History

import {
AppBar,
Box,
Drawer,
List,
ListItemButton,
ListItemText,
Toolbar,
Typography,
} from "@mui/material";
import type { ReactNode } from "react";
import { useLocation } from "react-router";
import { useNavigate, type Path } from "@/router";
const DRAWER_WIDTH = 240;
type NavItem = {
label: string;
to: Path;
testId: string;
};
const navItems: NavItem[] = [
{ label: "Dashboard", to: "/", testId: "nav-dashboard" },
{ label: "Approvals", to: "/approvals", testId: "nav-approvals" },
{ label: "Settings", to: "/settings", testId: "nav-settings" },
];
type AppShellProps = {
children: ReactNode;
};
export function AppShell({ children }: AppShellProps) {
const location = useLocation();
const navigate = useNavigate();
return (
<Box sx={{ display: "flex", minHeight: "100vh" }}>
<AppBar
position="fixed"
elevation={0}
sx={{
zIndex: (theme) => theme.zIndex.drawer + 1,
borderBottom: 1,
borderColor: "divider",
bgcolor: "background.paper",
color: "text.primary",
}}
>
<Toolbar>
<Typography variant="h6" sx={{ fontFamily: "inherit", fontWeight: 600 }}>
Sea Haven AP
</Typography>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
sx={{
width: DRAWER_WIDTH,
flexShrink: 0,
[`& .MuiDrawer-paper`]: {
width: DRAWER_WIDTH,
boxSizing: "border-box",
borderRight: 1,
borderColor: "divider",
},
}}
>
<Toolbar />
<Box sx={{ overflow: "auto", py: 1 }}>
<List dense>
{navItems.map((item) => {
const selected =
item.to === "/"
? location.pathname === "/"
: location.pathname === item.to || location.pathname.startsWith(`${item.to}/`);
return (
<ListItemButton
key={item.to}
selected={selected}
data-testid={item.testId}
onClick={() => navigate(item.to)}
>
<ListItemText primary={item.label} />
</ListItemButton>
);
})}
</List>
</Box>
</Drawer>
<Box
component="main"
sx={{
flexGrow: 1,
width: `calc(100% - ${DRAWER_WIDTH}px)`,
bgcolor: "background.default",
}}
>
<Toolbar />
{children}
</Box>
</Box>
);
}