export type FetchRequestStatus = | "pending" | "processing" | "paused" | "raw_expenses_done" | "enriched_done" | "completed" | "failed"; export type SourceType = "file" | "pdf" | "xlsx" | "csv" | "email"; export interface FileSource { type?: SourceType; path: string; bank: string; raw_lines?: string[]; txn_blocks?: Record; txn_dicts?: Record[]; txn_dict_count?: number; txn_dicts_count?: number; } export interface PdfSource extends FileSource { type: "pdf"; } export interface XlsxSource extends FileSource { type: "xlsx"; } export interface CsvSource extends FileSource { type: "csv"; } export interface EmailSource { type: "email"; bank: string; from_email?: string; subject?: string; raw_terms?: string[]; txn_dict_count?: number; txn_dicts_count?: number; } export type PipelineType = "heuristic" | "llm" | "llama_parse"; export type TrustFallback = "amount" | "balance"; export interface FetchRequestCreate { source: PdfSource | XlsxSource | CsvSource | EmailSource; account_name: string; pipeline?: PipelineType; trust_fallback?: TrustFallback | null; payor_username?: string; start_date?: string; end_date?: string; } export interface FetchRequestUpdate { status?: FetchRequestStatus; error_message?: string | null; } export interface FetchRequest extends FetchRequestCreate { id: string; status: FetchRequestStatus; fingerprint: string; completed_at?: string | null; error_message?: string | null; retry_count?: number; created_at: string; } export interface UploadResult { original_filename: string; saved_as: string; content_type: string; url: string; absolute_path: string; } export interface AmbiguityCandidate { amount: number; balance: number; } export interface PendingAmbiguity { id: string; fetch_request: string; step_index?: number; line: string; ocr_amount: number; ocr_balance: number; prev_balance: number; candidates: AmbiguityCandidate[]; chosen?: AmbiguityCandidate | null; resolved_at?: string | null; status: "pending" | "resolved"; created_at: string; } export interface ResolveAmbiguityPayload { chosen: { amount: number; balance: number; }; } export type SSEEventStep = | "load_content" | "raw_lines" | "txn_blocks" | "txn_dicts" | "resume_extract" | "extract" | "paused" | "complete" | "enrich" | "save_expenses" | "pipeline"; export type SSEEventStatus = | "started" | "completed" | "skipped" | "paused" | "progress" | "failed"; export interface ProgressMessage { lines?: number; blocks?: number; count?: number; unit?: string; raw_ocr_line?: string; error?: string; } export interface SSEEvent { step: SSEEventStep; status: SSEEventStatus; message: ProgressMessage; } export interface FetchRequestFilters { status?: FetchRequestStatus[]; account_name?: string; source_type?: SourceType; } export function formatApiError(err: any): string { if (!err?.response) return err?.message || "Request failed"; const data = err.response.data; const status = err.response.status; if (status === 422 && Array.isArray(data?.detail)) { return data.detail.map((d: any) => { const field = d.loc?.filter((s: string) => s !== "body").pop() || "field"; if (d.type === "value_error.missing") return `Missing: ${field}`; return `${field}: ${d.msg}`; }).join("; "); } if (typeof data?.detail === "string") return data.detail; return `Request failed (${status})`; } export const RETRY_MAX = 3;