"use client"; import { useState } from "react"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Markdown } from "@/components/ui/markdown"; import { useDecisionBlocks, useSaveBlock, type DecisionBlock, type BlockStatus, } from "@/lib/api/decision-blocks"; import { BLOCK_LABELS } from "@/lib/api/feedback"; import { AlertTriangle, Pencil, FileText } from "lucide-react"; /* ── status badge styling ─────────────────────────────── */ const STATUS_LABELS: Record = { empty: "ריק", draft: "טיוטה", review: "בבדיקה", final: "סופי", }; const STATUS_CLASSES: Record = { empty: "bg-rule-soft text-ink-muted border-rule", draft: "bg-gold/10 text-gold-deep border-gold/30", review: "bg-blue-50 text-blue-700 border-blue-200", final: "bg-success-bg text-success border-success/40", }; function blockLabel(b: DecisionBlock): string { return BLOCK_LABELS[b.block_id] ?? b.title ?? b.block_id; } /* ── Main panel ───────────────────────────────────────── */ export function DecisionBlocksPanel({ caseNumber }: { caseNumber: string }) { const { data, isLoading, error } = useDecisionBlocks(caseNumber); if (isLoading) { return

טוען...

; } if (error) { return (

שגיאה בטעינת תוכן ההחלטה: {error.message}

); } if (!data) return null; const written = data.blocks.filter((b) => b.word_count > 0).length; return (
{/* ── Source-of-truth warning ── */} {data.source_of_truth === "docx" && (

קיים קובץ DOCX מתוקן המשמש כמקור האמת לתיק זה. עריכת בלוקים כאן נשמרת ב-DB אך לא תעדכן את ה-DOCX עד הפקת טיוטה מחדש.

)} {/* ── Header line ── */}

תוכן ההחלטה לפי בלוקים

{written}/12 בלוקים נכתבו
{!data.has_decision && (

טרם נכתבו בלוקים לתיק זה. ניתן להתחיל לכתוב בכל בלוק להלן — הכתיבה תיצור את ההחלטה אוטומטית.

)} {/* ── 12 blocks ── */} {data.blocks.map((block) => (
{blockLabel(block)} {STATUS_LABELS[block.status]} {block.word_count > 0 && ( {block.word_count} מילים )}
))}
); } /* ── Per-block view / edit ────────────────────────────── */ type SaveState = | { kind: "idle" } | { kind: "saving" } | { kind: "saved"; at: Date } | { kind: "error"; message: string }; function BlockEditor({ caseNumber, block, }: { caseNumber: string; block: DecisionBlock; }) { const [editing, setEditing] = useState(false); const [value, setValue] = useState(block.content); const [state, setState] = useState({ kind: "idle" }); /* The last content known to be persisted — used to skip no-op saves. */ const [baseline, setBaseline] = useState(block.content); const save = useSaveBlock(caseNumber); /* Re-sync when the upstream query refetches (e.g. after another save) while * not actively editing. Adjusting state during render — the documented React * pattern for derived-from-props — avoids a setState-in-effect cascade. */ if (!editing && block.content !== baseline) { setBaseline(block.content); setValue(block.content); } async function handleSave() { const next = value; if (next === baseline) return; setState({ kind: "saving" }); try { await save.mutateAsync({ blockId: block.block_id, content: next }); setBaseline(next); setState({ kind: "saved", at: new Date() }); } catch (e) { setState({ kind: "error", message: e instanceof Error ? e.message : "שגיאה בשמירה", }); } } if (!editing) { return (
{block.content.trim() ? ( ) : (

בלוק ריק.

)}
); } return (
עריכת תוכן הבלוק (Markdown) — נשמר בעת יציאה מהשדה