Gate the submenu on enable_gold_credit and wire settings UI for SuperAdmin and tenant credit options. Co-authored-by: Cursor <cursoragent@cursor.com>
165 lines
5.1 KiB
Vue
165 lines
5.1 KiB
Vue
<template>
|
||
<BasicModal
|
||
v-model:visible="modalVisible"
|
||
title="تنظیمات اعتبار طلایی"
|
||
width="48rem"
|
||
ok-text="بستن"
|
||
@ok="modalVisible = false"
|
||
>
|
||
<a-spin :spinning="loading">
|
||
<div class="space-y-4">
|
||
<div class="text-sm text-gray-500 mb-2">
|
||
این تنظیمات در اپ کاربر اعمال میشوند. روشهای پرداخت غیرفعال در اپ نمایش داده نمیشوند؛
|
||
ادمین همچنان میتواند از همه روشها استفاده کند.
|
||
</div>
|
||
|
||
<div
|
||
v-for="row in rows"
|
||
:key="row.id ?? row.key"
|
||
class="rounded-xl border border-slate-100 bg-slate-50/60 p-3"
|
||
>
|
||
<div class="font-medium text-sm text-gray-800 mb-1">{{ row.title_fa || row.key }}</div>
|
||
<div v-if="row.description" class="text-xs text-gray-500 mb-2 whitespace-pre-wrap">
|
||
{{ displayDescription(row) }}
|
||
</div>
|
||
<SettingValueField
|
||
:model-value="drafts[row.id!] ?? ''"
|
||
:setting-key="row.key"
|
||
:setting-type="row.type"
|
||
:description="row.description"
|
||
:rows="row.key === 'gold_credit_tiers' ? 6 : 4"
|
||
:disabled="savingIds.has(row.id!)"
|
||
@update:value="onDraftChange(row, $event)"
|
||
/>
|
||
<div v-if="savedFlashIds.has(row.id!)" class="text-xs text-green-600 mt-1">ذخیره شد</div>
|
||
</div>
|
||
</div>
|
||
</a-spin>
|
||
</BasicModal>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, ref, watch } from 'vue'
|
||
import { message } from 'ant-design-vue'
|
||
import { getConfiguration, setConfiguration } from '@/api/configuration'
|
||
import type { Const } from '@/model/configuration'
|
||
import SettingValueField from '@/pages/menuPages/configuration/SettingValueField.vue'
|
||
import { formatValueForSave, resolveSettingFieldType } from '@/pages/menuPages/configuration/settingTypes'
|
||
|
||
const GOLD_CREDIT_KEYS = [
|
||
'gold_credit_pay_gold_enabled',
|
||
'gold_credit_pay_rial_enabled',
|
||
'gold_credit_pay_mixed_enabled',
|
||
'gold_credit_min_balance_gram',
|
||
'gold_credit_default_months',
|
||
'gold_credit_info_text',
|
||
'gold_credit_request_success_text',
|
||
'gold_credit_tiers',
|
||
'gold_credit_penalty_type',
|
||
'gold_credit_penalty_amount',
|
||
'gold_credit_grace_hours',
|
||
]
|
||
|
||
const props = defineProps<{ visible?: boolean }>()
|
||
const emit = defineEmits<{ (e: 'update:visible', value: boolean): void }>()
|
||
|
||
const modalVisible = computed({
|
||
get: () => props.visible ?? false,
|
||
set: (v: boolean) => emit('update:visible', v),
|
||
})
|
||
|
||
const loading = ref(false)
|
||
const rows = ref<Const[]>([])
|
||
const drafts = ref<Record<number, string>>({})
|
||
const savingIds = ref(new Set<number>())
|
||
const savedFlashIds = ref(new Set<number>())
|
||
const saveTimers = new Map<number, ReturnType<typeof setTimeout>>()
|
||
const debounceTimers = new Map<number, ReturnType<typeof setTimeout>>()
|
||
|
||
function displayDescription(row: Const) {
|
||
if (row.type === 'select' && row.description?.includes('|')) {
|
||
return row.description.split('|')[0]
|
||
}
|
||
return row.description
|
||
}
|
||
|
||
watch(
|
||
() => props.visible,
|
||
async (open) => {
|
||
if (!open) return
|
||
loading.value = true
|
||
try {
|
||
const all = (await getConfiguration()) ?? []
|
||
rows.value = all
|
||
.filter((r) => r.key && GOLD_CREDIT_KEYS.includes(r.key))
|
||
.sort(
|
||
(a, b) =>
|
||
GOLD_CREDIT_KEYS.indexOf(a.key!) - GOLD_CREDIT_KEYS.indexOf(b.key!)
|
||
)
|
||
const next: Record<number, string> = {}
|
||
for (const row of rows.value) {
|
||
if (row.id != null) next[row.id] = String(row.value ?? '')
|
||
}
|
||
drafts.value = next
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
)
|
||
|
||
function onDraftChange(row: Const, value: string) {
|
||
if (row.id == null) return
|
||
drafts.value[row.id] = value
|
||
scheduleSave(row)
|
||
}
|
||
|
||
function scheduleSave(row: Const) {
|
||
if (row.id == null) return
|
||
if (debounceTimers.has(row.id)) clearTimeout(debounceTimers.get(row.id))
|
||
debounceTimers.set(
|
||
row.id,
|
||
setTimeout(() => {
|
||
void saveRow(row)
|
||
}, 600)
|
||
)
|
||
}
|
||
|
||
async function saveRow(row: Const) {
|
||
if (row.id == null) return
|
||
const type = resolveSettingFieldType(row)
|
||
const raw = drafts.value[row.id] ?? row.value ?? ''
|
||
const value = formatValueForSave(raw, type)
|
||
if (value === (row.value ?? '')) return
|
||
|
||
savingIds.value.add(row.id)
|
||
savedFlashIds.value.delete(row.id)
|
||
try {
|
||
await setConfiguration({
|
||
id: row.id,
|
||
key: row.key,
|
||
value,
|
||
description: row.description ?? '',
|
||
is_active: row.is_active,
|
||
title_fa: row.title_fa ?? '',
|
||
type,
|
||
})
|
||
row.value = value
|
||
savedFlashIds.value.add(row.id)
|
||
if (saveTimers.has(row.id)) clearTimeout(saveTimers.get(row.id))
|
||
saveTimers.set(
|
||
row.id,
|
||
setTimeout(() => {
|
||
savedFlashIds.value.delete(row.id!)
|
||
savedFlashIds.value = new Set(savedFlashIds.value)
|
||
}, 2000)
|
||
)
|
||
} catch {
|
||
drafts.value[row.id] = String(row.value ?? '')
|
||
message.error(`ذخیره «${row.title_fa || row.key}» ناموفق بود`)
|
||
} finally {
|
||
savingIds.value.delete(row.id)
|
||
savingIds.value = new Set(savingIds.value)
|
||
}
|
||
}
|
||
</script>
|