spb/satelliteindex
Public
TypeScript 66.5%
Python 30.9%
JavaScript 1.4%
CSS 0.7%
1/** Formatting helpers. All API timestamps are UTC ISO strings; we render UTC explicitly (orbital convention). */23const nf0 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 });4const nf1 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 1, minimumFractionDigits: 1 });5const nf2 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 2, minimumFractionDigits: 2 });6const nfCompact = new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 });78export function fmtInt(v: number | string | null | undefined): string {9 if (v === null || v === undefined || v === '') return '—';10 const n = typeof v === 'string' ? Number(v) : v;11 return Number.isFinite(n) ? nf0.format(n) : '—';12}13export function fmt1(v: number | string | null | undefined): string {14 if (v === null || v === undefined || v === '') return '—';15 const n = typeof v === 'string' ? Number(v) : v;16 return Number.isFinite(n) ? nf1.format(n) : '—';17}18export function fmt2(v: number | string | null | undefined): string {19 if (v === null || v === undefined || v === '') return '—';20 const n = typeof v === 'string' ? Number(v) : v;21 return Number.isFinite(n) ? nf2.format(n) : '—';22}23export function fmtCompact(v: number | string | null | undefined): string {24 if (v === null || v === undefined || v === '') return '—';25 const n = typeof v === 'string' ? Number(v) : v;26 return Number.isFinite(n) ? nfCompact.format(n) : '—';27}28export function fmtKm(v: number | string | null | undefined, digits = 0): string {29 if (v === null || v === undefined || v === '') return '—';30 const n = typeof v === 'string' ? Number(v) : v;31 if (!Number.isFinite(n)) return '—';32 return `${digits ? nf1.format(n) : nf0.format(n)} km`;33}34export function fmtDeg(v: number | string | null | undefined): string {35 if (v === null || v === undefined || v === '') return '—';36 const n = typeof v === 'string' ? Number(v) : v;37 return Number.isFinite(n) ? `${nf2.format(n)}°` : '—';38}39export function fmtMinutes(v: number | string | null | undefined): string {40 if (v === null || v === undefined || v === '') return '—';41 const n = typeof v === 'string' ? Number(v) : v;42 if (!Number.isFinite(n)) return '—';43 if (n >= 1440) return `${nf1.format(n / 1440)} d`;44 if (n >= 120) return `${nf1.format(n / 60)} h`;45 return `${nf1.format(n)} min`;46}47export function fmtPct(v: number | string | null | undefined, digits = 1): string {48 if (v === null || v === undefined || v === '') return '—';49 const n = typeof v === 'string' ? Number(v) : v;50 return Number.isFinite(n) ? `${n.toFixed(digits)}%` : '—';51}5253export function fmtDate(v: string | null | undefined): string {54 if (!v) return '—';55 const d = new Date(v.length === 10 ? `${v}T00:00:00Z` : v);56 if (Number.isNaN(d.getTime())) return '—';57 return d.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', day: '2-digit', timeZone: 'UTC' });58}59export function fmtDateTime(v: string | null | undefined): string {60 if (!v) return '—';61 const d = new Date(v);62 if (Number.isNaN(d.getTime())) return '—';63 return `${d.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', day: '2-digit', timeZone: 'UTC' })} ${d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', timeZone: 'UTC' })} UTC`;64}65export function fmtAgo(v: string | null | undefined, now: number = Date.now()): string {66 if (!v) return 'unavailable';67 const t = new Date(v).getTime();68 if (Number.isNaN(t)) return 'unavailable';69 const s = Math.max(0, Math.round((now - t) / 1000));70 if (s < 60) return 'just now';71 const m = Math.round(s / 60);72 if (m < 60) return `${m} min ago`;73 const h = Math.round(m / 60);74 if (h < 48) return `${h} h ago`;75 const d = Math.round(h / 24);76 if (d < 60) return `${d} d ago`;77 return fmtDate(v);78}79export function fmtYear(v: string | null | undefined): string {80 return v ? v.slice(0, 4) : '—';81}82export function titleCase(s: string | null | undefined): string {83 if (!s) return '—';84 return s.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());85}86export function num(v: number | string | null | undefined): number | null {87 if (v === null || v === undefined || v === '') return null;88 const n = typeof v === 'string' ? Number(v) : v;89 return Number.isFinite(n) ? n : null;90}91