Add support panel UI, search filters, and money thousand separators.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-08 11:20:26 +03:30
co-authored by Cursor
parent ab4d9ef133
commit d0d621502f
13 changed files with 1169 additions and 132 deletions
+8 -67
View File
@@ -6,7 +6,9 @@ import { ArrowRightOutlined } from '@ant-design/icons-vue'
import type { TableColumnsType } from 'ant-design-vue'
import api from '@/api/http'
import JalaliDatePicker from '@/components/JalaliDatePicker.vue'
import MoneyInput from '@/components/MoneyInput.vue'
import { formatIranDateTime, todayIranDateInput } from '@/utils/datetime'
import { formatMoney, formatNumber } from '@/utils/money'
type BalanceLevel = 'healthy' | 'warning' | 'critical' | 'unknown'
@@ -96,42 +98,6 @@ const pageSize = 20
const tenantId = computed(() => String(route.params.tenantId || ''))
function toGroupedDisplay(value: number | null | undefined) {
if (value == null || Number.isNaN(Number(value))) return ''
const n = Math.trunc(Number(value))
const neg = n < 0
const digits = String(Math.abs(n))
const grouped = digits.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
return neg ? `-${grouped}` : grouped
}
function fromGroupedInput(raw: string, allowNegative = false) {
const text = String(raw ?? '')
const neg = allowNegative && text.trim().startsWith('-')
const digits = text.replace(/[^\d]/g, '')
if (!digits) return 0
const n = Number(digits)
return neg ? -n : n
}
function onGroupedMoneyInput(
apply: (n: number) => void,
value: string | number,
allowNegative = false,
) {
apply(fromGroupedInput(String(value ?? ''), allowNegative))
}
function formatNumber(value: number | null | undefined) {
if (value == null) return '—'
return new Intl.NumberFormat('fa-IR').format(value)
}
function formatMoney(value: number | null | undefined) {
if (value == null) return '—'
return `${formatNumber(value)} ریال`
}
function toneClass(level?: BalanceLevel) {
return level || 'unknown'
}
@@ -478,13 +444,7 @@ onMounted(async () => {
<div class="subcard sky">
<h3>تعرفه پیامک</h3>
<div class="inline-actions">
<a-input
size="large"
dir="ltr"
class="money-input"
:value="toGroupedDisplay(form.pricePerPartRials)"
@update:value="(v: string | number) => onGroupedMoneyInput((n) => (form.pricePerPartRials = n), v)"
/>
<MoneyInput v-model:value="form.pricePerPartRials" placeholder="قیمت هر پارت" />
<a-button type="primary" size="large" :loading="saving" @click="savePrice">ذخیره قیمت</a-button>
</div>
</div>
@@ -492,13 +452,10 @@ onMounted(async () => {
<div class="subcard emerald">
<h3>شارژ کیف پول</h3>
<div class="charge-grid">
<a-input
size="large"
dir="ltr"
class="money-input"
<MoneyInput
v-model:value="chargeForm.amountRials"
allow-negative
placeholder="مبلغ (ریال)"
:value="toGroupedDisplay(chargeForm.amountRials)"
@update:value="(v: string | number) => onGroupedMoneyInput((n) => (chargeForm.amountRials = n), v, true)"
/>
<a-input v-model:value="chargeForm.note" size="large" placeholder="توضیح (اختیاری)" />
<a-button type="primary" size="large" :loading="charging" @click="openChargeConfirm">
@@ -542,23 +499,11 @@ onMounted(async () => {
<div class="threshold-grid">
<label>
<span class="tone-label"><i class="dot critical" /> آستانه قرمز</span>
<a-input
size="large"
dir="ltr"
class="money-input"
:value="toGroupedDisplay(form.alertCriticalRials)"
@update:value="(v: string | number) => onGroupedMoneyInput((n) => (form.alertCriticalRials = n), v)"
/>
<MoneyInput v-model:value="form.alertCriticalRials" />
</label>
<label>
<span class="tone-label"><i class="dot warning" /> آستانه زرد</span>
<a-input
size="large"
dir="ltr"
class="money-input"
:value="toGroupedDisplay(form.alertWarningRials)"
@update:value="(v: string | number) => onGroupedMoneyInput((n) => (form.alertWarningRials = n), v)"
/>
<MoneyInput v-model:value="form.alertWarningRials" />
</label>
<a-button type="primary" size="large" :loading="saving" @click="saveThresholds">ذخیره آستانه‌ها</a-button>
</div>
@@ -1007,10 +952,6 @@ onMounted(async () => {
font-size: 0.78rem;
}
.money-input :deep(input) {
font-variant-numeric: tabular-nums;
}
.money-input,
.filter-row :deep(.jalali-date-field),
.filter-row :deep(.ant-input),