spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import type { MarketConcentration } from "@dci/core";2import { ApiNote, CoverageBar, HonestEmpty, ShareList } from "@/components/intelligence/shared";3import { fmtInt, fmtPct } from "@/lib/format";4import { routes } from "@/lib/routes";56function hhiLabel(hhi: number): string {7 if (hhi >= 2500) return "highly concentrated";8 if (hhi >= 1500) return "moderately concentrated";9 return "unconcentrated";10}1112function Figures({ hhi, top3, top5 }: { hhi: number; top3: number; top5: number }) {13 return (14 <dl className="mb-3 grid grid-cols-3 gap-x-3 border-y border-hair py-2">15 <div className="min-w-0">16 <dt className="label-caps truncate">HHI</dt>17 <dd className="figure text-[18px] font-medium leading-tight text-ink">{fmtInt(hhi)}</dd>18 <dd className="text-[11px] text-ink-3">{hhiLabel(hhi)} · 0–10 000</dd>19 </div>20 <div className="min-w-0">21 <dt className="label-caps truncate">Top-3 share</dt>22 <dd className="figure text-[18px] font-medium leading-tight text-ink">{fmtPct(top3)}</dd>23 </div>24 <div className="min-w-0">25 <dt className="label-caps truncate">Top-5 share</dt>26 <dd className="figure text-[18px] font-medium leading-tight text-ink">{fmtPct(top5)}</dd>27 </div>28 </dl>29 );30}3132/**33 * MARKET CONCENTRATION — computed on facility counts and, separately, on known MW (only when published34 * figures exist). The MW view always shows its coverage and says "incomplete MW data" when partial.35 */36export function MarketConcentrationPanel({ c, facilityCount }: { c: MarketConcentration; facilityCount: number }) {37 const f = c.facilities;38 const mw = c.knownMw;39 return (40 <div className="flex flex-col gap-4">41 <div className="grid gap-6 md:grid-cols-2">42 <div>43 <h3 className="text-[13.5px] font-semibold text-ink">On facility counts</h3>44 <p className="mb-2 text-[11.5px] text-ink-3">45 {fmtInt(c.operatorCount)} operators · {fmtInt(facilityCount)} counted facilities (campus rows with buildings excluded)46 </p>47 <Figures hhi={f.hhi} top3={f.top3Share} top5={f.top5Share} />48 <ShareList items={f.top.slice(0, 10).map((o) => ({ key: o.id, label: o.name, href: routes.operator(o.slug), count: o.count, share: o.share }))} unit="facilities" />49 </div>50 <div>51 <h3 className="text-[13.5px] font-semibold text-ink">On known MW</h3>52 {mw ? (53 <>54 <div className="mb-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-[11.5px] text-ink-3">55 <span>Published operational MW only</span>56 <CoverageBar value={mw.coverage} label="MW coverage" width="w-[140px]" />57 </div>58 {mw.coverage < 1 && (59 <p className="mb-2 rounded-sm border border-warn/30 bg-warn/5 px-2.5 py-1.5 text-[12px] text-ink-2" role="note">60 <strong className="text-ink">Incomplete MW data</strong> — {fmtPct(mw.coverage)} of facilities publish a figure; shares below describe the published subset, not the market.61 </p>62 )}63 <Figures hhi={mw.hhi} top3={mw.top3Share} top5={mw.top5Share} />64 <ShareList items={mw.top.slice(0, 10).map((o) => ({ key: o.id, label: o.name, href: routes.operator(o.slug), count: Math.round(o.mw), share: o.share }))} unit="MW" />65 </>66 ) : (67 <HonestEmpty title="Not computed on MW" detail="no operator in this market publishes an operational MW figure, so a capacity view would be invented" className="mt-2" />68 )}69 </div>70 </div>71 <ApiNote note={c.note} label="Method" />72 </div>73 );74}75