fix(admin): apply SuperAdmin flags on بروزرسانی and fix overlay card keys

Pull live program-settings, set menu flags from the response, and key setting cards by setting key so id-null overlay rows no longer share one toggle state.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-29 22:42:30 +03:30
co-authored by Cursor
parent 085c024654
commit 14d2bb603e
2 changed files with 87 additions and 21 deletions
+60 -21
View File
@@ -80,7 +80,7 @@
<a-row :gutter="[16, 16]" class="settings-grid"> <a-row :gutter="[16, 16]" class="settings-grid">
<a-col <a-col
v-for="row in block.rows" v-for="row in block.rows"
:key="row.id" :key="row.key || `id-${row.id}`"
:xs="24" :xs="24"
:sm="12" :sm="12"
:lg="rowColSpan(row)" :lg="rowColSpan(row)"
@@ -101,7 +101,7 @@
<a-row v-else :gutter="[16, 16]" class="settings-grid"> <a-row v-else :gutter="[16, 16]" class="settings-grid">
<a-col <a-col
v-for="row in groupedFiltered[group.id]" v-for="row in groupedFiltered[group.id]"
:key="row.id" :key="row.key || `id-${row.id}`"
:xs="24" :xs="24"
:sm="12" :sm="12"
:lg="rowColSpan(row)" :lg="rowColSpan(row)"
@@ -209,7 +209,7 @@ import SettingValueField from './SettingValueField.vue'
import SettingCard from './SettingCard.vue' import SettingCard from './SettingCard.vue'
import { refreshAppModeMenu } from '@/router' import { refreshAppModeMenu } from '@/router'
import { useUserStore } from '@/store/userStore' import { useUserStore } from '@/store/userStore'
import { applyBannerEnabledFromRows, applyTrackingSystemsEnabledFromRows, applyConditionalOrderEnabledFromRows, applyAutomationEnabledFromRows, applyProductFundsEnabledFromRows, applyPriceTriggerEnabledFromRows, applyAppNotificationEnabledFromRows, applyPriceProviderFromRows, applyPriceChannelAccessFromRows, applySupportChatMenuEnabledFromRows, applyGroupSmsFromRows, applyCustomerSpecialPricingFromRows, applyCustomerDirectSmsFromRows, applyCustomerLevelProfitEnabledFromRows, setBannerEnabledFromSettings, setConditionalOrderEnabledFromSettings, setAutomationEnabledFromSettings, setProductFundsEnabledFromSettings, setPriceTriggerEnabledFromSettings, setAppNotificationEnabledFromSettings, setPriceProviderFromSettings, setPriceChannelAccessFromSettings, setStoreModeFromSettings, setTrackingSystemsEnabledFromSettings, setGroupSmsEnabledFromSettings, setCustomerSpecialPricingEnabledFromSettings } from '@/utils/config' import { applyBannerEnabledFromRows, applyTrackingSystemsEnabledFromRows, applyConditionalOrderEnabledFromRows, applyAutomationEnabledFromRows, applyProductFundsEnabledFromRows, applyPriceTriggerEnabledFromRows, applyAppNotificationEnabledFromRows, applyPriceProviderFromRows, applyPriceChannelAccessFromRows, applySupportChatMenuEnabledFromRows, applyGroupSmsFromRows, applyCustomerSpecialPricingFromRows, applyCustomerDirectSmsFromRows, applyCustomerLevelProfitEnabledFromRows, applySuperAdminAccessFlagsFromItems, invalidateSettingsBootstrap, setBannerEnabledFromSettings, setConditionalOrderEnabledFromSettings, setAutomationEnabledFromSettings, setProductFundsEnabledFromSettings, setPriceTriggerEnabledFromSettings, setAppNotificationEnabledFromSettings, setPriceProviderFromSettings, setPriceChannelAccessFromSettings, setStoreModeFromSettings, setTrackingSystemsEnabledFromSettings, setGroupSmsEnabledFromSettings, setCustomerSpecialPricingEnabledFromSettings } from '@/utils/config'
import { import {
EDITABLE_SETTING_GROUPS, EDITABLE_SETTING_GROUPS,
SYSTEM_SETTING_GROUPS, SYSTEM_SETTING_GROUPS,
@@ -256,7 +256,8 @@ const typeSelectOptions = SETTING_TYPE_OPTIONS.map((t) => ({
})) }))
const baseData = ref<Array<Const>>([]) const baseData = ref<Array<Const>>([])
const drafts = ref<Record<number, string>>({}) /** draft بر اساس id یا key — ردیف‌های overlay سوپرادمین id=null دارند */
const drafts = ref<Record<string, string>>({})
const addConfigVisible = ref(false) const addConfigVisible = ref(false)
const search = ref('') const search = ref('')
const filterType = ref<string | undefined>() const filterType = ref<string | undefined>()
@@ -266,6 +267,11 @@ const savedFlashIds = ref<Set<number>>(new Set())
const saveTimers = new Map<number, ReturnType<typeof setTimeout>>() const saveTimers = new Map<number, ReturnType<typeof setTimeout>>()
const debouncedSave = new Map<number, ReturnType<typeof debounce>>() const debouncedSave = new Map<number, ReturnType<typeof debounce>>()
function rowDraftKey(row: Const): string {
if (row.id != null) return `id:${row.id}`
return `key:${row.key || ''}`
}
const form = ref<Const>({ const form = ref<Const>({
key: '', key: '',
value: '', value: '',
@@ -311,7 +317,7 @@ function matchesFilter(row: Const): boolean {
(row.key || '').toLowerCase().includes(q) || (row.key || '').toLowerCase().includes(q) ||
(row.title_fa || '').toLowerCase().includes(q) || (row.title_fa || '').toLowerCase().includes(q) ||
(row.value || '').toLowerCase().includes(q) || (row.value || '').toLowerCase().includes(q) ||
(drafts.value[row.id!] || '').toLowerCase().includes(q) (drafts.value[rowDraftKey(row)] || '').toLowerCase().includes(q)
) )
} }
@@ -453,8 +459,8 @@ function isReadonly(row: Const): boolean {
} }
function draftValue(row: Const): string { function draftValue(row: Const): string {
const id = row.id! const k = rowDraftKey(row)
if (drafts.value[id] !== undefined) return drafts.value[id] if (drafts.value[k] !== undefined) return drafts.value[k]
return row.value ?? '' return row.value ?? ''
} }
@@ -467,9 +473,9 @@ function isSavedFlash(id?: number): boolean {
} }
function syncDraftsFromBase() { function syncDraftsFromBase() {
const next: Record<number, string> = {} const next: Record<string, string> = {}
for (const row of baseData.value || []) { for (const row of baseData.value || []) {
if (row.id != null) next[row.id] = row.value ?? '' next[rowDraftKey(row)] = row.value ?? ''
} }
drafts.value = next drafts.value = next
} }
@@ -488,11 +494,39 @@ watch(
} }
) )
async function pullSuperAdminAndApplyFlags(): Promise<{
ok: boolean
message?: string
count?: number
}> {
invalidateSettingsBootstrap()
const res = await refreshSuperAdminSettings()
if (res?.items && typeof res.items === 'object') {
applySuperAdminAccessFlagsFromItems(res.items)
}
// get_settings کامل — فلگ منو و کارت‌ها با مقدار زنده هم‌تراز شوند
try {
const { getConfiguration } = await import('@/api/configuration')
const all = await getConfiguration()
const rows = Array.isArray(all) ? all : []
applySuperAdminAccessFlagsFromItems(rows)
applyPriceProviderFromRows(rows)
applyPriceChannelAccessFromRows(rows)
} catch {
/* فلگ از items می‌ماند */
}
return {
ok: res?.ok !== false,
message: res?.message,
count: res?.count
}
}
async function refreshFromSuperAdmin() { async function refreshFromSuperAdmin() {
if (refreshingSuperAdmin.value) return if (refreshingSuperAdmin.value) return
refreshingSuperAdmin.value = true refreshingSuperAdmin.value = true
try { try {
const res = await refreshSuperAdminSettings() const res = await pullSuperAdminAndApplyFlags()
await initConfig() await initConfig()
refreshMenuAfterStoreChange() refreshMenuAfterStoreChange()
message.success(res?.message || `همگام شد (${res?.count ?? 0} کلید دسترسی از سوپرادمین)`) message.success(res?.message || `همگام شد (${res?.count ?? 0} کلید دسترسی از سوپرادمین)`)
@@ -505,21 +539,23 @@ async function refreshFromSuperAdmin() {
} }
} }
/** بروزرسانی: اول از سوپرادمین زنده بخوان، بعد لیست تنظیمات را دوباره بگیر */ /** بروزرسانی: زنده از سوپرادمین بگیر → فلگ منو اعمال شود → لیست تنظیمات رفرش */
async function onRefreshClick() { async function onRefreshClick() {
if (refreshingSuperAdmin.value || loading.value) return if (refreshingSuperAdmin.value || loading.value) return
refreshingSuperAdmin.value = true refreshingSuperAdmin.value = true
try { try {
await refreshSuperAdminSettings() const res = await pullSuperAdminAndApplyFlags()
await initConfig()
refreshMenuAfterStoreChange()
message.success(res?.message || 'بروزرسانی شد')
} catch (e: any) { } catch (e: any) {
console.error(e) console.error(e)
const msg = e?.response?.data?.message || e?.message || 'همگام‌سازی از سوپرادمین ناموفق بود' const msg = e?.response?.data?.message || e?.message || 'همگام‌سازی از سوپرادمین ناموفق بود'
message.error(Array.isArray(msg) ? msg.join('، ') : msg) message.error(Array.isArray(msg) ? msg.join('، ') : msg)
await initConfig()
} finally { } finally {
refreshingSuperAdmin.value = false refreshingSuperAdmin.value = false
} }
await initConfig()
refreshMenuAfterStoreChange()
} }
async function initConfig() { async function initConfig() {
@@ -644,13 +680,15 @@ function syncMenuFlagsFromSetting(key: string | undefined, value: string) {
} }
function onValueChange(row: Const, value: string) { function onValueChange(row: Const, value: string) {
if (row.id == null || isReadonly(row)) return if (isReadonly(row)) return
drafts.value[row.id] = value drafts.value[rowDraftKey(row)] = value
// منو و UI وابسته را همان لحظه با سوئیچ همگام کن // منو و UI وابسته را همان لحظه با سوئیچ همگام کن
syncMenuFlagsFromSetting(row.key, value) syncMenuFlagsFromSetting(row.key, value)
if (row.id == null) return
// longtext هم باید ذخیره شود، فقط با delay بیشتر // longtext هم باید ذخیره شود، فقط با delay بیشتر
scheduleSave(row) scheduleSave(row)
} }
@@ -699,9 +737,10 @@ async function persistRow(row: Const) {
async function saveSettingRow(row: Const) { async function saveSettingRow(row: Const) {
if (row.id == null) return if (row.id == null) return
const id = row.id const id = row.id
const draftKey = rowDraftKey(row)
const type = resolveSettingFieldType(row) const type = resolveSettingFieldType(row)
const raw = drafts.value[id] ?? row.value ?? '' const raw = drafts.value[draftKey] ?? row.value ?? ''
const value = formatValueForSave(raw, type, row.key) const value = formatValueForSave(raw, type, row.key)
if (value === (row.value ?? '')) return if (value === (row.value ?? '')) return
@@ -723,9 +762,9 @@ async function saveSettingRow(row: Const) {
}) })
row.value = value row.value = value
row.is_active = true row.is_active = true
const currentDraft = drafts.value[id] ?? '' const currentDraft = drafts.value[draftKey] ?? ''
if (currentDraft === draftSnapshot) { if (currentDraft === draftSnapshot) {
drafts.value[id] = value drafts.value[draftKey] = value
} else { } else {
scheduleSave(row) scheduleSave(row)
} }
@@ -745,8 +784,8 @@ async function saveSettingRow(row: Const) {
) )
} catch (e) { } catch (e) {
console.error(e) console.error(e)
if ((drafts.value[id] ?? '') === draftSnapshot) { if ((drafts.value[draftKey] ?? '') === draftSnapshot) {
drafts.value[id] = row.value ?? '' drafts.value[draftKey] = row.value ?? ''
} }
// برگرداندن فلگ منو به مقدار ذخیره‌شده قبلی // برگرداندن فلگ منو به مقدار ذخیره‌شده قبلی
syncMenuFlagsFromSetting(row.key, row.value ?? '') syncMenuFlagsFromSetting(row.key, row.value ?? '')
+27
View File
@@ -283,6 +283,33 @@ export function applyCustomerSpecialPricingFromRows(
if (row) setCustomerSpecialPricingEnabledFromSettings(isSwitchOn(row.value)) if (row) setCustomerSpecialPricingEnabledFromSettings(isSwitchOn(row.value))
} }
/** بعد از همگام‌سازی سوپرادمین — bootstrap بعدی دوباره از API می‌خواند */
export function invalidateSettingsBootstrap(): void {
settingsBootstrapped = false
}
/**
* اعمال مستقیم ۱۴ دسترسی سوپرادمین روی فلگ‌های منو/UI
* (پاسخ refresh-superadmin.items یا ردیف‌های get_settings).
*/
export function applySuperAdminAccessFlagsFromItems(
items: Record<string, string> | Array<{ key?: string; value?: string | null }>
): void {
const rows = Array.isArray(items)
? items
: Object.entries(items).map(([key, value]) => ({ key, value }))
applyCustomerDirectSmsFromRows(rows)
applyGroupSmsFromRows(rows)
applyCustomerSpecialPricingFromRows(rows)
applyConditionalOrderEnabledFromRows(rows)
applyAutomationEnabledFromRows(rows)
applyProductFundsEnabledFromRows(rows)
applyCustomerLevelProfitEnabledFromRows(rows)
applyPriceTriggerEnabledFromRows(rows)
applyAppNotificationEnabledFromRows(rows)
applySupportChatMenuEnabledFromRows(rows)
}
export function applyConditionalOrderEnabledFromRows( export function applyConditionalOrderEnabledFromRows(
rows: Array<{ key?: string; value?: string | null }> rows: Array<{ key?: string; value?: string | null }>
) { ) {