export function asNumber(value: string | number | null | undefined) { if (value === null || value === undefined) { return 0; } const parsed = typeof value === 'number' ? value : Number(value); return Number.isFinite(parsed) ? parsed : 0; } type NumberScale = { divisor: number; suffix: string; }; const NUMBER_SCALES: NumberScale[] = [ { divisor: 1, suffix: '' }, { divisor: 1_000, suffix: 'K' }, { divisor: 1_000_000, suffix: 'M' }, { divisor: 1_000_000_000, suffix: 'B' } ]; type FormatScaledNumberOptions = { minimumFractionDigits?: number; maximumFractionDigits?: number; }; export function formatScaledNumber( value: string | number | null | undefined, options: FormatScaledNumberOptions = {} ) { const { minimumFractionDigits = 0, maximumFractionDigits = 1 } = options; const numeric = asNumber(value); const absolute = Math.abs(numeric); let scaleIndex = 0; if (absolute >= 1_000_000_000) { scaleIndex = 3; } else if (absolute >= 1_000_000) { scaleIndex = 2; } else if (absolute >= 1_000) { scaleIndex = 1; } let scaledAbsolute = absolute / NUMBER_SCALES[scaleIndex].divisor; if ( Number(scaledAbsolute.toFixed(maximumFractionDigits)) >= 1_000 && scaleIndex < NUMBER_SCALES.length - 1 ) { scaleIndex += 1; scaledAbsolute = absolute / NUMBER_SCALES[scaleIndex].divisor; } const scaled = numeric < 0 ? -scaledAbsolute : scaledAbsolute; const formatted = new Intl.NumberFormat('en-US', { minimumFractionDigits, maximumFractionDigits }).format(scaled); return `${formatted}${NUMBER_SCALES[scaleIndex].suffix}`; } export function formatCurrency(value: string | number | null | undefined) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 2 }).format(asNumber(value)); } export function formatPercent(value: string | number | null | undefined) { return `${asNumber(value).toFixed(2)}%`; } export function formatCompactCurrency(value: string | number | null | undefined) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', notation: 'compact', maximumFractionDigits: 2 }).format(asNumber(value)); }