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.2 KB · 91 lines typescript
Raw Blame History
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