export type OrchestratorStepType =
| "planning"
| "replanning"
| "plan-orders"
| "pr"
| "pr-update"
| "cleanup"
| "verification";
export type OrchestratorStepStatus = "running" | "completed" | "failed" | "pending";
export interface OrchestratorStepNodeProps {
type: OrchestratorStepType;
taskId: string;
status: OrchestratorStepStatus;
label: string;
hasQuestions?: boolean;
}
const TYPE_COLORS: Record<
OrchestratorStepType,
{ accent: string; bg: string; border: string; text: string; dot: string }
> = {
planning: {
accent: "#75aafc",
bg: "bg-[#0d1a30]",
border: "border-[#75aafc]",
text: "text-[#75aafc]",
dot: "bg-[#75aafc]",
},
replanning: {
accent: "#75aafc",
bg: "bg-[#0d1a30]",
border: "border-[#75aafc]",
text: "text-[#75aafc]",
dot: "bg-[#75aafc]",
},
"plan-orders": {
accent: "#c084fc",
bg: "bg-[#1a0d30]",
border: "border-[#c084fc]",
text: "text-[#c084fc]",
dot: "bg-[#c084fc]",
},
pr: {
accent: "#34d399",
bg: "bg-[#0a1a14]",
border: "border-[#34d399]",
text: "text-[#34d399]",
dot: "bg-[#34d399]",
},
"pr-update": {
accent: "#34d399",
bg: "bg-[#0a1a14]",
border: "border-[#34d399]",
text: "text-[#34d399]",
dot: "bg-[#34d399]",
},
cleanup: {
accent: "#7788aa",
bg: "bg-[#141a24]",
border: "border-[#7788aa]",
text: "text-[#7788aa]",
dot: "bg-[#7788aa]",
},
verification: {
accent: "#7788aa",
bg: "bg-[#141a24]",
border: "border-[#7788aa]",
text: "text-[#7788aa]",
dot: "bg-[#7788aa]",
},
};
const TYPE_LABELS: Record<OrchestratorStepType, string> = {
planning: "PLANNING",
replanning: "REPLANNING",
"plan-orders": "PLAN ORDERS",
pr: "PR",
"pr-update": "PR UPDATE",
cleanup: "CLEANUP",
verification: "VERIFICATION",
};
const STATUS_LABELS: Record<OrchestratorStepStatus, string> = {
pending: "PENDING",
running: "RUNNING",
completed: "DONE",
failed: "FAILED",
};
export function OrchestratorStepNode({
type,
taskId,
status,
label,
hasQuestions,
}: OrchestratorStepNodeProps) {
const colors = TYPE_COLORS[type];
const typeLabel = TYPE_LABELS[type];
const statusLabel = STATUS_LABELS[status];
return (
<div
className={`${colors.bg} ${colors.border} border border-dashed rounded px-3 py-2 min-w-[160px] max-w-[220px] relative`}
>
{/* Type badge */}
<div className="flex items-center justify-between gap-2 mb-1">
<div className="flex items-center gap-1.5 min-w-0">
{/* Status dot */}
{status === "running" && (
<span
className={`inline-block w-2 h-2 rounded-full ${colors.dot} animate-pulse shrink-0`}
/>
)}
{status === "completed" && (
<span className="inline-block w-2 h-2 rounded-full bg-emerald-400 shrink-0" />
)}
{status === "failed" && (
<span className="inline-block w-2 h-2 rounded-full bg-red-400 shrink-0" />
)}
{status === "pending" && (
<span className="inline-block w-2 h-2 rounded-full bg-[#556677] shrink-0" />
)}
<span className={`text-[9px] font-mono ${colors.text} uppercase shrink-0`}>
{typeLabel}
</span>
</div>
<div className="flex items-center gap-1 shrink-0">
{hasQuestions && (
<span className="inline-block w-2 h-2 rounded-full bg-purple-400 animate-pulse" title="Has pending questions" />
)}
<span
className={`text-[9px] font-mono uppercase ${
status === "completed"
? "text-emerald-400"
: status === "failed"
? "text-red-400"
: colors.text
}`}
>
{statusLabel}
</span>
</div>
</div>
{/* Label */}
<span className="text-[11px] font-mono text-white truncate block font-medium mb-1">
{label}
</span>
{/* Task link */}
<a
href={`/mesh/${taskId}`}
className={`text-[9px] font-mono text-[#556677] hover:${colors.text} underline block`}
>
{status === "running" ? "View running task" : "View task"}
</a>
</div>
);
}