Create a frontend wireframe. (#7)

Sets up API methods and types.
Sets up a colorscheme.
Sets up a homepage.

Removes tailwind in favor of mui for now.

Reviewed-on: #7
Co-authored-by: Drew Galbraith <drew@tiramisu.one>
Co-committed-by: Drew Galbraith <drew@tiramisu.one>
This commit is contained in:
Drew 2025-09-23 04:08:45 +00:00 committed by Drew
parent 7d2b7fc90c
commit d60d834f38
27 changed files with 3114 additions and 977 deletions

View file

@ -0,0 +1,216 @@
import React, { useState } from 'react'
import {
AppBar,
Box,
CssBaseline,
Drawer,
IconButton,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
Toolbar,
Typography,
useTheme,
Fab,
LinearProgress,
} from '@mui/material'
import {
Menu as MenuIcon,
Dashboard as DashboardIcon,
Add as AddIcon,
Settings as SettingsIcon,
} from '@mui/icons-material'
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>
</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>
{/* Quick capture FAB */}
<Fab
color="primary"
aria-label="add task"
sx={{
position: 'fixed',
bottom: 16,
right: 16,
zIndex: theme.zIndex.fab,
}}
>
<AddIcon />
</Fab>
</Box>
)
}