"use client"; import { useCallback, useState } from "react"; import { useDropzone } from "react-dropzone"; import { Upload, FileText, CheckCircle2, XCircle, Loader2 } from "lucide-react"; import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; import { Progress } from "@/components/ui/progress"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { useUploadDocument, useProgress, type ProgressEvent } from "@/lib/api/documents"; import { DOC_TYPE_OPTIONS } from "@/lib/doc-types"; /* * Upload sheet — drag-drop zone + doc-type selector, with live SSE * progress for the most-recent upload. Intentionally sequential: * a single file at a time keeps the SSE subscription simple and * matches how the FastAPI processor handles one task_id per file. * * The "auto" option is upload-only — it triggers backend classification. * After upload, the inline DocumentTypeEditor shows the resolved doc_type * and uses DOC_TYPE_OPTIONS directly (no "auto" entry). */ const DOC_TYPES: { value: string; label: string }[] = [ { value: "auto", label: "זיהוי אוטומטי" }, ...DOC_TYPE_OPTIONS, ]; type UploadRow = { id: string; filename: string; taskId: string | null; error?: string; }; function statusLabel(event: ProgressEvent | null): string { if (!event) return "מתחיל…"; if (event.status === "queued") return "בתור"; if (event.status === "processing") return event.step ? `בעיבוד · ${event.step}` : "בעיבוד"; if (event.status === "completed") return "הושלם"; if (event.status === "unknown") return "הושלם"; if (event.status === "failed") return event.error ?? "נכשל"; return event.status; } function progressPercent(event: ProgressEvent | null): number { if (!event) return 5; if (event.status === "queued") return 10; if (event.status === "processing") return 55; if (event.status === "completed") return 100; if (event.status === "unknown") return 100; if (event.status === "failed") return 100; return 25; } function UploadRowView({ row, caseNumber }: { row: UploadRow; caseNumber: string }) { const progress = useProgress(row.taskId, caseNumber); const pct = row.error ? 100 : progressPercent(progress); const failed = row.error || progress?.status === "failed"; const done = progress?.status === "completed" || progress?.status === "unknown"; return (
  • {done ? ( ) : failed ? ( ) : ( )} {row.filename} {row.error ?? statusLabel(progress)}
    div]:bg-danger" : done ? "[&>div]:bg-success" : ""} />
  • ); } export function UploadSheet({ caseNumber }: { caseNumber: string }) { const [open, setOpen] = useState(false); const [docType, setDocType] = useState("auto"); const [rows, setRows] = useState([]); const mutate = useUploadDocument(caseNumber); const onDrop = useCallback( async (files: File[]) => { for (const file of files) { const rowId = crypto.randomUUID(); setRows((r) => [ ...r, { id: rowId, filename: file.name, taskId: null }, ]); try { const res = await mutate.mutateAsync({ file, docType }); setRows((r) => r.map((row) => row.id === rowId ? { ...row, taskId: res.task_id } : row, ), ); } catch (e) { setRows((r) => r.map((row) => row.id === rowId ? { ...row, error: e instanceof Error ? e.message : "שגיאה" } : row, ), ); } } }, [docType, mutate], ); const dropzone = useDropzone({ onDrop, accept: { "application/pdf": [".pdf"], "application/msword": [".doc"], "application/vnd.openxmlformats-officedocument.wordprocessingml.document": [".docx"], "text/plain": [".txt"], "text/markdown": [".md"], }, maxSize: 50 * 1024 * 1024, }); return ( העלאת מסמכים לתיק {caseNumber} PDF, DOCX, DOC, TXT, MD — עד 50MB לקובץ. הקבצים מעובדים ברקע והסטטוס מתעדכן בזמן אמת.

    {dropzone.isDragActive ? "שחרר כאן להעלאה" : "גרור קבצים או לחץ לבחירה"}

    ניתן להעלות מספר קבצים בבת אחת

    {rows.length > 0 && (
      {rows.map((row) => ( ))}
    )}
    ); }