import { AGENT_STEPS, STEP_PHASE } from '@/lib/types/run' import type { AgentStep } from '@/lib/types/run' import type { StepStatus } from '@/lib/types/agents' type Phase = 'analysts' | 'researchers' | 'trader' | 'risk' type Props = { steps: Record } const PHASES: Phase[] = ['analysts', 'researchers', 'trader', 'risk'] const PHASE_META: Record = { analysts: { label: 'Analysis', code: 'PHASE-01', desc: 'Market data & signals' }, researchers: { label: 'Research', code: 'PHASE-02', desc: 'Bull/bear debate' }, trader: { label: 'Trade Plan', code: 'PHASE-03', desc: 'Strategy formulation' }, risk: { label: 'Risk Review', code: 'PHASE-04', desc: 'Risk-adjusted decision' }, } function phaseStatus(phase: Phase, steps: Record): StepStatus { const phaseSteps = AGENT_STEPS.filter((s) => STEP_PHASE[s as AgentStep] === phase) if (phaseSteps.every((s) => steps[s] === 'done')) return 'done' if (phaseSteps.some((s) => steps[s] === 'running')) return 'running' return 'pending' } export default function PipelineStepper({ steps }: Props) { const doneCount = PHASES.filter((p) => phaseStatus(p, steps) === 'done').length const progressPct = (doneCount / PHASES.length) * 100 return (
{/* Header row */}
Agent Pipeline
{doneCount} / {PHASES.length} PHASES
{/* Progress track */}
{/* Moving glow tip */} {progressPct > 0 && progressPct < 100 && (
)}
{/* Scanline on active */} {doneCount < PHASES.length && (
)}
{/* Phase nodes */}
{PHASES.map((phase, i) => { const status = phaseStatus(phase, steps) const isDone = status === 'done' const isRunning = status === 'running' const meta = PHASE_META[phase] return (
{/* Node circle */}
{isDone ? ( ) : isRunning ? (
) : ( {String(i + 1).padStart(2, '0')} )}
{/* Labels */}
{meta.label}
{meta.desc}
{/* Phase code */}
{meta.code}
) })}
) }