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
@@ -0,0 +1,52 @@
import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
import { api } from "../api/client";
import type { DemoManifest } from "../api/types";
interface DemoManifestState {
manifest: DemoManifest | null;
loading: boolean;
refresh: () => void;
}
const DemoManifestContext = createContext<DemoManifestState | undefined>(undefined);
export function DemoManifestProvider({ children }: { children: ReactNode }) {
const [manifest, setManifest] = useState<DemoManifest | null>(null);
const [loading, setLoading] = useState(true);
const [version, setVersion] = useState(0);
useEffect(() => {
let cancelled = false;
setLoading(true);
// Public endpoint by design: the demo-entry screen needs this before any session
// exists, so it is never gated behind auth.
api
.get<DemoManifest>("/api/v1/demo/manifest")
.then((result) => {
if (!cancelled) setManifest(result);
})
.catch(() => {
if (!cancelled) setManifest(null);
})
.finally(() => {
if (!cancelled) setLoading(false);
});
return () => {
cancelled = true;
};
}, [version]);
return (
<DemoManifestContext.Provider
value={{ manifest, loading, refresh: () => setVersion((v) => v + 1) }}
>
{children}
</DemoManifestContext.Provider>
);
}
export function useDemoManifest(): DemoManifestState {
const ctx = useContext(DemoManifestContext);
if (!ctx) throw new Error("useDemoManifest must be used within DemoManifestProvider");
return ctx;
}