import React, { useEffect, useMemo, useState } from "react"; import { Box, Container, Typography, Paper, Button, Alert, CircularProgress, } from "@mui/material"; import { useNavigate } from "react-router-dom"; import ReceiptLongIcon from "@mui/icons-material/ReceiptLong"; 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"; const CREATE_FIELDS = ["account", "bank", "pipeline", "start_date", "end_date", "source"]; function FetchRequestList() { const navigate = useNavigate(); const { list, resource } = useResource("fetch-requests"); const { resources: allResources } = useAppContext(); const [rows, setRows] = useState(null); const columns = useMemo(() => { if (!resource) return []; return resource.listColumns .map((n) => resource.fields.find((f) => f.name === n)) .filter(Boolean) as FieldConfig[]; }, [resource]); useEffect(() => { if (!resource) return; list({ limit: 20 }).then((res) => setRows(res.items ?? [])); }, [resource?.name]); if (!rows) { return ( ); } if (rows.length === 0) { return ( } title="No fetch requests yet" description="Upload a bank statement or configure email ingestion to import your first transactions." actionLabel="New Fetch Request" onAction={() => document.getElementById("new-fetch-request")?.scrollIntoView({ behavior: "smooth", block: "center" })} /> ); } return ( {rows.map((row, i) => { const displayFormat = resource?.displayFormat ?? ""; return ( navigate(`/fetch-requests/${row.id}`)} > {columns.map((col) => ( {col.label} r.name === col.fk!.resource)?.displayFormat ?? displayFormat) : displayFormat} /> ))} ); })} ); } 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 [formData, setFormData] = useState>({}); const [fkOptions, setFkOptions] = useState>({}); const [loading, setLoading] = useState(false); const [result, setResult] = useState<{ severity: "success" | "error"; message: string } | null>(null); useEffect(() => { if (!resource) return; resource.relationships.forEach((rel) => { const targetRes = allResources.find((r) => r.name === rel.config.resource); if (!targetRes) return; (async () => { try { const api = getApi(); const params: Record = {}; if (targetRes.pagination) params.limit = 0; const res = await api.get(targetRes.path, { params }); const items = targetRes.pagination ? (res.data.items ?? []) : (Array.isArray(res.data) ? res.data : []); const opts = items.map((item: any) => ({ value: item[targetRes.primaryKey], label: applyDisplayFormat(item, targetRes.displayFormat), })); setFkOptions((prev) => ({ ...prev, [rel.fieldName]: opts })); } catch (e) { console.warn(`Failed to load FK options for ${rel.fieldName}:`, e); } })(); }); }, [resource]); const formFields = useMemo(() => { if (!resource) return []; return resource.orderedFields.filter((f) => CREATE_FIELDS.includes(f.name)); }, [resource]); const handleChange = (fieldName: string, value: any) => { setFormData((prev) => ({ ...prev, [fieldName]: value })); setResult(null); }; const handleSubmit = async () => { setLoading(true); setResult(null); try { const created = await create(formData); const display = applyDisplayFormat(created, resource!.displayFormat); setResult({ severity: "success", message: `Created: ${display}` }); setFormData({}); } 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"); setResult({ severity: "error", message: `Failed: ${msg}` }); } finally { setLoading(false); } }; if (!resource) { return ( The fetch-requests resource was not found in this spec. ); } return ( New Fetch Request Choose an account, pipeline, and a file or email source to kick off an import. {formFields.map((field) => ( handleChange(field.name, val)} fkOptions={fkOptions[field.name]} /> ))} {result && ( setResult(null)}> {result.message} )} Recent Fetch Requests ); }