M1: implement operational core
Demo auth, seed import/reset, dashboard, vehicle/booking list+detail, audit trail. Backend: 19 tests passing, ruff clean. Frontend: React Router shell, typed API client, responsive pages. Verified end-to-end via curl and browser.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { NavLink, Outlet, useNavigate } from "react-router-dom";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ to: "/dashboard", label: "Dashboard" },
|
||||
{ to: "/vehicles", label: "Vehicles" },
|
||||
{ to: "/bookings", label: "Bookings" },
|
||||
{ to: "/audit", label: "Audit" },
|
||||
];
|
||||
|
||||
export function Layout() {
|
||||
const { user, logout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
function handleLogout() {
|
||||
logout();
|
||||
navigate("/login");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="app-shell">
|
||||
<p className="demo-banner">
|
||||
Synthetic demo environment — no real customer or vehicle data.
|
||||
</p>
|
||||
<header className="app-header">
|
||||
<span className="brand">MobilityOps</span>
|
||||
<nav aria-label="Primary">
|
||||
<ul>
|
||||
{NAV_ITEMS.map((item) => (
|
||||
<li key={item.to}>
|
||||
<NavLink to={item.to} className={({ isActive }) => (isActive ? "active" : "")}>
|
||||
{item.label}
|
||||
</NavLink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
<div className="user-badge">
|
||||
{user && (
|
||||
<>
|
||||
<span>
|
||||
{user.display_name} · {user.role === "operations_manager" ? "Operations Manager" : "Rental Employee"}
|
||||
</span>
|
||||
<button type="button" onClick={handleLogout}>
|
||||
Switch role
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
<main id="main-content">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user