Files
gochat/frontend/app/javascript/shared/composables/useNumberFormatter.js
T
rogee 321c61aaae Vendor Chatwoot Vue 3 frontend into frontend/
Copy the Chatwoot (v4.14.0) frontend runnable subset into frontend/ for
customization:
- app/javascript/ (Vue SPA: dashboard, widget, sdk, portal, superadmin)
- app/views/ (ERB templates for vite-plugin-ruby entrypoint resolution)
- app/helpers/, app/assets/ (Rails view helpers, static assets)
- enterprise/ (Enterprise edition frontend overlay)
- config/vite.json, vite.config.ts, bin/vite (Vite-Rails toolchain)
- package.json, pnpm-lock.yaml, tailwind/postcss/eslint configs
- Gemfile, Gemfile.lock (vite_rails gem for bin/vite binstub)

Excluded Rails backend: controllers, models, services, jobs, mailers,
policies, db, lib, spec, public, node_modules.

Update references to the new frontend location:
- .gitignore: exclude frontend build artifacts (node_modules, tmp, packs),
  keep frontend/bin/ and frontend/vendor/ via negation
- backend/scripts/parity_frontend_smoke.sh: CHATWOOT_DIR default
  reference/chatwoot -> ../frontend
- backend/scripts/parity_frontend_browser_smoke.mjs: same default update
- AGENTS.md: add frontend section with Rails/Vite coupling notes
- README: architecture tree includes frontend/
2026-07-07 14:56:01 +08:00

68 lines
2.4 KiB
JavaScript

import { useLocale } from './useLocale';
/**
* Composable for number formatting with i18n locale support
* Provides methods to format numbers in compact and full display formats
*/
export function useNumberFormatter() {
const { resolvedLocale } = useLocale();
/**
* Formats numbers for display with clean, minimal formatting
* - Up to 1,000: show exact number (e.g., 999)
* - 1,000 to 999,999: show as "Xk" for exact thousands or "Xk+" for remainder (e.g., 1000 → "1k", 1500 → "1k+")
* - 1,000,000+: show in millions with 1 decimal place (e.g., 1,234,000 → "1.2M")
*
* Uses browser-native Intl.NumberFormat with proper i18n locale support
*
* @param {number} num - The number to format
* @returns {string} Formatted number string
*/
const formatCompactNumber = num => {
if (typeof num !== 'number' || Number.isNaN(num)) {
return '0';
}
// For numbers between -1000 and 1000 (exclusive), show exact number with locale formatting
if (Math.abs(num) < 1000) {
return new Intl.NumberFormat(resolvedLocale.value).format(num);
}
// For numbers with absolute value above 1,000,000, show in millions with 1 decimal place
if (Math.abs(num) >= 1000000) {
const millions = num / 1000000;
return new Intl.NumberFormat(resolvedLocale.value, {
notation: 'compact',
compactDisplay: 'short',
maximumFractionDigits: 1,
minimumFractionDigits: millions % 1 === 0 ? 0 : 1,
}).format(num);
}
// For numbers with absolute value between 1,000 and 1,000,000, show as "Xk" or "Xk+" using floor value
// For negative numbers, we want to floor towards zero (truncate), not towards negative infinity
const thousands = num >= 0 ? Math.floor(num / 1000) : Math.ceil(num / 1000);
const remainder = Math.abs(num) % 1000;
const suffix = remainder === 0 ? 'k' : 'k+';
return `${new Intl.NumberFormat(resolvedLocale.value).format(thousands)}${suffix}`;
};
/**
* Format a number for full display with locale-specific formatting
* @param {number} num - The number to format
* @returns {string} Formatted number string with full precision and locale formatting (e.g., 1,234,567)
*/
const formatFullNumber = num => {
if (typeof num !== 'number' || Number.isNaN(num)) {
return '0';
}
return new Intl.NumberFormat(resolvedLocale.value).format(num);
};
return {
formatCompactNumber,
formatFullNumber,
};
}