'use client'; import { useState, useEffect } from 'react'; import { UploadCloud, FileUp, CheckCircle2 } from 'lucide-react'; import api from '@/lib/axios'; import { useDocumentsStore } from '@/store/documents'; import { useAuthStore } from '@/store/auth'; export function DocumentUpload() { const [dragging, setDragging] = useState(false); const [uploading, setUploading] = useState(false); const [uploadedDocId, setUploadedDocId] = useState(null); const [uploadError, setUploadError] = useState(null); const [source, setSource] = useState(''); const documents = useDocumentsStore((s) => s.documents); const upsertDocument = useDocumentsStore((s) => s.upsertDocument); const isAuthed = useAuthStore((s) => s.isAuthenticated); // Track real processing progress from the document store const liveDoc = uploadedDocId ? documents.find((d) => d.id === uploadedDocId) : null; const liveProgress = liveDoc?.processingProgress ?? 0; const liveStep = liveDoc?.processingStep || null; // Clear upload state when document completes or fails useEffect(() => { if (liveDoc && (liveDoc.status === 'COMPLETED' || liveDoc.status === 'FAILED')) { const timer = setTimeout(() => { setUploading(false); setUploadedDocId(null); setUploadError(null); }, 2000); return () => clearTimeout(timer); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [liveDoc?.status]); const ALLOWED_EXTS = new Set(['.pdf', '.txt', '.md', '.docx', '.doc', '.csv', '.json', '.html', '.xml']); const MAX_SIZE_BYTES = 10 * 1024 * 1024; async function uploadFile(file: File) { if (!isAuthed) { alert('Please log in to upload documents.'); return; } const ext = '.' + file.name.split('.').pop()?.toLowerCase(); if (!ALLOWED_EXTS.has(ext)) { setUploadError(`File type "${ext}" is not supported. Allowed: ${Array.from(ALLOWED_EXTS).join(', ')}`); return; } if (file.size > MAX_SIZE_BYTES) { setUploadError(`File exceeds 10MB limit (${(file.size / 1024 / 1024).toFixed(1)}MB).`); return; } if (file.size === 0) { setUploadError('Empty files are not allowed.'); return; } setUploading(true); setUploadError(null); setUploadedDocId(null); const form = new FormData(); form.append('file', file); if (source.trim()) { form.append('source', source.trim()); } try { const { data } = await api.post('/documents/upload/', form, { headers: { 'Content-Type': 'multipart/form-data' }, }); const doc = data.document; upsertDocument({ id: doc.id, name: doc.name, status: doc.status, uploadedAt: doc.uploaded_at ?? new Date().toISOString(), processingStep: doc.processing_step ?? null, processingProgress: doc.processing_progress ?? 0, }); setUploadedDocId(doc.id); } catch (err: any) { const msg = err?.response?.data?.error || err?.message || 'Upload failed.'; setUploadError(msg); setUploading(false); } } return (
setSource(e.target.value)} placeholder="e.g. research-paper, company-docs, internal-wiki" className="w-full rounded-md border border-border bg-bg-base px-3 py-1.5 text-xs text-text-primary placeholder:text-text-muted focus:border-accent-cyan focus:outline-none" />
{ e.preventDefault(); setDragging(true); }} onDragLeave={() => setDragging(false)} onDrop={(e) => { e.preventDefault(); setDragging(false); const f = e.dataTransfer.files?.[0]; if (f) uploadFile(f); }} className={`flex flex-col items-center justify-center rounded-lg border-2 border-dashed p-8 text-center transition-colors ${ dragging ? 'border-accent-cyan bg-accent-cyan/5' : 'border-border bg-bg-base' }`} >

Drop files here or browse

Supports: PDF, TXT, DOCX, Markdown, CSV, JSON, HTML, XML ยท Max 10MB

{uploadError && (
{uploadError}
)} {uploading && (

{liveDoc?.status === 'COMPLETED' ? ( Processing complete ) : liveStep ? ( <>{liveStep}... ) : ( <>Uploading... )}

)}
); }