diff --git a/src/FetchRequestDetail.tsx b/src/FetchRequestDetail.tsx index dac90e7..560013b 100644 --- a/src/FetchRequestDetail.tsx +++ b/src/FetchRequestDetail.tsx @@ -15,6 +15,7 @@ import { StepIcon, LinearProgress, IconButton, + Snackbar, } from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import ReplayIcon from "@mui/icons-material/Replay"; @@ -114,6 +115,7 @@ function formatProgressMessage(msg: ProgressMessage): string { if (msg.count !== undefined && msg.unit) return `${msg.count} ${msg.unit}`; if (msg.count !== undefined) return `${msg.count} items`; if (msg.raw_ocr_line) return `"${msg.raw_ocr_line.slice(0, 60)}${msg.raw_ocr_line.length > 60 ? "…" : ""}"`; + if (msg.error) return msg.error.slice(0, 80); return ""; } @@ -121,6 +123,7 @@ function sseIcon(status: SSEEvent["status"]) { switch (status) { case "started": return ; case "completed": return ; + case "failed": return ; case "skipped": return ; case "paused": return ; case "progress": return ( @@ -154,6 +157,7 @@ export default function FetchRequestDetail() { const [sseConnected, setSseConnected] = React.useState(false); const [liveParsedCount, setLiveParsedCount] = React.useState(undefined); const [stepStats, setStepStats] = React.useState>({}); + const [failNotif, setFailNotif] = React.useState(null); const sseRef = React.useRef(null); const feedRef = React.useRef(null); @@ -224,6 +228,10 @@ export default function FetchRequestDetail() { refetchRequest(); refetchAmbiguities(); } + if (parsed.status === "failed") { + setFailNotif(parsed.message.error || "Fetch request failed"); + refetchRequest(); + } if (parsed.status === "completed" || parsed.step === "resume_extract") { refetchRequest(); } @@ -254,7 +262,7 @@ export default function FetchRequestDetail() { } } - const terminalStatuses = new Set(["completed", "skipped", "paused"]); + const terminalStatuses = new Set(["completed", "skipped", "paused", "failed"]); return sseEvents.filter((e, i) => { if (progressSteps.has(e.step) && e.status === "progress") return i === lastProgressIdx[e.step]; if (e.status === "started") { @@ -271,6 +279,7 @@ export default function FetchRequestDetail() { for (const evt of sseEvents) { steps.add(evt.step); if (evt.status === "completed") steps.add(`${evt.step}/completed`); + if (evt.status === "failed") steps.add(`${evt.step}/failed`); if (evt.status === "started") steps.add(`${evt.step}/started`); if (evt.status === "progress") steps.add(`${evt.step}/progress`); } @@ -651,6 +660,16 @@ export default function FetchRequestDetail() { )} + setFailNotif(null)} + anchorOrigin={{ vertical: "bottom", horizontal: "center" }} + > + setFailNotif(null)} sx={{ borderRadius: 2 }}> + {failNotif} + + ); } diff --git a/src/features/fetch-requests/fetch-requests.models.ts b/src/features/fetch-requests/fetch-requests.models.ts index 110f13a..1265b4a 100644 --- a/src/features/fetch-requests/fetch-requests.models.ts +++ b/src/features/fetch-requests/fetch-requests.models.ts @@ -87,10 +87,10 @@ export interface ResolveAmbiguityPayload { export type SSEEventStep = | "load_content" | "raw_lines" | "txn_blocks" | "txn_dicts" | "resume_extract" | "extract" | "paused" | "complete" | "enrich" - | "save_expenses"; + | "save_expenses" | "pipeline"; export type SSEEventStatus = - | "started" | "completed" | "skipped" | "paused" | "progress"; + | "started" | "completed" | "skipped" | "paused" | "progress" | "failed"; export interface ProgressMessage { lines?: number; @@ -98,6 +98,7 @@ export interface ProgressMessage { count?: number; unit?: string; raw_ocr_line?: string; + error?: string; } export interface SSEEvent {