feat(cases): תצוגת "פסיקה שצוטטה בהחלטה" בעמוד-התיק + שחזור חיווט-הרמס
UI שביקש חיים: בכניסה להחלטה רואים את הפסיקה שצוטטה בתוכה — מקושרת לספרייה
(קליק → /precedents/[id]) מול חסרה (סומנה אוטומטית להעלאה).
- web/app.py: GET /api/cases/{case}/citations — מהשורה internal_committee של
ההחלטה ב-case_law → precedent_internal_citations: linked (join case_law) +
missing (unresolved + האם flagged ב-missing_precedents).
- web-ui: lib/api/citations.ts (hook) + CitationsSection ב-drafts-panel
(מוצג כשההחלטה בספרייה). מקושרת=ירוק/קליק, חסרה=ענבר "סומנה להעלאה".
- scripts/curator_apply_pipeline_branch.py: מקור-אמת לחיווט-הכפתורים של הרמס
(ה-prompt חי רק ב-Paperclip DB). מקדים branch שמריץ את pipeline-ה-final
ל-wake reason final_learning_*/final_halacha_* (HOME/DOTENV/DATA_DIR מוחלטים
→ מפתחות DeepSeek+Gemini + DATA_DIR נפתרים נכון). idempotent, שני הסוכנים.
כבר הוחל ב-DB; הסקריפט לשחזור אחרי reset.
אומת: py_compile ✓ · tsc ✓ · החיווט אומת חי על 8126 (deepseek+gemini, dedup,
✓ pipeline הושלם). G2 (יכולת חסרה) · INV-LRN1/G10 נשמרים.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,7 @@ import {
|
||||
BLOCK_LABELS,
|
||||
type FeedbackCategory,
|
||||
} from "@/lib/api/feedback";
|
||||
import { useCaseCitations } from "@/lib/api/citations";
|
||||
import type { CaseStatus } from "@/lib/api/cases";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
@@ -47,6 +48,8 @@ import {
|
||||
Brain,
|
||||
Scale,
|
||||
Stamp,
|
||||
Link2,
|
||||
AlertTriangle,
|
||||
} from "lucide-react";
|
||||
|
||||
/* Statuses at which a draft is considered ready */
|
||||
@@ -336,6 +339,9 @@ export function DraftsPanel({
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* ── Precedents cited inside the signed decision ── */}
|
||||
<CitationsSection caseNumber={caseNumber} />
|
||||
|
||||
{/* ── Exports list ── */}
|
||||
<section>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
@@ -587,6 +593,69 @@ export function DraftsPanel({
|
||||
);
|
||||
}
|
||||
|
||||
/* ── Precedents cited inside the decision ──────────── */
|
||||
|
||||
function CitationsSection({ caseNumber }: { caseNumber: string }) {
|
||||
const { data, isLoading } = useCaseCitations(caseNumber);
|
||||
|
||||
// Nothing to show until the signed decision is in the precedent library.
|
||||
if (isLoading || !data?.in_library) return null;
|
||||
if (!data.linked.length && !data.missing.length) return null;
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Scale className="w-4 h-4 text-navy" />
|
||||
<h3 className="text-navy text-base">פסיקה שצוטטה בהחלטה</h3>
|
||||
<Badge variant="outline" className="text-[0.65rem]">
|
||||
{data.linked.length} בספרייה · {data.missing.length} חסרות
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-rule overflow-hidden divide-y divide-rule">
|
||||
{data.linked.map((c) => (
|
||||
<a
|
||||
key={`l-${c.citation}`}
|
||||
href={`/precedents/${c.cited_id}`}
|
||||
className="flex items-center gap-2 px-4 py-2.5 text-sm hover:bg-rule-soft/20"
|
||||
>
|
||||
<Link2 className="w-3.5 h-3.5 text-success shrink-0" />
|
||||
<span className="font-medium text-ink">{c.citation}</span>
|
||||
{c.case_name && (
|
||||
<span className="text-ink-muted truncate">— {c.case_name}</span>
|
||||
)}
|
||||
<Badge className="ms-auto bg-success-bg text-success border-success/40 text-[0.65rem] shrink-0">
|
||||
בספרייה
|
||||
</Badge>
|
||||
</a>
|
||||
))}
|
||||
{data.missing.map((c) => (
|
||||
<div
|
||||
key={`m-${c.citation}`}
|
||||
className="flex items-center gap-2 px-4 py-2.5 text-sm"
|
||||
>
|
||||
<AlertTriangle className="w-3.5 h-3.5 text-gold-deep shrink-0" />
|
||||
<span className="font-medium text-ink">{c.citation}</span>
|
||||
<Badge
|
||||
className={`ms-auto text-[0.65rem] shrink-0 border ${
|
||||
c.flagged
|
||||
? "bg-gold-wash text-gold-deep border-gold/40"
|
||||
: "bg-rule-soft text-ink-soft border-rule"
|
||||
}`}
|
||||
>
|
||||
{c.flagged ? "חסרה — סומנה להעלאה" : "חסרה בספרייה"}
|
||||
</Badge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-[0.7rem] text-ink-muted mt-2">
|
||||
פסיקה מקושרת מחזקת את ההלכות שלה (corroboration); חסרות מסומנות אוטומטית
|
||||
להעלאה לספרייה.
|
||||
</p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── New feedback dialog (case-scoped) ─────────────── */
|
||||
|
||||
function NewCaseFeedbackDialog({ caseNumber }: { caseNumber: string }) {
|
||||
|
||||
37
web-ui/src/lib/api/citations.ts
Normal file
37
web-ui/src/lib/api/citations.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Citations domain — precedents CITED inside a case's signed decision.
|
||||
* Powers the case-page "פסיקה שצוטטה בהחלטה" panel.
|
||||
*/
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { apiRequest } from "./client";
|
||||
|
||||
export type LinkedCitation = {
|
||||
citation: string;
|
||||
cited_id: string;
|
||||
case_name: string;
|
||||
court: string;
|
||||
precedent_level: string;
|
||||
};
|
||||
|
||||
export type MissingCitation = {
|
||||
citation: string;
|
||||
flagged: boolean;
|
||||
};
|
||||
|
||||
export type CaseCitations = {
|
||||
in_library: boolean;
|
||||
case_law_id?: string;
|
||||
linked: LinkedCitation[];
|
||||
missing: MissingCitation[];
|
||||
};
|
||||
|
||||
export function useCaseCitations(caseNumber: string | undefined) {
|
||||
return useQuery({
|
||||
queryKey: ["case-citations", caseNumber ?? ""],
|
||||
queryFn: ({ signal }) =>
|
||||
apiRequest<CaseCitations>(`/api/cases/${caseNumber}/citations`, { signal }),
|
||||
enabled: Boolean(caseNumber),
|
||||
staleTime: 10_000,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user