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:
NuklearRabbit
2026-08-01 21:20:53 +02:00
parent 04d26f1f2e
commit 03c5b60235
47 changed files with 2518 additions and 70 deletions
+41
View File
@@ -0,0 +1,41 @@
import { useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom";
import { api } from "../api/client";
import type { Booking } from "../api/types";
import { StatusBadge } from "../components/Badge";
export function BookingDetail() {
const { publicRef } = useParams<{ publicRef: string }>();
const [booking, setBooking] = useState<Booking | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!publicRef) return;
setBooking(null);
setError(null);
api
.get<Booking>(`/api/v1/bookings/${publicRef}`)
.then(setBooking)
.catch(() => setError("This booking could not be found."));
}, [publicRef]);
if (error) return <p className="error" role="alert">{error}</p>;
if (!booking) return <p>Loading booking</p>;
return (
<div className="page">
<p><Link to="/bookings"> Back to bookings</Link></p>
<h1>{booking.public_ref}</h1>
<p><StatusBadge status={booking.status} /></p>
<dl className="detail-grid">
<div><dt>Customer</dt><dd>{booking.customer_name} ({booking.customer_ref})</dd></div>
<div><dt>Vehicle</dt><dd><Link to={`/vehicles/${booking.vehicle_ref}`}>{booking.vehicle_ref}</Link></dd></div>
<div><dt>Starts</dt><dd>{new Date(booking.starts_at).toLocaleString("en-GB", { timeZone: "Europe/Brussels" })}</dd></div>
<div><dt>Ends</dt><dd>{new Date(booking.ends_at).toLocaleString("en-GB", { timeZone: "Europe/Brussels" })}</dd></div>
<div><dt>Start odometer</dt><dd>{booking.start_odometer_km ?? "—"} km</dd></div>
<div><dt>End odometer</dt><dd>{booking.end_odometer_km ?? "—"} km</dd></div>
<div><dt>Requirements complete</dt><dd>{booking.requirements_complete ? "Yes" : "No"}</dd></div>
</dl>
</div>
);
}