import { formatMoney } from "@vmall/shared"; import type { Currency } from "@vmall/shared"; /** Currency-aware money formatter: exponents come from the API, cached app-wide. */ export function useMoney() { const { $api } = useNuxtApp(); const { locale } = useI18n(); const currencies = useState("vmall.currencies", () => []); async function load(): Promise { if (currencies.value.length > 0) return; try { currencies.value = await $api.listCurrencies(); } catch { /* keep empty; fmt falls back to exponent 2 */ } } function exponentOf(currency: string): number { return currencies.value.find((c) => c.code === currency)?.exponent ?? 2; } function fmt(amountMinor: number, currency: string): string { return formatMoney(amountMinor, currency, exponentOf(currency), locale.value); } /** Currency for money fields without an explicit currency: the table's base. */ const shopCurrency = computed( () => currencies.value.find((c) => c.is_base)?.code ?? currencies.value[0]?.code ?? "USD", ); /** Parse a major-unit decimal string into integer minor units; null when invalid. */ function majorToMinor(value: string, currency: string): number | null { const clean = value.trim(); if (!/^\d+(?:\.\d+)?$/.test(clean)) return null; const exponent = exponentOf(currency); const [whole, fraction = ""] = clean.split("."); if (fraction.length > exponent) return null; const minor = Number(whole) * 10 ** exponent + Number(fraction.padEnd(exponent, "0") || 0); return Number.isSafeInteger(minor) ? minor : null; } /** Render integer minor units as a major-unit string for form inputs. */ function minorToMajor(minor: number, currency: string): string { const exponent = exponentOf(currency); const sign = minor < 0 ? "-" : ""; const abs = Math.abs(minor); const whole = Math.trunc(abs / 10 ** exponent); if (exponent === 0) return `${sign}${whole}`; const fraction = (abs % 10 ** exponent).toString().padStart(exponent, "0"); return `${sign}${whole}.${fraction}`; } return { currencies, load, fmt, shopCurrency, majorToMinor, minorToMajor }; }