"use client"; import { useState } from "react"; import { Loader2, ChevronLeft, Check } from "lucide-react"; import { toast } from "sonner"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { useReconciliationLedger, useStyleDistance, usePairDetail, usePromoteLearning, type DraftFinalPair, } from "@/lib/api/learning"; /** * /training "למידה" tab (T6/T13) — the reconciliation ledger (INV-LRN4): every * decision and its draft↔final comparison status, plus per-case style-distance * (T7). The list is the chair's "have we closed each decision against Dafna's * final?" surface. */ const STATUS_LABEL: Record = { final_received: "התקבל סופי — ממתין לניתוח", analyzed: "נותח (הצעות ממתינות)", lessons_folded: "לקחים הוטמעו", }; const STATUS_CLASS: Record = { final_received: "bg-gold-wash text-gold-deep border-gold/40", analyzed: "bg-navy-soft/20 text-navy", lessons_folded: "bg-emerald-50 text-emerald-800 border-emerald-300/60", }; function fmtDate(iso: string | null) { if (!iso) return "—"; try { return new Date(iso).toLocaleDateString("he-IL"); } catch { return iso; } } function StyleDistanceDetail({ caseNumber }: { caseNumber: string }) { const { data, isPending, error } = useStyleDistance(caseNumber); if (isPending) return ; if (error || data?.error) return

{data?.error || "שגיאה"}

; if (!data) return null; const s = data.summary; return (
שינוי draft→final: {s.change_percent ?? "—"}% סטיית יחסי-זהב מקס׳: {s.ratio_max_deviation_pp ?? "—"} נק׳ אנטי-דפוסים: {s.anti_pattern_total}
{Object.keys(data.golden_ratio_adherence.sections).length > 0 && (
{Object.entries(data.golden_ratio_adherence.sections).map(([sec, v]) => ( 0 ? "text-danger" : ""}> {sec}: {v.actual_pct}% (יעד {v.target[0]}-{v.target[1]}) ))}
)} {data.anti_pattern_hits.total > 0 && (
{Object.entries(data.anti_pattern_hits.by_pattern).map(([n, v]) => ( {n}: {v.count} ))}
)}
); } function SelectableItem({ text, selected, onToggle }: { text: string; selected: boolean; onToggle: () => void; }) { return ( ); } /** T14 — chair approval gate (INV-G10/LRN1): review the curator's style_method * proposal and fold approved lessons/phrases into writer-consumed channels. */ function ProposalReview({ pairId }: { pairId: string }) { const { data, isPending, error } = usePairDetail(pairId); const promote = usePromoteLearning(pairId); const [lessons, setLessons] = useState>(new Set()); const [phrases, setPhrases] = useState>(new Set()); if (isPending) return ; if (error || !data) return

שגיאה בטעינת ההצעה.

; const lessonTexts = data.changes.map((c) => c.lesson).filter((l): l is string => Boolean(l)); const toggle = (set: Set, setFn: (s: Set) => void, v: string) => { const next = new Set(set); next.has(v) ? next.delete(v) : next.add(v); setFn(next); }; const total = lessons.size + phrases.size; return (

הצעת הדיסטילציה (סגנון/שיטה בלבד — INV-LRN5). בחר/י מה לאמץ; ייכתב לערוצים שהכותב צורך (שער-יו"ר).

{data.overall_assessment && (

{data.overall_assessment}

)} {lessonTexts.length > 0 && (
לקחי-סגנון → כללי-דיון
{lessonTexts.map((l, i) => ( toggle(lessons, setLessons, l)} /> ))}
)} {data.new_expressions.length > 0 && (
ביטויי-מעבר חדשים
{data.new_expressions.map((p, i) => ( toggle(phrases, setPhrases, p)} /> ))}
)} {lessonTexts.length === 0 && data.new_expressions.length === 0 && (

אין הצעות style_method.

)}
); } function Row({ pair }: { pair: DraftFinalPair }) { const [open, setOpen] = useState(false); return (
{open && (
{pair.status === "analyzed" && } {pair.case_number && }
)}
); } export function LearningPanel() { const { data, isPending, error } = useReconciliationLedger(); if (error) return

שגיאה בטעינת הפנקס.

; if (isPending) return ; const items = data?.items ?? []; return (

פנקס-ההתאמה (INV-LRN4): כל החלטה נסגרת מול הסופי של דפנה; כל סופי מנותח מול הטיוטה. לחיצה על תיק → מדד מרחק-הסגנון שלו.

{items.length === 0 ? (

אין עדיין השוואות. הן נוצרות כשמסמנים החלטה כסופית.

) : ( items.map((p) => ) )}
); }