// ============================================================================ // Formatting Utilities // ============================================================================ /** * Форматирование числа с разделением тысяч */ export const formatNumber = ( value: number | null | undefined, showSign: boolean = false ): string => { if (value === null || value === undefined) return "—"; const sign = showSign && value > 0 ? "+" : ""; return `${sign}${new Intl.NumberFormat("ru-RU").format(value)}`; }; /** * Форматирование валюты (рубли) */ export const formatCurrency = (value: number | null | undefined): string => { if (value === null || value === undefined) return "—"; return new Intl.NumberFormat("ru-RU", { style: "currency", currency: "RUB", minimumFractionDigits: 0, maximumFractionDigits: 0, }).format(value); }; /** * Форматирование CPF/CPM с двумя знаками после запятой */ export const formatMetric = (value: number | null | undefined): string => { if (value === null || value === undefined) return "—"; return new Intl.NumberFormat("ru-RU", { minimumFractionDigits: 2, maximumFractionDigits: 2, }).format(value); }; /** * Форматирование процента */ export const formatPercent = ( value: number | null | undefined, showSign: boolean = false ): string => { if (value === null || value === undefined) return "—"; const sign = showSign && value > 0 ? "+" : ""; return `${sign}${formatMetric(value)}%`; }; /** * Форматирование даты */ export const formatDate = ( date: string | Date | null | undefined, includeTime: boolean = false ): string => { if (!date) return "—"; const d = typeof date === "string" ? new Date(date) : date; if (includeTime) { return new Intl.DateTimeFormat("ru-RU", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", }).format(d); } return new Intl.DateTimeFormat("ru-RU", { day: "2-digit", month: "2-digit", year: "numeric", }).format(d); }; /** * Форматирование относительной даты (сколько прошло) */ export const formatRelativeDate = (date: string | Date): string => { const d = typeof date === "string" ? new Date(date) : date; const now = new Date(); const diff = now.getTime() - d.getTime(); const seconds = Math.floor(diff / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 7) { return formatDate(d); } else if (days > 0) { return `${days} ${pluralize(days, "день", "дня", "дней")} назад`; } else if (hours > 0) { return `${hours} ${pluralize(hours, "час", "часа", "часов")} назад`; } else if (minutes > 0) { return `${minutes} ${pluralize( minutes, "минута", "минуты", "минут" )} назад`; } else { return "только что"; } }; /** * Плюрализация русских слов */ export const pluralize = ( count: number, one: string, few: string, many: string ): string => { const mod10 = count % 10; const mod100 = count % 100; if (mod10 === 1 && mod100 !== 11) { return one; } else if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) { return few; } else { return many; } }; /** * Сокращение больших чисел (1000 -> 1K) */ export const formatCompactNumber = ( value: number | null | undefined ): string => { if (value === null || value === undefined) return "—"; if (value >= 1000000) { return `${(value / 1000000).toFixed(1)}M`; } else if (value >= 1000) { return `${(value / 1000).toFixed(1)}K`; } return formatNumber(value); }; /** * Форматирование username (с @) */ export const formatUsername = (username: string | null | undefined): string => { if (!username) return "—"; return username.startsWith("@") ? username : `@${username}`; }; /** * Обрезка длинного текста */ export const truncate = (text: string, maxLength: number = 50): string => { if (text.length <= maxLength) return text; return `${text.substring(0, maxLength)}...`; };