Add SMS auto-block threshold input on tenant page.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-08 14:12:30 +03:30
co-authored by Cursor
parent 8e696bb591
commit 196166e1e5
2 changed files with 47 additions and 3 deletions
+15 -3
View File
@@ -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))
}
</script>
@@ -47,6 +58,7 @@ function onInput(raw: string | number) {
:placeholder="placeholder"
:disabled="disabled"
:value="display"
allow-clear
@update:value="onInput"
/>
</template>
+32
View File
@@ -50,6 +50,7 @@ const summary = ref<SmsSummary | null>(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 () => {
<a-button type="primary" size="large" :loading="saving" @click="saveThresholds">ذخیره آستانه‌ها</a-button>
</div>
</div>
<div class="subcard auto-block">
<h3>مسدودسازی خودکار ارسال</h3>
<p class="hint">
اگر مبلغی بگذارید و موجودی از آن کمتر شود، ارسال پیامک خودکار قطع می‌شود (کلید مسدودسازی
روشن نمی‌شود). خالی بگذارید تا فقط با کلید دستی مسدود شود.
</p>
<div class="threshold-grid">
<label>
<span class="tone-label">آستانه مسدودسازی خودکار (تومان)</span>
<MoneyInput
v-model:value="form.autoBlockBelowRials"
nullable
placeholder="خالی = بدون مسدودسازی خودکار"
/>
</label>
<a-button type="primary" size="large" :loading="saving" @click="saveThresholds">
ذخیره آستانه
</a-button>
</div>
</div>
</section>
<section class="panel-card">
@@ -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;