fix(status): render pipeline phase for legacy statuses (analyst_verified)
A case parked on a status the trimmed PHASES list dropped — but the agents still set, e.g. `analyst_verified` (HEARTBEAT.md), `research_complete` (legal-researcher) — rendered with NO active phase: phaseIndexOf returned -1, so the pipeline stepper went fully blank and the chair couldn't see where the case stood (reported on 1043-02-26, currently at analyst_verified). Add LEGACY_STATUS_PHASE in case-status.ts mapping each legacy/intermediate status to its display phase (analyst_verified/research_complete → "ניתוח וכיוון"), and a single phaseIndexOf() exported from the SSoT. status-hero.tsx and workflow-timeline.tsx both dropped their identical local phaseIndexOf (which had the bug) and import the shared one — no parallel path (G2). Display-only data-mapping fix; the stepper design is unchanged. Does NOT address the root drift (agents still set analyst_verified) — that prompt-alignment is a separate, carefully-sequenced follow-up needing cross-company agent sync. Invariants: G2 (single phase-resolution path, legacy handled in the SSoT, no new parallel logic). No backend / status-model change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { PHASES, type CaseStatus } from "@/lib/api/case-status";
|
||||
import { PHASES, phaseIndexOf, type CaseStatus } from "@/lib/api/case-status";
|
||||
import { statusLabel } from "@/lib/api/case-status";
|
||||
import { StatusChanger } from "@/components/cases/status-changer";
|
||||
import { StatusGuide } from "@/components/cases/status-guide";
|
||||
@@ -22,11 +22,6 @@ const EXPECTED_OUTCOME_LABELS: Record<string, string> = Object.fromEntries(
|
||||
expectedOutcomes.map((o) => [o.value, o.label]),
|
||||
);
|
||||
|
||||
function phaseIndexOf(status?: CaseStatus): number {
|
||||
if (!status) return -1;
|
||||
return PHASES.findIndex((p) => p.statuses.includes(status));
|
||||
}
|
||||
|
||||
/** Horizontal RTL stepper over the 5 lifecycle phases (mockup 18b .stepper). */
|
||||
function HorizontalStepper({ status }: { status?: CaseStatus }) {
|
||||
const currentIdx = phaseIndexOf(status);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { PHASES, type CaseStatus, type PhaseKey } from "@/lib/api/case-status";
|
||||
import { PHASES, phaseIndexOf, type CaseStatus, type PhaseKey } from "@/lib/api/case-status";
|
||||
import { STATUS_LABELS, STATUS_ICONS, STATUS_DESCRIPTIONS } from "@/components/cases/status-badge";
|
||||
import {
|
||||
FolderInput, ClipboardList, Brain, PenLine, CheckCircle2,
|
||||
@@ -21,11 +21,6 @@ const PHASE_ICONS: Record<PhaseKey, LucideIcon> = {
|
||||
done: CheckCircle2,
|
||||
};
|
||||
|
||||
function phaseIndexOf(status?: CaseStatus): number {
|
||||
if (!status) return -1;
|
||||
return PHASES.findIndex((p) => p.statuses.includes(status));
|
||||
}
|
||||
|
||||
export function WorkflowTimeline({ status }: { status?: CaseStatus }) {
|
||||
const currentIdx = phaseIndexOf(status);
|
||||
|
||||
|
||||
@@ -40,10 +40,40 @@ export const PHASES: { key: PhaseKey; label: string; statuses: CaseStatus[] }[]
|
||||
{ key: "done", label: "סגירה", statuses: ["exported", "reviewed", "final"] },
|
||||
];
|
||||
|
||||
/** Which phase a status belongs to (undefined for unknown/legacy values). */
|
||||
/**
|
||||
* Legacy/intermediate statuses that the trimmed PHASES list dropped but the
|
||||
* Paperclip agents still set (e.g. the analyst writes `analyst_verified`, the
|
||||
* researcher writes `research_complete`). Without this map a case parked on one
|
||||
* of them renders with NO active phase — the pipeline stepper goes blank and the
|
||||
* chair can't see where the case stands. Map each to the phase it belongs to for
|
||||
* display purposes only (these are NOT selectable statuses).
|
||||
*/
|
||||
const LEGACY_STATUS_PHASE: Record<string, PhaseKey> = {
|
||||
analyst_verified: "thinking",
|
||||
research_complete: "thinking",
|
||||
analysis_enriched: "thinking",
|
||||
ready_for_writing: "writing",
|
||||
drafting: "writing",
|
||||
in_progress: "thinking",
|
||||
};
|
||||
|
||||
/** Which phase a status belongs to (undefined only for truly unknown values). */
|
||||
export function phaseOf(status?: CaseStatus | string): PhaseKey | undefined {
|
||||
if (!status) return undefined;
|
||||
return PHASES.find((p) => (p.statuses as string[]).includes(status))?.key;
|
||||
return (
|
||||
PHASES.find((p) => (p.statuses as string[]).includes(status))?.key ??
|
||||
LEGACY_STATUS_PHASE[status]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Index of a status within the 5-phase pipeline (−1 only for unknown values).
|
||||
* Single source of truth for the stepper — handles legacy statuses via phaseOf,
|
||||
* so a case on `analyst_verified` lands on "ניתוח וכיוון" instead of blank.
|
||||
*/
|
||||
export function phaseIndexOf(status?: CaseStatus | string): number {
|
||||
const key = phaseOf(status);
|
||||
return key ? PHASES.findIndex((p) => p.key === key) : -1;
|
||||
}
|
||||
|
||||
export const STATUS_LABELS: Record<CaseStatus, string> = {
|
||||
|
||||
Reference in New Issue
Block a user