spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import type { AiEvidence } from "@dci/core";2import type { Metadata } from "next";3import Link from "next/link";4import { Breadcrumbs } from "@/components/entity/breadcrumbs";5import { SectionNav } from "@/components/entity/section-nav";6import { SourcesFooter } from "@/components/entity/sources-footer";7import { EventList } from "@/components/event-list";8import { AiFacilitiesTable } from "@/components/intelligence/ai/ai-facilities-table";9import { ApiNote, CoverageBar, HonestEmpty, MwFigure, PillLink, PublishedOnlyNote, ShareList } from "@/components/intelligence/shared";10import { ProjectList } from "@/components/project-list";11import { AiMark } from "@/components/ui/badge";12import { Page, PageHeader, Section } from "@/components/ui/card";13import { Metric, MetricCell, MetricRow } from "@/components/ui/metric";14import { ErrorState, OfflineNotice } from "@/components/ui/states";15import { api } from "@/lib/api";16import { fmtInt, fmtMw, fmtPct } from "@/lib/format";17import { AI_EVIDENCE_LABEL, STATUS_LABEL, statusVar } from "@/lib/labels";18import { routes } from "@/lib/routes";19import { pageMetadata } from "@/lib/seo";2021export const revalidate = 120;2223export const metadata: Metadata = pageMetadata({24 title: "AI infrastructure",25 description: "AI / HPC data centers and projects with their evidence level (confirmed, likely, associated), the project pipeline by stage, top operators, markets and countries — published figures only.",26 path: routes.ai(),27});2829const LEVELS: Array<{ level: AiEvidence; rule: string; counted: boolean }> = [30 { level: "confirmed", rule: "A source explicitly describes the site as AI / HPC / GPU / accelerated-computing infrastructure.", counted: true },31 { level: "likely", rule: "AI-ready, high-density, liquid- or direct-to-chip cooling or GPU signals in the source.", counted: true },32 { level: "associated", rule: "Only an AI tenant, customer or company mention — reported separately, NOT counted as AI infrastructure.", counted: false },33 { level: "unknown", rule: "No signal in any indexed source. A single keyword never confirms a site.", counted: false },34];3536export default async function AiInfrastructurePage() {37 const r = await api.aiIndex();38 if (!r.ok) {39 return (40 <Page className="py-6">41 <Breadcrumbs items={[{ name: "AI infrastructure", href: routes.ai() }]} className="mb-4" />42 <OfflineNotice detail={r.error} className="mb-4" />43 <ErrorState />44 </Page>45 );46 }47 const d = r.data;48 const s = d.stats;49 const stages = [...d.pipelineByStage].sort((a, b) => b.count - a.count);50 const stageMax = Math.max(1, ...stages.map((x) => x.count));51 const sections = [52 { id: "evidence", label: "Evidence levels" },53 { id: "pipeline", label: "Project pipeline", count: stages.length },54 { id: "top", label: "Top operators & places" },55 { id: "announcements", label: "Announcements", count: d.recentAnnouncements.length },56 { id: "projects", label: "Recent projects", count: d.recentProjects.length },57 { id: "facilities", label: "Facilities", count: d.facilities.length },58 ];5960 return (61 <Page>62 <Breadcrumbs items={[{ name: "AI infrastructure", href: routes.ai() }]} className="pt-3" />63 <PageHeader64 eyebrow="Intelligence"65 title="AI infrastructure"66 description="Data centers and projects with evidence of AI / HPC use, ranked and counted only when a source says so. Counts and MW cover confirmed + likely sites; associated mentions are reported separately."67 aside={<PillLink href={routes.map("layer=ai")}>Open the AI layer on the map →</PillLink>}68 />6970 <MetricRow cols={5}>71 <MetricCell>72 <Metric value={fmtInt(s.facilities)} label="AI facilities" note={`${fmtInt(s.confirmed)} confirmed · ${fmtInt(s.likely)} likely`} />73 </MetricCell>74 <MetricCell>75 <Metric value={fmtInt(s.projects)} label="AI projects" note="live project records" />76 </MetricCell>77 <MetricCell>78 <Metric value={<MwFigure mw={s.plannedMw} estimate />} label="Planned MW" note={s.plannedMw === null ? "no published figure" : "projects, published ≈ figures"} />79 </MetricCell>80 <MetricCell>81 <Metric value={<MwFigure mw={s.constructionMw} />} label="Construction MW" note={s.constructionMw === null ? "no published figure" : "under construction"} />82 </MetricCell>83 <MetricCell>84 <Metric value={fmtPct(s.mwCoverage)} label="MW coverage" note="share of AI records with a figure" />85 </MetricCell>86 </MetricRow>87 <MetricRow cols={3} className="border-t-0 [&>div]:border-t-0">88 <MetricCell>89 <Metric size="sm" value={fmtInt(s.countries)} label="Countries" />90 </MetricCell>91 <MetricCell>92 <Metric size="sm" value={fmtInt(s.operators)} label="Operators" />93 </MetricCell>94 <MetricCell>95 <Metric size="sm" value={fmtInt(s.associated)} label="Associated mentions" note="reported separately, not counted" />96 </MetricCell>97 </MetricRow>9899 <SectionNav items={sections} className="mt-4" />100101 <div className="flex flex-col gap-8 pt-6">102 <Section id="evidence" title="Evidence levels" description="How a site earns the AI mark. Only confirmed and likely sites are counted above." className="scroll-mt-28">103 <ul className="divide-y divide-hair">104 {LEVELS.map((l) => (105 <li key={l.level} className="grid grid-cols-[auto_minmax(0,1fr)] items-start gap-x-3 py-2.5 sm:grid-cols-[140px_minmax(0,1fr)_120px]">106 <div className="pt-0.5">{l.level === "unknown" ? <span className="text-[11.5px] uppercase tracking-[0.06em] text-ink-4">Unknown</span> : <AiMark evidence={l.level} size="sm" />}</div>107 <p className="text-[13px] text-ink-2">108 <span className="mr-1 font-medium text-ink">{AI_EVIDENCE_LABEL[l.level].replace("AI · ", "").replace(/^./, (c) => c.toUpperCase())}.</span>109 {l.rule}110 </p>111 <span className={`col-start-2 text-[11.5px] sm:col-start-3 sm:text-right ${l.counted ? "text-ok" : "text-ink-4"}`}>{l.counted ? "counted" : "not counted"}</span>112 </li>113 ))}114 </ul>115 <ApiNote note={d.evidenceNote} label="From the index" className="mt-3" />116 </Section>117118 <Section id="pipeline" title="Project pipeline by stage" description="AI project records by lifecycle stage; MW are planned, published figures (≈) and are never added to operational capacity." className="scroll-mt-28">119 {stages.length === 0 ? (120 <HonestEmpty title="No AI project in the pipeline" detail="projects appear when an announcement with AI evidence is ingested" />121 ) : (122 <ol className="divide-y divide-hair">123 {stages.map((st) => (124 <li key={st.status} className="grid grid-cols-[minmax(0,1fr)_auto_auto] items-center gap-x-3 py-2 text-[13px]">125 <div className="flex min-w-0 items-center gap-2">126 <span aria-hidden className="inline-block size-2 shrink-0 rounded-full" style={{ background: statusVar(st.status) }} />127 <span className="truncate text-ink">{STATUS_LABEL[st.status]}</span>128 <span className="hidden h-[4px] min-w-[60px] flex-1 bg-surface-3 sm:block" aria-hidden>129 <span className="block h-full" style={{ width: `${(st.count / stageMax) * 100}%`, background: statusVar(st.status) }} />130 </span>131 </div>132 <span className="figure text-ink-2">133 {fmtInt(st.count)} <span className="text-ink-4">{st.count === 1 ? "project" : "projects"}</span>134 </span>135 <span className="figure w-[96px] text-right text-ink-2">{st.mw === null ? <span className="text-ink-4">MW —</span> : <>≈ {fmtMw(st.mw)}</>}</span>136 </li>137 ))}138 </ol>139 )}140 <PublishedOnlyNote className="mt-2" />141 </Section>142143 <Section id="top" title="Top operators, markets and countries" description="Ranked by confirmed + likely AI facilities and projects; planned MW shown when published." className="scroll-mt-28">144 <div className="grid gap-6 lg:grid-cols-3">145 <TopColumn title="Operators" rows={d.topOperators.map((o) => ({ key: o.id, label: o.name, href: routes.operator(o.slug), facilities: o.facilities, projects: o.projects, plannedMw: o.plannedMw }))} />146 <TopColumn title="Markets" rows={d.topMetros.map((m) => ({ key: m.id, label: m.name, href: routes.market(m.slug), facilities: m.facilities, projects: m.projects, plannedMw: m.plannedMw, iso2: m.countryIso2 }))} />147 <TopColumn title="Countries" rows={d.topCountries.map((c) => ({ key: c.iso2, label: c.name, href: routes.country(c.slug), facilities: c.facilities, projects: c.projects, plannedMw: c.plannedMw, iso2: c.iso2 }))} />148 </div>149 </Section>150151 <Section id="announcements" title="New announcements" description="Events with AI evidence, newest first, each linked to its source." className="scroll-mt-28" aside={<Link href={routes.live("ai=1")} className="text-[12.5px] text-ink-3 hover:text-ink">Live feed (AI) →</Link>}>152 <EventList events={d.recentAnnouncements} dense emptyTitle="No AI announcement detected yet" emptyDescription="Announcements appear here when a source describes new AI / HPC capacity." />153 </Section>154155 <Section id="projects" title="Recent projects" description="Latest AI project records." className="scroll-mt-28" aside={<Link href={routes.projects("ai=1")} className="text-[12.5px] text-ink-3 hover:text-ink">All AI projects →</Link>}>156 <ProjectList items={d.recentProjects} emptyTitle="No AI project yet" />157 </Section>158159 <Section id="facilities" title="Facilities" description="Every facility flagged AI, with the evidence level per row." className="scroll-mt-28" aside={<span className="inline-flex items-center gap-2 text-[12px] text-ink-3"><span>MW coverage</span><CoverageBar value={s.mwCoverage} width="w-[110px]" /></span>}>160 <AiFacilitiesTable items={d.facilities} />161 </Section>162 </div>163164 <SourcesFooter sources={r.sources} note="AI status is never inferred from a single keyword; each evidence level is backed by the sentence that supports it. MW figures are published, site-scoped and containment-aware." />165 </Page>166 );167}168169function TopColumn({ title, rows }: { title: string; rows: Array<{ key: string; label: string; href: string; facilities: number; projects: number; plannedMw: number | null; iso2?: string | null }> }) {170 const total = rows.reduce((a, x) => a + x.facilities + x.projects, 0);171 return (172 <div>173 <div className="label-caps mb-2">{title}</div>174 {rows.length === 0 ? (175 <HonestEmpty title={`No ${title.toLowerCase()} yet`} />176 ) : (177 <ShareList unit="records" items={rows.map((x) => ({ key: x.key, label: x.label, href: x.href, count: x.facilities + x.projects, share: total ? (x.facilities + x.projects) / total : 0, mw: x.plannedMw, iso2: x.iso2 ?? null }))} />178 )}179 <p className="mt-1.5 text-[11px] text-ink-4">records = facilities + projects · MW = planned, published</p>180 </div>181 );182}183