Spaces:
Running
Running
| /** | |
| * Enterprise-grade currency utility functions | |
| * Supports 10+ currencies with proper locale formatting | |
| */ | |
| export type Currency = 'USD' | 'EUR' | 'GBP' | 'INR' | 'JPY' | 'AUD' | 'CAD' | 'CHF' | 'CNY' | 'SGD'; | |
| export interface CurrencyInfo { | |
| code: Currency; | |
| symbol: string; | |
| name: string; | |
| locale: string; | |
| } | |
| export const CURRENCIES: Record<Currency, CurrencyInfo> = { | |
| USD: { code: 'USD', symbol: '$', name: 'US Dollar', locale: 'en-US' }, | |
| EUR: { code: 'EUR', symbol: '€', name: 'Euro', locale: 'de-DE' }, | |
| GBP: { code: 'GBP', symbol: '£', name: 'British Pound', locale: 'en-GB' }, | |
| INR: { code: 'INR', symbol: '₹', name: 'Indian Rupee', locale: 'en-IN' }, | |
| JPY: { code: 'JPY', symbol: '¥', name: 'Japanese Yen', locale: 'ja-JP' }, | |
| AUD: { code: 'AUD', symbol: 'A$', name: 'Australian Dollar', locale: 'en-AU' }, | |
| CAD: { code: 'CAD', symbol: 'C$', name: 'Canadian Dollar', locale: 'en-CA' }, | |
| CHF: { code: 'CHF', symbol: 'CHF', name: 'Swiss Franc', locale: 'de-CH' }, | |
| CNY: { code: 'CNY', symbol: '¥', name: 'Chinese Yuan', locale: 'zh-CN' }, | |
| SGD: { code: 'SGD', symbol: 'S$', name: 'Singapore Dollar', locale: 'en-SG' }, | |
| }; | |
| // Exchange rates: 1 USD = X units of currency (Real market rates Dec 2024) | |
| export const EXCHANGE_RATES_FROM_USD: Record<Currency, number> = { | |
| USD: 1.0, | |
| EUR: 0.95, // 1 USD = 0.95 EUR | |
| GBP: 0.79, // 1 USD = 0.79 GBP | |
| INR: 84.0, // 1 USD = 84 INR (REAL RATE) | |
| JPY: 149.5, // 1 USD = 149.5 JPY | |
| AUD: 1.54, // 1 USD = 1.54 AUD | |
| CAD: 1.35, // 1 USD = 1.35 CAD | |
| CHF: 0.88, // 1 USD = 0.88 CHF | |
| CNY: 7.15, // 1 USD = 7.15 CNY | |
| SGD: 1.35, // 1 USD = 1.35 SGD | |
| }; | |
| // Legacy alias for backward compatibility | |
| export const EXCHANGE_RATES_TO_USD = EXCHANGE_RATES_FROM_USD; | |
| /** | |
| * Convert amount from one currency to another | |
| * Uses real market exchange rates (1 USD = X currency format) | |
| * | |
| * Examples: | |
| * - $31,506 USD → ₹26,46,504 INR (×84) | |
| * - ₹1,00,000 INR → $1,190 USD (÷84) | |
| */ | |
| export function convertCurrency(amount: number, fromCurrency: Currency, toCurrency: Currency): number { | |
| if (fromCurrency === toCurrency) return amount; | |
| const fromRate = EXCHANGE_RATES_FROM_USD[fromCurrency] || 1; | |
| const toRate = EXCHANGE_RATES_FROM_USD[toCurrency] || 1; | |
| // Convert: source → USD → target | |
| // Step 1: source to USD (divide by source rate) | |
| // Step 2: USD to target (multiply by target rate) | |
| const usdAmount = amount / fromRate; | |
| const targetAmount = usdAmount * toRate; | |
| return targetAmount; | |
| } | |
| /** | |
| * Format amount with proper currency symbol and locale | |
| */ | |
| export function formatCurrency(amount: number, currency: Currency = 'USD'): string { | |
| const currencyInfo = CURRENCIES[currency] || CURRENCIES.USD; | |
| // Special handling for different currencies | |
| if (currency === 'INR') { | |
| return formatIndianCurrency(amount, currencyInfo.symbol); | |
| } | |
| if (currency === 'JPY') { | |
| // Yen typically has no decimal places | |
| const formatted = Math.round(amount).toLocaleString(currencyInfo.locale); | |
| return `${currencyInfo.symbol}${formatted}`; | |
| } | |
| const formatted = amount.toLocaleString(currencyInfo.locale, { | |
| minimumFractionDigits: 0, | |
| maximumFractionDigits: 2 | |
| }); | |
| return `${currencyInfo.symbol}${formatted}`; | |
| } | |
| /** | |
| * Format amount in Indian numbering system (lakhs, crores) | |
| */ | |
| function formatIndianCurrency(amount: number, symbol: string): string { | |
| const formatted = amount.toLocaleString('en-IN', { | |
| minimumFractionDigits: 0, | |
| maximumFractionDigits: 2 | |
| }); | |
| return `${symbol}${formatted}`; | |
| } | |
| /** | |
| * Get currency symbol | |
| */ | |
| export function getCurrencySymbol(currency: Currency = 'USD'): string { | |
| return (CURRENCIES[currency] || CURRENCIES.USD).symbol; | |
| } | |
| /** | |
| * Get full currency info | |
| */ | |
| export function getCurrencyInfo(currency: Currency = 'USD'): CurrencyInfo { | |
| return CURRENCIES[currency] || CURRENCIES.USD; | |
| } | |
| /** | |
| * Get user's preferred currency from Settings (localStorage) | |
| */ | |
| export function getUserPreferredCurrency(): Currency { | |
| try { | |
| const saved = localStorage.getItem('userPreferences'); | |
| if (saved) { | |
| const parsed = JSON.parse(saved); | |
| const currency = parsed?.preferences?.currency; | |
| if (currency && currency in CURRENCIES) { | |
| return currency as Currency; | |
| } | |
| } | |
| } catch (e) { | |
| console.warn('Failed to read currency preference:', e); | |
| } | |
| return 'INR'; // Default to INR | |
| } | |
| /** | |
| * Detect currency from analytics data - checks multiple sources | |
| * Falls back to user's preferred currency from Settings | |
| */ | |
| export function detectCurrency(data: any): Currency { | |
| // First, get user's preferred currency from settings | |
| const userPreference = getUserPreferredCurrency(); | |
| // Check if data has a specific currency | |
| const dataCurrency = | |
| data?.currency || | |
| data?.metrics?.currency || | |
| data?.report?.currency || | |
| data?.sections?.[0]?.data?.currency; | |
| // If data specifies a currency, use it; otherwise use user preference | |
| if (dataCurrency && dataCurrency in CURRENCIES) { | |
| return dataCurrency as Currency; | |
| } | |
| // Return user's preferred currency from settings | |
| return userPreference; | |
| } | |
| /** | |
| * Format compact currency (e.g., $1.2M, ₹10L) | |
| */ | |
| export function formatCompactCurrency(amount: number, currency: Currency = 'USD'): string { | |
| const symbol = getCurrencySymbol(currency); | |
| if (currency === 'INR') { | |
| // Indian format: Lakhs (L) and Crores (Cr) | |
| if (amount >= 10000000) { | |
| return `${symbol}${(amount / 10000000).toFixed(1)}Cr`; | |
| } else if (amount >= 100000) { | |
| return `${symbol}${(amount / 100000).toFixed(1)}L`; | |
| } else if (amount >= 1000) { | |
| return `${symbol}${(amount / 1000).toFixed(1)}K`; | |
| } | |
| } else { | |
| // Western format: K, M, B | |
| if (amount >= 1000000000) { | |
| return `${symbol}${(amount / 1000000000).toFixed(1)}B`; | |
| } else if (amount >= 1000000) { | |
| return `${symbol}${(amount / 1000000).toFixed(1)}M`; | |
| } else if (amount >= 1000) { | |
| return `${symbol}${(amount / 1000).toFixed(1)}K`; | |
| } | |
| } | |
| return formatCurrency(amount, currency); | |
| } | |