feat(case-ui): merge /compose editor into case tabs (X17 #3)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s

Folds the standalone /compose decision-editor route into the main case page so
there is no more "sub-page" (mockup 18f), with the final 6-tab workflow order:
סקירה · טיעונים ועמדות · אימות פסיקה · ההחלטה · טיוטות והערות · סוכנים.

- New PositionsPanel — the chair's editing surface (threshold claims + issues
  SubsectionCards + ChairEditor + background prose + case precedents + finish
  rail), extracted verbatim from the former /compose "עמדות וטענות" tab. Drops
  only the doc-strip (the overview DocumentsPanel is the sole owner — no dup).
- "טיעונים ועמדות" tab = PositionsPanel above + the aggregated by-party
  LegalArgumentsPanel in a collapsible below.
- "אימות פסיקה" promoted to its own top-level tab (CitationVerificationPanel,
  was /compose tab 3); placed BEFORE "ההחלטה" — verify citations, then write
  (INV-AH: the writer cites only verified precedents).
- "ההחלטה" stays the single 12-block editor (DecisionBlocksPanel) — no longer
  duplicated between /compose and the case page.
- Tabs are now controlled; the "פתח עורך החלטה" CTA switches to the ההחלטה tab
  instead of navigating to the deleted route.
- /compose route deleted; header-context /compose special-case removed.

No duplication remains (G2): blocks editor single, docs single (overview),
citation-verify single. Design-gate: mockup 18f approved in Claude Design X17.
tsc --noEmit + eslint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-30 18:52:07 +00:00
parent ff07e0bc98
commit 4d7e623607
4 changed files with 407 additions and 515 deletions

View File

@@ -1,10 +1,13 @@
"use client";
import { use } from "react";
import { use, 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 {
Accordion, AccordionContent, AccordionItem, AccordionTrigger,
} from "@/components/ui/accordion";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { CaseHeader } from "@/components/cases/case-header";
@@ -13,13 +16,15 @@ import { DocumentsPanel } from "@/components/cases/documents-panel";
import { DraftsPanel } from "@/components/cases/drafts-panel";
import { DecisionBlocksPanel } from "@/components/cases/decision-blocks-panel";
import { LegalArgumentsPanel } from "@/components/cases/legal-arguments-panel";
import { PositionsPanel } from "@/components/cases/positions-panel";
import { CitationVerificationPanel } from "@/components/compose/citation-verification-panel";
import { AgentActivityFeed } from "@/components/cases/agent-activity-feed";
import { AgentActivityPreview } from "@/components/cases/agent-activity-preview";
import { UploadSheet } from "@/components/documents/upload-sheet";
import { useCase, useStartWorkflow } from "@/lib/api/cases";
import { toast } from "sonner";
import {
Play, Loader2, LayoutGrid, Scale, FileText, MessageSquare, Users,
Play, Loader2, LayoutGrid, Scale, BadgeCheck, FileText, MessageSquare, Users,
type LucideIcon,
} from "lucide-react";
@@ -33,6 +38,7 @@ export default function CaseDetailPage({
params: Promise<{ caseNumber: string }>;
}) {
const { caseNumber } = use(params);
const [tab, setTab] = useState("overview");
const { data, isPending, error, refetch } = useCase(caseNumber);
const startWorkflow = useStartWorkflow(caseNumber);
const canStartWorkflow = data?.status === "new" || data?.status === "documents_ready";
@@ -62,9 +68,12 @@ export default function CaseDetailPage({
);
}
// Workflow order (X17): intake → arguments+positions → verify citations →
// write decision → drafts/final → agents (monitoring).
const tabDefs: [string, string, LucideIcon][] = [
["overview", "סקירה", LayoutGrid],
["arguments", "טיעונים", Scale],
["arguments", "טיעונים ועמדות", Scale],
["verify", "אימות פסיקה", BadgeCheck],
["decision", "ההחלטה", FileText],
["drafts", "טיוטות והערות", MessageSquare],
["agents", "סוכנים", Users],
@@ -93,8 +102,11 @@ export default function CaseDetailPage({
<>
{data && <CaseEditDialog data={data} />}
<UploadSheet caseNumber={caseNumber} />
<Button asChild className="bg-gold text-white hover:bg-gold-deep border-transparent">
<Link href={`/cases/${caseNumber}/compose`}>פתח עורך החלטה</Link>
<Button
className="bg-gold text-white hover:bg-gold-deep border-transparent"
onClick={() => setTab("decision")}
>
פתח עורך החלטה
</Button>
{canStartWorkflow && (
<Button
@@ -121,7 +133,7 @@ export default function CaseDetailPage({
return (
<AppShell>
<Tabs defaultValue="overview" dir="rtl">
<Tabs value={tab} onValueChange={setTab} dir="rtl">
{/* parchment band — header (title/chips/parties/actions) + tab strip */}
{isPending ? (
<div className="-mx-10 -mt-10 mb-2 bg-parchment border-b border-rule px-10 pt-6 pb-4 space-y-3">
@@ -144,10 +156,43 @@ export default function CaseDetailPage({
</div>
</TabsContent>
<TabsContent value="arguments" className="mt-0">
{/* טיעונים ועמדות — chair positions (editing) over the analyst's
issues, with the aggregated by-party arguments collapsible below
(merged from the deleted /compose editor, mockup 18f). */}
<TabsContent value="arguments" className="mt-0 space-y-4">
<Card className="bg-surface border-rule shadow-sm">
<CardContent className="px-6 py-5">
<LegalArgumentsPanel caseNumber={caseNumber} />
<PositionsPanel caseNumber={caseNumber} />
</CardContent>
</Card>
<Accordion type="single" collapsible>
<AccordionItem
value="byparty"
className="overflow-hidden rounded-xl border border-rule bg-surface shadow-sm"
>
<AccordionTrigger className="px-6 py-4 hover:no-underline">
<span className="flex flex-1 flex-col items-start">
<span className="text-navy text-[0.95rem] font-bold">
טיעונים מאוגדים לפי צד
</span>
<span className="text-ink-muted text-[0.78rem]">
תצוגה מסונתזת של הטענות הגולמיות, מקובצת לפי צד וקדימות
</span>
</span>
</AccordionTrigger>
<AccordionContent className="px-6 pb-5 pt-0">
<LegalArgumentsPanel caseNumber={caseNumber} />
</AccordionContent>
</AccordionItem>
</Accordion>
</TabsContent>
{/* אימות פסיקה — per-argument supporting-precedent verify gate (#154),
relocated from the deleted /compose editor to a top-level tab. */}
<TabsContent value="verify" className="mt-0">
<Card className="bg-surface border-rule shadow-sm">
<CardContent className="px-6 py-5">
<CitationVerificationPanel caseNumber={caseNumber} />
</CardContent>
</Card>
</TabsContent>