From c332e9df24bd29635a73404b391c1a090fcc2193 Mon Sep 17 00:00:00 2001 From: adonis Date: Tue, 18 Aug 2026 22:23:22 +0330 Subject: [PATCH] feat(admin): let price alerts pick a product Show the product on the form and table so each alert is tied to one item. Co-authored-by: Cursor --- src/api/superadminSettings.ts | 82 ------------------- src/model/priceTriggers.ts | 2 + .../components/PriceTriggerEditorFormBody.vue | 11 +++ .../hooks/priceTriggersColumns.tsx | 9 ++ src/pages/menuPages/priceTriggers/index.vue | 14 ++++ src/utils/config.ts | 18 +--- 6 files changed, 38 insertions(+), 98 deletions(-) delete mode 100644 src/api/superadminSettings.ts diff --git a/src/api/superadminSettings.ts b/src/api/superadminSettings.ts deleted file mode 100644 index da87eb0..0000000 --- a/src/api/superadminSettings.ts +++ /dev/null @@ -1,82 +0,0 @@ -export type SuperAdminSettingRow = { - id?: number - key?: string - value?: string | number | null - title_fa?: string - description?: string - is_active?: boolean | number | string - type?: string - can_edit?: number | boolean | string -} - -const SUPERADMIN_BASES = [ - 'https://adm.montzar.ir', - 'https://adm.montech.ir', - 'https://adm.montest.ir', -] as const - -const SA_RETRY_ROUNDS = 3 -const SA_PROGRAM_SETTINGS_PATH = '/api/public/program-settings' - -function resolveTenantDomain() { - const host = String(window.location.hostname || '').trim() - if (!host || host === 'localhost' || host === '127.0.0.1') { - return 'k.montzar.ir' - } - return host -} - -function parseSettingsPayload(data: unknown): SuperAdminSettingRow[] { - if (Array.isArray(data)) return data as SuperAdminSettingRow[] - if (data && typeof data === 'object') { - const obj = data as Record - if (Array.isArray(obj.settings)) return obj.settings as SuperAdminSettingRow[] - if (Array.isArray(obj.items)) return obj.items as SuperAdminSettingRow[] - if (Array.isArray(obj.data)) return obj.data as SuperAdminSettingRow[] - } - return [] -} - -/** کلید قفل‌شده در get_settings محلی — باید از سوپرادمین بیاید */ -export function isLockedCanEdit(canEdit: unknown): boolean { - return canEdit === 0 || canEdit === false || canEdit === '0' -} - -async function fetchFromUrl(url: string, timeout = 5000): Promise { - const controller = new AbortController() - const timer = window.setTimeout(() => controller.abort(), timeout) - try { - const res = await fetch(url, { - method: 'GET', - cache: 'no-store', - signal: controller.signal, - headers: { Accept: 'application/json' }, - }) - if (!res.ok) { - throw new Error(`HTTP ${res.status}`) - } - const contentType = res.headers.get('content-type') || '' - if (!contentType.includes('application/json')) { - throw new Error('SuperAdmin response is not JSON') - } - return parseSettingsPayload(await res.json()) - } finally { - window.clearTimeout(timer) - } -} - -/** تنظیمات پیشرفته نسخه از سوپرادمین — فقط GET /api/public/program-settings?domain= */ -export async function fetchSuperAdminProgramSettings(): Promise { - const domain = encodeURIComponent(resolveTenantDomain()) - let lastError: unknown - for (let round = 1; round <= SA_RETRY_ROUNDS; round++) { - for (const base of SUPERADMIN_BASES) { - try { - return await fetchFromUrl(`${base}${SA_PROGRAM_SETTINGS_PATH}?domain=${domain}`) - } catch (error) { - lastError = error - } - } - } - throw lastError instanceof Error ? lastError : new Error('SuperAdmin program-settings unreachable') -} diff --git a/src/model/priceTriggers.ts b/src/model/priceTriggers.ts index 12c6297..cdee492 100644 --- a/src/model/priceTriggers.ts +++ b/src/model/priceTriggers.ts @@ -1,6 +1,8 @@ export interface PriceTriggers{ id?: number customer_id: number + product_id?: number + product?: { id: number; title?: string } target_price: number price_condition: string action: string diff --git a/src/pages/menuPages/priceTriggers/components/PriceTriggerEditorFormBody.vue b/src/pages/menuPages/priceTriggers/components/PriceTriggerEditorFormBody.vue index 2ce95c2..ecf37e9 100644 --- a/src/pages/menuPages/priceTriggers/components/PriceTriggerEditorFormBody.vue +++ b/src/pages/menuPages/priceTriggers/components/PriceTriggerEditorFormBody.vue @@ -11,6 +11,16 @@ /> + + + + customerItems: Array<{ id: number; label: string }> + productItems: Array<{ id: number; label: string }> editId?: number }>() diff --git a/src/pages/menuPages/priceTriggers/hooks/priceTriggersColumns.tsx b/src/pages/menuPages/priceTriggers/hooks/priceTriggersColumns.tsx index 15f7c4b..2510c9f 100644 --- a/src/pages/menuPages/priceTriggers/hooks/priceTriggersColumns.tsx +++ b/src/pages/menuPages/priceTriggers/hooks/priceTriggersColumns.tsx @@ -39,6 +39,15 @@ export const cols: BaseTableColumn[] = [ return name ? `${name} (${record.customer_id})` : String(record.customer_id ?? '-') }, }, + { + title: 'محصول', + dataIndex: 'product', + key: 'product', + type: 'Text', + customRender: ({ record }) => { + return record.product?.title || (record.product_id ? `محصول ${record.product_id}` : 'طلا') + }, + }, { title: 'کد مشتری', dataIndex: 'customer_id', diff --git a/src/pages/menuPages/priceTriggers/index.vue b/src/pages/menuPages/priceTriggers/index.vue index 7285825..f0619bc 100644 --- a/src/pages/menuPages/priceTriggers/index.vue +++ b/src/pages/menuPages/priceTriggers/index.vue @@ -22,6 +22,7 @@ ref="editorRef" :form="form" :customer-items="customerItems" + :product-items="productItems" :edit-id="editId" /> @@ -38,6 +39,7 @@ import PriceTriggerEditorFormBody from './components/PriceTriggerEditorFormBody. import type { PriceTriggers } from '@/model/priceTriggers' import type { Customer } from '@/model/customer' import { getCustomersList } from '@/api/customer' +import { getCustomersList as getProductsList } from '@/api/product' import { createPriceTrigger, deletePriceTrigger, @@ -57,9 +59,11 @@ const loading = ref(false) const editorVisible = ref(false) const editId = ref() const customers = ref([]) +const products = ref>([]) const defaultForm = (): Partial => ({ customer_id: undefined, + product_id: undefined, target_price: undefined, price_condition: '>', action: 'sms', @@ -77,6 +81,13 @@ const customerItems = computed(() => })) ) +const productItems = computed(() => + products.value.map((p) => ({ + id: p.id, + label: p.title || String(p.id), + })) +) + function refreshTable() { tableRef.value?.fetch() } @@ -96,6 +107,7 @@ function openEdit(record: PriceTriggers) { editId.value = record.id form.value = cloneDeep({ customer_id: record.customer_id, + product_id: record.product_id ?? record.product?.id, target_price: record.target_price, price_condition: record.price_condition, action: record.action, @@ -111,6 +123,7 @@ async function submitForm() { const payload = { customer_id: Number(form.value.customer_id), + product_id: Number(form.value.product_id), target_price: Number(form.value.target_price), price_condition: form.value.price_condition, action: form.value.action, @@ -156,6 +169,7 @@ function deleteItem(id: number) { onMounted(async () => { try { customers.value = (await getCustomersList()) ?? [] + products.value = ((await getProductsList()) as Array<{ id: number; title?: string }>) ?? [] } catch (e) { console.error(e) } diff --git a/src/utils/config.ts b/src/utils/config.ts index 896850f..1086738 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -332,23 +332,9 @@ async function bootstrapSettingsFromApi(): Promise { try { const { getConfiguration } = await import('@/api/configuration') - const { fetchSuperAdminProgramSettings, isLockedCanEdit } = await import('@/api/superadminSettings') const { parseMainWalletProductIds } = await import('@/components/mobile/utils/wallet') - const localRows = await getConfiguration().catch(() => []) - const saRows = await fetchSuperAdminProgramSettings() - const local = Array.isArray(localRows) ? localRows : [] - const editable = local.filter((row) => !isLockedCanEdit(row?.can_edit)) - const merged = [...saRows] as typeof local - for (const item of editable) { - if (!item?.key) continue - const index = merged.findIndex((row) => row.key === item.key) - if (index >= 0) { - merged[index] = { ...merged[index], ...item } - } else { - merged.push(item) - } - } - const list = merged + const rows = await getConfiguration() + const list = Array.isArray(rows) ? rows : [] const isStoreRow = list.find((item) => item.key === 'is_store') const mainWalletRow = list.find((item) => item.key === 'main_wallet_products') const mainMetalRow = list.find((item) => item.key === 'main_metal')