"use client"; import { useCallback, useState } from "react"; import { useDropzone } from "react-dropzone"; import { UploadCloud, Loader2 } from "lucide-react"; import { cn } from "@/lib/utils"; function stringifyApiError(value: unknown): string | null { if (!value) return null; if (typeof value === "string") return value; if (Array.isArray(value)) { const parts = value .map((item) => { if (typeof item === "string") return item; if (item && typeof item === "object" && "msg" in item) { return String((item as { msg: unknown }).msg); } return JSON.stringify(item); }) .filter(Boolean); return parts.join("; "); } if (typeof value === "object") return JSON.stringify(value); return String(value); } export function FileUploader({ onUploaded }: { onUploaded: () => void }) { const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const [progress, setProgress] = useState(null); const upload = useCallback( async (files: File[]) => { setError(null); setBusy(true); try { for (let i = 0; i < files.length; i++) { const f = files[i]; setProgress(`Uploading ${i + 1}/${files.length}: ${f.name}`); const fd = new FormData(); fd.append("file", f); const r = await fetch("/api/upload", { method: "POST", body: fd }); if (!r.ok) { const contentType = r.headers.get("content-type") ?? ""; let message = "Upload failed"; if (contentType.includes("application/json")) { const payload = (await r.json().catch(() => null)) as | { detail?: unknown; error?: unknown } | null; message = stringifyApiError(payload?.detail) ?? stringifyApiError(payload?.error) ?? message; } else { message = (await r.text()).trim() || message; } throw new Error(`${f.name}: ${message}`); } } onUploaded(); } catch (e) { setError((e as Error).message); } finally { setBusy(false); setProgress(null); } }, [onUploaded] ); const { getRootProps, getInputProps, isDragActive } = useDropzone({ accept: { "application/pdf": [".pdf"] }, multiple: true, disabled: busy, onDrop: upload, }); return (
{busy ? ( ) : ( )}

{busy ? progress ?? "Uploading…" : isDragActive ? "Drop the PDFs here" : "Drag & drop PDFs here, or click to select"}

Files are pushed to your linked Hugging Face Dataset and indexed automatically.

{error &&

{error}

}
); }