import { Tooltip } from 'antd' import { useNavigate } from 'react-router-dom' import type { CampaignTimelineEntry } from '../api' import { colors, fontSizes, statusColors, statusLabels } from '../tokens' import { fmtTick } from './WindowTimeline' import { shortDateTime } from '../utils/date' // Expanded-row view of a campaign: one lane per scenario under a shared window // axis, each child Run a status-coloured node. For still-active campaigns a // dashed line marks how far the window has progressed (nowOffsetSeconds). interface CampaignRunTimelineProps { windowSeconds: number entries: CampaignTimelineEntry[] nowOffsetSeconds?: number | null } const TICK_COUNT = 6 const LANE_HEIGHT = 30 const GUTTER = 140 const TRACK_RIGHT = 12 export default function CampaignRunTimeline({ windowSeconds, entries, nowOffsetSeconds, }: CampaignRunTimelineProps) { const navigate = useNavigate() const window = windowSeconds > 0 ? windowSeconds : 1 const clampPct = (offset: number) => Math.min(1, Math.max(0, offset / window)) * 100 // Entries arrive sorted by offset — lanes appear in chronological first-use order. const lanes: { key: string; name: string; items: CampaignTimelineEntry[] }[] = [] for (const e of entries) { let lane = lanes.find((l) => l.key === e.scenario_id) if (!lane) { lane = { key: e.scenario_id, name: e.scenario_name, items: [] } lanes.push(lane) } lane.items.push(e) } const presentStatuses = Array.from(new Set(entries.map((e) => e.status))).sort() const ticks = Array.from({ length: TICK_COUNT + 1 }, (_, i) => i / TICK_COUNT) const nowPct = nowOffsetSeconds == null ? null : clampPct(nowOffsetSeconds) return (