import { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { api, ApiError } from "../api/client"; import type { AutomationRun, IntegrationStatus, KnowledgeHealth } from "../api/types"; import { StatusBadge } from "../components/Badge"; import { useAuth } from "../context/AuthContext"; import { useLocaleFormat } from "../i18n/format"; import { ErrorState, IntegrationMark, LoadingState, PageHeader, SectionHeading } from "../components/PageChrome"; import { N8N_STATE_META, MCP_STATE_META } from "../data/integrationLabels"; type ViewFilter = "attention" | "recent" | "succeeded" | "all"; function deriveDisplayRef(run: AutomationRun): string { const digits = run.aggregate_ref.match(/(\d+)$/)?.[1]; if (digits) return `AUT-RET-${digits}`; return `AUT-${run.event_id.slice(0, 4).toUpperCase()}`; } export function Automation() { const { t } = useTranslation(["integrations", "common"]); const { formatDateTime } = useLocaleFormat(); const { user } = useAuth(); const [runs, setRuns] = useState(null); const [error, setError] = useState(null); const [status, setStatus] = useState(""); const [view, setView] = useState("attention"); const [expandSucceeded, setExpandSucceeded] = useState(false); const [retryError, setRetryError] = useState(null); const [retrying, setRetrying] = useState(null); const [knowledge, setKnowledge] = useState(null); const [integrationStatus, setIntegrationStatus] = useState(null); const load = useCallback(() => { setRuns(null); setError(null); const params = new URLSearchParams(); if (status) params.set("status", status); api .get(`/api/v1/workflows?${params.toString()}`) .then(setRuns) .catch(() => setError(user?.role === "operations_manager" ? t("ledger.unavailable") : t("ledger.managerOnly")), ); }, [status, user, t]); useEffect(() => { load(); }, [load]); useEffect(() => { api.get("/api/v1/knowledge/status").then(setKnowledge).catch(() => setKnowledge(null)); }, []); const loadIntegrationStatus = useCallback(() => { api .get("/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 : t("ledger.retryFailed")); } finally { setRetrying(null); } } const visibleRuns = useMemo(() => { if (!runs) return null; switch (view) { case "attention": return runs.filter((r) => r.status === "failed" || r.status === "pending" || r.status === "delivering"); case "recent": return runs.slice(0, 5); case "succeeded": return runs.filter((r) => r.status === "succeeded"); default: return runs; } }, [runs, view]); const succeededRuns = useMemo(() => (visibleRuns ?? []).filter((r) => r.status === "succeeded"), [visibleRuns]); const otherRuns = useMemo(() => (visibleRuns ?? []).filter((r) => r.status !== "succeeded"), [visibleRuns]); const groupSucceeded = view !== "succeeded" && succeededRuns.length > 3; if (user?.role !== "operations_manager") { return (

{t("ledger.managerOnly")}

); } function renderRow(r: AutomationRun) { return (
{deriveDisplayRef(r)} {r.event_id}
{t(`ledger.eventTypes.${r.event_type}`, { defaultValue: r.event_type })} {r.aggregate_ref} {r.attempts} {r.last_error_code ? ( <> {t(`ledger.errorCodes.${r.last_error_code}`, { defaultValue: r.last_error ?? r.last_error_code })} {r.last_error && (
{t("common:actions.technicalDetails")}
{r.last_error}
)} ) : ( "—" )} {r.status === "failed" ? ( ) : ( t("ledger.noAction") )} ); } return (
{t("cards.orchestrationKicker")}

{t("cards.n8nTitle")}

{integrationStatus ? t("cards.n8nSummary", { succeeded: integrationStatus.n8n.succeeded, failed: integrationStatus.n8n.failed, pending: integrationStatus.n8n.pending, delivering: integrationStatus.n8n.delivering, }) : t("cards.n8nFallback")}

{(() => { const meta = integrationStatus ? N8N_STATE_META[integrationStatus.n8n.state] : null; return ( ); })()}
{t("cards.knowledgeKicker")}

{t("cards.knowledgeTitle")}

{knowledge ? t(knowledge.provider === "ragcore" ? "cards.knowledgeSummaryRagcore" : "cards.knowledgeSummaryDemo", { count: knowledge.document_count, collection: knowledge.collection, }) : t("cards.knowledgeUnavailable")}

{t("cards.gatewayKicker")}

{t("cards.mcpTitle")}

{integrationStatus?.mcp_hub.registration_enabled ? t("cards.mcpEnabled") : t("cards.mcpNotConnected")}

{(() => { const meta = integrationStatus ? MCP_STATE_META[integrationStatus.mcp_hub.state] : null; return ; })()}
{error && } {retryError &&

{retryError}

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

{t("ledger.empty")}

} {visibleRuns && visibleRuns.length > 0 && (
{t("ledger.count", { count: visibleRuns.length })}{t("ledger.boundedRetries")}
{otherRuns.map(renderRow)} {groupSucceeded ? ( <> {expandSucceeded && succeededRuns.map(renderRow)} ) : ( succeededRuns.map(renderRow) )}
{t("ledger.title")}
{t("ledger.columns.event")} {t("ledger.columns.type")} {t("ledger.columns.booking")} {t("ledger.columns.status")} {t("ledger.columns.attempts")} {t("ledger.columns.lastError")} {t("ledger.columns.when")} {t("ledger.columns.action")}
)}
); }