"use client"; import { useRef, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { useExports, useExportDocx, useUploadDraft, useMarkFinal, useDeleteDraft, useActiveDraft, } from "@/lib/api/exports"; import { useCaseFeedback, useCreateFeedback, useResolveFeedback, CATEGORY_LABELS, CATEGORY_COLORS, BLOCK_LABELS, type FeedbackCategory, } from "@/lib/api/feedback"; import type { CaseStatus } from "@/lib/api/cases"; import { toast } from "sonner"; import { FileText, Download, Upload, Award, Loader2, FileOutput, Plus, Trash2, } from "lucide-react"; /* Statuses at which a draft is considered ready */ const DRAFT_READY: CaseStatus[] = [ "drafted", "exported", "reviewed", "final", ]; function formatSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; const kb = bytes / 1024; if (kb < 1024) return `${kb.toFixed(0)} KB`; return `${(kb / 1024).toFixed(1)} MB`; } function formatDate(epoch: number): string { return new Date(epoch * 1000).toLocaleDateString("he-IL", { day: "numeric", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit", }); } /* ── Main component ─────────────────────────────────── */ export function DraftsPanel({ caseNumber, status, }: { caseNumber: string; status?: CaseStatus; }) { const { data: exports, isLoading: exportsLoading } = useExports(caseNumber); const { data: feedbacks, isLoading: feedbackLoading } = useCaseFeedback(caseNumber); const { data: activeDraft } = useActiveDraft(caseNumber); const exportDocx = useExportDocx(caseNumber); const uploadDraft = useUploadDraft(caseNumber); const markFinal = useMarkFinal(caseNumber); const deleteDraft = useDeleteDraft(caseNumber); const resolveMutation = useResolveFeedback(); const fileRef = useRef(null); const [deleteTarget, setDeleteTarget] = useState(null); const isDraftReady = status && DRAFT_READY.includes(status); const openFeedbacks = feedbacks?.filter((f) => !f.resolved) ?? []; // Determine draft label based on *actual* v-numbers in filenames (not counts). // "(מתוקנת)" suffix appears when there's at least one עריכה-* file. const draftLabel = (() => { if (!exports?.length) return "טיוטה מוכנה לעיון"; const drafts = exports.filter((f) => f.filename.startsWith("טיוטה-")); const revisions = exports.filter((f) => f.filename.startsWith("עריכה-")); if (!drafts.length) return "טיוטה מוכנה לעיון"; const versions = drafts .map((f) => { const m = f.filename.match(/v(\d+)/); return m ? parseInt(m[1], 10) : 0; }) .filter((n) => n > 0); const maxVer = versions.length ? Math.max(...versions) : drafts.length; const suffix = revisions.length > 0 ? " (מתוקנת)" : ""; return `טיוטה v${maxVer}${suffix} מוכנה לעיון`; })(); function handleUpload(file: File) { uploadDraft.mutate(file, { onSuccess: (data) => { const added = data.bookmarks_added?.length ?? 0; const missing = data.missing_blocks?.length ?? 0; if (data.apply_status === "completed" || data.apply_status === "ok") { if (added > 0) { toast.success(`הועלה: ${data.filename} — זוהו ${added} בלוקים`); } else { toast.success(`הועלה: ${data.filename}`); } if (missing > 0) { toast.warning( `שימו לב: ${missing} בלוקים לא זוהו — ייתכנו בעיות בתיקונים עתידיים`, ); } } else { toast.error(`הועלה אך השילוב נכשל: ${data.apply_status ?? "שגיאה"}`); } }, onError: (err) => toast.error(err instanceof Error ? err.message : "שגיאה בהעלאה"), }); } function handleExport() { exportDocx.mutate(undefined, { onSuccess: () => toast.success("הטיוטה יוצאה בהצלחה"), onError: () => toast.error("שגיאה בייצוא"), }); } function handleMarkFinal(filename: string) { markFinal.mutate(filename, { onSuccess: () => toast.success("סומן כסופי"), onError: () => toast.error("שגיאה בסימון"), }); } function handleResolve(id: string) { resolveMutation.mutate( { feedbackId: id, applied_to: [] }, { onSuccess: () => toast.success("ההערה סומנה כמטופלת"), onError: () => toast.error("שגיאה בעדכון"), }, ); } return (
{/* ── Banner ── */} {isDraftReady && (
{draftLabel}
)} {/* ── Active-draft badge — the DOCX that is the current source of truth ── */} {activeDraft?.filename && (
מקור האמת: {activeDraft.filename}
)} {/* ── Exports list ── */}

קבצי טיוטה

{ const f = e.target.files?.[0]; if (f) handleUpload(f); if (fileRef.current) fileRef.current.value = ""; }} />
{exportsLoading ? (

טוען...

) : !exports?.length ? (

אין טיוטות עדיין.{" "} {!isDraftReady && "הטיוטה תופיע כאן כשתהיה מוכנה."}

) : (
{exports.map((file) => ( ))}
File Size Date
{file.filename} {file.is_final && ( סופי )} {formatSize(file.size)} {formatDate(file.created_at)}
{!file.is_final && ( <> )}
)} {/* Delete confirmation dialog */} !open && setDeleteTarget(null)} > מחיקת טיוטה

למחוק את הקובץ{" "} {deleteTarget}?
פעולה זו לא ניתנת לביטול.

{/* ── Chair feedback ── */}

הערות יו״ר {openFeedbacks.length > 0 && ( {openFeedbacks.length} פתוחות )}

{feedbackLoading ? (

טוען...

) : !feedbacks?.length ? (

אין הערות לתיק זה.

) : (
{feedbacks.map((fb) => (
{CATEGORY_LABELS[fb.category]} {BLOCK_LABELS[fb.block_id] ?? fb.block_id} {fb.resolved && ( טופל )} {fb.created_at ? new Date(fb.created_at).toLocaleDateString("he-IL") : ""}

{fb.feedback_text}

{fb.lesson_extracted && (

לקח שהופק:

{fb.lesson_extracted}

)} {!fb.resolved && (
)}
))}
)}
); } /* ── New feedback dialog (case-scoped) ─────────────── */ function NewCaseFeedbackDialog({ caseNumber }: { caseNumber: string }) { const [open, setOpen] = useState(false); const createMutation = useCreateFeedback(); const [blockId, setBlockId] = useState("block-yod"); const [category, setCategory] = useState("missing_content"); const [feedbackText, setFeedbackText] = useState(""); const [lesson, setLesson] = useState(""); function handleSubmit(e: React.FormEvent) { e.preventDefault(); if (!feedbackText.trim()) return; createMutation.mutate( { case_number: caseNumber, block_id: blockId, feedback_text: feedbackText, category, lesson_extracted: lesson || undefined, }, { onSuccess: () => { toast.success("ההערה נרשמה בהצלחה"); setOpen(false); setFeedbackText(""); setLesson(""); }, onError: () => toast.error("שגיאה ברישום ההערה"), }, ); } return ( הערת יו״ר — תיק {caseNumber}