Files
agents-marketplace/buyer/src/ui/components/PdfUploadForm.tsx
FilipBl4gojevic 73700f983a Brand vocabulary scrub: settlement framing, UI strings, retire M1-era design docs
- docs/ARCHITECTURE.md: settlement-asset framing (AP3X bonded in escrow,
  settles on accepted work); remove references to external network
  adapters that are not part of the project
- docs/buyer + docs/supplier READMEs: commission/bond/settle vocabulary
- Buyer web UI: "Accept & Settle", escrow-framed labels, gateway-managed
  wallet wording; gateway landing title punctuation
- Delete EXECUTIVE_SUMMARY and local-llm-marketplace docs + exports:
  M1-era internal design snapshots superseded by ARCHITECTURE.md and the
  public docs site

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 13:23:35 +02:00

82 lines
2.7 KiB
TypeScript

/**
* buyer/src/ui/components/PdfUploadForm.tsx — step 1 of the book summarizer.
*
* Posts a PDF as multipart/form-data to POST /v1/pdf-upload. The server
* extracts + chunks it (no escrow yet) and returns a job_id + chunk count.
* No private key or chain access in the browser — everything goes through the
* same-origin /v1/* server routes.
*/
import { useState } from "react";
export interface UploadInfo {
job_id: string;
filename: string;
page_count: number;
chunk_count: number;
sample_chunk: string;
}
export default function PdfUploadForm({ onUploaded }: { onUploaded: (info: UploadInfo) => void }) {
const [file, setFile] = useState<File | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleSubmit = async (event: React.FormEvent): Promise<void> => {
event.preventDefault();
if (!file) return;
setLoading(true);
setError(null);
try {
const fd = new FormData();
fd.append("file", file);
const resp = await fetch("/v1/pdf-upload", {
method: "POST",
credentials: "same-origin",
body: fd,
});
const body = (await resp.json()) as UploadInfo & { error?: string; message?: string };
if (!resp.ok) {
throw new Error(`${body.error ?? resp.statusText}: ${body.message ?? ""}`);
}
onUploaded(body);
} catch (err) {
setError((err as Error).message ?? "upload failed");
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit} className="space-y-4">
<p className="text-sm text-gray-600">
Upload a text-based PDF book. The buyer will chunk it and commission marketplace
suppliers to summarize each chunk, then synthesize a full-book summary.
</p>
<input
type="file"
accept="application/pdf"
onChange={(e) => setFile(e.target.files?.[0] ?? null)}
disabled={loading}
aria-label="pdf file"
className="block text-sm text-gray-700 file:mr-3 file:rounded file:border-0 file:bg-indigo-600 file:px-4 file:py-2 file:text-white hover:file:bg-indigo-700"
/>
<div className="flex items-center gap-3">
<button
type="submit"
disabled={loading || !file}
className="rounded bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-700 disabled:bg-gray-400"
>
Upload &amp; analyze
</button>
{loading && <span className="text-sm text-gray-500">Extracting &amp; chunking…</span>}
</div>
{error && (
<div role="alert" className="rounded border border-red-300 bg-red-50 p-3 text-sm text-red-700">
{error}
</div>
)}
</form>
);
}