Chair-requested refinements to the case banner: - Tabs now span the full band width via justify-between — wide gaps with the current 6 tabs, tightening as more are added (gap-2 no-overlap floor). - Parties moved out of the always-on header line into a "צדדים" Popover, so the band is shorter and the tab content sits higher on the page. - Case subject (title) moved up onto the case-number line in a smaller font, instead of its own line below. tsc --noEmit + eslint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
159 lines
6.3 KiB
TypeScript
159 lines
6.3 KiB
TypeScript
import type { ReactNode } from "react";
|
||
import Link from "next/link";
|
||
import { Badge } from "@/components/ui/badge";
|
||
import { StatusBadge } from "@/components/cases/status-badge";
|
||
import { StatusRail } from "@/components/cases/status-rail";
|
||
import { CaseArchiveAction } from "@/components/cases/case-archive-action";
|
||
import { CreateRepoButton } from "@/components/cases/create-repo-button";
|
||
import {
|
||
PRACTICE_AREA_LABELS,
|
||
APPEAL_SUBTYPE_LABELS,
|
||
isBlamSubtype,
|
||
} from "@/lib/practice-area";
|
||
import {
|
||
Popover,
|
||
PopoverContent,
|
||
PopoverTrigger,
|
||
} from "@/components/ui/popover";
|
||
import { Users } from "lucide-react";
|
||
import type { CaseDetail } from "@/lib/api/cases";
|
||
|
||
/**
|
||
* Case header — parchment band (IA-redesign mockup 17): full-bleed band with
|
||
* the case title + status/type chips inline, a parties line, the case actions
|
||
* (edit / archive / repo / sync), and a metadata strip. The `tabs` slot renders
|
||
* the tab strip inside the band, anchored to its bottom edge.
|
||
*/
|
||
export function CaseHeader({
|
||
caseNumber,
|
||
data,
|
||
actions,
|
||
tabs,
|
||
}: {
|
||
caseNumber: string;
|
||
data?: CaseDetail;
|
||
actions?: ReactNode;
|
||
tabs?: ReactNode;
|
||
}) {
|
||
const appellants = data?.appellants?.filter(Boolean) ?? [];
|
||
const respondents = data?.respondents?.filter(Boolean) ?? [];
|
||
const hasParties = appellants.length > 0 || respondents.length > 0;
|
||
const isBlam =
|
||
data?.proceeding_type === 'בל"מ' || isBlamSubtype(data?.appeal_subtype);
|
||
|
||
return (
|
||
<div className="-mx-10 -mt-10 mb-2 bg-parchment border-b border-rule px-10 pt-6">
|
||
<nav className="text-[0.78rem] text-ink-muted mb-3 flex items-center gap-2">
|
||
<Link href="/" className="hover:text-gold-deep">בית</Link>
|
||
<span aria-hidden>·</span>
|
||
<span>תיקי ערר</span>
|
||
<span aria-hidden>·</span>
|
||
<span className="text-navy tabular-nums">{data?.case_number ?? "…"}</span>
|
||
</nav>
|
||
|
||
{/* title block + metadata on one row — no wrap, so the metadata dl stays
|
||
parallel to the H1 and a long parties line can't push it below. */}
|
||
<div className="flex items-start justify-between gap-6">
|
||
<div className="min-w-0 flex-1">
|
||
{/* title row — H1 + status/type/blam chips inline (mockup .band h1) */}
|
||
<h1 className="text-navy text-[1.7rem] font-bold leading-tight flex items-center gap-3 flex-wrap mb-0">
|
||
<span className="tabular-nums">
|
||
{data?.proceeding_type ?? "ערר"} {data?.case_number ?? "—"}
|
||
</span>
|
||
{/* case subject on the same line, smaller font (chair request) */}
|
||
{data?.title && (
|
||
<span className="text-navy/75 text-lg font-semibold leading-snug">
|
||
{data.title}
|
||
</span>
|
||
)}
|
||
{data?.status && <StatusBadge status={data.status} />}
|
||
{data?.archived_at && (
|
||
<Badge
|
||
variant="outline"
|
||
className="rounded-full px-3 py-0.5 text-[0.75rem] font-semibold bg-ink-muted/10 text-ink-muted border-ink-muted/30"
|
||
>
|
||
בארכיון
|
||
</Badge>
|
||
)}
|
||
{data?.practice_area && (
|
||
<Badge
|
||
variant="outline"
|
||
className="rounded-full px-3 py-0.5 text-[0.75rem] font-semibold bg-gold-wash text-gold-deep border-rule"
|
||
>
|
||
{PRACTICE_AREA_LABELS[data.practice_area]}
|
||
{data.appeal_subtype && data.appeal_subtype !== "unknown" && (
|
||
<> · {APPEAL_SUBTYPE_LABELS[data.appeal_subtype]}</>
|
||
)}
|
||
</Badge>
|
||
)}
|
||
{isBlam && (
|
||
<Badge
|
||
variant="outline"
|
||
className="rounded-full px-3 py-0.5 text-[0.75rem] font-bold bg-warn/10 text-warn-deep border-warn/40"
|
||
title="בקשה להארכת מועד להגשת ערר"
|
||
>
|
||
בל"מ
|
||
</Badge>
|
||
)}
|
||
</h1>
|
||
|
||
{/* case actions — parties now live in a popover (chair request) so
|
||
they don't push the tab content down the page. */}
|
||
<div className="flex items-center gap-2 flex-wrap mt-3">
|
||
{hasParties && (
|
||
<Popover>
|
||
<PopoverTrigger asChild>
|
||
<button
|
||
type="button"
|
||
className="inline-flex items-center gap-1.5 rounded-md border border-rule bg-surface px-3 py-1.5 text-[0.82rem] font-semibold text-ink-soft hover:bg-parchment transition-colors"
|
||
>
|
||
<Users className="w-3.5 h-3.5" />
|
||
צדדים
|
||
</button>
|
||
</PopoverTrigger>
|
||
<PopoverContent align="start" className="w-72 p-3 space-y-2.5 text-sm">
|
||
{appellants.length > 0 && (
|
||
<div>
|
||
<div className="text-[0.7rem] font-bold text-gold-deep uppercase tracking-wider mb-1">
|
||
עוררים
|
||
</div>
|
||
<ul className="space-y-0.5 text-ink-soft">
|
||
{appellants.map((a, i) => <li key={`a${i}`}>{a}</li>)}
|
||
</ul>
|
||
</div>
|
||
)}
|
||
{respondents.length > 0 && (
|
||
<div>
|
||
<div className="text-[0.7rem] font-bold text-gold-deep uppercase tracking-wider mb-1">
|
||
משיבים
|
||
</div>
|
||
<ul className="space-y-0.5 text-ink-soft">
|
||
{respondents.map((r, i) => <li key={`r${i}`}>{r}</li>)}
|
||
</ul>
|
||
</div>
|
||
)}
|
||
</PopoverContent>
|
||
</Popover>
|
||
)}
|
||
{data?.case_number && (
|
||
<CaseArchiveAction
|
||
caseNumber={data.case_number}
|
||
archivedAt={data.archived_at}
|
||
/>
|
||
)}
|
||
<CreateRepoButton data={data} />
|
||
{actions}
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
{/* ★ integrated status rail — visible on every tab (banner A, X17) */}
|
||
<StatusRail caseNumber={caseNumber} data={data} />
|
||
|
||
{/* tab strip anchored to band bottom */}
|
||
{tabs ? <div className="mt-4">{tabs}</div> : null}
|
||
</div>
|
||
);
|
||
}
|