import { useState, useRef, useEffect } from "react"; import { uploadFiles, uploadUrl, getUploadStatus, getDocuments } from "../api"; export default function FileUpload({ onUploadComplete, onBriefing, currentWorkspace = "default" }) { const [uploading, setUploading] = useState(false); const [stageMsg, setStageMsg] = useState(""); const [dragOver, setDragOver] = useState(false); const [error, setError] = useState(null); const [tab, setTab] = useState("file"); // "file" | "url" const [urlInput, setUrlInput] = useState(""); const fileInputRef = useRef(null); const pollRef = useRef(null); // Clean up polling on unmount useEffect(() => () => clearInterval(pollRef.current), []); function startPolling(jobId) { pollRef.current = setInterval(async () => { try { const status = await getUploadStatus(jobId); if (status.status !== "contextualizing") setStageMsg(status.message); if (status.status === "ready") { clearInterval(pollRef.current); const docs = await getDocuments(currentWorkspace); onUploadComplete(docs?.documents || []); if (status.briefing && onBriefing) onBriefing(status.briefing); setUploading(false); setStageMsg(""); } else if (status.status === "failed") { clearInterval(pollRef.current); setError(status.error || "Processing failed"); setUploading(false); setStageMsg(""); } } catch { // transient poll error — keep trying } }, 2000); } async function handleFiles(files) { if (!files.length) return; setUploading(true); setError(null); setStageMsg("Uploading..."); try { const data = await uploadFiles(files, currentWorkspace); setStageMsg(data.message || "Embedding..."); startPolling(data.job_id); } catch (err) { setError(err.response?.data?.detail || "Upload failed"); setUploading(false); setStageMsg(""); } } async function handleUrlSubmit(e) { e.preventDefault(); const url = urlInput.trim(); if (!url) return; setUploading(true); setError(null); setStageMsg("Ingesting URL..."); try { const data = await uploadUrl(url, currentWorkspace); onUploadComplete(data.documents); if (data.briefing && onBriefing) onBriefing(data.briefing); setUrlInput(""); } catch (err) { setError(err.response?.data?.detail || "URL ingestion failed"); } finally { setUploading(false); setStageMsg(""); } } function handleDrop(e) { e.preventDefault(); setDragOver(false); handleFiles(Array.from(e.dataTransfer.files)); } return (
{/* Tab switcher */}
{tab === "file" ? ( <>
!uploading && fileInputRef.current?.click()} onDrop={handleDrop} onDragOver={(e) => { e.preventDefault(); setDragOver(true); }} onDragLeave={() => setDragOver(false)} className={`border-2 border-dashed rounded-xl p-5 text-center transition-all ${ uploading ? "border-indigo-300 bg-indigo-50/50 cursor-default" : dragOver ? "border-indigo-500 bg-indigo-50 cursor-pointer" : "border-gray-200 hover:border-indigo-300 hover:bg-indigo-50/50 cursor-pointer" }`} > {uploading ? (

{stageMsg || "Processing..."}

) : ( <>

Drop files here

PDF, TXT, or CSV

)}
handleFiles(Array.from(e.target.files))} /> ) : (
setUrlInput(e.target.value)} placeholder="https://example.com/article" className="w-full px-3 py-2.5 border border-gray-200 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" disabled={uploading} />
)} {error &&

{error}

}
); }