Landing page.

This commit is contained in:
Drew 2025-09-22 02:32:43 -07:00
parent 80539fd199
commit 6ef9843835
16 changed files with 1405 additions and 57 deletions

View file

@ -0,0 +1,237 @@
import React, { useState, useEffect } 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,
DarkMode as DarkModeIcon,
LightMode as LightModeIcon,
} 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 [darkMode, setDarkMode] = useState(false)
// Check system preference and localStorage on mount
useEffect(() => {
const savedTheme = localStorage.getItem('theme')
const prefersDark = window.matchMedia(
'(prefers-color-scheme: dark)'
).matches
setDarkMode(savedTheme === 'dark' || (!savedTheme && prefersDark))
}, [])
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen)
}
const handleThemeToggle = () => {
const newTheme = !darkMode
setDarkMode(newTheme)
localStorage.setItem('theme', newTheme ? 'dark' : 'light')
document.documentElement.classList.toggle('dark', newTheme)
}
const menuItems = [
{
text: 'Tasks',
icon: <DashboardIcon />,
path: '/',
},
{
text: 'Settings',
icon: <SettingsIcon />,
path: '/settings',
},
]
const drawer = (
<div>
<Toolbar>
<Typography
variant="h6"
noWrap
component="div"
sx={{ fontWeight: 700 }}
>
Captain's Log
</Typography>
</Toolbar>
<List>
{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: { md: `calc(100% - ${drawerWidth}px)` },
ml: { md: `${drawerWidth}px` },
}}
>
<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 }}>
Captain's Log
</Typography>
<IconButton
color="inherit"
onClick={handleThemeToggle}
aria-label="toggle dark mode"
>
{darkMode ? <LightModeIcon /> : <DarkModeIcon />}
</IconButton>
</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,
},
}}
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>
)
}