feat(case-ui): merge /compose editor into case tabs (X17 #3)
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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user