Sync shared components — sidebar scrollbar, Layout, AuthGate updates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-14 12:07:11 +00:00
parent a3b458abca
commit 79eba484e0
2 changed files with 40 additions and 11 deletions

View file

@ -1,5 +1,6 @@
import { NavLink } from 'react-router-dom'
import { Wrench, ClipboardList, History, Boxes, HardHat, Repeat, MapPin, Settings } from 'lucide-react'
import { useState, useEffect } from 'react'
import { NavLink, useLocation } from 'react-router-dom'
import { Wrench, ClipboardList, History, Boxes, HardHat, Repeat, MapPin, Settings, Menu } from 'lucide-react'
import { useAuth } from './AuthGate'
import { can } from '../types'
@ -18,9 +19,13 @@ const NAV = [
export default function Layout({ children }: { children: React.ReactNode }) {
const { user } = useAuth()
const items = NAV.filter(n => can(user, n.cap))
const location = useLocation()
const [menuOpen, setMenuOpen] = useState(false)
useEffect(() => { setMenuOpen(false) }, [location.pathname])
return (
<div className="app-shell">
<div className={`app-shell${menuOpen ? ' menu-open' : ''}`}>
<aside className="sidebar">
<div className="sidebar-logo">
<Wrench size={18} strokeWidth={1.75} />
@ -37,16 +42,14 @@ export default function Layout({ children }: { children: React.ReactNode }) {
<div className="sidebar-user">{user.name}</div>
</aside>
{menuOpen && <div className="menu-backdrop" onClick={() => setMenuOpen(false)} />}
<header className="top-bar">
<button className="top-bar-burger" onClick={() => setMenuOpen(o => !o)}>
<Menu size={20} strokeWidth={1.75} />
</button>
<Wrench size={18} strokeWidth={1.75} color="var(--gold)" />
<span className="top-bar-title">Maintenance</span>
<nav className="top-bar-nav">
{items.map(({ to, label }) => (
<NavLink key={to} to={to} className={({ isActive }) => isActive ? 'active' : ''}>
{label}
</NavLink>
))}
</nav>
</header>
<main className="page-content">

View file

@ -124,9 +124,35 @@ html, body, #root { height: 100%; margin: 0; font-size: 14px; }
.page-content { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
@media (max-width: 768px) {
.sidebar { display: none; }
.sidebar {
position: fixed;
top: 0; left: 0; bottom: 0;
z-index: 200;
transform: translateX(calc(-1 * var(--sidebar-w)));
transition: transform 0.25s ease;
}
.app-shell.menu-open .sidebar { transform: translateX(0); }
.top-bar { display: flex; }
.app-shell { flex-direction: column; }
.field-row { flex-direction: column; }
}
.menu-backdrop {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 199;
}
.top-bar-burger {
background: none;
border: none;
color: var(--text);
cursor: pointer;
display: flex;
align-items: center;
padding: 4px;
flex-shrink: 0;
}
/* ── Page chrome ───────────────────────────────────────────── */