import Link from 'next/link'; import { Donut, HBars, SERIES } from '@/components/charts/charts'; import { fmtInt, num, titleCase } from '@/lib/format'; import { MISSION_LABELS, OBJECT_TYPE_LABELS, ORBIT_CLASS_COLORS, routes } from '@/lib/site'; import type { StatsSnapshot } from '@/lib/types'; import { Legend } from './local-charts'; import { ChartBlock, Note } from './shared'; const ORBIT_ORDER = ['LEO', 'MEO', 'GEO', 'HEO', 'OTHER', 'UNKNOWN']; /** Composition of the catalogue: orbit class (two donuts), object type, mission type, constellation shares. */ export function StatsComposition({ snapshot }: { snapshot: StatsSnapshot }) { const byOrbit = [...snapshot.by_orbit_class].sort((a, b) => ORBIT_ORDER.indexOf(a.orbit_class) - ORBIT_ORDER.indexOf(b.orbit_class)); const activeDonut = byOrbit.map((r) => ({ label: r.orbit_class === 'UNKNOWN' ? 'Unclassified' : r.orbit_class, value: num(r.active) ?? 0, color: ORBIT_CLASS_COLORS[r.orbit_class] ?? 'var(--other)' })).filter((d) => d.value > 0); const onOrbitDonut = byOrbit.map((r) => ({ label: r.orbit_class === 'UNKNOWN' ? 'Unclassified' : r.orbit_class, value: num(r.on_orbit) ?? 0, color: ORBIT_CLASS_COLORS[r.orbit_class] ?? 'var(--other)' })).filter((d) => d.value > 0); const byType = [...snapshot.by_object_type].sort((a, b) => (num(b.on_orbit) ?? 0) - (num(a.on_orbit) ?? 0)); const typeBars = byType.map((r, i) => ({ label: OBJECT_TYPE_LABELS[r.object_type] ?? titleCase(r.object_type), value: num(r.on_orbit) ?? 0, color: SERIES[i % SERIES.length] })); const byMission = [...snapshot.by_mission_type].sort((a, b) => (num(b.active) ?? 0) - (num(a.active) ?? 0)); const missionBars = byMission.map((r) => ({ label: MISSION_LABELS[r.mission_type] ?? titleCase(r.mission_type), value: num(r.active) ?? 0, color: 'var(--series-3)' })); const totalActive = num(snapshot.global.active_satellites) ?? 0; const constellations = [...snapshot.top_constellations].sort((a, b) => (num(b.active) ?? 0) - (num(a.active) ?? 0)).slice(0, 12); const constellationActive = constellations.reduce((s, c) => s + (num(c.active) ?? 0), 0); return (