/** Shared display-formatting helpers for the backoffice. */ /** snake_case / SCREAMING_CASE → Title Case. */ export function humanize(value: string): string { return value .toLowerCase() .split(/[_\s]+/) .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(" "); } export function formatDate(value: string | null | undefined): string { if (!value) return "—"; const d = new Date(value); return Number.isNaN(d.getTime()) ? "—" : d.toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric", }); } export function formatDateTime(value: string | null | undefined): string { if (!value) return "—"; const d = new Date(value); return Number.isNaN(d.getTime()) ? "—" : d.toLocaleString(undefined, { year: "numeric", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", }); } /** Pass `fractionDigits` where cents matter; the default matches the legacy whole-figure display. */ export function formatMoney( amount: number, currency: string, fractionDigits?: number, ): string { return new Intl.NumberFormat(undefined, { style: "currency", currency, ...(fractionDigits === undefined ? { maximumFractionDigits: 0 } : { minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits, }), }).format(amount); } export function formatBytes(bytes: number): string { if (!bytes) return "0 B"; const units = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(1024)); const value = bytes / Math.pow(1024, i); return `${value.toFixed(i === 0 ? 0 : 1)} ${units[i]}`; }