Files
legal-ai/web-ui/src/app/precedents/page.tsx
Chaim 37e881bf8c
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
feat(nav): הסרת פילי-טאבים ותגי-ניווט + הורדת "פסיקה חסרה" מהתפריט (#3/#4)
#3 — הסרת תגי-המספר הקטנים:
- app-shell: הוסרו ApprovalsBadge ("מרכז אישורים") ו-MissingPrecedentsBadge.
- precedents: הוסרו CountPill/PendingPill/PlansPendingPill/IncomingPill מהטאבים;
  הקו-תחתון-זהב לבדו נושא את מצב-הטאב-הפעיל.

#4 — "פסיקה חסרה" ירדה מתפריט-הניווט "פסיקה" (כפולה לטאב "פסיקה נכנסת"
ב-/precedents). הדף /missing-precedents נשאר נגיש מתוך אותו טאב.

מאושר דרך שער-העיצוב (Claude Design 07-precedents).

Invariants: G2 — הסרת תגים-מקבילים; INV-IA1 — מרכז-האישורים נשאר שער-יחיד
(התג ירד, הדף לא).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 18:49:56 +00:00

143 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { AppShell } from "@/components/app-shell";
import { LibraryListPanel } from "@/components/precedents/library-list-panel";
import { LibrarySearchPanel } from "@/components/precedents/library-search-panel";
import { HalachaReviewPanel } from "@/components/precedents/halacha-review-panel";
import { PlansReviewPanel } from "@/components/precedents/plans-review-panel";
import { LibraryStatsPanel } from "@/components/precedents/library-stats-panel";
import { useMissingPrecedents } from "@/lib/api/missing-precedents";
/**
* Precedent Library admin page.
*
* Four tabs:
* - ספרייה — browse all uploaded precedents (filters + upload + delete)
* - חיפוש סמנטי — semantic search across halachot + chunks
* - ממתין לאישור — chair review queue (PRIMARY tab; halachot from
* auto-extraction must be approved before agents can use them)
* - סטטיסטיקה — counts and coverage
*
* Distinct from /training (style corpus = Daphna's voice) and the
* per-case precedent attacher (chair-attached quotes scoped to a case).
*/
const PRECEDENT_TABS = new Set(["library", "search", "review", "plans", "incoming", "stats"]);
export default function PrecedentsPage() {
// Controlled so a deep link like /precedents?tab=review (e.g. from a pending
// halacha on a precedent-detail page, #133) lands on the right tab. Read after
// mount to avoid an SSR/CSR mismatch and the useSearchParams Suspense rule.
const [tab, setTab] = useState("library");
useEffect(() => {
// read post-mount (not lazy init) to avoid an SSR/CSR hydration mismatch
const t = new URLSearchParams(window.location.search).get("tab");
// eslint-disable-next-line react-hooks/set-state-in-effect
if (t && PRECEDENT_TABS.has(t)) setTab(t);
}, []);
return (
<AppShell>
<Tabs value={tab} onValueChange={setTab} dir="rtl">
<section className="space-y-6">
<header className="space-y-3">
<nav className="text-[0.78rem] text-ink-muted">
<Link href="/" className="hover:text-gold-deep">בית</Link>
<span aria-hidden> · </span>
<span className="text-navy">ספריית פסיקה</span>
</nav>
<div className="space-y-1">
<h1 className="text-navy mb-0">ספריית הפסיקה הסמכותית</h1>
<p className="text-ink-muted text-sm mt-1 max-w-3xl leading-relaxed">
קורפוס הפסיקה והלכות המערכת חיפוש סמנטי, תור-אישור והשלמת
פסיקה חסרה. כל קובץ עובר חילוץ הלכות אוטומטי, וההלכות ממתינות
לאישור היו&quot;ר לפני שהן זמינות לסוכני הכתיבה.
</p>
</div>
{/* tabs as a dedicated row under the header — underline-style
triggers; count pills removed (#3), the active underline alone
carries state (mockup 07). */}
<TabsList className="flex w-full justify-start gap-1 rounded-none border-0 border-b border-rule bg-transparent p-0 h-auto">
{[
{ value: "library", label: "ספרייה" },
{ value: "search", label: "חיפוש בקורפוס" },
{ value: "review", label: "תור הלכות" },
{ value: "plans", label: "תכניות" },
{ value: "incoming", label: "פסיקה נכנסת" },
{ value: "stats", label: "סטטיסטיקה" },
].map((t) => (
<TabsTrigger
key={t.value}
value={t.value}
className="rounded-none border-0 border-b-2 border-transparent bg-transparent px-4 py-2.5 -mb-px text-sm font-medium text-ink-muted shadow-none data-[state=active]:border-gold data-[state=active]:bg-transparent data-[state=active]:font-semibold data-[state=active]:text-navy data-[state=active]:shadow-none"
>
{t.label}
</TabsTrigger>
))}
</TabsList>
</header>
<TabsContent value="library" className="mt-0">
<LibraryListPanel />
</TabsContent>
<TabsContent value="search" className="mt-0">
<LibrarySearchPanel />
</TabsContent>
<TabsContent value="review" className="mt-0">
<HalachaReviewPanel />
</TabsContent>
<TabsContent value="plans" className="mt-0">
<PlansReviewPanel />
</TabsContent>
{/* "פסיקה נכנסת" — the incoming/missing-precedent queue. Kept as a
tab per the mockup; full management lives on /missing-precedents. */}
<TabsContent value="incoming" className="mt-0">
<IncomingTab />
</TabsContent>
<TabsContent value="stats" className="mt-0">
<LibraryStatsPanel />
</TabsContent>
</section>
</Tabs>
</AppShell>
);
}
/** Lightweight in-tab pointer to the dedicated missing-precedents page,
* preserving the mockup's "פסיקה נכנסת" tab without duplicating the table. */
function IncomingTab() {
const { data } = useMissingPrecedents({ status: "open", limit: 1 });
const open = data?.by_status?.open ?? 0;
return (
<div className="rounded-lg border border-rule bg-surface shadow-sm p-6 space-y-3">
<div className="flex items-baseline gap-3 flex-wrap">
<h2 className="text-navy text-lg font-semibold m-0">פסיקה נכנסת</h2>
{open ? (
<span className="inline-flex items-baseline gap-1.5 rounded-lg border border-rule bg-warn-bg px-3 py-1">
<span className="text-base font-bold text-warn tabular-nums">{open}</span>
<span className="text-[0.8rem] text-ink-soft">פתוחים</span>
</span>
) : null}
</div>
<p className="text-ink-soft text-sm leading-relaxed max-w-2xl">
פסיקה שצוטטה בכתבי-הטענות אך אינה קיימת בקורפוס. השלמתה מאפשרת
אימות-הלכה ועיגון-מקור (INV-AH).
</p>
<Link
href="/missing-precedents"
className="inline-flex items-center rounded-md bg-gold px-4 py-2 text-sm font-semibold text-white hover:bg-gold-deep"
>
לניהול פסיקה חסרה
</Link>
</div>
);
}