"use client"; import { useState } from "react"; import Link from "next/link"; import { Trash2, Plus, Pencil, Wand2 } from "lucide-react"; import { toast } from "sonner"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Skeleton } from "@/components/ui/skeleton"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { usePrecedents, useDeletePrecedent, useRequestHalachotExtraction, isPrecedentActive, type Precedent, type PracticeArea, } from "@/lib/api/precedent-library"; import { PRACTICE_AREAS, practiceAreaShort } from "./practice-area"; import { PrecedentUploadSheet } from "./precedent-upload-sheet"; import { PrecedentEditSheet } from "./precedent-edit-sheet"; import { FormattedCitation, CitationCopyButton, } from "./formatted-citation"; function formatDate(iso: string | null) { if (!iso) return "—"; try { return new Date(iso).toLocaleDateString("he-IL"); } catch { return iso; } } function cleanCitation(s: string | null | undefined): string { if (!s) return "—"; return s.replace(/[‎‏‪-‮⁦-⁩]/g, "").trim(); } // Show the "extract halachot" button only when the precedent hasn't had a // successful (or even attempted) extraction yet. Hide while processing or // after completion to avoid duplicate requests. function needsHalachaExtraction(p: Precedent): boolean { if (p.extraction_status !== "completed") return false; // text not ready if (p.halacha_extraction_status === "processing") return false; if (p.halacha_extraction_status === "completed") return false; if (p.halacha_extraction_status === "pending" && p.halacha_extraction_requested_at) { return false; // already queued } // Remaining cases: pending+no-requested_at (never tried) or failed (retry). return true; } function ActivePill({ label }: { label: string }) { return ( {label} ); } function StatusPill({ p }: { p: Precedent }) { if (p.extraction_status === "failed") { return ( נכשל ); } if (p.extraction_status === "processing") { return ; } if (p.extraction_status !== "completed") { return ( בתור ); } if (p.halacha_extraction_status === "processing") { return ; } if (p.halacha_extraction_status === "failed") { return ( חילוץ נכשל ); } if (p.halacha_extraction_status === "pending") { if (p.halacha_extraction_requested_at) { return ( ממתין לחילוץ ); } return ( לא חולץ ); } if (p.halachot_count === 0) { return ללא הלכות; } return ( {p.approved_count}/{p.halachot_count} מאושרות ); } function CourtRow({ p, onEdit }: { p: Precedent; onEdit: (id: string) => void }) { const del = useDeletePrecedent(); const reqHalachot = useRequestHalachotExtraction(); const active = isPrecedentActive(p); const showExtractHalachot = needsHalachaExtraction(p); const onDelete = async () => { if (active) { toast.error("מתבצע עיבוד — לא ניתן למחוק עכשיו."); return; } if (!window.confirm(`למחוק את ${p.case_number}?`)) return; try { await del.mutateAsync(p.id); toast.success("נמחק"); } catch (e) { toast.error(e instanceof Error ? e.message : "שגיאה"); } }; const onExtractHalachot = async () => { try { await reqHalachot.mutateAsync(p.id); toast.success("סומן לחילוץ הלכות. הריצי מ-Claude Code: precedent_process_pending_halachot"); } catch (e) { toast.error(e instanceof Error ? e.message : "שגיאה"); } }; return (
{p.citation_formatted ? ( ) : ( cleanCitation(p.case_number) )} {p.citation_formatted ? ( ) : null}
{/* Column "שם / ערכאה" hidden by request (case_name often equals case_number prefix). Keep field in DB; restore by un-hiding. */}
{cleanCitation(p.case_name)}
{p.court ?
{p.court}
: null}
{p.date ? formatDate(p.date) : } {p.practice_area ? ( {practiceAreaShort(p.practice_area)} ) : } {p.precedent_level || }
{showExtractHalachot && ( )}
); } function CommitteeRow({ p, onEdit }: { p: Precedent; onEdit: (id: string) => void }) { const del = useDeletePrecedent(); const reqHalachot = useRequestHalachotExtraction(); const active = isPrecedentActive(p); const showExtractHalachot = needsHalachaExtraction(p); const onDelete = async () => { if (active) { toast.error("מתבצע עיבוד — לא ניתן למחוק עכשיו."); return; } if (!window.confirm(`למחוק את ${p.case_number}?`)) return; try { await del.mutateAsync(p.id); toast.success("נמחק"); } catch (e) { toast.error(e instanceof Error ? e.message : "שגיאה"); } }; const onExtractHalachot = async () => { try { await reqHalachot.mutateAsync(p.id); toast.success("סומן לחילוץ הלכות. הריצי מ-Claude Code: precedent_process_pending_halachot"); } catch (e) { toast.error(e instanceof Error ? e.message : "שגיאה"); } }; return (
{p.citation_formatted ? ( ) : ( cleanCitation(p.case_number) )} {p.citation_formatted ? ( ) : null}
{/* Column "שם" hidden by request (case_name often equals case_number prefix). Keep field in DB; restore by un-hiding. */}
{cleanCitation(p.case_name)}
{p.district || } {p.chair_name || } {p.date ? formatDate(p.date) : } {p.practice_area ? ( {practiceAreaShort(p.practice_area)} ) : }
{showExtractHalachot && ( )}
); } function TableSkeleton({ cols }: { cols: number }) { return ( <> {[...Array(4)].map((_, i) => ( {[...Array(cols)].map((_, j) => ( ))} ))} ); } export function LibraryListPanel() { const [practiceArea, setPracticeArea] = useState(""); const [search, setSearch] = useState(""); const [uploadOpen, setUploadOpen] = useState(false); const [editingId, setEditingId] = useState(null); const sharedFilters = { practiceArea: practiceArea || undefined, search: search.trim() || undefined, limit: 200, }; const courts = usePrecedents({ ...sharedFilters, sourceKind: "external_upload", sourceType: "court_ruling" }); const committee = usePrecedents({ ...sharedFilters, sourceKind: "all_committees" }); return (
{/* Shared filters */}
setSearch(e.target.value)} placeholder="עע״מ 3975/22 / 1200-25" dir="rtl" />
{/* Table 1 — Court rulings */}

פסיקת בתי משפט

{courts.data && ( {courts.data.count} )}
{courts.error ? (
{courts.error.message}
) : (
מס׳ / מראה מקום {/* "שם / ערכאה" hidden by request — see CourtRow */} שם / ערכאה תאריך תחום רמה הלכות {courts.isPending ? ( ) : !courts.data?.items.length ? ( אין פסיקת בתי משפט בקורפוס. ) : ( courts.data.items.map((p) => ( )) )}
)}
{/* Table 2 — Appeals committee decisions */}

החלטות ועדות ערר

{committee.data && ( {committee.data.count} )}
{committee.error ? (
{committee.error.message}
) : (
מספר ערר {/* "שם" hidden by request — see CommitteeRow */} שם מחוז יו״ר תאריך תחום הלכות {committee.isPending ? ( ) : !committee.data?.items.length ? ( אין החלטות ועדת ערר בקורפוס. ) : ( committee.data.items.map((p) => ( )) )}
)}
{ if (!open) setEditingId(null); }} />
); }