export function formatInt(n: number): string { return new Intl.NumberFormat("pt-BR").format(Math.round(n)); } /** mm:ss a partir de segundos — usado por TME/TMA (agente.md secao 162). */ export function formatDuration(seconds: number): string { const total = Math.round(seconds); const mm = Math.floor(total / 60); const ss = total % 60; return `${mm}:${String(ss).padStart(2, "0")}`; } export function formatPercent(ratio: number): string { return new Intl.NumberFormat("pt-BR", { style: "percent", maximumFractionDigits: 1 }).format(ratio); } export function formatBytes(bytes: number): { value: string; unit: string } { if (bytes === 0) return { value: "0", unit: "B" }; const units = ["B", "KB", "MB", "GB", "TB"]; const exp = Math.min(Math.floor(Math.log(bytes) / Math.log(1000)), units.length - 1); const value = bytes / 1000 ** exp; return { value: value.toFixed(exp === 0 ? 0 : 1), unit: units[exp] }; } export const AI_USAGE_LABELS: Record = { AI_TRANSCRIPTION_SECONDS: "Transcrição (s)", AI_ANALYSIS_REQUEST: "Análises", AI_INPUT_TOKENS: "Tokens de entrada", AI_OUTPUT_TOKENS: "Tokens de saída", }; export function formatCurrency(value: number, currency = "BRL"): string { return new Intl.NumberFormat("pt-BR", { style: "currency", currency, maximumFractionDigits: 6 }).format(value); } export function formatDate(iso: string): string { return new Intl.DateTimeFormat("pt-BR", { day: "2-digit", month: "short", year: "numeric" }).format(new Date(iso)); } export function formatDateTime(iso: string): string { return new Intl.DateTimeFormat("pt-BR", { day: "2-digit", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit", }).format(new Date(iso)); } /** agente.md secao 128 — catálogo de preço, mesma nomenclatura do * `PriceItemType` do backend (packages/database/prisma/schema.prisma). */ export const PRICE_ITEM_TYPE_LABELS: Record = { BASE_SUBSCRIPTION: "Assinatura base", EXTENSION_MONTH: "Ramal / mês", AGENT_MONTH: "Agente / mês", TRUNK_MONTH: "Tronco / mês", CALL: "Chamada (fixo)", CALL_MINUTE: "Minuto de chamada (padrão)", FIXED_MINUTE: "Minuto — fixo", MOBILE_MINUTE: "Minuto — móvel", INTERNATIONAL_MINUTE: "Minuto — internacional", AI_TRANSCRIPTION_MINUTE: "IA — transcrição / minuto", AI_ANALYSIS_CALL: "IA — análise / chamada", AI_INPUT_TOKEN: "IA — token de entrada", AI_OUTPUT_TOKEN: "IA — token de saída", RECORDING_GB_MONTH: "Armazenamento / GB / mês", }; export const PRICE_ITEM_TYPES = Object.keys(PRICE_ITEM_TYPE_LABELS) as Array; /** agente.md secao 129 — tipos de destino de um RateDeckEntry. */ export const DESTINATION_TYPE_LABELS: Record = { FIXED: "Fixo", MOBILE: "Móvel", INTERNATIONAL: "Internacional", }; export const DESTINATION_TYPES = Object.keys(DESTINATION_TYPE_LABELS) as Array;