Add Fetch Request pipeline UI with real-time SSEs #8

Merged
aetos merged 16 commits from ai/fetch-request-steps-ui into fetch-request-steps-ui 2026-05-30 15:58:49 +00:00
2 changed files with 40 additions and 3 deletions
Showing only changes of commit ecdfc2094e - Show all commits

View File

@@ -264,10 +264,13 @@ export default function FetchRequestDetail() {
const isCompleted = index < activeStep;
const isActive = index === activeStep;
const isPaused = req.status === "paused" && isActive;
const isFailed = req.status === "failed" && isActive;
let icon: React.ReactNode;
if (isCompleted) {
icon = <CheckCircleIcon sx={{ color: "success.main" }} />;
} else if (isFailed) {
icon = <ErrorIcon sx={{ color: "error.main" }} />;
} else if (isPaused) {
icon = <WarningAmberIcon sx={{ color: "warning.main" }} />;
} else if (isActive) {

View File

@@ -30,6 +30,7 @@ import {
InputLabel,
FormControl,
OutlinedInput,
Autocomplete,
} from "@mui/material";
import DeleteIcon from "@mui/icons-material/Delete";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
@@ -56,6 +57,7 @@ import type {
} from "./features/fetch-requests";
import { RETRY_MAX } from "./features/fetch-requests";
import { useNavigate } from "react-router-dom";
import { useResourceByName, useConfig } from "../react-openapi";
const statusColors: Record<FetchRequestStatus, "default" | "primary" | "warning" | "info" | "success" | "error"> = {
pending: "default",
@@ -119,6 +121,16 @@ export default function FetchRequests() {
...(accountFilter ? { account_name: accountFilter } : {}),
...(sourceFilter !== "all" ? { source_type: sourceFilter } : {}),
});
const { useList: useAccountsList } = useResourceByName("accounts");
const { data: accountsData } = useAccountsList();
const accountOptions: string[] = React.useMemo(() => {
return (accountsData?.data ?? []).map((a: any) => a.name).filter(Boolean);
}, [accountsData]);
const config = useConfig();
const fetchRes = config?.resources.find((r: any) => r.name === "fetch-requests");
const formatOptions: string[] = (fetchRes?.fields?.source?.schema?.format?.options as string[]) ?? ["axis", "icici_ocr"];
const createMutation = useCreateFetchRequest();
const updateMutation = useUpdateFetchRequest();
const deleteMutation = useDeleteFetchRequest();
@@ -253,18 +265,40 @@ export default function FetchRequests() {
Uploaded as: {uploadedPath}
</Alert>
)}
<TextField label="Format (csv, pdf, ...)" value={format} onChange={(e) => setFormat(e.target.value)} size="small" />
<FormControl size="small">
<InputLabel>Format</InputLabel>
<Select value={format} onChange={(e) => setFormat(e.target.value)} label="Format">
{formatOptions.map((opt) => (
<MenuItem key={opt} value={opt}>{opt}</MenuItem>
))}
</Select>
</FormControl>
</>
) : (
<>
<TextField label="Format" value={format} onChange={(e) => setFormat(e.target.value)} size="small" helperText="e.g. email, pdf, csv" />
<FormControl size="small">
<InputLabel>Format</InputLabel>
<Select value={format} onChange={(e) => setFormat(e.target.value)} label="Format">
{formatOptions.map((opt) => (
<MenuItem key={opt} value={opt}>{opt}</MenuItem>
))}
</Select>
</FormControl>
<TextField label="From Email" value={fromEmail} onChange={(e) => setFromEmail(e.target.value)} size="small" />
<TextField label="Subject" value={subject} onChange={(e) => setSubject(e.target.value)} size="small" />
<TextField label="Raw Terms" value={rawTerms} onChange={(e) => setRawTerms(e.target.value)} size="small" helperText="Comma-separated search terms" />
</>
)}
<TextField label="Account Name" value={accountName} onChange={(e) => setAccountName(e.target.value)} size="small" required />
<Autocomplete
options={accountOptions}
value={accountName || null}
onChange={(_, val) => setAccountName(val ?? "")}
renderInput={(params) => (
<TextField {...params} label="Account Name" size="small" required />
)}
sx={{ "& .MuiOutlinedInput-root": { height: "auto", minHeight: "2.5rem" } }}
/>
<TextField label="Payor Username" value={payorUsername} onChange={(e) => setPayorUsername(e.target.value)} size="small" helperText="Default: aetos" />
<Box sx={{ display: "flex", gap: 2 }}>