feat(ui): WS6 — party accordion, full-width compose, status-hero overview (#206)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 9s

שלושת שינויי-ה-UI של WS6 לפי המוקאפים המאושרים (25b/03b/18b, X17):

1. טאב "טיעונים" (legal-arguments-panel) — אקורדיון ברמת-הצד, סגור
   כברירת-מחדל (type="multiple" ללא defaultValue). כל trigger מציג
   שם-צד + תת-כותרת + באדג'-מונה. בתוך כל צד נשמר הקיבוץ-לפי-קדימות
   והאקורדיון הפנימי ברמת-הטיעון.

2. טאב "עמדות וטענות" (compose) — רוחב-דף מלא: בוטל ה-grid 1fr/320px,
   ראיל "מסמכי התיק" הוסר (DocumentsPanel בסקירה הוא הבעלים היחיד)
   ועבר לפס-מסמכים מתקפל למעלה (סגור כברירת-מחדל). כרטיסי-הסוגיות
   2-up. פסיקה-מצורפת ושערי-הייצוא/העלאה שומרו (relocate, לא remove).
   מפת-StatusChip המקבילה הוסרה → StatusBadge המשותף (G2).

3. סטטוס רק בסקירה (overview) — StatusHero חדש (stepper אופקי + chip
   שלב-נוכחי + שינוי-סטטוס ידני + meta-strip), הבעלים היחיד של
   הסטטוס. WorkflowTimeline/StatusChanger/AgentStatusWidget ירדו משאר
   הטאבים; הטאבים האחרים רוחב-מלא. StatusBadge read-only נשאר ב-H1.

Invariants: INV-IA1 (בעלים-יחיד לסטטוס=hero), INV-IA3/G10 (כל שער-אנוש
נשמר במקום אחד — שינוי-סטטוס בסקירה, ייצוא/העלאה ב-compose),
INV-IA4 (compose=מרחב-עבודה ממוקד-משימה רוחב-מלא), INV-IA6 (מפת-סטטוסים
progressive-disclosure), INV-UI7/UI8, G2 (StatusBadge משותף, אין
מפת-סטטוס מקבילה).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-30 12:22:06 +00:00
parent 4feac756ba
commit f1e9103723
5 changed files with 485 additions and 300 deletions

View File

@@ -36,6 +36,23 @@ const PRIORITY_BADGE_TONE: Record<LegalArgumentPriority, string> = {
relief: "bg-emerald-50 text-emerald-900 border-emerald-200",
};
// Per-party colour mark + one-line role description (mockup 25b party trigger).
const PARTY_MARK_TONE: Record<LegalArgumentParty, string> = {
appellant: "bg-info",
respondent: "bg-gold-deep",
committee: "bg-success",
permit_applicant: "bg-warn",
unknown: "bg-ink-muted",
};
const PARTY_SUB_HE: Record<LegalArgumentParty, string> = {
appellant: "הצד היוזם של הערר",
respondent: "הצד המשיב לערר",
committee: "עמדת הגורם המאשר",
permit_applicant: "צד שלישי שזכותו עשויה להיפגע",
unknown: "צד שלא זוהה אוטומטית",
};
function groupByPriority(
args: LegalArgument[],
): Record<LegalArgumentPriority, LegalArgument[]> {
@@ -55,22 +72,18 @@ function groupByPriority(
}
type PartySectionProps = {
party: LegalArgumentParty;
args: LegalArgument[];
};
function PartySection({ party, args }: PartySectionProps) {
/**
* Inner body of a single party — the priority-grouping + per-argument inner
* accordion. The party header (name / sub / count) now lives on the enclosing
* party-accordion trigger (mockup 25b), so this renders the groups only.
*/
function PartySection({ args }: PartySectionProps) {
const grouped = useMemo(() => groupByPriority(args), [args]);
return (
<div className="space-y-3">
<div className="flex items-baseline justify-between border-b border-rule pb-2">
<h3 className="text-navy text-base font-semibold">
{PARTY_LABELS_HE[party] ?? party}
</h3>
<span className="text-ink-muted text-xs">
{args.length} טיעונים
</span>
</div>
<div className="space-y-4">
{PRIORITY_ORDER.map((priority) => {
const list = grouped[priority];
if (!list?.length) return null;
@@ -253,15 +266,47 @@ export function LegalArgumentsPanel({ caseNumber }: LegalArgumentsPanelProps) {
החישוב למנתח המשפטי.
</p>
) : (
<div className="space-y-6">
{parties.map((party) => (
<PartySection
key={party}
party={party}
args={data.by_party[party] ?? []}
/>
))}
</div>
// Top-level party accordion — each party is its own collapsible section,
// closed by default (empty defaultValue). The count badge on every
// trigger shows the scope without expanding (mockup 25b).
<Accordion type="multiple" className="space-y-3.5">
{parties.map((party) => {
const args = data.by_party[party] ?? [];
return (
<AccordionItem
key={party}
value={party}
className="overflow-hidden rounded-lg border border-rule bg-surface shadow-sm"
>
<AccordionTrigger className="px-5 py-4 hover:no-underline data-[state=open]:border-b data-[state=open]:border-rule-soft data-[state=open]:bg-parchment">
<span className="flex flex-1 items-center gap-3.5">
<span
className={`h-8 w-1.5 flex-none rounded ${PARTY_MARK_TONE[party]}`}
aria-hidden
/>
<span className="flex flex-1 flex-col items-start gap-0.5">
<span className="text-navy text-base font-bold leading-tight">
{PARTY_LABELS_HE[party] ?? party}
</span>
<span className="text-ink-muted text-xs">
{PARTY_SUB_HE[party]}
</span>
</span>
<Badge
variant="outline"
className="bg-gold-wash text-gold-deep border-rule rounded-full px-3 py-0.5 text-xs font-bold whitespace-nowrap"
>
{args.length} טיעונים
</Badge>
</span>
</AccordionTrigger>
<AccordionContent className="px-5 pb-4 pt-4">
<PartySection args={args} />
</AccordionContent>
</AccordionItem>
);
})}
</Accordion>
)}
</div>
);