Files
legal-ai/web-ui/src/components/precedents/library-stats-panel.tsx
Chaim 7ee90dce31
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m27s
feat: external precedent library with auto halacha extraction
Adds a third corpus of legal authority distinct from style_corpus
(Daphna's prior decisions for voice) and case_precedents (chair-attached
quotes per case). The new corpus holds chair-uploaded court rulings and
other appeals committee decisions, with binding rules (הלכות) extracted
automatically and queued for chair approval.

Pipeline (web/app.py + services/precedent_library.py):
file → extract → chunk → Voyage embed → halacha_extractor → store +
publish progress over the existing Redis SSE channel.

Schema V7 (services/db.py): extends case_law with source_kind +
extraction status fields under a CHECK constraint pinning practice_area
to the three appeals committee domains (rishuy_uvniya, betterment_levy,
compensation_197). New precedent_chunks (vector(1024)) and halachot
tables (vector(1024) over rule_statement, IVFFlat indexes, gin on
practice_areas/subject_tags). Halachot start as pending_review; only
approved/published rows are visible to search_precedent_library.

Agents: legal-writer, legal-researcher, legal-analyst, legal-ceo,
legal-qa get search_precedent_library. legal-writer prompt explains
the three-corpus distinction and CREAC use; legal-qa now verifies that
every cited halacha resolves to an approved row in the corpus.

UI: /precedents page with four tabs — library / semantic search /
pending review (J/K nav, A/R/E shortcuts, badge count) / stats.
Reuses the existing upload-sheet progress + SSE pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 08:38:18 +00:00

90 lines
3.3 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 { Skeleton } from "@/components/ui/skeleton";
import { useLibraryStats } from "@/lib/api/precedent-library";
import { practiceAreaLabel } from "./practice-area";
function StatCard({ label, value, accent }: { label: string; value: number | string; accent?: boolean }) {
return (
<div
className={`
rounded-lg border p-5 bg-surface shadow-sm
${accent ? "border-gold bg-gold-wash/40" : "border-rule"}
`}
>
<div className="text-[0.78rem] text-ink-muted mb-1">{label}</div>
<div className={`text-3xl font-bold tabular-nums ${accent ? "text-gold-deep" : "text-navy"}`}>
{value}
</div>
</div>
);
}
export function LibraryStatsPanel() {
const { data, isPending, error } = useLibraryStats();
if (error) {
return (
<div className="rounded bg-danger-bg border border-danger/40 px-6 py-5 text-danger text-center">
{error.message}
</div>
);
}
if (isPending || !data) {
return (
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[...Array(4)].map((_, i) => <Skeleton key={i} className="h-28 w-full" />)}
</div>
);
}
return (
<div className="space-y-6">
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
<StatCard label="פסיקה בקורפוס" value={data.precedents_total} />
<StatCard label="הלכות בסך הכל" value={data.halachot_total} />
<StatCard
label="ממתינות לאישור" value={data.halachot_pending}
accent={data.halachot_pending > 0}
/>
<StatCard label="מאושרות (זמינות לסוכנים)" value={data.halachot_approved} />
</div>
<div className="grid md:grid-cols-2 gap-4">
<div className="rounded-lg border border-rule bg-surface p-5">
<h3 className="text-navy font-semibold mb-3">פילוח לפי תחום</h3>
{data.by_practice_area.length === 0 ? (
<p className="text-ink-muted text-sm">אין נתונים</p>
) : (
<ul className="space-y-2">
{data.by_practice_area.map((row) => (
<li key={row.practice_area || "—"} className="flex items-center gap-2 text-sm">
<span className="text-ink">{practiceAreaLabel(row.practice_area || null)}</span>
<span className="ms-auto tabular-nums text-navy font-semibold">{row.count}</span>
</li>
))}
</ul>
)}
</div>
<div className="rounded-lg border border-rule bg-surface p-5">
<h3 className="text-navy font-semibold mb-3">פילוח לפי רמת תקדים</h3>
{data.by_precedent_level.length === 0 ? (
<p className="text-ink-muted text-sm">אין נתונים</p>
) : (
<ul className="space-y-2">
{data.by_precedent_level.map((row) => (
<li key={row.precedent_level || "—"} className="flex items-center gap-2 text-sm">
<span className="text-ink">{row.precedent_level || "—"}</span>
<span className="ms-auto tabular-nums text-navy font-semibold">{row.count}</span>
</li>
))}
</ul>
)}
</div>
</div>
</div>
);
}