diff --git a/web-ui/src/components/precedents/precedent-edit-sheet.tsx b/web-ui/src/components/precedents/precedent-edit-sheet.tsx index eda34c6..8b9c29e 100644 --- a/web-ui/src/components/precedents/precedent-edit-sheet.tsx +++ b/web-ui/src/components/precedents/precedent-edit-sheet.tsx @@ -1,11 +1,12 @@ "use client"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { Save, Sparkles } from "lucide-react"; import { toast } from "sonner"; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, } from "@/components/ui/sheet"; +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -18,6 +19,7 @@ import { usePrecedent, useUpdatePrecedent, useRequestMetadataExtraction, + type Halacha, type PracticeArea, type SourceType, } from "@/lib/api/precedent-library"; @@ -25,6 +27,200 @@ import { PRACTICE_AREAS, PRECEDENT_LEVELS, SOURCE_TYPES, } from "./practice-area"; +const RULE_TYPE_LABELS: Record = { + binding: "הלכה מחייבת", + interpretive: "פרשני", + procedural: "פרוצדורלי", + obiter: "אמרת אגב", + application: "יישום הלכה", + persuasive: "משכנע", +}; + +type StatusFilter = "all" | "approved" | "pending" | "rejected"; + +function ReviewStatusPill({ status }: { status: Halacha["review_status"] }) { + if (status === "approved" || status === "published") { + return ( + + מאושרת + + ); + } + if (status === "pending_review") { + return ( + + ממתינה + + ); + } + return ( + + נדחתה + + ); +} + +/* Read-only roll-up of every halacha extracted from this precedent — + * approved + pending + rejected. The "ממתין לאישור" tab only surfaces + * pending items globally; this section is the per-case view. To act on + * an item (approve / edit / reject), go to the review tab — keeping the + * surfaces separated avoids duplicate review UX in two places. */ +function ExtractedHalachotSection({ halachot }: { halachot: Halacha[] }) { + const [filter, setFilter] = useState("all"); + + const counts = useMemo(() => { + const c = { all: halachot.length, approved: 0, pending: 0, rejected: 0 }; + for (const h of halachot) { + if (h.review_status === "approved" || h.review_status === "published") { + c.approved++; + } else if (h.review_status === "pending_review") { + c.pending++; + } else if (h.review_status === "rejected") { + c.rejected++; + } + } + return c; + }, [halachot]); + + const sorted = useMemo(() => { + const matches = (h: Halacha) => { + if (filter === "all") return true; + if (filter === "approved") { + return h.review_status === "approved" || h.review_status === "published"; + } + if (filter === "pending") return h.review_status === "pending_review"; + return h.review_status === "rejected"; + }; + return halachot + .filter(matches) + .sort((a, b) => a.halacha_index - b.halacha_index); + }, [halachot, filter]); + + if (!halachot.length) { + return ( +
+ עדיין לא חולצו הלכות מהפסיקה הזו. +
+ ); + } + + const tabs: { key: StatusFilter; label: string; count: number }[] = [ + { key: "all", label: "הכל", count: counts.all }, + { key: "approved", label: "מאושרות", count: counts.approved }, + { key: "pending", label: "ממתינות", count: counts.pending }, + { key: "rejected", label: "נדחו", count: counts.rejected }, + ]; + + return ( +
+
+

+ הלכות שחולצו ({counts.all}) +

+
+ {tabs.map((t) => { + const active = filter === t.key; + return ( + + ); + })} +
+
+ + {!sorted.length ? ( +
+ אין הלכות בקטגוריה זו. +
+ ) : ( +
    + {sorted.map((h) => ( +
  1. +
    + + #{h.halacha_index} + + + + {RULE_TYPE_LABELS[h.rule_type] ?? h.rule_type} + + + ביטחון {h.confidence.toFixed(2)} + + {h.page_reference ? ( + + {h.page_reference} + + ) : null} +
    + +

    + {h.rule_statement} +

    + + {h.reasoning_summary ? ( +

    + היגיון: + {h.reasoning_summary} +

    + ) : null} + + {h.supporting_quote ? ( +
    + “{h.supporting_quote}” +
    + ) : null} + + {h.subject_tags?.length ? ( +
    + {h.subject_tags.map((t) => ( + + {t} + + ))} +
    + ) : null} +
  2. + ))} +
+ )} +
+ ); +} + type Props = { caseLawId: string | null; onOpenChange: (open: boolean) => void; @@ -144,6 +340,7 @@ export function PrecedentEditSheet({ caseLawId, onOpenChange }: Props) { {[...Array(6)].map((_, i) => )} ) : ( + <>
@@ -278,6 +475,10 @@ export function PrecedentEditSheet({ caseLawId, onOpenChange }: Props) {
+
+ +
+ )}