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:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { use, useRef, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { FileText } from "lucide-react";
|
||||
import { AppShell } from "@/components/app-shell";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -9,9 +10,51 @@ import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { SubsectionCard } from "@/components/compose/subsection-card";
|
||||
import { PrecedentsSection } from "@/components/compose/precedents-section";
|
||||
import { Markdown } from "@/components/ui/markdown";
|
||||
import { useCase } from "@/lib/api/cases";
|
||||
import { useCase, type CaseStatus } from "@/lib/api/cases";
|
||||
import { useResearchAnalysis } from "@/lib/api/research";
|
||||
import { useCasePrecedents } from "@/lib/api/precedents";
|
||||
import { APPEAL_SUBTYPES } from "@/lib/practice-area";
|
||||
import { DOC_TYPE_LABELS, type DocType } from "@/lib/doc-types";
|
||||
|
||||
// ── Case-status → Hebrew label + tone (mockup 03 status chip) ────────────────
|
||||
const STATUS_CHIP: Record<string, { label: string; cls: string }> = {
|
||||
new: { label: "חדש", cls: "bg-rule-soft text-ink-muted border-rule" },
|
||||
uploading: { label: "בהעלאה", cls: "bg-info-bg text-info border-info/30" },
|
||||
processing: { label: "בעיבוד", cls: "bg-info-bg text-info border-info/30" },
|
||||
documents_ready: { label: "מסמכים מוכנים", cls: "bg-info-bg text-info border-info/30" },
|
||||
analyst_verified: { label: "אומת ע״י אנליסט", cls: "bg-info-bg text-info border-info/30" },
|
||||
research_complete: { label: "מחקר הושלם", cls: "bg-info-bg text-info border-info/30" },
|
||||
outcome_set: { label: "תוצאה נקבעה", cls: "bg-info-bg text-info border-info/30" },
|
||||
brainstorming: { label: "סיעור-מוחות", cls: "bg-info-bg text-info border-info/30" },
|
||||
direction_approved: { label: "כיוון אושר", cls: "bg-info-bg text-info border-info/30" },
|
||||
analysis_enriched: { label: "ניתוח הועשר", cls: "bg-info-bg text-info border-info/30" },
|
||||
ready_for_writing: { label: "מוכן לכתיבה", cls: "bg-gold-wash text-gold-deep border-gold/40" },
|
||||
drafting: { label: "בעריכה", cls: "bg-info-bg text-info border-info/30" },
|
||||
qa_review: { label: "בדיקת-איכות", cls: "bg-gold-wash text-gold-deep border-gold/40" },
|
||||
drafted: { label: "טיוטה", cls: "bg-gold-wash text-gold-deep border-gold/40" },
|
||||
exported: { label: "יוצא", cls: "bg-success-bg text-success border-success/40" },
|
||||
reviewed: { label: "נסקר", cls: "bg-success-bg text-success border-success/40" },
|
||||
final: { label: "סופי", cls: "bg-success-bg text-success border-success/40" },
|
||||
};
|
||||
|
||||
function StatusChip({ status }: { status?: CaseStatus }) {
|
||||
const c = (status && STATUS_CHIP[status]) || {
|
||||
label: "בעריכה",
|
||||
cls: "bg-info-bg text-info border-info/30",
|
||||
};
|
||||
return (
|
||||
<span
|
||||
className={`rounded-full text-[0.78rem] font-semibold px-3 py-0.5 border ${c.cls}`}
|
||||
>
|
||||
{c.label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function subtypeLabel(subtype?: string | null): string | null {
|
||||
if (!subtype) return null;
|
||||
return APPEAL_SUBTYPES.find((s) => s.value === subtype)?.label ?? null;
|
||||
}
|
||||
|
||||
function ProseSection({ title, content }: { title: string; content?: string }) {
|
||||
if (!content?.trim()) return null;
|
||||
@@ -25,7 +68,8 @@ function ProseSection({ title, content }: { title: string; content?: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
function AnalysisActions({
|
||||
// ── "השלמה והעברה" rail card — DOCX export, upload, download (all real) ──────
|
||||
function FinishRail({
|
||||
caseNumber,
|
||||
hasAnalysis,
|
||||
onUploaded,
|
||||
@@ -55,7 +99,7 @@ function AnalysisActions({
|
||||
}
|
||||
setUploadMsg({
|
||||
ok: true,
|
||||
text: `הקובץ הועלה בהצלחה — ${data.sections.threshold_claims} טענות סף, ${data.sections.issues} סוגיות`,
|
||||
text: `הקובץ הועלה — ${data.sections.threshold_claims} טענות סף, ${data.sections.issues} סוגיות`,
|
||||
});
|
||||
onUploaded();
|
||||
} catch {
|
||||
@@ -67,58 +111,79 @@ function AnalysisActions({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{uploadMsg && (
|
||||
<span className={`text-xs ${uploadMsg.ok ? "text-green-700" : "text-red-600"}`}>
|
||||
{uploadMsg.text}
|
||||
</span>
|
||||
)}
|
||||
<input
|
||||
ref={fileRef}
|
||||
type="file"
|
||||
accept=".md"
|
||||
className="hidden"
|
||||
onChange={(e) => {
|
||||
const f = e.target.files?.[0];
|
||||
if (f) handleUpload(f);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={uploading}
|
||||
onClick={() => fileRef.current?.click()}
|
||||
>
|
||||
{uploading ? "מעלה..." : "העלה ניתוח מעודכן"}
|
||||
</Button>
|
||||
{hasAnalysis && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
const a = document.createElement("a");
|
||||
a.href = `/api/cases/${caseNumber}/research/analysis/download`;
|
||||
a.download = `analysis-${caseNumber}.md`;
|
||||
a.click();
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-4 py-4">
|
||||
<h3 className="text-navy text-[0.9rem] font-semibold mb-3">השלמה והעברה</h3>
|
||||
|
||||
<input
|
||||
ref={fileRef}
|
||||
type="file"
|
||||
accept=".md"
|
||||
className="hidden"
|
||||
onChange={(e) => {
|
||||
const f = e.target.files?.[0];
|
||||
if (f) handleUpload(f);
|
||||
}}
|
||||
>
|
||||
הורד ניתוח
|
||||
/>
|
||||
|
||||
<div className="space-y-2">
|
||||
{hasAnalysis && (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-center"
|
||||
onClick={() => {
|
||||
const a = document.createElement("a");
|
||||
a.href = `/api/cases/${caseNumber}/research/analysis/export-docx`;
|
||||
a.click();
|
||||
}}
|
||||
>
|
||||
ייצוא DOCX
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
className="w-full justify-center bg-gold text-white hover:bg-gold-deep"
|
||||
disabled={uploading}
|
||||
onClick={() => fileRef.current?.click()}
|
||||
>
|
||||
{uploading ? "מעלה…" : "העלאת ניתוח מעודכן"}
|
||||
</Button>
|
||||
{hasAnalysis && (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-center"
|
||||
onClick={() => {
|
||||
const a = document.createElement("a");
|
||||
a.href = `/api/cases/${caseNumber}/research/analysis/download`;
|
||||
a.download = `analysis-${caseNumber}.md`;
|
||||
a.click();
|
||||
}}
|
||||
>
|
||||
הורד ניתוח (MD)
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{uploadMsg && (
|
||||
<p className={`text-xs mt-2 ${uploadMsg.ok ? "text-success" : "text-danger"}`}>
|
||||
{uploadMsg.text}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* mockup 03: stage indicators — informational pointers, not actions */}
|
||||
<div className="mt-3 space-y-0">
|
||||
<div className="text-[0.78rem] text-ink-muted pt-2 border-t border-rule-soft">
|
||||
<b className="text-navy">הרץ למידת-קול</b> — ממתין להעלאת הסופי
|
||||
</div>
|
||||
<div className="text-[0.78rem] text-ink-muted pt-2 mt-2 border-t border-rule-soft">
|
||||
<b className="text-navy">הרץ אימות-הלכות</b> — ממתין להעלאת הסופי
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button asChild variant="ghost" className="w-full justify-center mt-3 text-ink-muted">
|
||||
<Link href={`/cases/${caseNumber}`}>חזרה לתיק</Link>
|
||||
</Button>
|
||||
)}
|
||||
{hasAnalysis && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
const a = document.createElement("a");
|
||||
a.href = `/api/cases/${caseNumber}/research/analysis/export-docx`;
|
||||
a.click();
|
||||
}}
|
||||
>
|
||||
הורד כ-DOCX
|
||||
</Button>
|
||||
)}
|
||||
<Button asChild variant="outline">
|
||||
<Link href={`/cases/${caseNumber}`}>חזרה לתיק</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -145,6 +210,18 @@ export default function ComposePage({
|
||||
}
|
||||
}
|
||||
const practiceArea = caseQuery.data?.practice_area ?? null;
|
||||
const subtype = subtypeLabel(caseQuery.data?.appeal_subtype);
|
||||
const parties = (() => {
|
||||
const c = caseQuery.data;
|
||||
if (!c) return null;
|
||||
const app = c.appellants?.length ? c.appellants.join(", ") : null;
|
||||
const resp = c.respondents?.length ? c.respondents.join(", ") : null;
|
||||
const out: string[] = [];
|
||||
if (app) out.push(`עוררים: ${app}`);
|
||||
if (resp) out.push(`משיבה: ${resp}`);
|
||||
return out.length ? out.join(" · ") : c.title || null;
|
||||
})();
|
||||
const documents = caseQuery.data?.documents ?? [];
|
||||
|
||||
const isNotFound =
|
||||
analysis.error instanceof Error &&
|
||||
@@ -152,121 +229,84 @@ export default function ComposePage({
|
||||
|
||||
return (
|
||||
<AppShell>
|
||||
<section className="space-y-6">
|
||||
{/* Header strip */}
|
||||
<div className="flex items-center justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<nav className="text-[0.78rem] text-ink-muted flex items-center gap-2 mb-1">
|
||||
<Link href="/" className="hover:text-gold-deep">בית</Link>
|
||||
<span aria-hidden>·</span>
|
||||
<Link
|
||||
href={`/cases/${caseNumber}`}
|
||||
className="hover:text-gold-deep"
|
||||
>
|
||||
ערר {caseNumber}
|
||||
</Link>
|
||||
<span aria-hidden>·</span>
|
||||
<span className="text-navy">עורך החלטה</span>
|
||||
</nav>
|
||||
<h1 className="text-navy mb-0">ניתוח משפטי וכתיבת עמדה</h1>
|
||||
{caseQuery.data?.title && (
|
||||
<p className="text-ink-muted text-sm mt-1 max-w-2xl">
|
||||
{caseQuery.data.title}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<AnalysisActions caseNumber={caseNumber} hasAnalysis={!!analysis.data} onUploaded={() => analysis.refetch()} />
|
||||
{/* ── Case header band (mockup 03) — parchment strip, full-bleed to the
|
||||
AppShell <main> edges (which pads px-10 py-10) ── */}
|
||||
<div className="-mx-10 -mt-10 mb-6 border-b border-rule bg-parchment px-10 py-5">
|
||||
<nav className="text-[0.78rem] text-ink-muted flex items-center gap-2 mb-2">
|
||||
<Link href="/" className="hover:text-gold-deep">בית</Link>
|
||||
<span aria-hidden>·</span>
|
||||
<Link href={`/cases/${caseNumber}`} className="hover:text-gold-deep">
|
||||
ערר {caseNumber}
|
||||
</Link>
|
||||
<span aria-hidden>·</span>
|
||||
<span className="text-navy">עורך החלטה</span>
|
||||
</nav>
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
<h1 className="text-navy text-2xl font-bold mb-0">ערר {caseNumber}</h1>
|
||||
<StatusChip status={caseQuery.data?.status} />
|
||||
{subtype && (
|
||||
<span className="rounded-full text-[0.78rem] font-semibold px-3 py-0.5 border border-rule bg-gold-wash text-gold-deep">
|
||||
{subtype}
|
||||
</span>
|
||||
)}
|
||||
{/* INV-G10: source-of-truth pill — the blocks are the canonical text */}
|
||||
<span className="ms-auto rounded-lg text-[0.8rem] font-semibold px-3.5 py-1.5 border border-gold bg-gold-wash text-gold-deep">
|
||||
מקור-אמת: בלוקים
|
||||
</span>
|
||||
</div>
|
||||
{parties && <p className="text-ink-soft text-sm mt-2">{parties}</p>}
|
||||
</div>
|
||||
|
||||
<div className="h-[2px] bg-gradient-to-l from-transparent via-gold to-transparent" />
|
||||
|
||||
{analysis.isPending ? (
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-6 py-5 space-y-3">
|
||||
<Skeleton className="h-6 w-48" />
|
||||
<Skeleton className="h-4 w-96" />
|
||||
<Skeleton className="h-4 w-80" />
|
||||
<Skeleton className="h-32 w-full" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : isNotFound ? (
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-6 py-12 text-center space-y-3">
|
||||
<div className="text-gold text-3xl" aria-hidden>❦</div>
|
||||
<h2 className="text-navy text-lg mb-0">
|
||||
טרם בוצע ניתוח משפטי לתיק זה
|
||||
</h2>
|
||||
<p className="text-ink-muted text-sm max-w-md mx-auto">
|
||||
לאחר שקובץ <code>analysis-and-research.md</code> ייווצר, תוכלי
|
||||
לערוך כאן את עמדת הוועדה לכל טענת סף וסוגיה.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : analysis.error ? (
|
||||
<Card className="bg-danger-bg border-danger/40">
|
||||
<CardContent className="px-6 py-5 text-center">
|
||||
<p className="text-danger">{analysis.error.message}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : analysis.data ? (
|
||||
<div className="space-y-6">
|
||||
{/* Case-level general precedents */}
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-6 py-5">
|
||||
<h2 className="text-navy text-xl mb-1">פסיקה כללית לדיון</h2>
|
||||
<p className="text-[0.78rem] text-ink-muted mb-4">
|
||||
ציטוטים התומכים בעמדה באופן רוחבי — ישולבו בפתיחת בלוק י (דיון).
|
||||
</p>
|
||||
<PrecedentsSection
|
||||
caseNumber={caseNumber}
|
||||
sectionId={null}
|
||||
precedents={caseLevelPrecedents}
|
||||
practiceArea={practiceArea}
|
||||
emptyHelperText="עדיין לא צורפה פסיקה כללית לתיק"
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Threshold claims */}
|
||||
{analysis.data.threshold_claims &&
|
||||
analysis.data.threshold_claims.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-navy text-xl mb-0">טענות סף</h2>
|
||||
<span className="text-[0.72rem] rounded-full bg-gold-wash text-gold-deep px-2 py-0.5 border border-gold/40 tabular-nums">
|
||||
{analysis.data.threshold_claims.length}
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{analysis.data.threshold_claims.map((tc) => (
|
||||
<SubsectionCard
|
||||
key={tc.id}
|
||||
caseNumber={caseNumber}
|
||||
item={tc}
|
||||
precedents={precedentsBySection.get(tc.id) ?? []}
|
||||
practiceArea={practiceArea}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Issues */}
|
||||
{analysis.data.issues && analysis.data.issues.length > 0 && (
|
||||
{analysis.isPending ? (
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-6 py-5 space-y-3">
|
||||
<Skeleton className="h-6 w-48" />
|
||||
<Skeleton className="h-4 w-96" />
|
||||
<Skeleton className="h-4 w-80" />
|
||||
<Skeleton className="h-32 w-full" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : isNotFound ? (
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-6 py-12 text-center space-y-3">
|
||||
<div className="text-gold text-3xl" aria-hidden>❦</div>
|
||||
<h2 className="text-navy text-lg mb-0">
|
||||
טרם בוצע ניתוח משפטי לתיק זה
|
||||
</h2>
|
||||
<p className="text-ink-muted text-sm max-w-md mx-auto">
|
||||
לאחר שקובץ <code>analysis-and-research.md</code> ייווצר, תוכלי
|
||||
לערוך כאן את עמדת הוועדה לכל טענת סף וסוגיה.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : analysis.error ? (
|
||||
<Card className="bg-danger-bg border-danger/40">
|
||||
<CardContent className="px-6 py-5 text-center">
|
||||
<p className="text-danger">{analysis.error.message}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : analysis.data ? (
|
||||
/* ── Two-column workspace: main editor list + 320px side rail ──────── */
|
||||
<div className="grid gap-6 lg:grid-cols-[1fr_320px] items-start">
|
||||
{/* MAIN — the block/subsection editor list */}
|
||||
<div className="space-y-6 min-w-0">
|
||||
{/* Threshold claims */}
|
||||
{analysis.data.threshold_claims &&
|
||||
analysis.data.threshold_claims.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-navy text-xl mb-0">סוגיות להכרעה</h2>
|
||||
<h2 className="text-navy text-lg font-semibold mb-0">טענות סף</h2>
|
||||
<span className="text-[0.72rem] rounded-full bg-gold-wash text-gold-deep px-2 py-0.5 border border-gold/40 tabular-nums">
|
||||
{analysis.data.issues.length}
|
||||
{analysis.data.threshold_claims.length}
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{analysis.data.issues.map((iss) => (
|
||||
<div className="space-y-2.5">
|
||||
{analysis.data.threshold_claims.map((tc) => (
|
||||
<SubsectionCard
|
||||
key={iss.id}
|
||||
key={tc.id}
|
||||
caseNumber={caseNumber}
|
||||
item={iss}
|
||||
precedents={precedentsBySection.get(iss.id) ?? []}
|
||||
item={tc}
|
||||
precedents={precedentsBySection.get(tc.id) ?? []}
|
||||
practiceArea={practiceArea}
|
||||
/>
|
||||
))}
|
||||
@@ -274,8 +314,31 @@ export default function ComposePage({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(!analysis.data.threshold_claims?.length &&
|
||||
!analysis.data.issues?.length) && (
|
||||
{/* Issues */}
|
||||
{analysis.data.issues && analysis.data.issues.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-navy text-lg font-semibold mb-0">סוגיות להכרעה</h2>
|
||||
<span className="text-[0.72rem] rounded-full bg-gold-wash text-gold-deep px-2 py-0.5 border border-gold/40 tabular-nums">
|
||||
{analysis.data.issues.length}
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-2.5">
|
||||
{analysis.data.issues.map((iss) => (
|
||||
<SubsectionCard
|
||||
key={iss.id}
|
||||
caseNumber={caseNumber}
|
||||
item={iss}
|
||||
precedents={precedentsBySection.get(iss.id) ?? []}
|
||||
practiceArea={practiceArea}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!analysis.data.threshold_claims?.length &&
|
||||
!analysis.data.issues?.length && (
|
||||
<Card className="bg-surface border-rule">
|
||||
<CardContent className="px-6 py-10 text-center text-ink-muted">
|
||||
לא נמצאו טענות סף או סוגיות בניתוח זה.
|
||||
@@ -283,42 +346,82 @@ export default function ComposePage({
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Background prose — moved below the issues so it reads as
|
||||
supporting context after the chair has seen the main
|
||||
decision points, not as a wall of text beside them. */}
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-6 py-5 space-y-5">
|
||||
<h2 className="text-navy text-xl mb-0">רקע לניתוח</h2>
|
||||
<ProseSection
|
||||
title="צד מיוצג"
|
||||
content={analysis.data.represented_party}
|
||||
/>
|
||||
<ProseSection
|
||||
title="רקע דיוני"
|
||||
content={analysis.data.procedural_background}
|
||||
/>
|
||||
<ProseSection
|
||||
title="עובדות מוסכמות"
|
||||
content={analysis.data.agreed_facts}
|
||||
/>
|
||||
<ProseSection
|
||||
title="עובדות במחלוקת"
|
||||
content={analysis.data.disputed_facts}
|
||||
/>
|
||||
{/* Background prose — supporting context after the decision points */}
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-6 py-5 space-y-5">
|
||||
<h2 className="text-navy text-lg font-semibold mb-0">רקע לניתוח</h2>
|
||||
<ProseSection title="צד מיוצג" content={analysis.data.represented_party} />
|
||||
<ProseSection title="רקע דיוני" content={analysis.data.procedural_background} />
|
||||
<ProseSection title="עובדות מוסכמות" content={analysis.data.agreed_facts} />
|
||||
<ProseSection title="עובדות במחלוקת" content={analysis.data.disputed_facts} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{analysis.data.conclusions?.trim() && (
|
||||
<Card className="bg-gold-wash border-gold/40 shadow-sm">
|
||||
<CardContent className="px-6 py-5 space-y-3">
|
||||
<h2 className="text-gold-deep text-lg font-semibold mb-0">מסקנות</h2>
|
||||
<Markdown content={analysis.data.conclusions.trim()} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{analysis.data.conclusions?.trim() && (
|
||||
<Card className="bg-gold-wash border-gold/40 shadow-sm">
|
||||
<CardContent className="px-6 py-5 space-y-3">
|
||||
<h2 className="text-gold-deep text-xl mb-0">מסקנות</h2>
|
||||
<Markdown content={analysis.data.conclusions.trim()} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
{/* SIDE RAIL — documents · attached precedents · finish-and-transfer */}
|
||||
<aside className="space-y-4 lg:sticky lg:top-4">
|
||||
{/* מסמכי התיק */}
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-4 py-4">
|
||||
<h3 className="text-navy text-[0.9rem] font-semibold mb-2">מסמכי התיק</h3>
|
||||
{documents.length === 0 ? (
|
||||
<p className="text-[0.78rem] text-ink-muted">אין מסמכים מצורפים</p>
|
||||
) : (
|
||||
<ul>
|
||||
{documents.map((d) => (
|
||||
<li
|
||||
key={d.id}
|
||||
className="flex items-center gap-2 text-[0.82rem] text-ink-soft py-1.5 border-b border-rule-soft last:border-0"
|
||||
>
|
||||
<FileText className="w-3.5 h-3.5 text-ink-muted shrink-0" aria-hidden />
|
||||
<span className="truncate flex-1" title={d.title}>
|
||||
{d.title || "מסמך"}
|
||||
</span>
|
||||
<span className="rounded bg-rule-soft text-ink-muted text-[0.68rem] px-1.5 py-0.5 shrink-0 whitespace-nowrap">
|
||||
{DOC_TYPE_LABELS[d.doc_type as DocType] ?? d.doc_type}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* פסיקה מצורפת (case-level) */}
|
||||
<Card className="bg-surface border-rule shadow-sm">
|
||||
<CardContent className="px-4 py-4">
|
||||
<h3 className="text-navy text-[0.9rem] font-semibold mb-1">פסיקה מצורפת</h3>
|
||||
<p className="text-[0.72rem] text-ink-muted mb-3">
|
||||
ציטוטים התומכים בעמדה באופן רוחבי — ישולבו בפתיחת בלוק י (דיון).
|
||||
</p>
|
||||
<PrecedentsSection
|
||||
caseNumber={caseNumber}
|
||||
sectionId={null}
|
||||
precedents={caseLevelPrecedents}
|
||||
practiceArea={practiceArea}
|
||||
emptyHelperText="עדיין לא צורפה פסיקה כללית לתיק"
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* השלמה והעברה */}
|
||||
<FinishRail
|
||||
caseNumber={caseNumber}
|
||||
hasAnalysis={!!analysis.data}
|
||||
onUploaded={() => analysis.refetch()}
|
||||
/>
|
||||
</aside>
|
||||
</div>
|
||||
) : null}
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user