feat(demo): add demo manifest, Dutch demo entry, permanent badge and About page

Adds GET /api/v1/demo/manifest as a single source of truth for the demo's
fictional org identity (Northstar Mobility -- surfacing the project's
already-locked tenant name), synthetic-data/reset state, and live scenario
readiness. Rewrites the login screen in Dutch with an honest, no-password
demo entry and a guided-demo entry point, replaces the loud full-width
demo banner with a subtle badge + popover, and adds a compact About page
explaining what's real vs. synthetic vs. not yet connected.
This commit is contained in:
NuklearRabbit
2026-08-03 13:45:55 +02:00
parent 728e380d63
commit ac427f4427
25 changed files with 919 additions and 108 deletions
+124
View File
@@ -0,0 +1,124 @@
import { useAuth } from "../context/AuthContext";
import { useDemoManifest } from "../context/DemoManifestContext";
import { Icon } from "../components/Icons";
import { IntegrationMark, LoadingState, PageHeader, SectionHeading } from "../components/PageChrome";
function formatDateTime(value: string | null): string {
if (!value) return "onbekend";
return new Date(value).toLocaleString("nl-BE", {
dateStyle: "medium",
timeStyle: "short",
timeZone: "Europe/Brussels",
});
}
const INTEGRATION_ICON: Record<string, "n8n" | "rag" | "mcp"> = {
n8n: "n8n",
ragcore: "rag",
mcp_hub: "mcp",
};
export function AboutDemo() {
const { manifest, loading } = useDemoManifest();
const { user } = useAuth();
return (
<div className="page">
<PageHeader
eyebrow="Over deze demo"
title="Wat MobilityOps wel en niet is"
description={
manifest
? `${manifest.organization_name} is een fictieve verhuurorganisatie die dient om deze demo tastbaar te maken — geen bestaand bedrijf.`
: undefined
}
/>
{loading && <LoadingState label="Demo-informatie laden…" />}
{manifest && (
<>
<section className="record-surface about-card">
<h2>Het fictieve probleem</h2>
<p>
{manifest.organization_name} verhuurt zo'n 50 campers en bestelwagens vanuit één
hoofdlocatie. Boekingen, retours, klantgegevens en onderhoud kwamen tot nu toe uit
losse spreadsheets en mondelinge afspraken, waardoor fouten (dubbele klanten,
foutieve kilometerstanden, dubbel geboekte voertuigen) laat aan het licht kwamen.
MobilityOps toont hoe één samenhangend systeem die problemen vroeg signaleert en
gecontroleerd laat oplossen.
</p>
</section>
<section className="record-surface about-card">
<h2>Wat écht werkt</h2>
<p>
Alles hieronder is functionele code, niet alleen een mockup: rol-gebaseerde
toegang en sessies, voertuig- en boekingsbeheer, retourverwerking met
serverzijdige validatie, vijf datakwaliteitsregels met elk een eigen
oplossingsstap, een volledige audit trail, geautomatiseerde aflevering naar n8n
met begrensde herpogingen, Docker-gebaseerde deployment en een geautomatiseerde
testsuite (backend en Playwright end-to-end).
</p>
</section>
<section className="record-surface about-card">
<h2>Wat synthetisch is</h2>
<p>
De organisatie, alle klanten, voertuigen, boekingen, onderhoudsgeschiedenis,
procedures in de kennisbank en de vooraf ingerichte scenario's zijn volledig
verzonnen. Geen enkel gegeven verwijst naar een bestaand persoon, voertuig of
bedrijf; e-mailadressen gebruiken uitsluitend het testdomein <code>.test</code>.
</p>
</section>
<section aria-label="Koppelingsstatus" className="record-surface">
<SectionHeading
title="Koppelingen — eerlijk gelabeld"
description="Wat operationeel is, wat demomodus is, en wat nog niet gekoppeld is."
/>
<div className="integration-cards">
{manifest.integrations.map((integration) => (
<article key={integration.key}>
<IntegrationMark kind={INTEGRATION_ICON[integration.key]} />
<div>
<span className="integration-kicker">{integration.status_label}</span>
<h2>{integration.label}</h2>
<p>{integration.detail}</p>
</div>
</article>
))}
</div>
</section>
<section className="record-surface about-card">
<h2>Demo-omgeving herstellen</h2>
<p>
De omgeving is op elk moment terug te zetten naar de startsituatie. Laatste reset:{" "}
<strong>{formatDateTime(manifest.last_reset_at)}</strong>.{" "}
{user?.role === "operations_manager" ? (
<>
Gebruik <strong>Reset demo data</strong> in de zijbalk om opnieuw te beginnen.
</>
) : (
<>Een Operations Manager kan de demo-omgeving herstellen via de zijbalk.</>
)}
</p>
</section>
<section className="access-note record-surface" aria-label="Beperkingen">
<Icon name="shield" />
<p>
<strong>Beperkingen</strong>
<span>
Dit is een gerichte proof of concept, geen volledig ERP. RAGcore en de ITWorx
MCP Hub zijn nog niet live gekoppeld; de kennisassistent gebruikt een lokale,
afgebakende demokennisbank in plaats van een live RAGcore-omgeving.
</span>
</p>
</section>
</>
)}
</div>
);
}