feat(case-ui): banner-A status rail + V2 segmented tabs (X17 #2)
Implements the approved X17 case-detail redesign foundation (mockups 18c "A" + 18e "V2"): - New StatusRail component — the integrated status strip (current-phase chip, expected-outcome semantic tag, hearing/updated/sync meta inline, agents Popover, manual-changer Popover) over the 5-phase pipeline stepper. Lives INSIDE the parchment band (CaseHeader), so status is now visible on EVERY tab (supersedes the overview-only StatusHero / reverses the prior INV-IA1 overview-only decision, deliberately). - CaseHeader absorbs the rail and drops its separate hearing/updated/sync dl (those moved into the rail — no duplication). - Tabs restyled to the V2 "segmented" look (each tab a pill, active = raised white card) with per-tab icons. - Overview tab body is now just documents + agent-activity preview (status left the body). - StatusHero deleted — its content moved wholesale into StatusRail, so no parallel status surface remains (G2). Reuses the shared StatusChanger / StatusGuide / AgentStatusWidget verbatim (G2). Design-gate: mockups 18c + 18e 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:
@@ -9,7 +9,6 @@ import { Button } from "@/components/ui/button";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { CaseHeader } from "@/components/cases/case-header";
|
||||
import { CaseEditDialog } from "@/components/cases/case-edit-dialog";
|
||||
import { StatusHero } from "@/components/cases/status-hero";
|
||||
import { DocumentsPanel } from "@/components/cases/documents-panel";
|
||||
import { DraftsPanel } from "@/components/cases/drafts-panel";
|
||||
import { DecisionBlocksPanel } from "@/components/cases/decision-blocks-panel";
|
||||
@@ -19,7 +18,10 @@ 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 } from "lucide-react";
|
||||
import {
|
||||
Play, Loader2, LayoutGrid, Scale, FileText, MessageSquare, Users,
|
||||
type LucideIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
/*
|
||||
* Next 16 breaking change: route params are now a Promise.
|
||||
@@ -60,23 +62,27 @@ export default function CaseDetailPage({
|
||||
);
|
||||
}
|
||||
|
||||
const tabDefs: [string, string, LucideIcon][] = [
|
||||
["overview", "סקירה", LayoutGrid],
|
||||
["arguments", "טיעונים", Scale],
|
||||
["decision", "ההחלטה", FileText],
|
||||
["drafts", "טיוטות והערות", MessageSquare],
|
||||
["agents", "סוכנים", Users],
|
||||
];
|
||||
|
||||
// V2 "segmented" tab strip (X17): each tab a pill, active = raised white card.
|
||||
const tabsList = (
|
||||
<TabsList
|
||||
variant="line"
|
||||
className="gap-6 h-auto p-0 rounded-none -mb-px"
|
||||
className="!h-auto gap-1.5 p-0 flex-wrap justify-start"
|
||||
>
|
||||
{[
|
||||
["overview", "סקירה"],
|
||||
["arguments", "טיעונים"],
|
||||
["decision", "ההחלטה"],
|
||||
["drafts", "טיוטות והערות"],
|
||||
["agents", "סוכנים"],
|
||||
].map(([value, label]) => (
|
||||
{tabDefs.map(([value, label, Icon]) => (
|
||||
<TabsTrigger
|
||||
key={value}
|
||||
value={value}
|
||||
className="flex-none rounded-none px-0 pb-3.5 pt-0 text-[0.92rem] font-medium text-ink-muted data-active:text-navy data-active:font-semibold data-active:after:bg-gold data-active:after:bottom-0"
|
||||
className="flex-none gap-1.5 rounded-lg border border-transparent px-3.5 py-1.5 text-[0.9rem] font-medium text-ink-soft after:hidden hover:bg-parchment/60 data-active:bg-surface data-active:text-navy data-active:font-semibold data-active:border-rule data-active:shadow-sm [&_svg]:text-ink-light data-active:[&_svg]:text-gold-deep"
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
{label}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
@@ -124,22 +130,18 @@ export default function CaseDetailPage({
|
||||
<Skeleton className="h-6 w-96" />
|
||||
</div>
|
||||
) : (
|
||||
<CaseHeader data={data} actions={bandActions} tabs={tabsList} />
|
||||
<CaseHeader caseNumber={caseNumber} data={data} actions={bandActions} tabs={tabsList} />
|
||||
)}
|
||||
|
||||
{/* full-width tab content — status lives ONLY in the overview hero
|
||||
(mockup 18b); the other tabs lose the status rail entirely (INV-IA1). */}
|
||||
{/* full-width tab content — status now lives in the always-visible band
|
||||
StatusRail (banner A, X17), so every tab keeps the status context. */}
|
||||
<div className="min-w-0 mt-6">
|
||||
<TabsContent value="overview" className="mt-0 space-y-6">
|
||||
{/* ★ STATUS HERO — the single home for case status (INV-IA1/INV-UI7) */}
|
||||
<StatusHero caseNumber={caseNumber} data={data} />
|
||||
|
||||
{/* documents + agents two-column below the hero (mockup 18b) */}
|
||||
<TabsContent value="overview" className="mt-0">
|
||||
{/* documents + agents two-column (mockup 18j) */}
|
||||
<div className="grid gap-6 lg:grid-cols-2 items-start">
|
||||
<DocumentsPanel data={data} />
|
||||
<AgentActivityPreview caseNumber={caseNumber} />
|
||||
</div>
|
||||
{/* decision-editor CTA moved to the band actions (visible on all tabs) */}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="arguments" className="mt-0">
|
||||
|
||||
Reference in New Issue
Block a user