spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import type { MarketMomentum } from "@dci/core";2import Link from "next/link";3import { CounterGrid, type CounterItem } from "@/components/intelligence/shared";4import { fmtInt } from "@/lib/format";5import { routes } from "@/lib/routes";67/**8 * MARKET MOMENTUM — each component shown on its own, never collapsed into a score. Counts come from9 * events / projects detected in the window; MW are published figures only ("not published" otherwise).10 */11export function MarketMomentumPanel({ m, metroSlug, countryIso2 }: { m: MarketMomentum; metroSlug: string; countryIso2: string }) {12 const items: CounterItem[] = [13 { key: "pa", value: m.projectsAnnounced, label: "projects announced", href: routes.projects(`metro=${metroSlug}`) },14 { key: "pc", value: m.projectsEnteredConstruction, label: "projects entered construction", href: routes.projects(`metro=${metroSlug}&status=under_construction`) },15 { key: "cmw", value: m.constructionMw, label: "of construction MW (published, ≈)", format: "mw" },16 { key: "amw", value: m.announcedMw, label: "of announced MW (published, ≈)", format: "mw" },17 { key: "ne", value: m.newEntrants.length, label: m.newEntrants.length === 1 ? "operator entered the market" : "operators entered the market", note: "first facility observed in the window" },18 { key: "fo", value: m.facilitiesOpened, label: "facilities opened", href: routes.live(`metro=${metroSlug}&type=facility_opened`) },19 { key: "cr", value: m.cloudRegionsAdded, label: "cloud regions added", href: routes.live(`metro=${metroSlug}&type=cloud_region_announced,cloud_region_launched`) },20 { key: "ge", value: m.gridEvents, label: "grid / power events", href: routes.live(`metro=${metroSlug}&type=grid_constraint,grid_connection,utility_event,power_agreement`) },21 { key: "et", value: m.eventsTotal, label: "changes detected in total", href: routes.live(`country=${countryIso2}`) },22 ];23 return (24 <div className="flex flex-col gap-3">25 <CounterGrid items={items} suffix="· last 12 months" />26 {m.newEntrants.length > 0 && (27 <div>28 <div className="label-caps mb-1.5">New entrants ({fmtInt(m.newEntrants.length)})</div>29 <ul className="flex flex-wrap gap-1.5">30 {m.newEntrants.map((o) => (31 <li key={o.id}>32 <Link href={routes.operator(o.slug)} className="inline-flex h-10 items-center rounded-sm border border-hair bg-surface px-2 text-[12px] text-ink hover:border-hair-2 hover:text-accent sm:h-8">33 {o.name}34 </Link>35 </li>36 ))}37 </ul>38 <p className="mt-1.5 text-[11px] text-ink-4">“New entrant” = an operator whose first facility in this market was first observed by the index within the window; for a recently connected source this reflects indexing, not market entry.</p>39 </div>40 )}41 </div>42 );43}44