Make boursecu branch boursecu-only and simplify app loading.

Remove multi-tenant branches and default views, show logo-only boot and splash screens, and polish boursecu transactions tab styling.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 12:35:42 +02:00
co-authored by Cursor
parent 2affe61dd3
commit 59cceaf581
76 changed files with 211 additions and 2932 deletions
+2 -13
View File
@@ -5,21 +5,12 @@
:class="{ 'app-header--scrolled': isHeaderScrolled }"
>
<div class="flex justify-between my-2 items-center">
<div v-if="isBoursecuProject()" class="flex items-center gap-2">
<div class="flex items-center gap-2">
<img :src="projectLogo" alt="" class="h-10 w-auto max-w-[9rem] object-contain" />
</div>
<div v-else class="flex items-center gap-2" @click.stop="router.push({ name: 'user' })">
<div class="z-100000 rounded-full border-1 text-primary border-primary">
<Icon icon="vuesax-linear:user" size="20" />
</div>
<div class="text-primary color-transition font-bold">
{{ userStore.userInfo?.name }}
</div>
</div>
<div class="flex items-center gap-2">
<button
v-if="isBoursecuProject()"
type="button"
class="mhome-header-btn"
@click="router.push({ name: 'user' })"
@@ -42,16 +33,14 @@
<script setup lang="ts">
import { computed, nextTick, onActivated, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { useUserStore } from '@/store/userStore'
import router from '@/router'
import Icon from '@/components/BasicPacks/BasicIcon.vue'
import { getProjectMobileNavLogo, isBoursecuProject } from '@/utils/config'
import { getProjectMobileNavLogo } from '@/utils/config'
const headerRef = ref<HTMLElement | null>(null)
const isHeaderScrolled = ref(false)
const route = useRoute()
const userStore = useUserStore()
const projectLogo = computed(() => getProjectMobileNavLogo())
let intersectionObserver: IntersectionObserver | null = null
@@ -1,127 +1,18 @@
<template>
<div :class="rootClass">
<div
v-if="!boursecuLayout && meta?.has_count_based_products"
:class="rowClass"
class="flex justify-center w-full z-20 mb-2"
>
محصولات:
<div class="text-l mr-2 font-bold">{{ usePriceWithoutType(countBasedInventory, false) }} عدد</div>
</div>
<div class="assets-section assets-section--boursecu rounded-lg px-3 py-3 mt-2">
<BoursecuAssetsSummary
v-if="boursecuLayout"
variant="mobile"
icon-color="primary"
/>
<template v-else-if="meta?.single_metal && primaryMetalTotal">
<div :class="[rowClass, meta?.has_count_based_products ? 'mt-2' : '']" class="flex justify-center w-full z-20">
{{ primaryMetalTotal.label }}:
<div class="text-l mr-2 font-bold">{{ formatMetalTotal(primaryMetalTotal) }}</div>
<BasicIcon
icon="vuesax-linear:info-circle"
size="18"
:color="iconColor"
@click="$emit('openGoldModal', primaryMetalTotal)"
/>
</div>
<PortfolioProfitInsight variant="mobile" />
<div :class="[rowClass, 'mt-2']" class="flex justify-center w-full z-20">
دارایی ریالی:
<div class="text-l mr-2 font-bold">{{ usePriceWithoutType(cashWallet) }} ریال</div>
<BasicIcon
icon="vuesax-linear:info-circle"
size="18"
:color="iconColor"
@click="$emit('openRialModal')"
/>
</div>
</template>
<template v-else-if="!boursecuLayout">
<div
v-for="(metal, index) in metalTotals"
:key="metal.metal_id"
:class="[rowClass, index > 0 || meta?.has_count_based_products ? 'mt-2' : '']"
class="flex justify-center w-full z-20"
>
{{ metal.label }}:
<div class="text-l mr-2 font-bold">{{ formatMetalTotal(metal) }}</div>
<BasicIcon
v-if="metal.metal_id === metalTotals[0]?.metal_id"
icon="vuesax-linear:info-circle"
size="18"
:color="iconColor"
@click="$emit('openGoldModal', metal)"
/>
</div>
<PortfolioProfitInsight variant="mobile" />
<div :class="[rowClass, 'mt-2']" class="flex justify-center w-full z-20">
دارایی ریالی:
<div class="text-l mr-2 font-bold">{{ usePriceWithoutType(cashWallet) }} ریال</div>
<BasicIcon
icon="vuesax-linear:info-circle"
size="18"
:color="iconColor"
@click="$emit('openRialModal')"
/>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import BoursecuAssetsSummary from './BoursecuAssetsSummary.vue'
import PortfolioProfitInsight from './PortfolioProfitInsight.vue'
import { formatWeightAmount, usePriceWithoutType } from '@/utils/usePrice'
import { useWalletAssets } from '@/composables/useWalletAssets'
import type { WalletMetalTotal } from '@/models/walletAssets'
import { resolveSummaryMetalWeight } from '@/utils/productAssets'
const props = withDefaults(
defineProps<{
plain?: boolean
boursecuLayout?: boolean
}>(),
{ plain: false, boursecuLayout: false }
)
defineEmits<{
openGoldModal: [metal: WalletMetalTotal, modalText?: string, modalTitle?: string, modalContent?: string]
openRialModal: []
}>()
const { assetsSummary } = useWalletAssets()
const rowClass = computed(() => (props.plain ? 'text-primary' : 'text-tertiary'))
const iconColor = computed(() => (props.plain ? 'primary' : 'tertiary'))
const rootClass = computed(() => {
if (props.boursecuLayout) {
return 'assets-section assets-section--boursecu rounded-lg px-3 py-3 mt-2'
}
if (props.plain) {
return 'py-2'
}
return 'bg-primary rounded-lg p-2 py-3 mt-2'
})
const meta = computed(() => assetsSummary.value?.meta)
const metalTotals = computed(() => assetsSummary.value?.metal_totals ?? [])
const primaryMetalTotal = computed(() => metalTotals.value[0])
const cashWallet = computed(() => assetsSummary.value?.cash_wallet ?? 0)
const inventoryCards = computed(() => assetsSummary.value?.inventory_cards ?? [])
const countBasedInventory = computed(() =>
(assetsSummary.value?.count_products ?? []).reduce((sum, item) => sum + Number(item.quantity ?? 0), 0)
)
function formatMetalTotal(metal: WalletMetalTotal) {
return `${formatWeightAmount(resolveSummaryMetalWeight(metal, inventoryCards.value))}${metal.unit}`
}
</script>
@@ -4,12 +4,12 @@
:class="containerClass"
>
<template v-if="displayState !== 'open'">
<div class="status-box" :class="isBoursecu ? 'status-box--boursecu' : 'status-box--default'">
<div class="status-box status-box--boursecu">
<div class="status-box__icon-wrap">
<Icon
:icon="displayState === 'closed' ? 'vuesax-linear:lock' : 'vuesax-linear:info-circle'"
size="22"
:color="isBoursecu ? '#ffffff' : '#64748b'"
color="#ffffff"
/>
</div>
<div class="status-box__title">{{ statusTitle }}</div>
@@ -18,8 +18,8 @@
</template>
<template v-else>
<div class="price-label" :class="isBoursecu ? 'price-label--boursecu' : 'text-gray-600 text-[12px] ml-2 w-full text-center'">
{{ priceLabel }}
<div class="price-label price-label--boursecu">
قیمت لحظه‌ای کاتد مس
</div>
<div class="w-full items-center flex justify-center">
<div v-if="price" class="flex gap-1 text-xs items-center">
@@ -38,11 +38,10 @@
</div>
<Vue3Odometer
:value="displayGoldVal"
class="o-text !text-xl font-bold"
:class="isBoursecu ? 'price-odometer--boursecu' : ''"
class="o-text !text-xl font-bold price-odometer--boursecu"
style="direction: ltr"
/>
<span :class="isBoursecu ? 'price-unit--boursecu' : ''">ریال</span>
<span class="price-unit--boursecu">ریال</span>
</div>
</div>
</template>
@@ -52,9 +51,7 @@
<script setup lang="ts">
import { computed } from 'vue'
import Icon from '@/components/BasicPacks/BasicIcon.vue'
import { useConfigStore } from '@/store/configStore'
import { useUserStore } from '@/store/userStore'
import { isBoursecuProject } from '@/utils/config'
import { getTradeBuyPrice, lastPrice, percentPriceChanged, price } from '@/utils/price'
import {
NO_QUOTE_MESSAGE,
@@ -65,13 +62,7 @@ import Vue3Odometer from 'vue3-odometer'
import 'odometer/themes/odometer-theme-default.css'
import { formatSignedPercentChange } from '@/utils/usePrice'
const config = useConfigStore()
const userStore = useUserStore()
const isBoursecu = isBoursecuProject()
const priceLabel = computed(() =>
isBoursecu ? 'قیمت لحظه‌ای کاتد مس' : `قیمت خرید از ${config.config.name_fa}`
)
const displayState = computed(() => getPriceDisplayState())
@@ -91,9 +82,6 @@ const statusHint = computed(() => {
})
const containerClass = computed(() => {
if (!isBoursecu) {
return 'border-gray-200 border-1'
}
if (displayState.value !== 'open') {
return 'boursecu-price-box boursecu-price-box--closed'
}
@@ -190,18 +178,6 @@ void price
color: rgba(255, 255, 255, 0.78);
}
.status-box--default .status-box__icon-wrap {
background: #f1f5f9;
}
.status-box--default .status-box__title {
color: #334155;
}
.status-box--default .status-box__hint {
color: #64748b;
}
.price-change-badge {
display: inline-flex;
align-items: center;
@@ -1,6 +1,5 @@
<script setup lang="ts">
import Icon from '@/components/BasicPacks/BasicIcon.vue'
import { isBoursecuProject } from '@/utils/config'
import type { HomeQuickActionItem } from '../layouts/types'
defineProps<{
@@ -10,15 +9,12 @@ defineProps<{
defineEmits<{
click: []
}>()
const isBoursecu = isBoursecuProject()
</script>
<template>
<button
type="button"
class="home-quick-action-box flex flex-col items-center justify-center gap-2 rounded-xl border-2 p-1 min-h-[96px] w-full"
:class="{ 'home-quick-action-box--boursecu': isBoursecu }"
class="home-quick-action-box home-quick-action-box--boursecu flex flex-col items-center justify-center gap-2 rounded-xl border-2 p-1 min-h-[96px] w-full"
@click="$emit('click')"
>
<div class="flex h-12 w-12 shrink-0 items-center justify-center">
@@ -126,7 +126,6 @@ const { width: chartWidth } = useElementSize(chartContainerRef)
const seriesLength = computed(() => props.priceChartSeries?.[0]?.data?.length ?? 0)
const amplifyKatodChart = computed(() =>
shouldAmplifyKatodMesChart({
isBoursecu: props.layout === 'boursecu',
metalTypeSetting: settingsStore.metalTypeSettingValue,
})
)
@@ -141,7 +140,6 @@ const katodDayRange = computed(() => {
return resolveMetalDayRangePrices({
price: price.value,
metal: props.selectedMetal,
isBoursecuLayout: true,
metalRatio: selectedMetalChartRatio.value,
})
})
@@ -2,7 +2,7 @@
<div
class="w-full flex justify-evenly rounded-lg py-2 home_appear_fade_in"
:class="[
isBoursecuLayout ? 'mt-8 pt-2' : 'mt-6',
'mt-8 pt-2',
variant === 'mobile' ? 'pb-20' : 'mt-0 pb-0',
]"
>
@@ -34,7 +34,6 @@
import { computed } from 'vue'
import { usePriceWithoutType } from '../../../../utils/usePrice'
import { price } from '@/utils/price'
import { getHomeLayoutId } from '../layouts/registry'
import { resolveMetalDayRangePrices, usePriceChart } from '../composables/usePriceChart'
withDefaults(
@@ -47,13 +46,11 @@ withDefaults(
)
const { selectedChartMetal, selectedMetalChartRatio } = usePriceChart()
const isBoursecuLayout = computed(() => getHomeLayoutId() === 'boursecu')
const rangePrices = computed(() =>
resolveMetalDayRangePrices({
price: price.value,
metal: selectedChartMetal.value,
isBoursecuLayout: isBoursecuLayout.value,
metalRatio: selectedMetalChartRatio.value,
})
)