From 17e4f14e462ea070a8beb73a9820b59c83e6f287 Mon Sep 17 00:00:00 2001 From: safavimohadeseh78 Date: Tue, 16 Jun 2026 10:51:55 +0330 Subject: [PATCH] Fix boursecu asset info modals with per-product copper breakdown. Show total product wallet sum alongside the clicked product inventory in readable multi-line modals on mobile and desktop. Co-authored-by: Cursor --- src/utils/productAssets.ts | 195 +++++++++++++++--- .../base/home/components/AssetsSection.vue | 4 +- .../base/home/composables/useHomePage.ts | 38 +++- .../components/DesktopAssetsMetrics.vue | 8 +- 4 files changed, 208 insertions(+), 37 deletions(-) diff --git a/src/utils/productAssets.ts b/src/utils/productAssets.ts index 7946c51..7d95cb1 100644 --- a/src/utils/productAssets.ts +++ b/src/utils/productAssets.ts @@ -105,21 +105,21 @@ export interface HomeAssetRow { info: HomeAssetRowInfo /** boursecu — متن مودال توضیحات (info) */ modalText?: string + /** boursecu — عنوان مودال (مثلاً کل دارایی مس) */ + modalTitle?: string + /** boursecu — محتوای چندخطی مودال */ + modalContent?: string } function formatInventoryCardProductLabel(card: WalletInventoryCard): string { return `دارایی ${card.title}` } -function formatBoursecuProductModalText(card: WalletInventoryCard): string { - return `${formatInventoryCardProductLabel(card)} = موجودی ${card.title}` -} - /** boursecu — متن مودال برای ردیف‌های metal_totals */ export function formatBoursecuMetalModalText(metal: WalletMetalTotal): string { switch (metal.metal_id) { case METAL_COPPER: - return 'مجموع دارایی مس = جمع کاتد و مفتول و شمش' + return getTotalMetalLabel(METAL_COPPER) case METAL_GOLD: return 'دارایی شمش مس = موجودی شمش مس' case METAL_SILVER: @@ -129,6 +129,121 @@ export function formatBoursecuMetalModalText(metal: WalletMetalTotal): string { } } +/** boursecu — جمع موجودی کاتد، مفتول و شمش از کارت‌های دارایی صفحه اصلی */ +function sumBoursecuHomeProductInventories( + inventoryCards: WalletInventoryCard[], + countProducts: WalletInventoryCard[] +): number { + let sum = 0 + + for (const card of inventoryCards) { + if (card.display_type === 'count') { + continue + } + sum += Number(card.quantity ?? 0) + } + + for (const card of countProducts) { + const quantity = Number(card.quantity ?? 0) + const unitWeight = Number(card.unit_weight ?? 0) + if (unitWeight > 0) { + sum += quantity * unitWeight + continue + } + + if (card.unit?.includes('کیلو')) { + sum += quantity + } + } + + return sum +} + +type BoursecuModalProduct = { + title: string + quantity: number + unit: string +} + +function toBoursecuModalProduct(card: WalletInventoryCard): BoursecuModalProduct { + return { + title: card.title, + quantity: Number(card.quantity ?? 0), + unit: card.unit ?? (card.display_type === 'count' ? 'عدد' : 'کیلوگرم'), + } +} + +function resolveBoursecuMetalModalProduct( + metal: WalletMetalTotal, + countProducts: WalletInventoryCard[] +): BoursecuModalProduct { + const countCard = + countProducts.find((card) => Number(card.metal_id) === metal.metal_id) ?? + countProducts.find((card) => card.title?.includes('شمش')) + + if (countCard) { + return toBoursecuModalProduct(countCard) + } + + if (metal.metal_id === METAL_GOLD) { + return { + title: 'شمش مس', + quantity: Number(metal.total_weight ?? 0), + unit: 'عدد', + } + } + + return { + title: metal.label, + quantity: Number(metal.total_weight ?? 0), + unit: metal.unit ?? 'کیلوگرم', + } +} + +/** boursecu — جزئیات کل دارایی مس در مودال info */ +export function formatBoursecuCopperAssetsModalContent( + metal: WalletMetalTotal, + inventoryCards: WalletInventoryCard[], + countProducts: WalletInventoryCard[], + product: BoursecuModalProduct +): string { + const weightUnit = metal.unit || 'کیلوگرم' + const productWeight = usePriceWithoutType( + sumBoursecuHomeProductInventories(inventoryCards, countProducts), + false + ) + const clickedQuantity = usePriceWithoutType(product.quantity, false) + + return [ + `مجموع کیف محصولات: ${productWeight} ${weightUnit}`, + `موجودی ${product.title}: ${clickedQuantity} ${product.unit}`, + ].join('\n') +} + +function withBoursecuCopperModal( + row: Omit, + copperTotal: WalletMetalTotal | undefined, + inventoryCards: WalletInventoryCard[], + countProducts: WalletInventoryCard[], + product: BoursecuModalProduct +): HomeAssetRow { + if (!copperTotal) { + return row + } + + return { + ...row, + metal: copperTotal, + modalTitle: getTotalMetalLabel(METAL_COPPER), + modalContent: formatBoursecuCopperAssetsModalContent( + copperTotal, + inventoryCards, + countProducts, + product + ), + } +} + function findMetalTotalById(metalTotals: WalletMetalTotal[], metalId: number) { return metalTotals.find((metal) => metal.metal_id === metalId) } @@ -158,20 +273,28 @@ export function buildBoursecuHomeAssetRows( const metalTotals = summary.metal_totals ?? [] const inventoryCards = summary.inventory_cards ?? [] const countProducts = summary.count_products ?? [] + const copperTotal = findMetalTotalById(metalTotals, METAL_COPPER) for (const card of inventoryCards) { if (card.display_type === 'count') { continue } - rows.push({ - id: `inventory-${card.product_id}`, - label: formatInventoryCardProductLabel(card), - value: formatInventoryCardValue(card), - metal: findMetalTotalById(metalTotals, card.metal_id), - info: 'metal', - modalText: formatBoursecuProductModalText(card), - }) + rows.push( + withBoursecuCopperModal( + { + id: `inventory-${card.product_id}`, + label: formatInventoryCardProductLabel(card), + value: formatInventoryCardValue(card), + metal: findMetalTotalById(metalTotals, card.metal_id), + info: 'metal', + }, + copperTotal, + inventoryCards, + countProducts, + toBoursecuModalProduct(card) + ) + ) } for (const metal of metalTotals) { @@ -179,25 +302,39 @@ export function buildBoursecuHomeAssetRows( continue } - rows.push({ - id: `metal-${metal.metal_id}`, - label: resolveMetalAssetsDisplayLabel(metal, true), - value: formatBoursecuMetalRowValue(metal), - metal, - info: 'metal', - modalText: formatBoursecuMetalModalText(metal), - }) + rows.push( + withBoursecuCopperModal( + { + id: `metal-${metal.metal_id}`, + label: resolveMetalAssetsDisplayLabel(metal, true), + value: formatBoursecuMetalRowValue(metal), + metal, + info: 'metal', + }, + copperTotal, + inventoryCards, + countProducts, + resolveBoursecuMetalModalProduct(metal, countProducts) + ) + ) } for (const card of countProducts) { - rows.push({ - id: `count-${card.product_id}`, - label: formatInventoryCardProductLabel(card), - value: formatInventoryCardValue(card), - metal: findMetalTotalById(metalTotals, card.metal_id), - info: 'metal', - modalText: formatBoursecuProductModalText(card), - }) + rows.push( + withBoursecuCopperModal( + { + id: `count-${card.product_id}`, + label: formatInventoryCardProductLabel(card), + value: formatInventoryCardValue(card), + metal: findMetalTotalById(metalTotals, card.metal_id), + info: 'metal', + }, + copperTotal, + inventoryCards, + countProducts, + toBoursecuModalProduct(card) + ) + ) } rows.push({ diff --git a/src/views/base/home/components/AssetsSection.vue b/src/views/base/home/components/AssetsSection.vue index 5c8463e..81498b8 100644 --- a/src/views/base/home/components/AssetsSection.vue +++ b/src/views/base/home/components/AssetsSection.vue @@ -94,7 +94,7 @@ const props = withDefaults( ) const emit = defineEmits<{ - openGoldModal: [metal: WalletMetalTotal, modalText?: string] + openGoldModal: [metal: WalletMetalTotal, modalText?: string, modalTitle?: string, modalContent?: string] openRialModal: [] }>() @@ -136,7 +136,7 @@ function handleRowInfo(row: HomeAssetRow) { } if (row.metal) { - emit('openGoldModal', row.metal, row.modalText) + emit('openGoldModal', row.metal, row.modalText, row.modalTitle, row.modalContent) } } diff --git a/src/views/base/home/composables/useHomePage.ts b/src/views/base/home/composables/useHomePage.ts index bcaebb7..74b8001 100644 --- a/src/views/base/home/composables/useHomePage.ts +++ b/src/views/base/home/composables/useHomePage.ts @@ -1,4 +1,4 @@ -import { computed, inject, onMounted, onUnmounted, provide, ref, type InjectionKey } from 'vue' +import { computed, h, inject, onMounted, onUnmounted, provide, ref, type InjectionKey } from 'vue' import router from '@/router' import { loading, removeToken } from '@/utils' import { useUserStore } from '@/store/userStore' @@ -41,7 +41,12 @@ export interface HomePageContext { setChartMetal: (metal: ChartMetalType) => void isBoursecuHomeLayout: ReturnType> handleBannerClick: (banner: { typeClick?: string; modalText?: string; link?: string }) => void - openGoldModal: (metal: WalletMetalTotal, modalText?: string) => void + openGoldModal: ( + metal: WalletMetalTotal, + modalText?: string, + modalTitle?: string, + modalContent?: string + ) => void openRialModal: () => void } @@ -147,8 +152,32 @@ export function useHomePage() { window.location.reload() } - function openGoldModal(metal: WalletMetalTotal, modalText?: string) { + function renderModalLines(text?: string) { + if (!text) { + return undefined + } + + return h( + 'div', + { style: { lineHeight: '1.9' } }, + text.split('\n').map((line, index) => + h( + 'div', + { key: index, style: index > 0 ? { marginTop: '6px' } : undefined }, + line + ) + ) + ) + } + + function openGoldModal( + metal: WalletMetalTotal, + modalText?: string, + modalTitle?: string, + modalContent?: string + ) { const title = + modalTitle ?? modalText ?? (isBoursecuHomeLayout.value ? formatBoursecuMetalModalText(metal) @@ -156,6 +185,7 @@ export function useHomePage() { Modal.confirm({ title, + content: renderModalLines(modalContent), okText: 'متوجه شدم', centered: true, cancelButtonProps: { style: { display: 'none' } }, @@ -168,7 +198,7 @@ export function useHomePage() { const metalRial = resolveAssetsRialValue(summary, getTradeBuyPrice(lastPrice.value)) Modal.confirm({ title: 'دارایی ریالی', - content: formatRialAssetsInfoText(cash, metalRial), + content: renderModalLines(formatRialAssetsInfoText(cash, metalRial)), okText: 'متوجه شدم', centered: true, cancelButtonProps: { style: { display: 'none' } }, diff --git a/src/views/base/home/desktop/components/DesktopAssetsMetrics.vue b/src/views/base/home/desktop/components/DesktopAssetsMetrics.vue index d1d2169..f3f1eb2 100644 --- a/src/views/base/home/desktop/components/DesktopAssetsMetrics.vue +++ b/src/views/base/home/desktop/components/DesktopAssetsMetrics.vue @@ -8,7 +8,7 @@ import { buildBoursecuHomeAssetRows, resolveSummaryMetalWeight } from '@/utils/p import { useHomePageContext } from '../../composables/useHomePage' const emit = defineEmits<{ - openGoldModal: [metal: WalletMetalTotal, modalText?: string] + openGoldModal: [metal: WalletMetalTotal, modalText?: string, modalTitle?: string, modalContent?: string] openRialModal: [] }>() @@ -30,6 +30,8 @@ type MetricItem = { value: string metal?: WalletMetalTotal modalText?: string + modalTitle?: string + modalContent?: string showInfo?: boolean onInfo?: 'gold' | 'rial' } @@ -53,6 +55,8 @@ const metrics = computed(() => { value: row.value, metal: row.metal, modalText: row.modalText, + modalTitle: row.modalTitle, + modalContent: row.modalContent, showInfo: row.info !== 'none', onInfo: row.info === 'rial' ? 'rial' : 'gold', }) @@ -104,7 +108,7 @@ function handleInfo(metric: MetricItem) { return } if (metric.metal) { - emit('openGoldModal', metric.metal, metric.modalText) + emit('openGoldModal', metric.metal, metric.modalText, metric.modalTitle, metric.modalContent) } }