/** pt-BR display formatting helpers — currency/document masks only, no business logic. */ const currencyFormatter = new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }); /** * Formats a money value for display. Accepts the API's `string | null` * NUMERIC representation (never coerce it to `number` for anything other * than display) or a plain `number` from a controlled form input. */ export function formatCurrencyBRL(value: string | number | null | undefined): string { if (value === null || value === undefined || value === "") return currencyFormatter.format(0); const numeric = typeof value === "number" ? value : Number(value); if (Number.isNaN(numeric)) return currencyFormatter.format(0); return currencyFormatter.format(numeric); } export function formatDateBR(value: string | Date | null | undefined): string { if (!value) return "—"; const date = typeof value === "string" ? new Date(value) : value; if (Number.isNaN(date.getTime())) return "—"; return new Intl.DateTimeFormat("pt-BR").format(date); } /** Same as `formatDateBR` but includes the time — for timestamps where the hour matters (e.g. SLA due dates). */ export function formatDateTimeBR(value: string | Date | null | undefined): string { if (!value) return "—"; const date = typeof value === "string" ? new Date(value) : value; if (Number.isNaN(date.getTime())) return "—"; return new Intl.DateTimeFormat("pt-BR", { dateStyle: "short", timeStyle: "short" }).format(date); } /** CNPJ (14 digits) or CPF (11 digits) mask — falls back to the raw value for anything else. */ export function formatDocument(value: string | null | undefined): string { if (!value) return "—"; const digits = value.replace(/\D/g, ""); if (digits.length === 14) { return digits.replace(/^(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})$/, "$1.$2.$3/$4-$5"); } if (digits.length === 11) { return digits.replace(/^(\d{3})(\d{3})(\d{3})(\d{2})$/, "$1.$2.$3-$4"); } return value; }