"use client"; import { useEffect, useMemo, useState, type ReactNode } from "react"; import { Check, X, Edit2, ChevronDown, ChevronLeft, AlertTriangle, Clock, RotateCcw } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Skeleton } from "@/components/ui/skeleton"; import { Textarea } from "@/components/ui/textarea"; import { CorroborationBadge } from "./corroboration-badge"; import { practiceAreaLabel } from "./practice-area"; import { useHalachotPending, useHalachotByStatus, useUpdateHalacha, useBatchReviewHalachot, type Halacha, } from "@/lib/api/precedent-library"; import { AuthorityBadge, ruleTypeLabel } from "./halacha-meta"; /** #81 strict-rubric flags — why an item was held back from auto-approval. */ const QUALITY_FLAG_LABELS: Record = { non_decision: "אי-הכרעה", truncated_quote: "ציטוט קטוע", thin_restatement: "ניסוח דק", quote_unverified: "ציטוט לא מאומת", nli_unsupported: "כלל לא נגזר מהציטוט", application: "יישום תלוי-עובדות", near_duplicate: "כפילות-קרובה", nevo_preamble_leak: "דליפת רציו נבו", }; function formatDate(iso: string | null | undefined) { if (!iso) return "—"; try { return new Date(iso).toLocaleDateString("he-IL"); } catch { return iso; } } /* Strip Unicode bidi marks that render as zero-width but shift visual position. */ function cleanCitation(s: string | null | undefined): string { if (!s) return "—"; return s.replace(/[‎‏‪-‮⁦-⁩]/g, "").trim(); } type EditState = { rule_statement: string; reasoning_summary: string }; // ─── Pending-queue card (full interactions) ─────────────────────────────────── function HalachaCard({ h, focused, onApprove, onReject, onDefer, onSave, }: { h: Halacha & { variants?: Halacha[] }; focused: boolean; onApprove: () => void; onReject: () => void; onDefer: () => void; onSave: (patch: Partial) => Promise; }) { const variants = h.variants ?? []; const equivalents = h.equivalents ?? []; const [showVariants, setShowVariants] = useState(false); const [showEquiv, setShowEquiv] = useState(false); const [editing, setEditing] = useState(false); const [draft, setDraft] = useState({ rule_statement: h.rule_statement, reasoning_summary: h.reasoning_summary, }); useEffect(() => { // eslint-disable-next-line react-hooks/set-state-in-effect setDraft({ rule_statement: h.rule_statement, reasoning_summary: h.reasoning_summary, }); }, [h.id, h.rule_statement, h.reasoning_summary]); const onSubmitEdit = async () => { await onSave(draft); setEditing(false); }; return (
{h.page_reference && ( {h.page_reference} )} {h.quote_verified ? ( ציטוט מאומת ) : ( ציטוט לא מאומת )} ביטחון {h.confidence.toFixed(2)} {ruleTypeLabel(h.rule_type)} {variants.length > 0 && ( +{variants.length} וריאנטים )} {equivalents.length > 0 && ( עיקרון מקביל ב-{equivalents.length} )}
{h.quality_flags && h.quality_flags.length > 0 && (
{h.quality_flags.map((f) => ( {QUALITY_FLAG_LABELS[f] ?? f} ))}
)}
ניסוח הכלל
{editing ? (