diff --git a/frontend/src/pages/Audit.tsx b/frontend/src/pages/Audit.tsx index 485d3f5..55bf1ef 100644 --- a/frontend/src/pages/Audit.tsx +++ b/frontend/src/pages/Audit.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from "react"; import { api } from "../api/client"; import type { AuditEvent } from "../api/types"; +import { EmptyState, ErrorState, LoadingState, PageHeader } from "../components/PageChrome"; export function Audit() { const [events, setEvents] = useState(null); @@ -8,6 +9,8 @@ export function Audit() { const [action, setAction] = useState(""); useEffect(() => { + setEvents(null); + setError(null); const params = new URLSearchParams(); if (action) params.set("action", action); api @@ -18,7 +21,7 @@ export function Audit() { return (
-

Audit

+
- {error &&

{error}

} - {!error && !events &&

Loading audit trail…

} - {events && events.length === 0 &&

No audit events match this filter.

} + {error && } + {!error && !events && } + {events && events.length === 0 && } {events && events.length > 0 && ( - +
{events.length} eventsUTC stored · Brussels rendered
@@ -51,19 +54,19 @@ export function Audit() { {events.map((e) => ( - - - - - + + + + ))} -
Audit events
+ {e.actor_label} ({e.actor_type}){e.action}{e.entity_type}{e.correlation_id.slice(0, 8)}{e.actor_label}{e.actor_type}{e.action.replace(/_/g, " ")}{e.entity_type}
{e.correlation_id.slice(0, 8)}
{JSON.stringify(e.metadata ?? {}, null, 2)}
+
)} ); diff --git a/frontend/src/pages/Automation.tsx b/frontend/src/pages/Automation.tsx index c5e6ac4..8630be1 100644 --- a/frontend/src/pages/Automation.tsx +++ b/frontend/src/pages/Automation.tsx @@ -1,8 +1,9 @@ import { useCallback, useEffect, useState } from "react"; import { api, ApiError } from "../api/client"; -import type { AutomationRun } from "../api/types"; +import type { AutomationRun, KnowledgeHealth } from "../api/types"; import { StatusBadge } from "../components/Badge"; import { useAuth } from "../context/AuthContext"; +import { ErrorState, IntegrationMark, LoadingState, PageHeader, SectionHeading } from "../components/PageChrome"; export function Automation() { const { user } = useAuth(); @@ -11,8 +12,11 @@ export function Automation() { const [status, setStatus] = useState(""); const [retryError, setRetryError] = useState(null); const [retrying, setRetrying] = useState(null); + const [knowledge, setKnowledge] = useState(null); const load = useCallback(() => { + setRuns(null); + setError(null); const params = new URLSearchParams(); if (status) params.set("status", status); api @@ -31,6 +35,10 @@ export function Automation() { load(); }, [load]); + useEffect(() => { + api.get("/api/v1/knowledge/status").then(setKnowledge).catch(() => setKnowledge(null)); + }, []); + async function handleRetry(eventId: string) { setRetryError(null); setRetrying(eventId); @@ -47,7 +55,7 @@ export function Automation() { if (user?.role !== "operations_manager") { return (
-

Automation

+

Automation delivery status is visible to Operations Managers only.

); @@ -55,7 +63,15 @@ export function Automation() { return (
-

Automation

+ + +
+
Orchestration

n8n delivery

Return events are committed locally first and then delivered through the outbox.

+
Knowledge

RAGcore

{knowledge ? `${knowledge.document_count} procedures indexed in ${knowledge.collection}.` : "Health evidence is currently unavailable."}

+
Tool gateway

MCP Hub

No active MobilityOps adapter is configured in this proof of concept.

+
+ +
- {error &&

{error}

} + {error && } {retryError &&

{retryError}

} - {!error && !runs &&

Loading automation runs…

} + {!error && !runs && } {runs && runs.length === 0 &&

No automation runs match this filter.

} {runs && runs.length > 0 && ( - +
{runs.length} workflow eventsBounded retries
@@ -93,20 +109,20 @@ export function Automation() { {runs.map((r) => ( - - - - + + + - - - + + - -
Automation runs
{r.event_id.slice(0, 8)}{r.event_type}{r.aggregate_ref} + {r.event_id.slice(0, 8)}{r.event_type}{r.aggregate_ref} {r.attempts}{r.last_error ?? "—"} + {r.attempts}{r.last_error ?? "—"} + {r.status === "failed" ? (
+
)} ); diff --git a/frontend/src/pages/Knowledge.tsx b/frontend/src/pages/Knowledge.tsx index 5967a0c..e3dcfc3 100644 --- a/frontend/src/pages/Knowledge.tsx +++ b/frontend/src/pages/Knowledge.tsx @@ -1,6 +1,8 @@ import { useEffect, useState, type FormEvent } from "react"; import { api, ApiError } from "../api/client"; import type { GroundedAnswer, KnowledgeHealth } from "../api/types"; +import { Icon } from "../components/Icons"; +import { PageHeader } from "../components/PageChrome"; interface Exchange { question: string; @@ -45,17 +47,13 @@ export function Knowledge() { return (
-

Knowledge

+ {status && ( -

- Provider: {status.provider} ·{" "} - {status.available ? "available" : "unavailable"} · {status.document_count} procedures - indexed -

+
{status.provider}{status.available ? "Available" : "Unavailable"} · {status.document_count} procedures indexed
{status.collection}
)}
-

Ask a procedure question

+

Ask a procedure question

Retrieval → evidence check → grounded answer

@@ -70,22 +68,23 @@ export function Knowledge() { maxLength={1000} required /> -
{error &&

{error}

} {exchanges.length === 0 && !error && ( -

Ask a question about one of the ten operational procedures to see cited sources.

+

Evidence before answers

Ask about returns, damage, inspections or another indexed procedure. MobilityOps will not invent an answer when evidence is missing.

)}
    {exchanges.map((exchange, index) => (
  • - Q: {exchange.question} + Question {exchange.question}

    {EVIDENCE_LABEL[exchange.answer.evidence_state]}