/** Format numbers for display — use abbreviation only for very large numbers */ export function formatNumber(value: any, decimals = 2): string { // Coerce to number — backend may return numeric stats as strings const num = typeof value === 'number' ? value : Number(value); if (value == null || isNaN(num)) return '-'; if (Math.abs(num) >= 10_000_000) return `${(num / 1_000_000).toFixed(1)}M`; if (Math.abs(num) >= 1_000_000) return `${(num / 1_000_000).toFixed(decimals)}M`; if (Number.isInteger(num)) return num.toLocaleString(); return num.toFixed(decimals); } /** Format a cell value for display in tables */ export function formatCellValue(value: any): string { if (value == null) return '\u2014'; // em dash if (typeof value === 'number') { // For small integers, just show them plainly if (Number.isInteger(value) && Math.abs(value) < 1_000_000) return value.toLocaleString(); return formatNumber(value); } if (typeof value === 'boolean') return value ? 'true' : 'false'; return String(value); } /** Return a CSS badge class for column type */ export function typeBadgeClass(columnType: string): string { const map: Record = { numeric: 'badge-numeric', categorical: 'badge-categorical', datetime: 'badge-datetime', text: 'badge-text', geo_lat_lon: 'badge-geo', boolean: 'badge-boolean', unknown: 'badge-text', }; return map[columnType] || 'badge-text'; } /** Short label for column type */ export function typeLabel(columnType: string): string { const map: Record = { numeric: '#', categorical: 'Aa', datetime: 'DT', text: 'T', geo_lat_lon: 'G', boolean: 'T/F', unknown: '?', }; return map[columnType] || '?'; }