"use client"; import type { CloudRegionSummary, DensityView, FacilityFilters, FacilityStatus, MapLayer, MapPoint, MapResponse, SearchHit, SearchResponse } from "@dci/core"; import { DENSITY_VIEWS, MAP_LAYERS, PIPELINE_STATUSES } from "@dci/core"; import { ChevronRight, List, Locate, PanelRightClose, PanelRightOpen, SlidersHorizontal } from "lucide-react"; import dynamic from "next/dynamic"; import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import { useCallback, useEffect, useMemo, useState, type ReactNode } from "react"; import { FacilityFilterFields } from "@/components/facility-filters"; import type { FlyTarget, MapExtraQuery, MapView } from "@/components/map/map-canvas"; import { DensityLegend, DensityToggle, LAYER_HINT, ModesRail, TimeMachineControl } from "@/components/map/map-modes"; import { AiMark, Badge, ConfidenceBadge, FlagBadges, PrecisionBadge, StatusBadge, TypeBadge } from "@/components/ui/badge"; import { Button, IconButton } from "@/components/ui/button"; import { cn } from "@/components/ui/cn"; import { CountryFlag } from "@/components/ui/country-flag"; import { FilterChips, ToggleChip, type Chip } from "@/components/ui/filter-chips"; import { SearchInput } from "@/components/ui/search-input"; import { Sheet } from "@/components/ui/sheet"; import { Skeleton } from "@/components/ui/skeleton"; import { StatusDot } from "@/components/ui/status-dot"; import { apiGet, clientApiBase } from "@/lib/api"; import { countryName, fmtInt, fmtMw, fmtPct } from "@/lib/format"; import { useDebounced, useMediaQuery } from "@/lib/hooks"; import { DENSITY_LABEL, MAP_LAYER_LABEL, PRECISION_LABEL, STATUS_LABEL, STATUS_SHORT, TYPE_LABEL } from "@/lib/labels"; import { countActiveFilters, describeFilters, filtersToQuery, parseFilters, removeFilter } from "@/lib/query"; import { routes } from "@/lib/routes"; const MapCanvas = dynamic(() => import("@/components/map/map-canvas").then((m) => m.MapCanvas), { ssr: false, loading: () => }); /* ------------------------------------------------------------------ URL state beyond FacilityFilters */ interface MapExtras { layer: MapLayer; density: DensityView | null; year: number | null; location_precision?: string; ai_evidence?: string; project_status?: string; expected_from?: number; expected_to?: number; } const PRECISION_GROUPS: Array<{ key: string; label: string; hint: string }> = [ { key: "exact,parcel,street", label: "Precise", hint: "exact / parcel / street" }, { key: "city,metro", label: "City / metro", hint: "placed at the city or metro point" }, { key: "approximate,unknown", label: "Approximate", hint: "approximate or unknown" }, ]; const AI_EVIDENCE = ["confirmed", "likely", "associated"] as const; const STAGES: FacilityStatus[] = [...PIPELINE_STATUSES, "cancelled"]; function parseExtras(sp: URLSearchParams): MapExtras { const layer = sp.get("layer"); const density = sp.get("density"); const year = Number(sp.get("year")); const num = (k: string) => { const v = Number(sp.get(k)); return sp.get(k) && Number.isFinite(v) ? v : undefined; }; return { layer: (MAP_LAYERS as readonly string[]).includes(layer ?? "") ? (layer as MapLayer) : "facilities", density: (DENSITY_VIEWS as readonly string[]).includes(density ?? "") ? (density as DensityView) : null, year: sp.get("year") && Number.isFinite(year) && year > 1900 ? year : null, location_precision: sp.get("location_precision") ?? undefined, ai_evidence: sp.get("ai_evidence") ?? undefined, project_status: sp.get("project_status") ?? undefined, expected_from: num("expected_from"), expected_to: num("expected_to"), }; } function extrasToQuery(x: MapExtras): MapExtraQuery { return { location_precision: x.location_precision, ai_evidence: x.ai_evidence, project_status: x.project_status, expected_from: x.expected_from, expected_to: x.expected_to }; } const LEGEND: Array<{ tone: Parameters[0]["status"]; label: string; hollow?: boolean }> = [ { tone: "operational", label: "Operational" }, { tone: "construction", label: "Under construction" }, { tone: "planned", label: "Planned / announced" }, { tone: "permitting", label: "Permitting / approved" }, { tone: "delayed", label: "Delayed" }, { tone: "closed", label: "Cancelled / closed" }, { tone: "rumored", label: "Rumored" }, ]; /* ------------------------------------------------------------------ extra filter fields */ function ExtraFilterFields({ value, onChange, layer }: { value: MapExtras; onChange: (x: MapExtras) => void; layer: MapLayer }) { const csv = (s: string | undefined) => (s ? s.split(",").filter(Boolean) : []); const toggleCsv = (key: "ai_evidence" | "project_status", member: string) => { const cur = csv(value[key]); const next = cur.includes(member) ? cur.filter((x) => x !== member) : [...cur, member]; onChange({ ...value, [key]: next.length ? next.join(",") : undefined }); }; const field = "figure h-9 w-full rounded-sm border border-hair-2 bg-surface px-2 text-[13px] outline-none focus:border-ink"; return (
Location precision
{PRECISION_GROUPS.map((g) => ( onChange({ ...value, location_precision: value.location_precision === g.key ? undefined : g.key })} dot={}> {g.label} ))}

Hollow rings mark city / metro / approximate points — never an exact site.

AI evidence
{AI_EVIDENCE.map((a) => ( toggleCsv("ai_evidence", a)}> {a} ))}
Project stage
{STAGES.map((s) => ( toggleCsv("project_status", s)} dot={}> {STATUS_SHORT[s]} ))}
Expected opening
–
); } /* ------------------------------------------------------------------ page */ export function MapPage() { const router = useRouter(); const sp = useSearchParams(); const isDesktop = useMediaQuery("(min-width: 768px)"); const isWide = useMediaQuery("(min-width: 1180px)"); const filters = useMemo(() => parseFilters(sp), [sp]); const extras = useMemo(() => parseExtras(sp), [sp]); const showCloud = sp.get("cr") === "1"; const initialView = useMemo(() => { const z = Number(sp.get("z")); const lat = Number(sp.get("lat")); const lng = Number(sp.get("lng")); return Number.isFinite(z) && Number.isFinite(lat) && Number.isFinite(lng) && sp.get("z") ? { zoom: z, lat, lng } : undefined; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const [panel, setPanel] = useState(false); // mobile filter sheet const [inspector, setInspector] = useState(true); // desktop right inspector const [inspectorTab, setInspectorTab] = useState<"filters" | "details">("filters"); const [draft, setDraft] = useState(filters); const [draftX, setDraftX] = useState(extras); const [synced, setSynced] = useState(filters); const [syncedX, setSyncedX] = useState(extras); if (synced !== filters) { setSynced(filters); setDraft(filters); } if (syncedX !== extras) { setSyncedX(extras); setDraftX(extras); } const [selected, setSelected] = useState(null); const [selectedCloud, setSelectedCloud] = useState(null); const [data, setData] = useState(null); const [error, setError] = useState(null); const [fly, setFly] = useState(null); const [legend, setLegend] = useState(false); // search box const [q, setQ] = useState(sp.get("q") ?? ""); const dq = useDebounced(q.trim(), 180); const [hits, setHits] = useState([]); const [hitsOpen, setHitsOpen] = useState(false); useEffect(() => { if (dq.length < 2) return; const ctrl = new AbortController(); apiGet("/search", { q: dq }, { base: clientApiBase(), signal: ctrl.signal, timeoutMs: 6000 }).then((r) => { if (!ctrl.signal.aborted && r.ok) setHits(r.data.hits.filter((h) => h.meta && (typeof h.meta.lat === "number" || Array.isArray(h.meta.bbox))).concat(r.data.hits.filter((h) => !(h.meta && (typeof h.meta.lat === "number" || Array.isArray(h.meta.bbox))))).slice(0, 8)); }); return () => ctrl.abort(); }, [dq]); /* URL writer: filters + extras via router, view via history.replaceState. */ const writeUrl = useCallback( (f: FacilityFilters, x: MapExtras, extra?: Record) => { const params = new URLSearchParams(filtersToQuery(f)); if (x.layer !== "facilities") params.set("layer", x.layer); if (x.density) params.set("density", x.density); if (x.year !== null) params.set("year", String(x.year)); for (const k of ["location_precision", "ai_evidence", "project_status"] as const) if (x[k]) params.set(k, x[k]!); for (const k of ["expected_from", "expected_to"] as const) if (x[k] !== undefined) params.set(k, String(x[k])); // the current view is kept in the URL by onViewChange (history.replaceState) — copy it forward const cur = new URLSearchParams(window.location.search); for (const k of ["z", "lat", "lng"]) { const v = cur.get(k); if (v !== null) params.set(k, v); } if (showCloud) params.set("cr", "1"); if (extra) { for (const [k, val] of Object.entries(extra)) { if (val === undefined) params.delete(k); else params.set(k, val); } } const s = params.toString(); router.replace(`${routes.map()}${s ? `?${s}` : ""}`, { scroll: false }); }, [router, showCloud], ); const setExtras = useCallback((patch: Partial) => writeUrl(filters, { ...extras, ...patch }), [writeUrl, filters, extras]); const onViewChange = useCallback((v: MapView) => { const params = new URLSearchParams(window.location.search); params.set("z", String(v.zoom)); params.set("lat", String(v.lat)); params.set("lng", String(v.lng)); window.history.replaceState(window.history.state, "", `${window.location.pathname}?${params.toString()}`); }, []); const onData = useCallback((r: MapResponse | null, err?: string) => { setData(r); setError(r ? null : (err ?? "unavailable")); }, []); const onSelect = useCallback((p: MapPoint) => { setSelectedCloud(null); setSelected(p); setInspectorTab("details"); setInspector(true); }, []); // hide mobile bottom nav while a sheet is open on the map useEffect(() => { const open = !isDesktop && (!!selected || !!selectedCloud || panel); if (open) document.body.setAttribute("data-hide-mobile-nav", ""); else document.body.removeAttribute("data-hide-mobile-nav"); return () => document.body.removeAttribute("data-hide-mobile-nav"); }, [selected, selectedCloud, panel, isDesktop]); const activeExtras = [extras.location_precision, extras.ai_evidence, extras.project_status, extras.expected_from, extras.expected_to].filter((v) => v !== undefined).length; const active = countActiveFilters(filters) + activeExtras; const chips: Chip[] = describeFilters(filters, { status: STATUS_LABEL, type: TYPE_LABEL }).map((c) => ({ key: `${c.key}:${c.member ?? c.value}`, label: c.label, value: c.value, onRemove: () => writeUrl(removeFilter(filters, c), extras) })); if (extras.location_precision) chips.push({ key: "lp", label: "Precision", value: PRECISION_GROUPS.find((g) => g.key === extras.location_precision)?.label ?? extras.location_precision, onRemove: () => setExtras({ location_precision: undefined }) }); if (extras.ai_evidence) chips.push({ key: "aie", label: "AI evidence", value: extras.ai_evidence.replace(/,/g, ", "), onRemove: () => setExtras({ ai_evidence: undefined }) }); if (extras.project_status) chips.push({ key: "ps", label: "Stage", value: extras.project_status.split(",").map((s) => STATUS_SHORT[s as FacilityStatus] ?? s).join(", "), onRemove: () => setExtras({ project_status: undefined }) }); if (extras.expected_from !== undefined || extras.expected_to !== undefined) chips.push({ key: "exp", label: "Expected", value: `${extras.expected_from ?? "…"}–${extras.expected_to ?? "…"}`, onRemove: () => setExtras({ expected_from: undefined, expected_to: undefined }) }); if (extras.year !== null) chips.push({ key: "year", label: "Known open by", value: String(extras.year), onRemove: () => setExtras({ year: null }) }); const pickHit = (h: SearchHit) => { setHitsOpen(false); setQ(h.title); const m = h.meta ?? {}; if (Array.isArray(m.bbox) && m.bbox.length === 4) setFly({ bbox: m.bbox as [number, number, number, number], key: Date.now() }); else if (typeof m.lat === "number" && typeof m.lng === "number") setFly({ center: [m.lng, m.lat], zoom: h.type === "facility" ? 13 : h.type === "country" ? 5 : 10, key: Date.now() }); else if (h.type === "country") writeUrl({ ...filters, country: (m.iso2 as string | undefined) ?? filters.country }, extras); else if (h.type === "operator") writeUrl({ ...filters, operator: h.slug }, extras); else if (h.type === "metro") writeUrl({ ...filters, metro: h.slug }, extras); else writeUrl({ ...filters, q: h.title }, extras); }; const total = data?.total ?? null; const yearCoverage = data?.yearCoverage ?? null; const densityMax = data?.density?.max ?? null; const statusLine = error ? "Data unavailable" : total === null ? "Loading…" : data?.mode === "density" ? `${fmtInt(total)} records · ${DENSITY_LABEL[data.density?.view ?? "facilities"].toLowerCase()}` : `${fmtInt(total)} ${extras.layer === "facilities" || extras.layer === "capacity" ? "facilities" : MAP_LAYER_LABEL[extras.layer].toLowerCase()}${data?.mode === "points" ? " in view" : ""}${data?.degraded ? " (clustered)" : ""}`; const facilityFields = (onChange: (n: FacilityFilters) => void, value: FacilityFilters, extraNode?: ReactNode) => ( writeUrl(value, extras, { cr: showCloud ? undefined : "1" })} dot={}> Cloud regions overlay {extraNode} } /> ); const legendPanel = (
    {LEGEND.map((l) => (
  • {l.label}
  • ))}
  • Size = known MW
  • City / metro ring
  • AI / HPC
  • Project (stage colour)
  • Internet exchange
  • Cloud region
  • n Cluster (count)
{extras.density && (
)}
); const railW = isWide ? 176 : 56; const inspW = 340; const leftOffset = isDesktop ? railW + 12 : 0; const rightOffset = isDesktop && inspector ? inspW + 12 : 0; return (

Global map of data center infrastructure

{ setSelected(null); setSelectedCloud(c); setInspectorTab("details"); setInspector(true); }} flyTo={fly} paddingBottom={isDesktop ? 0 : 260} />
{/* ---------------------------------------------------------------- desktop left rail: modes · density · time machine */} {isDesktop && ( )} {/* ---------------------------------------------------------------- desktop right inspector: filters · details */} {isDesktop && ( )} {isDesktop && !inspector && (
)} {/* ---------------------------------------------------------------- top bar: search + chips (+ mobile modes) */}
{ setQ(v); setHitsOpen(true); }} onFocus={() => setHitsOpen(true)} onBlur={() => setTimeout(() => setHitsOpen(false), 120)} onKeyDown={(e) => { if (e.key === "Enter" && hits[0]) pickHit(hits[0]); if (e.key === "Escape") setHitsOpen(false); }} placeholder="Search a place, operator or facility…" aria-label="Search the map" wrapperClassName="bg-surface/95 backdrop-blur" trailing={ !isDesktop && ( ) } /> {hitsOpen && dq.length >= 2 && hits.length > 0 && (
    {hits.map((h) => (
  • ))}
)}
{!isDesktop && setExtras({ layer: l })} orientation="horizontal" className="-mx-3 px-3" />} {data && total === 0 && !error && (

{extras.layer === "projects" || extras.layer === "pipeline" ? ( <> No project with published coordinates in this view — most projects carry a city-level location only.{" "} Browse the project list . ) : extras.year !== null ? ( <>No facility with a known opening date on or before {extras.year} in this view — opening dates are published for a small share of facilities. ) : ( <>Nothing published for the {MAP_LAYER_LABEL[extras.layer].toLowerCase()} layer in this view{active ? " with these filters" : ""}. )}

)} {(chips.length > 0 || showCloud) && ( writeUrl({}, { layer: extras.layer, density: null, year: null })} className="[&>*]:bg-surface/95 [&>*]:backdrop-blur"> {showCloud && ( )} )}
{/* ---------------------------------------------------------------- bottom-left: count + legend (+ mobile time machine) */}
{legend && !isDesktop &&
{legendPanel}
} {!isDesktop && extras.year !== null && (
setExtras({ year: y })} coverage={yearCoverage} compact />
)}
{!isDesktop && ( )} {statusLine} {extras.year !== null && isDesktop && Known open by {extras.year}{yearCoverage !== null && · dates known for {fmtPct(yearCoverage, yearCoverage < 0.1 ? 1 : 0)}}}
{/* ---------------------------------------------------------------- mobile filter sheet: filters + density + time machine */} {!isDesktop && ( setPanel(false)} title="Filter the map" description={total !== null ? statusLine : undefined} heightVh={84} id="map-filters" footer={
} >
setDraftX({ ...draftX, year: y })} coverage={yearCoverage} />
Density
{DENSITY_VIEWS.map((d) => ( setDraftX({ ...draftX, density: draftX.density === d ? null : d })}> {DENSITY_LABEL[d]} ))}
{facilityFields(setDraft, draft)}
)} {/* ---------------------------------------------------------------- mobile detail sheets (swipeable) */} {!isDesktop && ( <> setSelected(null)} modal={false} heightVh={48} width={380} title={selected?.n} description={selected ? [selected.o, selected.c ? countryName(selected.c) : null].filter(Boolean).join(" · ") : undefined}> {selected && } setSelectedCloud(null)} modal={false} heightVh={40} width={380} title={selectedCloud ? `${selectedCloud.provider.name} · ${selectedCloud.code}` : undefined} description={selectedCloud?.name}> {selectedCloud && } )}
); } /* ------------------------------------------------------------------ profiles */ function kindLabel(k: MapPoint["k"]): string { switch (k) { case "project": return "Project"; case "ixp": return "Internet exchange"; case "cloud_region": return "Cloud region"; case "landing_station": return "Cable landing station"; case "substation": return "Substation"; case "power_plant": return "Power plant"; default: return "Data center"; } } function hrefFor(p: MapPoint): string { switch (p.k) { case "project": return routes.project(p.slug); case "ixp": return routes.ixp(p.slug); case "cloud_region": return routes.cloudRegion(p.slug); default: return routes.facility(p.slug); } } function QuickProfile({ p, onClose }: { p: MapPoint; onClose?: () => void }) { const imprecise = p.p === "city" || p.p === "metro" || p.p === "approximate" || p.p === "unknown"; return (
{onClose && (
{kindLabel(p.k)}
{p.n}
{(p.o || p.c) &&
{[p.o, p.c ? countryName(p.c) : null].filter(Boolean).join(" · ")}
}
)}
{p.k !== "project" && p.k !== "ixp" && } {p.ai ? : null} {p.rs === "campus" && ( Campus )}
{p.k === "project" ? "Planned" : "Capacity"}
{p.mw === null || p.mw === undefined ? — : `${p.k === "project" ? "≈ " : ""}${fmtMw(p.mw)}`}
{(p.mw === null || p.mw === undefined) &&
not published
}
Status
{STATUS_SHORT[p.s]}
{p.y ? "Opened" : "Country"}
{p.y ? {p.y} : }

{PRECISION_LABEL[p.p]} {imprecise ? " — the ring marks the area, not a site" : ` · ${p.lat.toFixed(4)}, ${p.lng.toFixed(4)}`}

{p.o && ( )}

Confidence and per-field evidence: on the full profile (every figure opens its sources).

); } function CloudProfile({ c }: { c: CloudRegionSummary }) { return (
Cloud region
{c.provider.name} · {c.code}
{c.name}
{c.status} {c.isSovereign && ( Sovereign )}
Location
{c.city ?? countryName(c.countryIso2)}
Availability zones
{c.availabilityZones ?? "—"}

Providers publish the metro, not the building — the point marks the market.

); }