diff --git a/src/components/MoneyInput.vue b/src/components/MoneyInput.vue index c7dbcf7..29a2bbd 100644 --- a/src/components/MoneyInput.vue +++ b/src/components/MoneyInput.vue @@ -10,6 +10,7 @@ import { /** * Money field bound to **rial** storage values (API/DB). * User types and sees **toman** (÷10 / ×10 at the edges). + * With `nullable`, empty input emits `null` (for optional thresholds). */ const props = withDefaults( defineProps<{ @@ -18,23 +19,33 @@ const props = withDefaults( placeholder?: string allowNegative?: boolean disabled?: boolean + nullable?: boolean }>(), { size: 'large', placeholder: '', allowNegative: false, disabled: false, + nullable: false, }, ) const emit = defineEmits<{ - (e: 'update:value', value: number): void + (e: 'update:value', value: number | null): void }>() -const display = computed(() => toGroupedDisplay(rialToToman(props.value ?? 0))) +const display = computed(() => { + if (props.nullable && (props.value == null || props.value === undefined)) return '' + return toGroupedDisplay(rialToToman(props.value ?? 0)) +}) function onInput(raw: string | number) { - const toman = fromGroupedInput(String(raw ?? ''), props.allowNegative) + const text = String(raw ?? '').trim() + if (props.nullable && !text) { + emit('update:value', null) + return + } + const toman = fromGroupedInput(text, props.allowNegative) emit('update:value', tomanToRial(toman)) } @@ -47,6 +58,7 @@ function onInput(raw: string | number) { :placeholder="placeholder" :disabled="disabled" :value="display" + allow-clear @update:value="onInput" /> diff --git a/src/pages/SmsTenantPage.vue b/src/pages/SmsTenantPage.vue index 0ef455f..382b517 100644 --- a/src/pages/SmsTenantPage.vue +++ b/src/pages/SmsTenantPage.vue @@ -50,6 +50,7 @@ const summary = ref(null) const form = reactive({ pricePerPartRials: 2500, isBlocked: false, + autoBlockBelowRials: null as number | null, alertCriticalRials: 10000, alertWarningRials: 100000, }) @@ -113,6 +114,8 @@ async function loadDetail() { Object.assign(form, { pricePerPartRials: data.account.pricePerPartRials, isBlocked: data.account.isBlocked, + autoBlockBelowRials: + data.account.autoBlockBelowRials == null ? null : data.account.autoBlockBelowRials, alertCriticalRials: data.account.alertCriticalRials, alertWarningRials: data.account.alertWarningRials, }) @@ -150,6 +153,7 @@ async function saveThresholds() { await api.patch(`/sms/tenants/${tenantId.value}`, { alertCriticalRials: form.alertCriticalRials, alertWarningRials: form.alertWarningRials, + autoBlockBelowRials: form.autoBlockBelowRials, }) message.success('آستانه‌ها ذخیره شد') await loadDetail() @@ -511,6 +515,27 @@ onMounted(async () => { ذخیره آستانه‌ها + +
+

مسدودسازی خودکار ارسال

+

+ اگر مبلغی بگذارید و موجودی از آن کمتر شود، ارسال پیامک خودکار قطع می‌شود (کلید مسدودسازی + روشن نمی‌شود). خالی بگذارید تا فقط با کلید دستی مسدود شود. +

+
+ + + ذخیره آستانه + +
+
@@ -845,6 +870,13 @@ onMounted(async () => { font-size: 0.92rem; } +.subcard .hint { + margin: 8px 0 12px; + color: #64748b; + font-size: 0.85rem; + line-height: 1.7; +} + .subcard.sky { background: linear-gradient(160deg, #f0f9ff, #fff); border-color: #bae6fd;