From abaacf5a998cca822950fe9fce4607b4fd5a1ccb Mon Sep 17 00:00:00 2001 From: adonis Date: Mon, 24 Aug 2026 11:42:44 +0330 Subject: [PATCH] fix(get-user): apply shared khanegi nav sample to all tenants Keep bottom menu at 4 slots so outdated per-tenant JSON cannot overflow the bar. Co-authored-by: Cursor --- src/get-user/get-user.service.ts | 19 ++++--- src/get-user/khanegi-nav-sample.ts | 82 ++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 src/get-user/khanegi-nav-sample.ts diff --git a/src/get-user/get-user.service.ts b/src/get-user/get-user.service.ts index fe2da4c..9259859 100644 --- a/src/get-user/get-user.service.ts +++ b/src/get-user/get-user.service.ts @@ -1,6 +1,7 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { iranDaysUntil } from '../common/iran-time.util'; import { PrismaService } from '../prisma/prisma.service'; +import { applyKhanegiNavSample } from './khanegi-nav-sample'; type UiSetting = { key: string; value: string }; @@ -318,10 +319,13 @@ export class GetUserService { } const navigation = app.navigation; - if (!navigation || typeof navigation !== 'object') return; - - if (navigation.type === 'sidebar+bottom') navigation.type = 'both'; + if (!navigation || typeof navigation !== 'object') { + app.navigation = { type: 'both' }; + } else if (navigation.type === 'sidebar+bottom') { + navigation.type = 'both'; + } + const nav = app.navigation; const mapItems = (items: unknown) => { if (!Array.isArray(items)) return items; return items.map((item) => { @@ -331,11 +335,12 @@ export class GetUserService { }); }; - navigation.sidebar = mapItems(navigation.sidebar); - navigation.bottom = mapItems(navigation.bottom); - if (navigation.sidebarReferral?.route === 'products') { - navigation.sidebarReferral.route = 'buyProduct'; + nav.sidebar = mapItems(nav.sidebar); + nav.bottom = mapItems(nav.bottom); + if (nav.sidebarReferral?.route === 'products') { + nav.sidebarReferral.route = 'buyProduct'; } + applyKhanegiNavSample(nav); } private buildHamkarUiSettings(ui: Record): UiSetting[] { diff --git a/src/get-user/khanegi-nav-sample.ts b/src/get-user/khanegi-nav-sample.ts new file mode 100644 index 0000000..f90ad1e --- /dev/null +++ b/src/get-user/khanegi-nav-sample.ts @@ -0,0 +1,82 @@ +/** + * سمپل منوی خانگی — با controlpanel_user/src/config/defaultKhanegiUiConfig.ts یکی بماند. + * آپدیت این لیست روی همهٔ نسخه‌ها از طریق get_user اعمال می‌شود. + */ +export const MAX_BOTTOM_NAV_SLOTS = 4 + +export type KhanegiNavItem = { + id: string + label: string + icon: string + route: string + order: number + visible: boolean + query?: Record +} + +export const KHANEGI_SAMPLE_SIDEBAR: KhanegiNavItem[] = [ + { id: 'home', label: 'داشبورد', icon: 'vuesax-linear:chart', route: 'home', order: 1, visible: true }, + { id: 'buy', label: 'خرید طلا', icon: 'vuesax-linear:money-receive', route: 'gold', order: 2, visible: true, query: { state: 'buy' } }, + { id: 'sell', label: 'فروش طلا', icon: 'vuesax-linear:money-send', route: 'gold', order: 3, visible: true, query: { state: 'sell' } }, + { id: 'wallet', label: 'کیف پول', icon: 'vuesax-linear:wallet-1', route: 'wallet', order: 4, visible: true }, + { id: 'transactions', label: 'تراکنش‌ها', icon: 'vuesax-linear:receipt-item', route: 'transactions', order: 5, visible: true }, + { id: 'products', label: 'محصولات', icon: 'vuesax-linear:coin', route: 'buyProduct', order: 7, visible: true }, + { id: 'user', label: 'تنظیمات', icon: 'vuesax-linear:setting-2', route: 'user', order: 8, visible: true }, +] + +export const KHANEGI_SAMPLE_BOTTOM: KhanegiNavItem[] = [ + { id: 'gold', label: 'خرید و فروش', icon: 'vuesax-linear:coin', route: 'gold', order: 1, visible: true }, + { id: 'transactions', label: 'تراکنش‌ها', icon: 'vuesax-linear:receipt-item', route: 'transactions', order: 2, visible: true }, + { id: 'wallet', label: 'کیف پول', icon: 'vuesax-linear:wallet-add', route: 'wallet', order: 3, visible: true }, + { id: 'user', label: 'تنظیمات', icon: 'vuesax-linear:setting-2', route: 'user', order: 4, visible: true }, +] + +export function mergeNavItemsFromSample( + sample: KhanegiNavItem[], + stored: unknown, + maxVisible?: number, +): KhanegiNavItem[] { + const sampleById = new Map(sample.map((item) => [item.id, item])) + const storedList = Array.isArray(stored) ? stored : [] + const kept: KhanegiNavItem[] = [] + const seen = new Set() + + for (const prev of storedList) { + if (!prev || typeof prev !== 'object' || typeof prev.id !== 'string') continue + const base = sampleById.get(prev.id) + if (!base || seen.has(prev.id)) continue + seen.add(prev.id) + kept.push({ + ...base, + label: prev.label || base.label, + icon: prev.icon || base.icon, + visible: typeof prev.visible === 'boolean' ? prev.visible : base.visible, + query: prev.query ?? base.query, + }) + } + + for (const item of sample) { + if (seen.has(item.id)) continue + kept.push({ ...item }) + } + + if (typeof maxVisible === 'number') { + let visibleCount = 0 + for (const item of kept) { + if (!item.visible) continue + visibleCount += 1 + if (visibleCount > maxVisible) item.visible = false + } + } + + return kept.map((item, index) => ({ ...item, order: index + 1 })) +} + +export function applyKhanegiNavSample(navigation: Record) { + navigation.sidebar = mergeNavItemsFromSample(KHANEGI_SAMPLE_SIDEBAR, navigation.sidebar) + navigation.bottom = mergeNavItemsFromSample( + KHANEGI_SAMPLE_BOTTOM.slice(0, MAX_BOTTOM_NAV_SLOTS), + navigation.bottom, + MAX_BOTTOM_NAV_SLOTS, + ).slice(0, MAX_BOTTOM_NAV_SLOTS) +}