@mundogamernetwork/shared-ui 1.8.13 → 1.8.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,67 @@
1
+ <template>
2
+ <input
3
+ :id="id"
4
+ type="text"
5
+ inputmode="numeric"
6
+ autocomplete="off"
7
+ :value="display"
8
+ :placeholder="placeholder"
9
+ :required="required"
10
+ class="mg-money-input"
11
+ @input="onInput"
12
+ @change="emit('change')"
13
+ />
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ // Cents-based currency mask: every keystroke keeps only the digits typed so
18
+ // far and rebuilds the formatted string from the right (like a POS/bank
19
+ // amount field), so the caret always lands at the end. modelValue stays a
20
+ // plain decimal number (e.g. 19.99) or null for "unset" — matching what the
21
+ // rate-card/bundle forms already send to the API, so this is a drop-in
22
+ // replacement for a plain <input type="number"> without touching save logic.
23
+ const props = defineProps<{
24
+ modelValue: number | string | null;
25
+ /** ISO currency code (USD/BRL/EUR/...) — drives which locale's grouping/decimal marks are used. */
26
+ currency: string;
27
+ placeholder?: string;
28
+ id?: string;
29
+ required?: boolean;
30
+ }>();
31
+
32
+ const emit = defineEmits<{
33
+ (e: 'update:modelValue', value: number | null): void;
34
+ (e: 'change'): void;
35
+ }>();
36
+
37
+ const { formatterFor } = useCurrencyFormat();
38
+
39
+ const numericValue = computed(() => {
40
+ const n = Number(props.modelValue);
41
+ return props.modelValue !== null && props.modelValue !== '' && Number.isFinite(n) ? n : null;
42
+ });
43
+
44
+ const display = computed(() => (numericValue.value == null ? '' : formatterFor(props.currency).format(numericValue.value)));
45
+
46
+ function onInput(event: Event) {
47
+ const digits = (event.target as HTMLInputElement).value.replace(/\D/g, '').replace(/^0+(?=\d)/, '');
48
+ emit('update:modelValue', digits === '' ? null : Number(digits) / 100);
49
+ }
50
+ </script>
51
+
52
+ <style scoped>
53
+ .mg-money-input {
54
+ width: 100%;
55
+ background: #272930;
56
+ border: 1px solid #333;
57
+ color: #fff;
58
+ padding: 8px;
59
+ font-size: 13px;
60
+ outline: none;
61
+ font-variant-numeric: tabular-nums;
62
+ }
63
+
64
+ .mg-money-input:focus {
65
+ border-color: #fdb215;
66
+ }
67
+ </style>
@@ -77,7 +77,7 @@
77
77
  </div>
78
78
  <div class="form-group">
79
79
  <label>{{ $t("media_kit.bundles.bundle_price") }}</label>
80
- <input v-model="form.bundle_price" type="number" step="0.01" min="0" />
80
+ <MgMoneyInput v-model="form.bundle_price" :currency="form.currency" />
81
81
  </div>
82
82
  <div class="form-group">
83
83
  <label>{{ $t("media_kit.bundles.currency") }}</label>
@@ -318,9 +318,7 @@ function getBundleDiscount(bundle: Bundle): number {
318
318
  return Math.round(((bundle.original_price - bundle.bundle_price) / bundle.original_price) * 100);
319
319
  }
320
320
 
321
- const formatPrice = (value: string, currency = 'USD') => {
322
- return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(Number(value));
323
- };
321
+ const { formatMoney: formatPrice } = useCurrencyFormat();
324
322
 
325
323
  function openNewForm() {
326
324
  form.value = { ...defaultForm, rate_card_ids: [], quantities: {} };
@@ -36,12 +36,12 @@
36
36
  <div class="form-row">
37
37
  <div class="form-group">
38
38
  <label>{{ $t("media_kit.editor.price_min") }}</label>
39
- <input v-model="form.price_min" type="number" step="0.01" min="0" :class="{ 'input-error': rateCardErrors.price_min }" @input="delete rateCardErrors.price_min" required />
39
+ <MgMoneyInput v-model="form.price_min" :currency="form.currency" :class="{ 'input-error': rateCardErrors.price_min }" required @change="delete rateCardErrors.price_min" />
40
40
  <span v-if="rateCardErrors.price_min" class="field-error">{{ $t('media_kit.editor.error_required') }}</span>
41
41
  </div>
42
42
  <div class="form-group">
43
43
  <label>{{ $t("media_kit.editor.price_max") }}</label>
44
- <input v-model="form.price_max" type="number" step="0.01" min="0" />
44
+ <MgMoneyInput v-model="form.price_max" :currency="form.currency" />
45
45
  </div>
46
46
  <div class="form-group">
47
47
  <label>{{ $t("media_kit.editor.currency") }}</label>
@@ -325,15 +325,15 @@ const removeCard = async (id: number) => {
325
325
  }
326
326
  };
327
327
 
328
+ const { formatMoney } = useCurrencyFormat();
329
+
328
330
  const formatPrice = (min: string, max?: string, currency = 'USD') => {
329
- const fmt = (v: string) => new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(Number(v));
330
- if (max && Number(max) > Number(min)) return `${fmt(min)} - ${fmt(max)}`;
331
- return fmt(min);
331
+ if (max && Number(max) > Number(min)) return `${formatMoney(min, currency)} - ${formatMoney(max, currency)}`;
332
+ return formatMoney(min, currency);
332
333
  };
333
334
 
334
335
  // --- MGN fee preview -------------------------------------------------------
335
- const fmtMoney = (v: number) =>
336
- new Intl.NumberFormat('en-US', { style: 'currency', currency: form.value.currency || 'USD' }).format(v);
336
+ const fmtMoney = (v: number) => formatMoney(v, form.value.currency || 'USD');
337
337
 
338
338
  const netOf = (v: string): number | null => {
339
339
  const n = Number(v);
@@ -0,0 +1,31 @@
1
+ // Fixed locale per currency code (not the viewer's own UI locale) — a BRL/USD/EUR
2
+ // price should always format with that currency's own grouping/decimal
3
+ // conventions, regardless of whether the dashboard is being browsed in
4
+ // pt-BR/en/es/de/ro. Same convention already proven in ideia-bosta's MoneyInput.
5
+ const CURRENCY_LOCALE: Record<string, string> = {
6
+ EUR: 'de-DE',
7
+ USD: 'en-US',
8
+ BRL: 'pt-BR',
9
+ };
10
+
11
+ export function useCurrencyFormat() {
12
+ function formatterFor(currency: string) {
13
+ return new Intl.NumberFormat(CURRENCY_LOCALE[currency] ?? 'en-US', {
14
+ style: 'currency',
15
+ currency: currency || 'USD',
16
+ });
17
+ }
18
+
19
+ function formatMoney(value: number | string | null | undefined, currency = 'USD'): string {
20
+ const n = Number(value);
21
+ if (value == null || value === '' || !Number.isFinite(n)) return '';
22
+ try {
23
+ return formatterFor(currency).format(n);
24
+ } catch {
25
+ // Unknown/invalid ISO currency code — fall back rather than throw.
26
+ return `${currency} ${n.toFixed(2)}`;
27
+ }
28
+ }
29
+
30
+ return { formatMoney, formatterFor, CURRENCY_LOCALE };
31
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mundogamernetwork/shared-ui",
3
- "version": "1.8.13",
3
+ "version": "1.8.14",
4
4
  "description": "Mundo Gamer Network - Shared UI Layer (Nuxt 3)",
5
5
  "type": "module",
6
6
  "main": "./nuxt.config.ts",