feat(ui): IA redesign → production · יישום נאמן של 16 הדפים הנותרים למוקאפים
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
תיקון הגישה: יישום מלא ונאמן של עיצוב-המוקאפים המאושרים (Claude Design) על כל הדפים — שינוי-הרכב אמיתי פר-מוקאפ, לא ליטוש-טוקנים. כל hook/query/mutation/טאב/ טופס/נתון נשמר (אומת: tsc נקי + בדיקת-נוכחות hooks קריטיים; 0 פונקציונליות נמחקה). דפים (← מוקאפ): - בית — לוח: KPI + "תיקים לפי סטטוס" (bars) + כרטיס-אישורים + CTA כפול. - ארכיון — filter-bar שטוח + טבלה נקייה + צ'יפי-סוג/תוצאה. - הערות יו״ר — פריסה דו-טורית + טופס-הוספה חי + כרטיסי-הערה. - ספריית-פסיקה — tabs קו-תחתון + כרטיסי-תוצאה halacha/קטע + AuthorityBadge. - דף-תקדים — באנר-meta parchment + דו-טורי + provenance pills. - פסיקה-חסרה — pill פתוחים + צ'יפי-סטטוס + CTA העלאה. - יומונים — אזור-העלאה מקווקו + כרטיסי-digest + "ממתין" כתווית פסיבית. - גרף — פאנל-צד שכבות/אנליטיקה + canvas parchment. - אימון-סגנון — פורטרט: banner + KPI + אנטומיה + ביטויי-חתימה. - מתודולוגיה — עורך-צ'קליסט + "חל על:" + canon chip. - מיומנויות/סקריפטים — טבלאות אמיתיות + צ'יפי-סטטוס. - הגדרות — sidenav דו-טורי + env-rows עם "ממתין ל-redeploy". - דף-תיק — באנר-תיק parchment + tabs + timeline + "פתח עורך החלטה". - תפעול — SectionHeaders + טבלת-שירותים + כרטיסי-שער gold-wash. - compose — באנר-תיק + SOT pill + פריסה דו-טורית + "השלמה והעברה". תיקונים שלי אחרי הסוכנים: documents-panel (הוצאת רכיב Shell מ-render — React Compiler), scripts useMemo deps. /approvals כבר נבנה מחדש נאמנה (commit קודם). בדיקות: npx tsc --noEmit ✓ · eslint ✓ (לבד מ-learning-panel:109 קיים-מראש). שימור-פונקציונליות אומת. CI Docker build = שער סופי לפני deploy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,9 +3,6 @@
|
||||
import { useState } from "react";
|
||||
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 { Input } from "@/components/ui/input";
|
||||
import {
|
||||
useMissingPrecedents,
|
||||
@@ -17,32 +14,26 @@ import { MissingPrecedentsTable } from "@/components/missing-precedents/missing-
|
||||
* Missing-precedents page (TaskMaster #35).
|
||||
*
|
||||
* Surfaces citations that party briefs invoke but which aren't yet in the
|
||||
* precedent_library. Four tabs by status; each tab uses the same table
|
||||
* component with a different filter. Drawer (sheet) opens on row click
|
||||
* with metadata + upload form that routes to internal_decision_upload
|
||||
* (ערר/בל"מ citations) or precedent_library_upload (court rulings).
|
||||
* precedent_library. A status filter (chips) narrows the table; each row uses
|
||||
* the same table component. Drawer (sheet) opens on row click with metadata +
|
||||
* upload form that routes to internal_decision_upload (ערר/בל"מ citations) or
|
||||
* precedent_library_upload (court rulings).
|
||||
*/
|
||||
function StatusBadge({ status, count }: { status: MissingPrecedentStatus; count: number }) {
|
||||
if (!count) return null;
|
||||
const variants: Record<MissingPrecedentStatus, string> = {
|
||||
open: "bg-gold-wash text-gold-deep border-gold/40",
|
||||
uploaded: "bg-rule-soft text-ink-muted border-rule",
|
||||
closed: "bg-emerald-50 text-emerald-800 border-emerald-300/60",
|
||||
irrelevant: "bg-rule-soft text-ink-muted border-rule",
|
||||
};
|
||||
return (
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={`ms-1 text-[0.65rem] ${variants[status]}`}
|
||||
>
|
||||
{count}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
type StatusFilter = MissingPrecedentStatus | "all";
|
||||
|
||||
const STATUS_CHIPS: { value: StatusFilter; label: string }[] = [
|
||||
{ value: "open", label: "פתוח" },
|
||||
{ value: "uploaded", label: "הועלה" },
|
||||
{ value: "closed", label: "נסגר" },
|
||||
{ value: "irrelevant", label: "לא-רלוונטי" },
|
||||
{ value: "all", label: "הכל" },
|
||||
];
|
||||
|
||||
export default function MissingPrecedentsPage() {
|
||||
const [caseNumber, setCaseNumber] = useState("");
|
||||
const [legalTopic, setLegalTopic] = useState("");
|
||||
const [filter, setFilter] = useState<StatusFilter>("open");
|
||||
|
||||
const counts = useMissingPrecedents({ limit: 1 });
|
||||
const byStatus = counts.data?.by_status ?? {};
|
||||
@@ -50,124 +41,129 @@ export default function MissingPrecedentsPage() {
|
||||
return (
|
||||
<AppShell>
|
||||
<section className="space-y-6">
|
||||
<header>
|
||||
<nav className="text-[0.78rem] text-ink-muted mb-1">
|
||||
<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="flex items-end justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h1 className="text-navy mb-0">פסיקה חסרה בקורפוס</h1>
|
||||
<p className="text-ink-muted text-sm mt-1 max-w-3xl">
|
||||
פסיקות שצוטטו בכתבי הטענות אך אינן עדיין בקורפוס. סוכן המחקר רושם
|
||||
פערים אוטומטית; היו"ר סוגר אותם על־ידי העלאת המסמך — ניתוב
|
||||
אוטומטי בין הקורפוס הסמכותי (פסקי דין) להחלטות ועדות ערר.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* title + inline open-count pill (mockup 09 `.open-count`) */}
|
||||
<div className="flex items-baseline gap-3.5 flex-wrap">
|
||||
<h1 className="text-navy mb-0">פסיקה חסרה בקורפוס</h1>
|
||||
{byStatus.open ? (
|
||||
<div className="inline-flex items-baseline gap-2 rounded-lg border border-rule bg-warn-bg px-4 py-2.5">
|
||||
<span className="text-2xl font-semibold text-warn leading-none tabular-nums">
|
||||
<span className="inline-flex items-baseline gap-1.5 rounded-lg border border-rule bg-warn-bg px-3.5 py-1">
|
||||
<span className="text-lg font-bold text-warn tabular-nums leading-none">
|
||||
{byStatus.open}
|
||||
</span>
|
||||
<span className="text-[0.85rem] text-ink-soft">פתוחים</span>
|
||||
</div>
|
||||
<span className="text-[0.8rem] text-ink-soft">פתוחים</span>
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<p className="text-ink-muted text-sm max-w-3xl leading-relaxed">
|
||||
פסיקה שצוטטה בכתבי-הטענות אך אינה קיימת בקורפוס. השלמתה מאפשרת
|
||||
אימות-הלכה ועיגון-מקור (INV-AH). סוכן המחקר רושם פערים אוטומטית;
|
||||
היו"ר סוגר אותם על־ידי העלאת המסמך — ניתוב אוטומטי בין הקורפוס
|
||||
הסמכותי (פסקי דין) להחלטות ועדות ערר.
|
||||
</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 space-y-5">
|
||||
{/* Shared filters */}
|
||||
<div className="flex items-end gap-3 flex-wrap">
|
||||
<div className="flex-1 min-w-[200px]">
|
||||
<label className="text-[0.78rem] text-ink-muted">תיק (מספר ערר)</label>
|
||||
<Input
|
||||
value={caseNumber}
|
||||
onChange={(e) => setCaseNumber(e.target.value)}
|
||||
placeholder="1017-03-26"
|
||||
dir="rtl"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 min-w-[200px]">
|
||||
<label className="text-[0.78rem] text-ink-muted">נושא משפטי</label>
|
||||
<Input
|
||||
value={legalTopic}
|
||||
onChange={(e) => setLegalTopic(e.target.value)}
|
||||
placeholder="זכות עמידה"
|
||||
dir="rtl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* shared filters */}
|
||||
<div className="flex items-end gap-3 flex-wrap">
|
||||
<div className="flex-1 min-w-[200px]">
|
||||
<label className="block text-[0.78rem] text-ink-muted mb-1.5">תיק (מספר ערר)</label>
|
||||
<Input
|
||||
value={caseNumber}
|
||||
onChange={(e) => setCaseNumber(e.target.value)}
|
||||
placeholder="1017-03-26"
|
||||
dir="rtl"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 min-w-[200px]">
|
||||
<label className="block text-[0.78rem] text-ink-muted mb-1.5">נושא משפטי</label>
|
||||
<Input
|
||||
value={legalTopic}
|
||||
onChange={(e) => setLegalTopic(e.target.value)}
|
||||
placeholder="זכות עמידה"
|
||||
dir="rtl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="open" dir="rtl">
|
||||
<TabsList className="bg-rule-soft/60">
|
||||
<TabsTrigger value="open">
|
||||
פתוחות
|
||||
<StatusBadge status="open" count={byStatus.open ?? 0} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="uploaded">
|
||||
הועלו
|
||||
<StatusBadge status="uploaded" count={byStatus.uploaded ?? 0} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="closed">
|
||||
נסגרו
|
||||
<StatusBadge status="closed" count={byStatus.closed ?? 0} />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="irrelevant">
|
||||
לא רלוונטי
|
||||
<StatusBadge
|
||||
status="irrelevant"
|
||||
count={byStatus.irrelevant ?? 0}
|
||||
/>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="all">הכל</TabsTrigger>
|
||||
</TabsList>
|
||||
{/* status filter chips (mockup 09 `.filters`) — active = navy filled */}
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{STATUS_CHIPS.map((c) => {
|
||||
const active = filter === c.value;
|
||||
const count =
|
||||
c.value === "all"
|
||||
? undefined
|
||||
: (byStatus[c.value as MissingPrecedentStatus] ?? 0);
|
||||
return (
|
||||
<button
|
||||
key={c.value}
|
||||
type="button"
|
||||
onClick={() => setFilter(c.value)}
|
||||
aria-pressed={active}
|
||||
className={`rounded-full border px-4 py-1.5 text-[0.82rem] transition-colors ${
|
||||
active
|
||||
? "bg-navy text-white border-navy font-semibold"
|
||||
: "bg-surface text-ink-soft border-rule font-medium hover:bg-rule-soft/50"
|
||||
}`}
|
||||
>
|
||||
{c.label}
|
||||
{count ? (
|
||||
<span className="ms-1.5 tabular-nums opacity-80">({count})</span>
|
||||
) : null}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<TabsContent value="open" className="mt-4">
|
||||
<MissingPrecedentsTable
|
||||
status="open"
|
||||
caseNumber={caseNumber.trim() || undefined}
|
||||
legalTopic={legalTopic.trim() || undefined}
|
||||
/>
|
||||
</TabsContent>
|
||||
<MissingPrecedentsTable
|
||||
status={filter === "all" ? "" : filter}
|
||||
caseNumber={caseNumber.trim() || undefined}
|
||||
legalTopic={legalTopic.trim() || undefined}
|
||||
/>
|
||||
|
||||
<TabsContent value="uploaded" className="mt-4">
|
||||
<MissingPrecedentsTable
|
||||
status="uploaded"
|
||||
caseNumber={caseNumber.trim() || undefined}
|
||||
legalTopic={legalTopic.trim() || undefined}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="closed" className="mt-4">
|
||||
<MissingPrecedentsTable
|
||||
status="closed"
|
||||
caseNumber={caseNumber.trim() || undefined}
|
||||
legalTopic={legalTopic.trim() || undefined}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="irrelevant" className="mt-4">
|
||||
<MissingPrecedentsTable
|
||||
status="irrelevant"
|
||||
caseNumber={caseNumber.trim() || undefined}
|
||||
legalTopic={legalTopic.trim() || undefined}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="all" className="mt-4">
|
||||
<MissingPrecedentsTable
|
||||
caseNumber={caseNumber.trim() || undefined}
|
||||
legalTopic={legalTopic.trim() || undefined}
|
||||
/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/* lifecycle note (mockup 09 `.lifecycle`) */}
|
||||
<div className="rounded-lg border border-rule bg-parchment px-5 py-3.5 text-[0.82rem] text-ink-muted leading-7">
|
||||
<b className="text-ink-soft">מחזור-חיים:</b>{" "}
|
||||
<LifecycleChip tone="open">פתוח</LifecycleChip> →{" "}
|
||||
<LifecycleChip tone="up">הועלה</LifecycleChip> →{" "}
|
||||
<LifecycleChip tone="closed">נסגר</LifecycleChip>. פריט נפתח אוטומטית
|
||||
בעת חילוץ ציטוט שאין לו תקדים בקורפוס; בהעלאת פסק-הדין הוא מקושר לרשומת
|
||||
הפסיקה דרך{" "}
|
||||
<code className="rounded border border-rule bg-surface px-1.5 py-0.5 text-[0.75rem] text-gold-deep" dir="ltr">
|
||||
linked_case_law_id
|
||||
</code>{" "}
|
||||
ונסגר. פריט שאינו רלוונטי מסומן{" "}
|
||||
<LifecycleChip tone="na">לא-רלוונטי</LifecycleChip> מבלי שתידרש העלאה.
|
||||
</div>
|
||||
</section>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
|
||||
type LifecycleTone = "open" | "up" | "closed" | "na";
|
||||
|
||||
function LifecycleChip({
|
||||
tone,
|
||||
children,
|
||||
}: {
|
||||
tone: LifecycleTone;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const cls: Record<LifecycleTone, string> = {
|
||||
open: "bg-warn-bg text-warn",
|
||||
up: "bg-info-bg text-info",
|
||||
closed: "bg-success-bg text-success",
|
||||
na: "bg-rule-soft text-ink-muted",
|
||||
};
|
||||
return (
|
||||
<span className={`inline-block rounded-full px-2.5 py-0.5 text-[0.72rem] font-semibold ${cls[tone]}`}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user