שלושת שינויי-ה-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>
92 lines
3.2 KiB
TypeScript
92 lines
3.2 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import { toast } from "sonner";
|
||
import {
|
||
Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
|
||
} from "@/components/ui/select";
|
||
import { Button } from "@/components/ui/button";
|
||
import { STATUS_LABELS, STATUS_ICONS } from "@/components/cases/status-badge";
|
||
import { useUpdateCase } from "@/lib/api/cases";
|
||
import { CASE_STATUSES, type CaseStatus } from "@/lib/api/case-status";
|
||
|
||
/*
|
||
* Dropdown for manually overriding the case status — skip a step
|
||
* or revert to an earlier stage. Calls PUT /api/cases/:caseNumber
|
||
* with { status: newValue }. The option list is the SSoT lifecycle.
|
||
*/
|
||
|
||
const ALL_STATUSES: readonly CaseStatus[] = CASE_STATUSES;
|
||
|
||
export function StatusChanger({
|
||
caseNumber,
|
||
currentStatus,
|
||
inline = false,
|
||
}: {
|
||
caseNumber: string;
|
||
currentStatus?: CaseStatus;
|
||
/** Compact horizontal layout for the status hero (mockup 18b hero-controls);
|
||
* default is the stacked rail layout. */
|
||
inline?: boolean;
|
||
}) {
|
||
// `null` = untouched → the dropdown tracks the live `currentStatus` (which
|
||
// arrives async and changes on the 5s poll / external updates). Only an
|
||
// explicit pick overrides it, until save resets back to tracking.
|
||
const [picked, setPicked] = useState<CaseStatus | null>(null);
|
||
const mutate = useUpdateCase(caseNumber);
|
||
const effective: CaseStatus | "" = picked ?? currentStatus ?? "";
|
||
const canSave = Boolean(effective) && effective !== currentStatus;
|
||
|
||
const handleSave = async () => {
|
||
if (!canSave || !effective) return;
|
||
try {
|
||
await mutate.mutateAsync({ status: effective });
|
||
toast.success(`הסטטוס עודכן ל${STATUS_LABELS[effective]}`);
|
||
setPicked(null);
|
||
} catch (e) {
|
||
toast.error(e instanceof Error ? e.message : "שגיאה בעדכון הסטטוס");
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className={inline ? "space-y-0" : "mt-4 border-t border-rule pt-3 space-y-2"}>
|
||
{!inline && (
|
||
<label className="text-[0.72rem] text-ink-muted block">שינוי סטטוס ידני</label>
|
||
)}
|
||
<div className="flex items-center gap-2">
|
||
<Select
|
||
value={effective || "__current__"}
|
||
onValueChange={(v) => setPicked(v === "__current__" ? null : v as CaseStatus)}
|
||
dir="rtl"
|
||
>
|
||
<SelectTrigger className="text-[0.75rem] h-8">
|
||
<SelectValue />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
{ALL_STATUSES.map((s) => {
|
||
const Icon = STATUS_ICONS[s];
|
||
return (
|
||
<SelectItem key={s} value={s} className="text-[0.75rem]">
|
||
<span className="inline-flex items-center gap-1.5">
|
||
<Icon className="w-3 h-3 shrink-0" />
|
||
{STATUS_LABELS[s]}
|
||
</span>
|
||
</SelectItem>
|
||
);
|
||
})}
|
||
</SelectContent>
|
||
</Select>
|
||
<Button
|
||
size="sm"
|
||
variant="outline"
|
||
className="h-8 text-[0.72rem] px-3 shrink-0"
|
||
disabled={!canSave || mutate.isPending}
|
||
onClick={handleSave}
|
||
>
|
||
{mutate.isPending ? "שומר…" : inline ? "עדכן סטטוס" : "עדכן"}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|