"use client"; import type { NearbyInfrastructure } from "@dci/core"; import Link from "next/link"; import { useEffect, useState } from "react"; import { EstimateMark, FlagBadges, PrecisionBadge, StatusBadge } from "@/components/ui/badge"; import { cn } from "@/components/ui/cn"; import { CountryFlag } from "@/components/ui/country-flag"; import { SegmentedControl } from "@/components/ui/segmented"; import { Skeleton } from "@/components/ui/skeleton"; import { apiGet, clientApiBase } from "@/lib/api"; import { bestMw, fmtInt, fmtMw } from "@/lib/format"; import { isImprecise } from "@/lib/labels"; import { routes } from "@/lib/routes"; const RADII = [5, 10, 25, 50, 100] as const; type Radius = (typeof RADII)[number]; function km(d: number): string { return d < 1 ? `${Math.round(d * 1000)} m` : `${d.toFixed(d < 10 ? 1 : 0)} km`; } function Group({ title, count, children, note }: { title: string; count: number; children?: React.ReactNode; note?: string }) { return (

{title}

{fmtInt(count)}
{count === 0 ?

{note ?? "None within this radius."}

: children}
); } /** * NEARBY INFRASTRUCTURE (§23): facilities, projects, IXPs, cloud regions and metros within a radius, from GET /nearby. * Wording is always "nearby" — proximity is a great-circle distance between published coordinates and never implies a * connection. Layers whose connectors do not exist yet (landing stations, substations, power plants) show the API note. */ export function NearbyInfrastructurePanel({ initial, center, excludeId }: { initial: NearbyInfrastructure | null; center: { lat: number; lng: number }; excludeId?: string }) { const [radius, setRadius] = useState((initial?.radiusKm as Radius | undefined) && RADII.includes(initial!.radiusKm as Radius) ? (initial!.radiusKm as Radius) : 25); // results cached per radius; the server-provided payload seeds its own radius const [fetched, setFetched] = useState>>({}); const [error, setError] = useState(null); const data: NearbyInfrastructure | null = initial && radius === initial.radiusKm ? initial : (fetched[radius] ?? null); const loading = !data && !error; useEffect(() => { if (data) return; const ctrl = new AbortController(); apiGet("/nearby", { lat: center.lat, lng: center.lng, radius_km: radius }, { base: clientApiBase(), signal: ctrl.signal, timeoutMs: 10000 }).then((r) => { if (ctrl.signal.aborted) return; if (r.ok) { setFetched((f) => ({ ...f, [radius]: r.data })); setError(null); } else setError(r.error); }); return () => ctrl.abort(); }, [radius, center.lat, center.lng, data]); const facilities = (data?.facilities ?? []).filter((f) => f.id !== excludeId); const empty = ["landingStations", "substations", "powerPlants"] as const; const emptyLabels: Record<(typeof empty)[number], string> = { landingStations: "Cable landing stations", substations: "Substations", powerPlants: "Power plants" }; return (
{ setError(null); setRadius(Number(v) as Radius); }} options={RADII.map((r) => ({ value: String(r), label: `${r} km` }))} size="xs" /> {loading ? "Loading…" : error ? `Unavailable (${error})` : data ? `${fmtInt(facilities.length)} facilities · ${fmtInt(data.ixps.length)} IXPs · ${fmtInt(data.cloudRegions.length)} cloud regions · ${fmtInt(data.projects.length)} projects` : ""} Open on map →
{loading && !data ? ( ) : !data ? (

Nearby infrastructure is unavailable right now.

) : (
    {facilities.slice(0, 12).map((n) => { const { mw, kind } = bestMw(n); return (
  • {n.name}
    {n.operator?.name ?? "Operator unknown"} {isImprecise(n.geoPrecision) && }
    {mw === null ? — : ( <> {kind === "planned" && } {fmtMw(mw)} )} {km(n.distanceKm)}
  • ); })}
{facilities.length > 12 &&

Showing 12 of {fmtInt(facilities.length)}.

}
    {data.ixps.slice(0, 8).map((x) => (
  • {x.name} {x.networkCount !== null ? `${fmtInt(x.networkCount)} nets · ` : ""} {km(x.distanceKm)}
  • ))}
    {data.cloudRegions.slice(0, 8).map((c) => (
  • {c.provider.name} {c.code} {km(c.distanceKm)}
  • ))}
    {data.projects.slice(0, 8).map((p) => (
  • {p.name} {p.plannedMw !== null ? `≈ ${fmtMw(p.plannedMw)} · ` : ""} {km(p.distanceKm)}
  • ))}
{data.metros.length > 0 && (
    {data.metros.map((m) => (
  • {m.name} {km(m.distanceKm)}
  • ))}
)}
)} {data && (
{empty.map((k) => (
{emptyLabels[k]} {fmtInt(data[k].length)}
{data[k].length === 0 &&

Empty until a licensed connector for this layer exists — never inferred.

}
))}

{data.note}

)}
); }