import { useEffect, useMemo, useState } from "react"; import { Link, useSearchParams } from "react-router-dom"; import { api } from "../api/client"; import type { Dashboard as DashboardData, IntegrationStatus, KnowledgeHealth } from "../api/types"; import { useAuth } from "../context/AuthContext"; import { useDemoGuide } from "../context/DemoGuideContext"; import { useDemoManifest } from "../context/DemoManifestContext"; import { SeverityBadge, StatusBadge } from "../components/Badge"; import { Icon } from "../components/Icons"; import { ErrorState, IntegrationMark, LoadingState, PageHeader, SectionHeading } from "../components/PageChrome"; import { N8N_STATE_META, MCP_STATE_META } from "../data/integrationLabels"; const FLEET_METRICS: Array<{ key: keyof DashboardData["metrics"]; label: string; tone: string }> = [ { key: "available", label: "Available", tone: "ready" }, { key: "rented", label: "Rented", tone: "neutral" }, { key: "cleaning", label: "Cleaning", tone: "neutral" }, { key: "maintenance", label: "Maintenance", tone: "warning" }, { key: "blocked", label: "Blocked", tone: "critical" }, ]; function localTime(value: string, withDate = false) { return new Date(value).toLocaleString("en-GB", { ...(withDate ? { day: "2-digit", month: "short" } : {}), hour: "2-digit", minute: "2-digit", timeZone: "Europe/Brussels", }); } export function Dashboard() { const { user } = useAuth(); const { manifest } = useDemoManifest(); const { openGuide, restart, currentIndex, completed, totalSteps } = useDemoGuide(); const [searchParams, setSearchParams] = useSearchParams(); const canSeeQuality = user?.role === "operations_manager"; const canSeeAutomation = user?.role === "operations_manager"; useEffect(() => { if (searchParams.get("guide") === "start") { restart(); openGuide(); setSearchParams({}, { replace: true }); } // Only ever react to the initial `?guide=start` marker set by the login screen's // "Start begeleide demo" CTA, so this intentionally runs once on mount. }, []); const [data, setData] = useState(null); const [knowledge, setKnowledge] = useState(null); const [integrationStatus, setIntegrationStatus] = useState(null); const [error, setError] = useState(null); const [severity, setSeverity] = useState("all"); const [query, setQuery] = useState(""); useEffect(() => { api.get("/api/v1/dashboard").then(setData).catch(() => setError("Dashboard data is unavailable right now.")); api.get("/api/v1/knowledge/status").then(setKnowledge).catch(() => setKnowledge(null)); }, []); useEffect(() => { if (!canSeeAutomation) return; api .get("/api/v1/integrations/status") .then(setIntegrationStatus) .catch(() => setIntegrationStatus(null)); }, [canSeeAutomation]); const attention = useMemo(() => data?.attention_items.filter((item) => { const matchesSeverity = severity === "all" || item.severity === severity; const haystack = `${item.title} ${item.detail} ${item.link_ref}`.toLowerCase(); return matchesSeverity && haystack.includes(query.toLowerCase()); }) ?? [], [data, query, severity]); if (error) return ; if (!data) return ; const latestRun = data.recent_automation[0]; const n8nState = !latestRun ? "No delivery yet" : latestRun.status === "failed" ? "Needs attention" : latestRun.status; return (
View fleet} />
Probeer een demonstratiescenario {manifest ? `${manifest.scenarios.filter((s) => s.ready).length} van ${manifest.scenarios.length} scenario's klaar voor demo.` : "Vijf afgebakende scenario's."}
{user?.role === "operations_manager" && ( )} Bekijk scenario's

Fleet readiness

Live from persisted vehicle state

{FLEET_METRICS.map((metric) => (
{metric.label}
{data.metrics[metric.key]}
))}
Open fleet
Review queue : undefined} />
{attention.length === 0 ?

No issues match this filter.

: (
    {attention.slice(0, 6).map((item, index) => (
  • {item.issue_ref && canSeeQuality ? ( {item.title} ) : item.link_type === "vehicle" ? ( {item.title} ) : ( item.title )}

    {item.detail}

    {item.link_ref}
  • ))}
)}
All bookings } /> {data.today.length === 0 ?

No movements scheduled today.

: (
    {data.today.slice(0, 6).map((item) => (
  1. {item.kind}{item.booking_ref}{item.vehicle_ref}
  2. ))}
)}
System detail : undefined} />
  • n8n delivery {integrationStatus ? `${integrationStatus.n8n.succeeded} succeeded · ${integrationStatus.n8n.failed} failed` : latestRun ? `Latest event ${latestRun.aggregate_ref}` : "No workflow evidence recorded"}
    {(() => { const meta = integrationStatus ? N8N_STATE_META[integrationStatus.n8n.state] : null; return ( ); })()}
  • Knowledge assistant {knowledge ? `${knowledge.provider === "ragcore" ? "RAGcore" : "Demo knowledge base"} · ${knowledge.document_count} procedures indexed` : "Health check unavailable"}
  • MCP Hub {integrationStatus?.mcp_hub.registration_enabled ? "Registration enabled" : "Not yet connected"}
    {(() => { const meta = integrationStatus ? MCP_STATE_META[integrationStatus.mcp_hub.state] : null; return ; })()}
{data.recent_automation.length === 0 ?

No automation activity recorded.

: (
    {data.recent_automation.slice(0, 4).map((run) => (
  • {run.event_type.replace(/_/g, " ")}{run.aggregate_ref}
  • ))}
)}
); }