feat(demo): add reset UI and wire truthful integration status into pages

Add a "Reset demo data" action to the sidebar (Operations Manager only,
explicit confirmation, progress, error handling) -- POST /api/v1/demo/reset
already existed and was already role-gated server-side, but had no UI
trigger. Reset invalidates the acting session server-side, so the flow
signs the user out and returns them to login afterward.

Wire the new GET /api/v1/integrations/status into Automation.tsx and
Dashboard.tsx so both show the aggregate n8n state instead of the most
recent event's status, and the MCP Hub card reflects the actual
registration_enabled setting instead of a hardcoded "not configured" label.
This commit is contained in:
NuklearRabbit
2026-08-02 06:42:27 +02:00
parent 4437b8792a
commit 1867828a9d
3 changed files with 112 additions and 6 deletions
+48 -3
View File
@@ -1,10 +1,18 @@
import { useCallback, useEffect, useState } from "react";
import { api, ApiError } from "../api/client";
import type { AutomationRun, KnowledgeHealth } from "../api/types";
import type { AutomationRun, IntegrationStatus, KnowledgeHealth } from "../api/types";
import { StatusBadge } from "../components/Badge";
import { useAuth } from "../context/AuthContext";
import { ErrorState, IntegrationMark, LoadingState, PageHeader, SectionHeading } from "../components/PageChrome";
const N8N_STATE_LABEL: Record<IntegrationStatus["n8n"]["state"], string> = {
disabled: "disabled",
unavailable: "unavailable",
degraded: "degraded",
operational: "available",
no_evidence: "no_events",
};
export function Automation() {
const { user } = useAuth();
const [runs, setRuns] = useState<AutomationRun[] | null>(null);
@@ -13,6 +21,7 @@ export function Automation() {
const [retryError, setRetryError] = useState<string | null>(null);
const [retrying, setRetrying] = useState<string | null>(null);
const [knowledge, setKnowledge] = useState<KnowledgeHealth | null>(null);
const [integrationStatus, setIntegrationStatus] = useState<IntegrationStatus | null>(null);
const load = useCallback(() => {
setRuns(null);
@@ -39,12 +48,24 @@ export function Automation() {
api.get<KnowledgeHealth>("/api/v1/knowledge/status").then(setKnowledge).catch(() => setKnowledge(null));
}, []);
const loadIntegrationStatus = useCallback(() => {
api
.get<IntegrationStatus>("/api/v1/integrations/status")
.then(setIntegrationStatus)
.catch(() => setIntegrationStatus(null));
}, []);
useEffect(() => {
loadIntegrationStatus();
}, [loadIntegrationStatus]);
async function handleRetry(eventId: string) {
setRetryError(null);
setRetrying(eventId);
try {
await api.post(`/api/v1/workflows/${eventId}/retry`);
load();
loadIntegrationStatus();
} catch (err) {
setRetryError(err instanceof ApiError ? err.message : "Could not retry this delivery.");
} finally {
@@ -66,9 +87,33 @@ export function Automation() {
<PageHeader eyebrow="Assurance / Integrations" title="Integration control" description="Monitor delivery health, graceful degradation and retryable workflow events." />
<section className="integration-cards" aria-label="Integration health">
<article><IntegrationMark kind="n8n" /><div><span className="integration-kicker">Orchestration</span><h2>n8n delivery</h2><p>Return events are committed locally first and then delivered through the outbox.</p></div><StatusBadge status={runs?.[0]?.status ?? "no_events"} /></article>
<article>
<IntegrationMark kind="n8n" />
<div>
<span className="integration-kicker">Orchestration</span>
<h2>n8n delivery</h2>
<p>
{integrationStatus
? `${integrationStatus.n8n.succeeded} succeeded · ${integrationStatus.n8n.failed} failed · ${integrationStatus.n8n.pending} pending · ${integrationStatus.n8n.delivering} delivering.`
: "Return events are committed locally first and then delivered through the outbox."}
</p>
</div>
<StatusBadge status={integrationStatus ? N8N_STATE_LABEL[integrationStatus.n8n.state] : (runs?.[0]?.status ?? "no_events")} />
</article>
<article><IntegrationMark kind="rag" /><div><span className="integration-kicker">Knowledge</span><h2>RAGcore</h2><p>{knowledge ? `${knowledge.document_count} procedures indexed in ${knowledge.collection}.` : "Health evidence is currently unavailable."}</p></div><StatusBadge status={knowledge?.available ? "available" : "unavailable"} /></article>
<article><IntegrationMark kind="mcp" /><div><span className="integration-kicker">Tool gateway</span><h2>MCP Hub</h2><p>No active MobilityOps adapter is configured in this proof of concept.</p></div><StatusBadge status="not_configured" /></article>
<article>
<IntegrationMark kind="mcp" />
<div>
<span className="integration-kicker">Tool gateway</span>
<h2>MCP Hub</h2>
<p>
{integrationStatus?.mcp_hub.registration_enabled
? "Registration is enabled for this deployment."
: "No active MobilityOps adapter is configured in this proof of concept."}
</p>
</div>
<StatusBadge status={integrationStatus?.mcp_hub.state ?? "not_configured"} />
</article>
</section>
<SectionHeading title="Delivery ledger" description="Persisted outbox attempts with the latest failure evidence." />