@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
|
-
<
|
|
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
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
|
|
330
|
-
|
|
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
|
+
}
|