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 <cursoragent@cursor.com>
This commit is contained in:
+166
-29
@@ -105,21 +105,21 @@ export interface HomeAssetRow {
|
|||||||
info: HomeAssetRowInfo
|
info: HomeAssetRowInfo
|
||||||
/** boursecu — متن مودال توضیحات (info) */
|
/** boursecu — متن مودال توضیحات (info) */
|
||||||
modalText?: string
|
modalText?: string
|
||||||
|
/** boursecu — عنوان مودال (مثلاً کل دارایی مس) */
|
||||||
|
modalTitle?: string
|
||||||
|
/** boursecu — محتوای چندخطی مودال */
|
||||||
|
modalContent?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatInventoryCardProductLabel(card: WalletInventoryCard): string {
|
function formatInventoryCardProductLabel(card: WalletInventoryCard): string {
|
||||||
return `دارایی ${card.title}`
|
return `دارایی ${card.title}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatBoursecuProductModalText(card: WalletInventoryCard): string {
|
|
||||||
return `${formatInventoryCardProductLabel(card)} = موجودی ${card.title}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/** boursecu — متن مودال برای ردیفهای metal_totals */
|
/** boursecu — متن مودال برای ردیفهای metal_totals */
|
||||||
export function formatBoursecuMetalModalText(metal: WalletMetalTotal): string {
|
export function formatBoursecuMetalModalText(metal: WalletMetalTotal): string {
|
||||||
switch (metal.metal_id) {
|
switch (metal.metal_id) {
|
||||||
case METAL_COPPER:
|
case METAL_COPPER:
|
||||||
return 'مجموع دارایی مس = جمع کاتد و مفتول و شمش'
|
return getTotalMetalLabel(METAL_COPPER)
|
||||||
case METAL_GOLD:
|
case METAL_GOLD:
|
||||||
return 'دارایی شمش مس = موجودی شمش مس'
|
return 'دارایی شمش مس = موجودی شمش مس'
|
||||||
case METAL_SILVER:
|
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<HomeAssetRow, 'modalTitle' | 'modalContent'>,
|
||||||
|
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) {
|
function findMetalTotalById(metalTotals: WalletMetalTotal[], metalId: number) {
|
||||||
return metalTotals.find((metal) => metal.metal_id === metalId)
|
return metalTotals.find((metal) => metal.metal_id === metalId)
|
||||||
}
|
}
|
||||||
@@ -158,20 +273,28 @@ export function buildBoursecuHomeAssetRows(
|
|||||||
const metalTotals = summary.metal_totals ?? []
|
const metalTotals = summary.metal_totals ?? []
|
||||||
const inventoryCards = summary.inventory_cards ?? []
|
const inventoryCards = summary.inventory_cards ?? []
|
||||||
const countProducts = summary.count_products ?? []
|
const countProducts = summary.count_products ?? []
|
||||||
|
const copperTotal = findMetalTotalById(metalTotals, METAL_COPPER)
|
||||||
|
|
||||||
for (const card of inventoryCards) {
|
for (const card of inventoryCards) {
|
||||||
if (card.display_type === 'count') {
|
if (card.display_type === 'count') {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rows.push({
|
rows.push(
|
||||||
id: `inventory-${card.product_id}`,
|
withBoursecuCopperModal(
|
||||||
label: formatInventoryCardProductLabel(card),
|
{
|
||||||
value: formatInventoryCardValue(card),
|
id: `inventory-${card.product_id}`,
|
||||||
metal: findMetalTotalById(metalTotals, card.metal_id),
|
label: formatInventoryCardProductLabel(card),
|
||||||
info: 'metal',
|
value: formatInventoryCardValue(card),
|
||||||
modalText: formatBoursecuProductModalText(card),
|
metal: findMetalTotalById(metalTotals, card.metal_id),
|
||||||
})
|
info: 'metal',
|
||||||
|
},
|
||||||
|
copperTotal,
|
||||||
|
inventoryCards,
|
||||||
|
countProducts,
|
||||||
|
toBoursecuModalProduct(card)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const metal of metalTotals) {
|
for (const metal of metalTotals) {
|
||||||
@@ -179,25 +302,39 @@ export function buildBoursecuHomeAssetRows(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rows.push({
|
rows.push(
|
||||||
id: `metal-${metal.metal_id}`,
|
withBoursecuCopperModal(
|
||||||
label: resolveMetalAssetsDisplayLabel(metal, true),
|
{
|
||||||
value: formatBoursecuMetalRowValue(metal),
|
id: `metal-${metal.metal_id}`,
|
||||||
metal,
|
label: resolveMetalAssetsDisplayLabel(metal, true),
|
||||||
info: 'metal',
|
value: formatBoursecuMetalRowValue(metal),
|
||||||
modalText: formatBoursecuMetalModalText(metal),
|
metal,
|
||||||
})
|
info: 'metal',
|
||||||
|
},
|
||||||
|
copperTotal,
|
||||||
|
inventoryCards,
|
||||||
|
countProducts,
|
||||||
|
resolveBoursecuMetalModalProduct(metal, countProducts)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const card of countProducts) {
|
for (const card of countProducts) {
|
||||||
rows.push({
|
rows.push(
|
||||||
id: `count-${card.product_id}`,
|
withBoursecuCopperModal(
|
||||||
label: formatInventoryCardProductLabel(card),
|
{
|
||||||
value: formatInventoryCardValue(card),
|
id: `count-${card.product_id}`,
|
||||||
metal: findMetalTotalById(metalTotals, card.metal_id),
|
label: formatInventoryCardProductLabel(card),
|
||||||
info: 'metal',
|
value: formatInventoryCardValue(card),
|
||||||
modalText: formatBoursecuProductModalText(card),
|
metal: findMetalTotalById(metalTotals, card.metal_id),
|
||||||
})
|
info: 'metal',
|
||||||
|
},
|
||||||
|
copperTotal,
|
||||||
|
inventoryCards,
|
||||||
|
countProducts,
|
||||||
|
toBoursecuModalProduct(card)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
rows.push({
|
rows.push({
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ const props = withDefaults(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
openGoldModal: [metal: WalletMetalTotal, modalText?: string]
|
openGoldModal: [metal: WalletMetalTotal, modalText?: string, modalTitle?: string, modalContent?: string]
|
||||||
openRialModal: []
|
openRialModal: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ function handleRowInfo(row: HomeAssetRow) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (row.metal) {
|
if (row.metal) {
|
||||||
emit('openGoldModal', row.metal, row.modalText)
|
emit('openGoldModal', row.metal, row.modalText, row.modalTitle, row.modalContent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -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 router from '@/router'
|
||||||
import { loading, removeToken } from '@/utils'
|
import { loading, removeToken } from '@/utils'
|
||||||
import { useUserStore } from '@/store/userStore'
|
import { useUserStore } from '@/store/userStore'
|
||||||
@@ -41,7 +41,12 @@ export interface HomePageContext {
|
|||||||
setChartMetal: (metal: ChartMetalType) => void
|
setChartMetal: (metal: ChartMetalType) => void
|
||||||
isBoursecuHomeLayout: ReturnType<typeof computed<boolean>>
|
isBoursecuHomeLayout: ReturnType<typeof computed<boolean>>
|
||||||
handleBannerClick: (banner: { typeClick?: string; modalText?: string; link?: string }) => void
|
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
|
openRialModal: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,8 +152,32 @@ export function useHomePage() {
|
|||||||
window.location.reload()
|
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 =
|
const title =
|
||||||
|
modalTitle ??
|
||||||
modalText ??
|
modalText ??
|
||||||
(isBoursecuHomeLayout.value
|
(isBoursecuHomeLayout.value
|
||||||
? formatBoursecuMetalModalText(metal)
|
? formatBoursecuMetalModalText(metal)
|
||||||
@@ -156,6 +185,7 @@ export function useHomePage() {
|
|||||||
|
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title,
|
title,
|
||||||
|
content: renderModalLines(modalContent),
|
||||||
okText: 'متوجه شدم',
|
okText: 'متوجه شدم',
|
||||||
centered: true,
|
centered: true,
|
||||||
cancelButtonProps: { style: { display: 'none' } },
|
cancelButtonProps: { style: { display: 'none' } },
|
||||||
@@ -168,7 +198,7 @@ export function useHomePage() {
|
|||||||
const metalRial = resolveAssetsRialValue(summary, getTradeBuyPrice(lastPrice.value))
|
const metalRial = resolveAssetsRialValue(summary, getTradeBuyPrice(lastPrice.value))
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: 'دارایی ریالی',
|
title: 'دارایی ریالی',
|
||||||
content: formatRialAssetsInfoText(cash, metalRial),
|
content: renderModalLines(formatRialAssetsInfoText(cash, metalRial)),
|
||||||
okText: 'متوجه شدم',
|
okText: 'متوجه شدم',
|
||||||
centered: true,
|
centered: true,
|
||||||
cancelButtonProps: { style: { display: 'none' } },
|
cancelButtonProps: { style: { display: 'none' } },
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { buildBoursecuHomeAssetRows, resolveSummaryMetalWeight } from '@/utils/p
|
|||||||
import { useHomePageContext } from '../../composables/useHomePage'
|
import { useHomePageContext } from '../../composables/useHomePage'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
openGoldModal: [metal: WalletMetalTotal, modalText?: string]
|
openGoldModal: [metal: WalletMetalTotal, modalText?: string, modalTitle?: string, modalContent?: string]
|
||||||
openRialModal: []
|
openRialModal: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -30,6 +30,8 @@ type MetricItem = {
|
|||||||
value: string
|
value: string
|
||||||
metal?: WalletMetalTotal
|
metal?: WalletMetalTotal
|
||||||
modalText?: string
|
modalText?: string
|
||||||
|
modalTitle?: string
|
||||||
|
modalContent?: string
|
||||||
showInfo?: boolean
|
showInfo?: boolean
|
||||||
onInfo?: 'gold' | 'rial'
|
onInfo?: 'gold' | 'rial'
|
||||||
}
|
}
|
||||||
@@ -53,6 +55,8 @@ const metrics = computed<MetricItem[]>(() => {
|
|||||||
value: row.value,
|
value: row.value,
|
||||||
metal: row.metal,
|
metal: row.metal,
|
||||||
modalText: row.modalText,
|
modalText: row.modalText,
|
||||||
|
modalTitle: row.modalTitle,
|
||||||
|
modalContent: row.modalContent,
|
||||||
showInfo: row.info !== 'none',
|
showInfo: row.info !== 'none',
|
||||||
onInfo: row.info === 'rial' ? 'rial' : 'gold',
|
onInfo: row.info === 'rial' ? 'rial' : 'gold',
|
||||||
})
|
})
|
||||||
@@ -104,7 +108,7 @@ function handleInfo(metric: MetricItem) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (metric.metal) {
|
if (metric.metal) {
|
||||||
emit('openGoldModal', metric.metal, metric.modalText)
|
emit('openGoldModal', metric.metal, metric.modalText, metric.modalTitle, metric.modalContent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user