feat(demo): plain-language integration status, richer audit, reset integrity

Integration status badges across Dashboard/Automation now show honest
plain-language labels instead of raw backend state strings (and fix a
few states that had no matching CSS colour class at all). Audit trail
gets a "view related events" action reusing the existing correlation_id
filter. About page gains scope/architecture/security/testing sections
and a guided-demo entry point. POST /api/v1/demo/reset now runs and
records a server-side scenario-integrity check. Also fixes a second real
race condition (caught by the return-review e2e test): the odometer
scenario pre-fill now resolves before ReturnForm mounts instead of
patching its value in after the fact.
This commit is contained in:
NuklearRabbit
2026-08-03 15:00:11 +02:00
parent cf9a889547
commit 5fa4fe0811
13 changed files with 254 additions and 48 deletions
+2 -2
View File
@@ -3,6 +3,6 @@ export function SeverityBadge({ severity }: { severity: "low" | "medium" | "high
return <span className={`badge severity-${severity}`}>{label} severity</span>;
}
export function StatusBadge({ status }: { status: string }) {
return <span className={`badge status-${status}`}>{status.replace(/_/g, " ")}</span>;
export function StatusBadge({ status, label }: { status: string; label?: string }) {
return <span className={`badge status-${status}`}>{label ?? status.replace(/_/g, " ")}</span>;
}
+2 -16
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState, type FormEvent } from "react";
import { useState, type FormEvent } from "react";
import { Link, useNavigate } from "react-router-dom";
import { api, ApiError } from "../api/client";
import type { RegisterReturnRequest, RegisterReturnResult, ReturnPreviewResult } from "../api/types";
@@ -95,7 +95,6 @@ export function ReturnForm({
suggestedOdometerKm?: number;
}) {
const [odometer, setOdometer] = useState(suggestedOdometerKm !== undefined ? String(suggestedOdometerKm) : "");
const odometerEditedByUser = useRef(false);
const [fuel, setFuel] = useState("50");
const [cleanlinessOk, setCleanlinessOk] = useState(true);
const [damageReported, setDamageReported] = useState(false);
@@ -108,16 +107,6 @@ export function ReturnForm({
const [step, setStep] = useState<"capture" | "review">("capture");
const [preview, setPreview] = useState<ReturnPreviewResult | null>(null);
useEffect(() => {
// suggestedOdometerKm arrives asynchronously (BookingDetail fetches the vehicle's
// canonical odometer after this form has already mounted), so the initial useState
// value alone won't catch it -- sync it once it resolves. Never overwrite a value the
// visitor has already started typing.
if (suggestedOdometerKm !== undefined && !odometerEditedByUser.current) {
setOdometer(String(suggestedOdometerKm));
}
}, [suggestedOdometerKm]);
function currentBody(): RegisterReturnRequest {
return {
end_odometer_km: Number(odometer),
@@ -184,10 +173,7 @@ export function ReturnForm({
required
min={0}
value={odometer}
onChange={(e) => {
odometerEditedByUser.current = true;
setOdometer(e.target.value);
}}
onChange={(e) => setOdometer(e.target.value)}
/>
</label>