/** * Central binary (1024) data-size formatting for the UI. * * - Use formatBinaryDataSizeBytes for raw byte counts (files, RAG totals, …). * - Use formatBinaryDataSizeFromMebibytes for API fields stored as MB (mebibytes), e.g. maxDataVolumeMB. */ const BINARY_BASE = 1024; const BINARY_UNITS = ['B', 'KB', 'MB', 'GB', 'TB'] as const; function _maxFractionDigits(value: number): number { if (value >= 100 || Number.isInteger(value)) return 0; if (value >= 10) return 1; return 2; } /** * Human-readable size from a byte count; picks B … TB automatically (1024-based). */ export function formatBinaryDataSizeBytes(bytes: number, localeId = 'de-CH'): string { if (!Number.isFinite(bytes)) return '—'; if (bytes < 0) return '—'; if (bytes === 0) return `0 ${BINARY_UNITS[0]}`; const rawExp = Math.floor(Math.log(bytes) / Math.log(BINARY_BASE)); const exp = Math.max(0, Math.min(BINARY_UNITS.length - 1, rawExp)); const value = bytes / BINARY_BASE ** exp; const maxFrac = _maxFractionDigits(value); const formatted = new Intl.NumberFormat(localeId, { maximumFractionDigits: maxFrac, minimumFractionDigits: 0, }).format(value); return `${formatted} ${BINARY_UNITS[exp]}`; } /** * Same as formatBinaryDataSizeBytes, but input is mebibytes (API convention for plan limits). */ export function formatBinaryDataSizeFromMebibytes(mebibytes: number, localeId = 'de-CH'): string { if (!Number.isFinite(mebibytes) || mebibytes < 0) return '—'; if (mebibytes === 0) return `0 ${BINARY_UNITS[0]}`; return formatBinaryDataSizeBytes(mebibytes * BINARY_BASE * BINARY_BASE, localeId); }