226 lines
5.2 KiB
TypeScript
226 lines
5.2 KiB
TypeScript
import React, { useState } from 'react'
|
|
import {
|
|
AppBar,
|
|
Box,
|
|
CssBaseline,
|
|
Drawer,
|
|
IconButton,
|
|
List,
|
|
ListItem,
|
|
ListItemButton,
|
|
ListItemIcon,
|
|
ListItemText,
|
|
Toolbar,
|
|
Typography,
|
|
useTheme,
|
|
LinearProgress,
|
|
Button,
|
|
} from '@mui/material'
|
|
import {
|
|
Menu as MenuIcon,
|
|
Dashboard as DashboardIcon,
|
|
Add as AddIcon,
|
|
Settings as SettingsIcon,
|
|
} from '@mui/icons-material'
|
|
import { Link } from 'react-router'
|
|
|
|
const drawerWidth = 240
|
|
|
|
interface LayoutProps {
|
|
children: React.ReactNode
|
|
loading?: boolean
|
|
}
|
|
|
|
export default function Layout({ children, loading = false }: LayoutProps) {
|
|
const theme = useTheme()
|
|
const [mobileOpen, setMobileOpen] = useState(false)
|
|
|
|
const handleDrawerToggle = () => {
|
|
setMobileOpen(!mobileOpen)
|
|
}
|
|
|
|
const menuItems = [
|
|
{
|
|
text: 'Tasks',
|
|
icon: <DashboardIcon />,
|
|
path: '/',
|
|
},
|
|
{
|
|
text: 'Settings',
|
|
icon: <SettingsIcon />,
|
|
path: '/settings',
|
|
},
|
|
]
|
|
|
|
const drawer = (
|
|
<div>
|
|
<List sx={{ pt: 2 }}>
|
|
{menuItems.map(item => (
|
|
<ListItem key={item.text} disablePadding>
|
|
<ListItemButton
|
|
component="a"
|
|
href={item.path}
|
|
sx={{
|
|
borderRadius: 2,
|
|
mx: 1,
|
|
my: 0.5,
|
|
'&:hover': {
|
|
backgroundColor: theme.palette.primary.main + '10',
|
|
},
|
|
}}
|
|
>
|
|
<ListItemIcon sx={{ color: theme.palette.primary.main }}>
|
|
{item.icon}
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary={item.text}
|
|
primaryTypographyProps={{
|
|
fontWeight: 500,
|
|
}}
|
|
/>
|
|
</ListItemButton>
|
|
</ListItem>
|
|
))}
|
|
</List>
|
|
</div>
|
|
)
|
|
|
|
return (
|
|
<Box sx={{ display: 'flex' }}>
|
|
<CssBaseline />
|
|
|
|
{/* Loading indicator */}
|
|
{loading && (
|
|
<LinearProgress
|
|
sx={{
|
|
position: 'fixed',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
zIndex: theme.zIndex.appBar + 1,
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
{/* App Bar */}
|
|
<AppBar
|
|
position="fixed"
|
|
sx={{
|
|
width: '100%',
|
|
backgroundColor: theme.palette.primary.main,
|
|
color: theme.palette.primary.contrastText,
|
|
zIndex: theme.zIndex.drawer + 1,
|
|
}}
|
|
>
|
|
<Toolbar>
|
|
<IconButton
|
|
color="inherit"
|
|
aria-label="open drawer"
|
|
edge="start"
|
|
onClick={handleDrawerToggle}
|
|
sx={{ mr: 2, display: { md: 'none' } }}
|
|
>
|
|
<MenuIcon />
|
|
</IconButton>
|
|
|
|
<Typography
|
|
variant="h6"
|
|
noWrap
|
|
component="div"
|
|
sx={{
|
|
flexGrow: 1,
|
|
color: '#ffffff',
|
|
fontWeight: 700,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: 1,
|
|
}}
|
|
>
|
|
⚓ Captain's Log
|
|
</Typography>
|
|
|
|
<Button
|
|
component={Link}
|
|
to="/task/new"
|
|
variant="contained"
|
|
startIcon={<AddIcon />}
|
|
sx={{
|
|
backgroundColor: theme.palette.secondary.main,
|
|
color: theme.palette.secondary.contrastText,
|
|
'&:hover': {
|
|
backgroundColor: theme.palette.secondary.dark || '#b7791f',
|
|
},
|
|
fontWeight: 600,
|
|
fontSize: '1rem',
|
|
px: 2,
|
|
py: 1.5,
|
|
minWidth: 'auto',
|
|
height: '44px',
|
|
}}
|
|
>
|
|
New Task
|
|
</Button>
|
|
</Toolbar>
|
|
</AppBar>
|
|
|
|
{/* Drawer */}
|
|
<Box
|
|
component="nav"
|
|
sx={{ width: { md: drawerWidth }, flexShrink: { md: 0 } }}
|
|
aria-label="navigation menu"
|
|
>
|
|
{/* Mobile drawer */}
|
|
<Drawer
|
|
variant="temporary"
|
|
open={mobileOpen}
|
|
onClose={handleDrawerToggle}
|
|
ModalProps={{
|
|
keepMounted: true, // Better open performance on mobile.
|
|
}}
|
|
sx={{
|
|
display: { xs: 'block', md: 'none' },
|
|
'& .MuiDrawer-paper': {
|
|
boxSizing: 'border-box',
|
|
width: drawerWidth,
|
|
},
|
|
}}
|
|
>
|
|
{drawer}
|
|
</Drawer>
|
|
|
|
{/* Desktop drawer */}
|
|
<Drawer
|
|
variant="permanent"
|
|
sx={{
|
|
display: { xs: 'none', md: 'block' },
|
|
'& .MuiDrawer-paper': {
|
|
boxSizing: 'border-box',
|
|
width: drawerWidth,
|
|
top: '64px', // Position below AppBar
|
|
height: 'calc(100% - 64px)', // Adjust height to account for AppBar
|
|
},
|
|
}}
|
|
open
|
|
>
|
|
{drawer}
|
|
</Drawer>
|
|
</Box>
|
|
|
|
{/* Main content */}
|
|
<Box
|
|
component="main"
|
|
sx={{
|
|
flexGrow: 1,
|
|
p: 3,
|
|
width: { md: `calc(100% - ${drawerWidth}px)` },
|
|
minHeight: '100vh',
|
|
backgroundColor: theme.palette.background.default,
|
|
}}
|
|
>
|
|
<Toolbar />
|
|
{children}
|
|
</Box>
|
|
|
|
</Box>
|
|
)
|
|
}
|