SPB Git forge
3commits 1branches 0releases
417.0 KBsize
maindefault branch
10 days agolast push
TypeScript 66.5% Python 30.9% JavaScript 1.4% CSS 0.7%
4.9 KB · 93 lines tsx
Raw Blame History
1import Link from 'next/link';2import { Bars, StackedBars } from '@/components/charts/charts';3import { fmtInt, num } from '@/lib/format';4import { routes } from '@/lib/site';5import type { StatsSnapshot } from '@/lib/types';6import { ChartBlock, Derived, Note, TwoCol } from './shared';78/** Full-history charts (since 1957) + derived growth-rate table. */9export function StatsHistory({ snapshot }: { snapshot: StatsSnapshot }) {10  const launches = [...snapshot.launches_by_year].sort((a, b) => a.year - b.year);11  const payloads = [...snapshot.payloads_by_launch_year].sort((a, b) => a.year - b.year);12  const decays = [...snapshot.decays_by_year].sort((a, b) => a.year - b.year);13  const first = launches[0]?.year;14  const last = launches[launches.length - 1]?.year;15  const span = first && last ? `${first}–${last}` : undefined;1617  const launchSeries = launches.map((r) => ({ x: r.year, y: num(r.launches) ?? 0 }));18  const payloadStack = payloads.map((r) => {19    const p = num(r.payloads) ?? 0;20    const a = num(r.still_active) ?? 0;21    return { x: r.year, still_active: a, retired: Math.max(0, p - a) };22  });23  const decaySeries = decays.map((r) => ({ x: r.year, y: num(r.decayed) ?? 0 }));2425  // Derived: year-over-year change of payloads launched (last 10 complete + current year).26  const growth = payloads27    .map((r, i) => {28      const cur = num(r.payloads) ?? 0;29      const prev = i > 0 ? (num(payloads[i - 1]?.payloads) ?? 0) : null;30      const pct = prev && prev > 0 ? ((cur - prev) / prev) * 100 : null;31      return { year: r.year, payloads: cur, prev, pct, active: num(r.still_active) ?? 0 };32    })33    .slice(-10)34    .reverse();35  const currentYear = new Date().getUTCFullYear();3637  return (38    <div className="space-y-12">39      <ChartBlock title="Orbital launches per year" hint={span}>40        <Bars data={launchSeries} title="Orbital launches per year" height={190} xTicks={7} color="var(--series-1)" highlightLast />41        <Note className="mt-2">Launches with at least one catalogued object (CelesTrak SATCAT, grouped by COSPAR launch id). The last bar is the current, incomplete year.</Note>42      </ChartBlock>4344      <TwoCol>45        <ChartBlock title="Payloads by launch year" hint="Still active vs retired">46          <StackedBars data={payloadStack} keys={['still_active', 'retired']} labels={{ still_active: 'Still active', retired: 'Retired / decayed' }} title="Payloads by launch year, still active vs retired" height={200} xTicks={7} />47          <Note className="mt-2">Retired = payloads catalogued for that launch year − payloads still active today (derived from the two snapshot series).</Note>48        </ChartBlock>49        <ChartBlock title="Decays per year" hint="All object types">50          <Bars data={decaySeries} title="Objects decayed per year" height={200} xTicks={7} color="var(--series-4)" highlightLast />51          <Note className="mt-2">52            Objects with a published decay date in the SATCAT. Debris-only breakdown on <Link href={routes.debris()} className="text-accent hover:underline">/debris</Link>; recent reentries on{' '}53            <Link href={routes.reentries()} className="text-accent hover:underline">/reentries</Link>.54          </Note>55        </ChartBlock>56      </TwoCol>5758      <ChartBlock title="Satellite growth rate" hint="Last 10 years" derived>59        <table className="data-table stack text-sm">60          <thead>61            <tr>62              <th>Year</th>63              <th className="num">Payloads launched</th>64              <th className="num">Previous year</th>65              <th className="num">YoY change</th>66              <th className="num">Still active</th>67            </tr>68          </thead>69          <tbody>70            {growth.map((g) => (71              <tr key={g.year}>72                <td className="primary mono" data-label="Year">73                  {g.year}74                  {g.year === currentYear && <span className="ml-2 text-[11px] text-ink-3">year to date</span>}75                </td>76                <td className="num tnum" data-label="Payloads launched">{fmtInt(g.payloads)}</td>77                <td className="num tnum text-ink-3" data-label="Previous year">{g.prev === null ? '—' : fmtInt(g.prev)}</td>78                <td className="num tnum" data-label="YoY change">79                  {g.pct === null ? <span className="text-ink-3">—</span> : <span className={g.pct >= 0 ? 'text-active' : 'text-danger'}>{g.pct >= 0 ? '+' : ''}{g.pct.toFixed(1)}%</span>}80                </td>81                <td className="num tnum" data-label="Still active">{fmtInt(g.active)}</td>82              </tr>83            ))}84          </tbody>85        </table>86        <Note className="mt-3">87          <Derived className="mr-2" /> YoY change = (payloads launched in year − payloads launched the previous year) ÷ previous year, computed from the snapshot series. The current year is partial, so its change is not comparable.88        </Note>89      </ChartBlock>90    </div>91  );92}93