import { useCallback, useRef, useState } from "react"; import { uploadFile, uploadText } from "./api"; const MAX_MB = 10; // UI state machine: idle → uploading → (ready | error), with error/ready // returning to idle on a new attempt. const IDLE = "idle"; const UPLOADING = "uploading"; const READY = "ready"; const ERROR = "error"; export default function DocumentUploader({ onReady }) { const [state, setState] = useState(IDLE); const [error, setError] = useState(null); const [doc, setDoc] = useState(null); const [dragging, setDragging] = useState(false); const [mode, setMode] = useState("file"); // "file" | "text" const [text, setText] = useState(""); const inputRef = useRef(null); const submit = useCallback( async (promise) => { setState(UPLOADING); setError(null); try { const payload = await promise; setDoc(payload); setState(READY); onReady?.(payload); } catch (err) { setError(err.message); setState(ERROR); } }, [onReady], ); const handleFile = useCallback( (file) => { if (!file) return; const isPdf = file.type === "application/pdf" || file.name.toLowerCase().endsWith(".pdf"); if (!isPdf) { setError("Only PDF files are supported."); setState(ERROR); return; } if (file.size > MAX_MB * 1024 * 1024) { setError(`File is too large (max ${MAX_MB} MB).`); setState(ERROR); return; } submit(uploadFile(file)); }, [submit], ); const onDrop = useCallback( (e) => { e.preventDefault(); setDragging(false); handleFile(e.dataTransfer.files?.[0]); }, [handleFile], ); const reset = () => { setState(IDLE); setError(null); setDoc(null); setText(""); }; if (state === READY && doc) { return (

Document ready

{doc.filename || "Pasted text"}

Chunks
{doc.num_chunks}
Characters
{doc.num_chars.toLocaleString()}

id: {doc.document_id}

); } const busy = state === UPLOADING; return (
{mode === "file" ? (
{ e.preventDefault(); if (!busy) setDragging(true); }} onDragLeave={() => setDragging(false)} onDrop={busy ? (e) => e.preventDefault() : onDrop} onClick={() => !busy && inputRef.current?.click()} role="button" tabIndex={0} onKeyDown={(e) => { if ((e.key === "Enter" || e.key === " ") && !busy) inputRef.current?.click(); }} className={`flex cursor-pointer flex-col items-center justify-center rounded-xl border-2 border-dashed px-6 py-10 text-center transition ${ dragging ? "border-emerald-400 bg-emerald-50" : "border-slate-300 bg-slate-50 hover:border-slate-400" } ${busy ? "pointer-events-none opacity-60" : ""}`} > handleFile(e.target.files?.[0])} /> {busy ? ( <>

Parsing…

) : ( <> 📄

Drag a PDF here, or click to browse

PDF up to {MAX_MB} MB

)}
) : (