From 22e660392453109f8efef65e5b4a3da59821a52d Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Sun, 23 Aug 2026 01:39:56 +0530 Subject: [PATCH 1/5] feat(fetch-requests): support llama_parse pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix PipelineType union: "llama_parser" → "llama_parse" (matches backend enum) - Add "llama_extract" to SSEEventStep union - PipelineStepper: advance Extract stage on llama_extract events Dropdown/badge rendering is OpenAPI-spec-driven and picks the new value up automatically; no other changes required. --- src/FetchRequest/components/PipelineStepper.tsx | 8 +++++++- src/features/fetch-requests/fetch-requests.models.ts | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/FetchRequest/components/PipelineStepper.tsx b/src/FetchRequest/components/PipelineStepper.tsx index fce4931..d4280b0 100644 --- a/src/FetchRequest/components/PipelineStepper.tsx +++ b/src/FetchRequest/components/PipelineStepper.tsx @@ -29,7 +29,13 @@ function computeProgressPercent( let pct = 0; - if (seenSteps.has("raw_lines") || seenSteps.has("txn_blocks")) pct += 10; + if ( + seenSteps.has("raw_lines") || + seenSteps.has("txn_blocks") || + seenSteps.has("llama_extract") + ) { + pct += 10; + } if (txnBlockCount > 0) { const current = Math.max(liveCount, stepStats.txn_dicts ?? 0); diff --git a/src/features/fetch-requests/fetch-requests.models.ts b/src/features/fetch-requests/fetch-requests.models.ts index e7c76e1..a3614f6 100644 --- a/src/features/fetch-requests/fetch-requests.models.ts +++ b/src/features/fetch-requests/fetch-requests.models.ts @@ -26,7 +26,7 @@ export interface EmailSource { txn_dicts_count?: number; } -export type PipelineType = "heuristic" | "llm" | "llama_parser"; +export type PipelineType = "heuristic" | "llm" | "llama_parse"; export interface FetchRequestCreate { source: FileSource | EmailSource; @@ -90,7 +90,7 @@ export interface ResolveAmbiguityPayload { export type SSEEventStep = | "load_content" | "raw_lines" | "txn_blocks" | "txn_dicts" | "resume_extract" | "extract" | "paused" | "complete" | "enrich" - | "save_expenses" | "pipeline"; + | "save_expenses" | "pipeline" | "llama_extract"; export type SSEEventStatus = | "started" | "completed" | "skipped" | "paused" | "progress" | "failed"; -- 2.49.1 From 86945378a31f56c3745b8608ff3d4f0a096cedc6 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Sun, 23 Aug 2026 14:01:36 +0530 Subject: [PATCH 2/5] feat(fetch-requests): delete fetch requests from detail page and list rows - Detail page: red Delete button beside Retry (disabled while processing or in-flight), confirm dialog, toast feedback, navigates back to the list - List rows: per-card delete IconButton with stopPropagation, disabled while processing; reloads after success - Backend cascades linked expenses + ambiguities and evicts the expenses response cache on delete --- src/FetchRequest/FetchRequestCreate.tsx | 46 +++++++++++++++++++++++-- src/FetchRequest/FetchRequestDetail.tsx | 39 ++++++++++++++++++++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/FetchRequest/FetchRequestCreate.tsx b/src/FetchRequest/FetchRequestCreate.tsx index ababafa..13636d7 100644 --- a/src/FetchRequest/FetchRequestCreate.tsx +++ b/src/FetchRequest/FetchRequestCreate.tsx @@ -1,22 +1,32 @@ import React, { useEffect, useMemo, useState } from "react"; import { Box, Container, Typography, Paper, Button, Alert, - CircularProgress, + CircularProgress, IconButton, Tooltip, } from "@mui/material"; import { useNavigate } from "react-router-dom"; import ReceiptLongIcon from "@mui/icons-material/ReceiptLong"; +import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import { useAppContext, useResource, ListCellRenderer, FormFieldRenderer, getApi, applyDisplayFormat } from "../../react-openapi"; import type { FieldConfig } from "../../react-openapi"; import { PageHeader } from "../ui/PageHeader"; import { EmptyState } from "../ui/EmptyState"; +import { formatApiError } from "../features/fetch-requests"; +import { useToast } from "../ui/Toast"; const CREATE_FIELDS = ["account", "bank", "pipeline", "start_date", "end_date", "source"]; function FetchRequestList() { const navigate = useNavigate(); - const { list, resource } = useResource("fetch-requests"); + const { list, remove, resource } = useResource("fetch-requests"); const { resources: allResources } = useAppContext(); const [rows, setRows] = useState(null); + const [deletingId, setDeletingId] = useState(null); + const { showToast } = useToast(); + + const reload = () => { + if (!resource) return; + list({ limit: 20 }).then((res) => setRows(res.items ?? [])); + }; const columns = useMemo(() => { if (!resource) return []; @@ -30,6 +40,26 @@ function FetchRequestList() { list({ limit: 20 }).then((res) => setRows(res.items ?? [])); }, [resource?.name]); + const handleDelete = async (e: React.MouseEvent, id: string) => { + e.stopPropagation(); + if ( + !window.confirm( + "Delete this fetch request along with all its expenses and ambiguities? This cannot be undone.", + ) + ) + return; + setDeletingId(id); + try { + await remove(id); + showToast("Fetch request deleted"); + reload(); + } catch (err: any) { + showToast(formatApiError(err), "error"); + } finally { + setDeletingId(null); + } + }; + if (!rows) { return ( @@ -82,6 +112,18 @@ function FetchRequestList() { /> ))} + + + handleDelete(e, row.id)} + > + + + + ); diff --git a/src/FetchRequest/FetchRequestDetail.tsx b/src/FetchRequest/FetchRequestDetail.tsx index b05d22d..e56b6fc 100644 --- a/src/FetchRequest/FetchRequestDetail.tsx +++ b/src/FetchRequest/FetchRequestDetail.tsx @@ -10,6 +10,7 @@ import { CircularProgress, Alert, Divider, + Tooltip, } from "@mui/material"; import ReplayIcon from "@mui/icons-material/Replay"; import CheckCircleIcon from "@mui/icons-material/CheckCircle"; @@ -18,6 +19,7 @@ import WarningAmberIcon from "@mui/icons-material/WarningAmber"; import PlayArrowIcon from "@mui/icons-material/PlayArrow"; import RemoveCircleOutlineIcon from "@mui/icons-material/RemoveCircleOutline"; import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord"; +import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import { useResource, useItemSse, useAppContext, DetailFieldRenderer, applyDisplayFormat } from "../../react-openapi"; import { useQuery } from "@tanstack/react-query"; import { RETRY_MAX, formatApiError } from "../features/fetch-requests"; @@ -85,11 +87,12 @@ function Section({ title, children, action }: { title: string; children: React.R export default function FetchRequestDetail() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); - const { get, patch, resource } = useResource("fetch-requests"); + const { get, patch, remove, resource } = useResource("fetch-requests"); const { resources: allResources } = useAppContext(); const [stepStats, setStepStats] = useState>({}); const [liveParsedCount, setLiveParsedCount] = useState(undefined); const [retrying, setRetrying] = useState(false); + const [deleting, setDeleting] = useState(false); const { showToast } = useToast(); const feedRef = useRef(null); @@ -174,6 +177,26 @@ export default function FetchRequestDetail() { } }; + const handleDelete = async () => { + if (!id || !remove || deleting) return; + if ( + !window.confirm( + "Delete this fetch request along with all its expenses and ambiguities? This cannot be undone.", + ) + ) + return; + setDeleting(true); + try { + await remove(id); + showToast("Fetch request deleted"); + navigate("/fetch-requests"); + } catch (err: any) { + showToast(formatApiError(err), "error"); + } finally { + setDeleting(false); + } + }; + const req = fetchRequest as any; const retryCount = req?.retry_count ?? 0; const isRetryExhausted = retryCount >= RETRY_MAX; @@ -286,6 +309,20 @@ export default function FetchRequestDetail() { Retry )} + + + + + -- 2.49.1 From 1c5d8ed76522222e822a1c781b6907fff77ef796 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Sun, 23 Aug 2026 14:02:23 +0530 Subject: [PATCH 3/5] updated .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9cb3400..98e23dd 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ dist dist-ssr *.local .idea +tsconfig.tsbuildinfo -- 2.49.1 From c6622d43e04f36b0b7183fa79834e53f22d5f7e6 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Sun, 23 Aug 2026 15:01:43 +0530 Subject: [PATCH 4/5] feat(fetch-requests): navigate to detail page after successful create --- src/FetchRequest/FetchRequestCreate.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/FetchRequest/FetchRequestCreate.tsx b/src/FetchRequest/FetchRequestCreate.tsx index 13636d7..5b19e7b 100644 --- a/src/FetchRequest/FetchRequestCreate.tsx +++ b/src/FetchRequest/FetchRequestCreate.tsx @@ -136,6 +136,7 @@ export default function FetchRequestCreate() { const { resources: allResources } = useAppContext(); const resource = useMemo(() => allResources.find((r) => r.name === "fetch-requests"), [allResources]); const { create } = useResource("fetch-requests"); + const navigate = useNavigate(); const [formData, setFormData] = useState>({}); const [fkOptions, setFkOptions] = useState>({}); const [loading, setLoading] = useState(false); @@ -185,6 +186,10 @@ export default function FetchRequestCreate() { const display = applyDisplayFormat(created, resource!.displayFormat); setResult({ severity: "success", message: `Created: ${display}` }); setFormData({}); + const newId = (created as any)?.id; + if (newId) { + navigate(`/fetch-requests/${newId}`); + } } catch (e: any) { const detail = e?.response?.data?.detail; const msg = Array.isArray(detail) ? detail.map((d: any) => d.msg).join("; ") : (detail ?? e?.message ?? "Unknown error"); -- 2.49.1 From c585b6277923e84a7980b71db09f381fecd2c277 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Sun, 23 Aug 2026 16:54:48 +0530 Subject: [PATCH 5/5] refactor(fetch-requests): drop dead llama_extract SSE step llama_parse FRs now emit the standard raw_lines/txn_blocks/txn_dicts events, so remove the special-cased step from SSEEventStep and stepper progress calc. --- src/FetchRequest/components/PipelineStepper.tsx | 3 +-- src/features/fetch-requests/fetch-requests.models.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/FetchRequest/components/PipelineStepper.tsx b/src/FetchRequest/components/PipelineStepper.tsx index d4280b0..a2af937 100644 --- a/src/FetchRequest/components/PipelineStepper.tsx +++ b/src/FetchRequest/components/PipelineStepper.tsx @@ -31,8 +31,7 @@ function computeProgressPercent( if ( seenSteps.has("raw_lines") || - seenSteps.has("txn_blocks") || - seenSteps.has("llama_extract") + seenSteps.has("txn_blocks") ) { pct += 10; } diff --git a/src/features/fetch-requests/fetch-requests.models.ts b/src/features/fetch-requests/fetch-requests.models.ts index a3614f6..82a41eb 100644 --- a/src/features/fetch-requests/fetch-requests.models.ts +++ b/src/features/fetch-requests/fetch-requests.models.ts @@ -90,7 +90,7 @@ export interface ResolveAmbiguityPayload { export type SSEEventStep = | "load_content" | "raw_lines" | "txn_blocks" | "txn_dicts" | "resume_extract" | "extract" | "paused" | "complete" | "enrich" - | "save_expenses" | "pipeline" | "llama_extract"; + | "save_expenses" | "pipeline"; export type SSEEventStatus = | "started" | "completed" | "skipped" | "paused" | "progress" | "failed"; -- 2.49.1