import type { FacilityStatus } from "@dci/core"; import Link from "next/link"; import { cn } from "@/components/ui/cn"; import { StatusDot } from "@/components/ui/status-dot"; import { fmtInt, splitMw } from "@/lib/format"; import { routes } from "@/lib/routes"; export interface FunnelInput { /** project counts + published MW by status */ byStatus: Array<{ status: FacilityStatus; count: number; mw: number | null }>; /** facilities opened in the last 12 months (from a pulse / velocity window) — null when the API does not expose it */ openedLast12m?: { count: number; mw: number | null } | null; } const STEPS: Array<{ key: string; label: string; statuses: FacilityStatus[]; tone: FacilityStatus }> = [ { key: "announced", label: "Announced", statuses: ["rumored", "proposed", "announced", "expansion"], tone: "announced" }, { key: "permitting", label: "Permitting", statuses: ["permitting"], tone: "permitting" }, { key: "approved", label: "Approved", statuses: ["approved"], tone: "approved" }, { key: "construction", label: "Construction", statuses: ["under_construction", "partially_operational"], tone: "under_construction" }, ]; /** * Construction pipeline funnel (§33): ANNOUNCED → PERMITTING → APPROVED → CONSTRUCTION → OPENED, each with project * count and the sum of published MW (GW when ≥ 1 000). Bars are proportional to the count; MW are labelled * "known published MW only" because most projects do not publish a figure. */ export function PipelineFunnel({ data, className, compact }: { data: FunnelInput; className?: string; compact?: boolean }) { const agg = STEPS.map((s) => { const rows = data.byStatus.filter((r) => s.statuses.includes(r.status)); return { ...s, count: rows.reduce((a, r) => a + r.count, 0), mw: rows.some((r) => r.mw !== null) ? rows.reduce((a, r) => a + (r.mw ?? 0), 0) : null }; }); const opened = data.openedLast12m ?? null; const delayed = data.byStatus.filter((r) => r.status === "delayed" || r.status === "cancelled"); const max = Math.max(1, ...agg.map((a) => a.count), opened?.count ?? 0); const cells = [...agg.map((a) => ({ key: a.key, label: a.label, count: a.count, mw: a.mw, tone: a.tone as FacilityStatus, href: routes.projects(`status=${a.statuses.join(",")}`) })), { key: "opened", label: "Opened · 12 m", count: opened?.count ?? null, mw: opened?.mw ?? null, tone: "operational" as FacilityStatus, href: routes.datacenters("status=operational&sort=opened") }]; return (