feat(digests): Phase 2 — API endpoints + /digests UI (X12)
משטחי-משתמש לקורפוס היומונים: endpoints ב-FastAPI + דף UI נפרד /digests (לדפדוף, חיפוש, העלאה, וקישור לפסק המקורי). היומון נשאר מקור-משני המצביע על הפסק — אינו מצוטט בהחלטה (INV-DIG1) ואינו מחלץ הלכות (INV-DIG2). Backend (container-safe + local split): - digest_library: פוצל ל-create_pending_digest (CONTAINER-SAFE: stage+ extract_text+create row 'pending', בלי LLM) ↔ enrich_digest/ process_pending_digests (local: LLM+embed+autolink). ingest_digest מאחד. - db.list_pending_digests; MCP digest_process_pending (tool+server) — חלופה ל-batch script לריקון התור. - web/app.py: 10 endpoints /api/digests/* (upload/list/search/queue-pending/ get/patch/delete/link/relink/unlink). upload=INSERT-only pending (ה-LLM רץ מקומית — claude_session local-only). כולם מחזירים dict בדפוס precedent. Frontend (Next 16, ללא api:types — hooks עם טיפוסים hand-written כמו precedent-library.ts): - lib/api/digests.ts — hooks (useDigests/useDigestSearch/useDigestPending/ useUploadDigest/useLink/Relink/Unlink/Delete/Update). - דף /digests נפרד (לא כרטיסייה ב-/precedents — לשמור גבול סמכותי/משני, INV-DIG1): טאבים יומונים/חיפוש + DigestCard (badge קישור-לפסק) + DigestUploadDialog + pending badge. nav + header-context. אומת: backend round-trip מלא (create_pending→list_pending→process_pending→ search→restore); web-ui מתקמפל (webpack/tsc נקי, route /digests נוצר). הערה: build דיפולטי (turbopack) נכשל ב-worktree עקב symlink ל-node_modules — ב-CI/Docker (node_modules אמיתי) עובד; אומת עם --webpack. Invariants: מקיים INV-DIG1/2 (upload לא מחלץ הלכות, UI מציג "מצביע לא מצוטט"), INV-DIG3 (link/relink/queue). G4 (אין בליעה — שגיאות→toast/HTTP), G2 (מסלול נפרד, לא מקביל). X6 (חוזה UI↔API — endpoints בדפוס precedent; hooks hand-written כמו שאר ה-domain modules). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
82
web-ui/src/app/digests/page.tsx
Normal file
82
web-ui/src/app/digests/page.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { AppShell } from "@/components/app-shell";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { DigestListPanel } from "@/components/digests/digest-list-panel";
|
||||
import { DigestSearchPanel } from "@/components/digests/digest-search-panel";
|
||||
import { useDigestPending } from "@/lib/api/digests";
|
||||
|
||||
/**
|
||||
* Digests radar page (X12) — a SECONDARY discovery layer ABOVE the citation
|
||||
* corpora. Deliberately a SEPARATE page from /precedents to keep the
|
||||
* authoritative/secondary boundary visible: a digest POINTS at a ruling, it is
|
||||
* never cited in a decision (INV-DIG1) and never extracts halachot (INV-DIG2).
|
||||
*
|
||||
* Two tabs:
|
||||
* - יומונים — browse + upload + link to the underlying ruling
|
||||
* - חיפוש — semantic radar search
|
||||
*/
|
||||
|
||||
function PendingBadge() {
|
||||
const { data } = useDigestPending();
|
||||
const n = data?.count ?? 0;
|
||||
if (!n) return null;
|
||||
return (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="ms-1 bg-gold-wash text-gold-deep border-gold/40 text-[0.65rem]"
|
||||
>
|
||||
{n} ממתינים
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DigestsPage() {
|
||||
return (
|
||||
<AppShell>
|
||||
<section className="space-y-6">
|
||||
<header>
|
||||
<nav className="text-[0.78rem] text-ink-muted mb-1">
|
||||
<Link href="/" className="hover:text-gold-deep">בית</Link>
|
||||
<span aria-hidden> · </span>
|
||||
<span className="text-navy">יומונים</span>
|
||||
</nav>
|
||||
<h1 className="text-navy mb-0">יומונים — רדאר פסיקה</h1>
|
||||
<p className="text-ink-muted text-sm mt-1 max-w-3xl">
|
||||
סיכומי "כל יום" (עפר טויסטר) של פסקי דין והחלטות עדכניים.
|
||||
שכבת-גילוי בלבד: כל יומון <strong>מצביע</strong> על פסק הדין המקורי —
|
||||
הוא אינו מצוטט בהחלטה ואינו מחלץ הלכות. כשהפסק רלוונטי, מעלים אותו
|
||||
לספריית הפסיקה ומצטטים משם.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div className="h-[2px] bg-gradient-to-l from-transparent via-gold to-transparent" />
|
||||
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-6 py-5">
|
||||
<Tabs defaultValue="list" dir="rtl">
|
||||
<TabsList className="bg-rule-soft/60">
|
||||
<TabsTrigger value="list">
|
||||
יומונים
|
||||
<PendingBadge />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="search">חיפוש</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="list" className="mt-5">
|
||||
<DigestListPanel />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="search" className="mt-5">
|
||||
<DigestSearchPanel />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</section>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user