import type { ProjectDetail, ProjectStageDTO } from "@dci/core"; import { ExternalLink } from "lucide-react"; import { StatusDot } from "@/components/ui/status-dot"; import { cn } from "@/components/ui/cn"; import { fmtInt } from "@/lib/format"; import { fmtPartialDateSafe } from "@/lib/format-project"; import { STAGE_LABEL } from "@/lib/labels"; const MAIN: ProjectStageDTO["stage"][] = ["rumored", "proposed", "announced", "permitting", "approved", "under_construction", "partially_operational", "operational"]; const SIDE: ProjectStageDTO["stage"][] = ["delayed", "cancelled"]; /** * Lifecycle strip (§32): one cell per stage in funnel order — reached stages filled, the current one accented, * unreached ones hollow; dates and the evidence link (source that moved the project there) under each. * Delayed / cancelled are side branches shown only when reached. */ export function ProjectStages({ stages }: { stages: ProjectStageDTO[] }) { const by = new Map(stages.map((s) => [s.stage, s])); const main = MAIN.map((k) => by.get(k)).filter((s): s is ProjectStageDTO => !!s); const side = SIDE.map((k) => by.get(k)).filter((s): s is ProjectStageDTO => !!s && s.reached); if (!main.length) return

No lifecycle stage recorded.

; return (
    {main.map((s, i) => (
  1. {STAGE_LABEL[s.stage]}
    {s.date ? fmtPartialDateSafe(s.date) : s.reached ? date not published : —}
    {s.reached && (s.url || s.sourceName) && (
    {s.url ? ( {s.sourceName ?? "evidence"} ) : ( s.sourceName )}
    )} {i === main.length - 1 && null}
  2. ))}
{side.length > 0 && ( )}
); } const VELOCITY: Array<{ key: keyof ProjectDetail["velocityDays"]; label: string }> = [ { key: "announcedToPermitting", label: "Announced → permitting" }, { key: "permittingToApproval", label: "Permitting → approval" }, { key: "approvalToConstruction", label: "Approval → construction" }, { key: "constructionToOpening", label: "Construction → opening" }, { key: "announcedToConstruction", label: "Announced → construction" }, ]; /** Days between reached stages; "—" when either date is unknown (never estimated). */ export function ProjectVelocity({ v }: { v: ProjectDetail["velocityDays"] }) { const any = VELOCITY.some((x) => v[x.key] !== null); return (
{VELOCITY.map((x) => { const d = v[x.key]; return (
{d === null ? "—" : fmtInt(d)} {d !== null && days}
{x.label}
); })}
{!any &&

Velocity needs two dated stages; none of the stage transitions has published dates yet.

}
); }