"use client"; /* * Per-decision lessons editor — lives inside CorpusDetailDrawer's * "מה למדנו" tab. Lessons are persisted in the decision_lessons table * (one-to-many on style_corpus) and consumed by hermes-curator and * future style_analyzer runs as context. * * The chair can: * - Add a lesson typed manually (category = "general" by default) * - Edit / delete existing lessons * - Approve / reject a lesson (review_status — INV-LRN1/G10): only an * approved lesson flows to the writer. This is the SINGLE writer gate; * the old informative-only applied_to_skill flag was removed (LRN-1/INV-IA3). * * Lessons from the curator arrive with source="curator" and are visually * distinguished by a badge so the chair can audit auto-suggestions. */ import { useState } from "react"; import { Plus, Save, Trash2, Loader2, Check, X, Undo2, } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Textarea } from "@/components/ui/textarea"; import { Badge } from "@/components/ui/badge"; import { Skeleton } from "@/components/ui/skeleton"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { useAddLesson, useCorpusLessons, useDeleteLesson, usePatchLesson, type DecisionLesson, } from "@/lib/api/training"; const CATEGORIES = [ { value: "general", label: "כללי" }, { value: "style", label: "סגנון" }, { value: "structure", label: "מבנה" }, { value: "lexicon", label: "לקסיקון" }, { value: "tabular", label: "טבלאי" }, ] as const; // All labels in Hebrew. Keyed loosely (string) so new sources — e.g. the // two-judge style panel — render correctly with a graceful fallback. const SOURCE_BADGE: Record = { manual: { label: "ידני", cls: "bg-rule-soft text-ink-soft" }, chair: { label: "יו״ר", cls: "bg-gold-wash text-gold-deep" }, curator: { label: "הרמס (סקירה)", cls: "bg-info-bg text-info" }, style_analyzer: { label: "מנתח-סגנון", cls: "bg-success-bg text-success" }, "panel:deepseek+gemini": { label: "פאנל: דיפסיק+גמיני", cls: "bg-gold-wash text-gold-deep" }, // #158/INV-LRN8 — a super-lesson merging overlapping lessons; navy-filled so it // reads as the consolidated/elevated one. synthesis: { label: "סינתזה", cls: "bg-navy text-parchment" }, }; const SOURCE_BADGE_FALLBACK = { label: "פאנל", cls: "bg-rule-soft text-ink-soft" }; // review gate (INV-LRN1/G10): only "approved" lessons flow to the writer. const REVIEW_BADGE: Record = { proposed: { label: "ממתין לאישור", cls: "bg-gold-wash text-gold-deep border-gold/40" }, approved: { label: "מאושר · זורם לכותב", cls: "bg-success-bg text-success border-success/40" }, rejected: { label: "נדחה", cls: "bg-rule-soft text-ink-muted line-through" }, // #158/INV-LRN8 — merged into a synthesis super-lesson; kept as provenance, no // longer fed to the writer. Muted (not struck — it wasn't rejected). superseded: { label: "מוזג", cls: "bg-rule-soft text-ink-muted" }, }; export function LessonsTab({ corpusId }: { corpusId: string }) { const { data, isPending } = useCorpusLessons(corpusId); const add = useAddLesson(corpusId); const [draftText, setDraftText] = useState(""); const [draftCategory, setDraftCategory] = useState("general"); const onAdd = async () => { const text = draftText.trim(); if (!text) return; try { await add.mutateAsync({ lesson_text: text, category: draftCategory }); setDraftText(""); setDraftCategory("general"); toast.success("הלקח נוסף"); } catch (e) { toast.error(e instanceof Error ? e.message : "כשל בשמירה"); } }; return (
{/* Composer */}

הוסף לקח להחלטה