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
+35 -71
View File
@@ -17,8 +17,8 @@
<!----> <!---->
<style> <style>
:root { :root {
--boot-bg: #ffffff; --boot-bg: #133050;
--boot-bg-soft: #f8fafc; --boot-bg-soft: #133050;
--boot-card: rgba(255, 255, 255, 0.88); --boot-card: rgba(255, 255, 255, 0.88);
--boot-border: rgba(15, 23, 42, 0.08); --boot-border: rgba(15, 23, 42, 0.08);
--boot-text: #111827; --boot-text: #111827;
@@ -52,75 +52,26 @@
BlinkMacSystemFont, BlinkMacSystemFont,
"Segoe UI", "Segoe UI",
sans-serif; sans-serif;
background: background: var(--boot-bg);
radial-gradient(circle at top, var(--boot-orb), transparent 38%),
linear-gradient(
135deg,
var(--boot-bg) 0%,
var(--boot-bg-soft) 100%
);
color: var(--boot-text); color: var(--boot-text);
overflow: hidden; overflow: hidden;
} }
.boot-loader__card { .boot-loader__logo {
position: relative; width: min(72vw, 280px);
width: min(340px, calc(100vw - 48px)); height: auto;
padding: 30px 22px 28px; object-fit: contain;
border-radius: 24px; animation: boot-logo-fade-in 0.45s ease-out both;
text-align: center;
background: var(--boot-card);
border: 1px solid var(--boot-border);
box-shadow: 0 18px 48px var(--boot-shadow);
backdrop-filter: blur(10px);
} }
.boot-loader__orb { @keyframes boot-logo-fade-in {
position: absolute; from {
top: -72px; opacity: 0;
left: 50%; transform: scale(0.96);
width: 150px; }
height: 150px;
transform: translateX(-50%);
border-radius: 999px;
background: var(--boot-orb);
filter: blur(8px);
pointer-events: none;
}
.boot-loader__spinner {
position: relative;
z-index: 1;
width: 56px;
height: 56px;
margin: 0 auto 20px;
border-radius: 999px;
border: 4px solid var(--boot-spinner-track);
border-top-color: var(--boot-accent);
animation: boot-spin 0.9s linear infinite;
}
.boot-loader__title {
position: relative;
z-index: 1;
margin: 0;
font-size: 19px;
font-weight: 800;
letter-spacing: -0.02em;
}
.boot-loader__text {
position: relative;
z-index: 1;
margin: 10px 0 0;
color: var(--boot-muted);
font-size: 14px;
line-height: 2;
}
@keyframes boot-spin {
to { to {
transform: rotate(360deg); opacity: 1;
transform: scale(1);
} }
} }
@@ -323,6 +274,20 @@
setVar('--App-background-color', tenantBackground) setVar('--App-background-color', tenantBackground)
} }
var logoEl = document.getElementById('boot-logo')
if (logoEl) {
if (user.name === 'boursecu') {
logoEl.src = '/boursecu-splash.png'
} else if (user.avatar) {
logoEl.src = user.avatar
}
}
if (user.name === 'boursecu') {
setVar('--boot-bg', '#133050')
setVar('--boot-bg-soft', '#133050')
}
document.title = user.name_fa || user.name || 'در حال بارگذاری...' document.title = user.name_fa || user.name || 'در حال بارگذاری...'
} }
@@ -334,13 +299,12 @@
<body> <body>
<div id="app"> <div id="app">
<div class="boot-loader"> <div class="boot-loader">
<div class="boot-loader__card"> <img
<div class="boot-loader__orb"></div> id="boot-logo"
<div class="boot-loader__spinner"></div> class="boot-loader__logo"
src="/boursecu-splash.png"
<h1 class="boot-loader__title">در حال هدایت به سامانه</h1> alt=""
<p class="boot-loader__text">لطفاً چند لحظه صبر کنید...</p> />
</div>
</div> </div>
</div> </div>
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+3 -51
View File
@@ -11,42 +11,13 @@
</div> </div>
<AConfigProvider :theme="antTheme ?? undefined" direction="rtl"> <AConfigProvider :theme="antTheme ?? undefined" direction="rtl">
<div v-if="showSplash" class="app-splash-screen flex justify-center" :style="{ backgroundColor: getProjectSplashBackground() }"> <div v-if="showSplash" class="app-splash-screen flex justify-center" :style="{ backgroundColor: getProjectSplashBackground() }">
<template v-if="!isBoursecuProject()">
<div class="app-splash-screen__gradient app-splash-screen__gradient--top h-1/3 flex">
<img :src="img_topGradiant" alt="" class="absolute z-10 w-full h-full object-cover"/>
<img :src="img_pattern" alt="" class="absolute w-full h-full object-none"/>
</div>
<div class="app-splash-screen__gradient app-splash-screen__gradient--bottom h-1/3">
<img
:src="img_bottomGradiant"
alt=""
class="absolute z-10 w-full h-full object-cover"
/>
<img :src="img_pattern" alt="" class="absolute w-full h-full object-none"/>
</div>
</template>
<div class="self-center splash_appear_fade_in"> <div class="self-center splash_appear_fade_in">
<img <img
:src="getProjectSplashLogo(config?.config?.avatar)" :src="getProjectSplashLogo(config?.config?.avatar)"
:class="isBoursecuProject() class="w-[min(72vw,280px)] h-auto object-contain"
? 'w-[min(72vw,280px)] h-auto object-contain'
: hasCustomLoginLogoLayout()
? 'w-[80vw] max-w-[320px] h-auto object-contain'
: 'h-[20vh] object-contain'"
alt="" alt=""
> >
</div> </div>
<div class="fixed w-full bottom-5 home_appear_fade_in_up">
<div
class="text-center text-xs"
:class="isBoursecuProject() ? 'text-white' : ''"
>
نسخه: {{ displayVersion }}
</div>
</div>
<div class="absolute bottom-10">
<a-spin :indicator="splashIndicator" size="small"/>
</div>
</div> </div>
<div v-else class="app-main"> <div v-else class="app-main">
@@ -90,16 +61,10 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import pkg from '../package.json' import {computed, defineAsyncComponent, onMounted, ref, watch} from 'vue'
import { getDisplayAppVersion } from '@/config/config-cache'
import {computed, defineAsyncComponent, h, onMounted, ref, watch} from 'vue'
import img_topGradiant from '@/views/login/assets/img/gr_top_white.svg'
import img_pattern from '@/views/login/assets/img/pattern.svg'
import {LoadingOutlined} from '@ant-design/icons-vue'
import img_bottomGradiant from '@/views/login/assets/img/gr_bottom_white.svg'
import BasicButton from '@/components/BasicPacks/BasicButton.vue' import BasicButton from '@/components/BasicPacks/BasicButton.vue'
import {useConfigStore} from "@/store/configStore"; import {useConfigStore} from "@/store/configStore";
import {getProjectFontFamily, getProjectSplashBackground, getProjectSplashLogo, getProjectBrandRed, getProjectPrimaryColor, hasCustomLoginLogoLayout, isBoursecuProject, resetAppSurfaceBackground} from "@/utils/config"; import {getProjectFontFamily, getProjectSplashBackground, getProjectSplashLogo, getProjectBrandRed, getProjectPrimaryColor, resetAppSurfaceBackground} from "@/utils/config";
import {ConfigProvider as AConfigProvider} from 'ant-design-vue' import {ConfigProvider as AConfigProvider} from 'ant-design-vue'
import BasicIcon from "@/components/BasicPacks/BasicIcon.vue"; import BasicIcon from "@/components/BasicPacks/BasicIcon.vue";
import { useApplyDesktopLayoutClass, useDesktopBreakpoint } from '@/layouts/composables/useAppLayout' import { useApplyDesktopLayoutClass, useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
@@ -126,24 +91,11 @@ const showDesktopFloatingCredit = computed(() => {
return route.name === 'login' || !!route.meta.hideMenu return route.name === 'login' || !!route.meta.hideMenu
}) })
const displayVersion = getDisplayAppVersion(pkg.version)
const config = useConfigStore() const config = useConfigStore()
const splashIndicator = computed(() => h(LoadingOutlined, {
style: {
fontSize: '24px',
color: isBoursecuProject() ? '#ffffff' : undefined,
},
spin: true
}))
const showSplash = ref(true) const showSplash = ref(true)
const popVisible = ref(false) const popVisible = ref(false)
// const GAROOSI_SUPPORT_URL = 'https://ble.ir/garoosigallery'
const supportLink = computed(() => { const supportLink = computed(() => {
// if (config.config?.name === 'garoosi') {
// return GAROOSI_SUPPORT_URL
// }
return buildSupportMessengerUrl( return buildSupportMessengerUrl(
supportMessengerSettings.value.messenger, supportMessengerSettings.value.messenger,
supportMessengerSettings.value.messengerId supportMessengerSettings.value.messengerId
-5
View File
@@ -5,7 +5,6 @@ import { useUserStore } from '@/store/userStore'
import { useFundUserStore } from '@/store/fundStore' import { useFundUserStore } from '@/store/fundStore'
import type { NavigationGuardNext } from 'vue-router' import type { NavigationGuardNext } from 'vue-router'
import { useAuthStore } from '@/store/useAuthStore' import { useAuthStore } from '@/store/useAuthStore'
import { getConfigApp } from '@/utils/config'
import { recoverMobileViewportAfterAuth } from '@/layouts/composables/useMobileScrollReset' import { recoverMobileViewportAfterAuth } from '@/layouts/composables/useMobileScrollReset'
const PUBLIC_ROUTE_NAMES = ['login', '404', 'educate', 'aboutUs'] as const const PUBLIC_ROUTE_NAMES = ['login', '404', 'educate', 'aboutUs'] as const
@@ -68,10 +67,6 @@ export function setupRouterGuards() {
clearTimeout(timeoutId) clearTimeout(timeoutId)
} }
if (to.name === 'giftCard' && getConfigApp('name') !== 'garoosi') {
return next({ name: (from.name as string) || 'home' })
}
const authenticated = await resolveSession() const authenticated = await resolveSession()
if (!authenticated) { if (!authenticated) {
+1 -11
View File
@@ -1,7 +1,6 @@
import { import {
getBoursecuActionIconSrc, getBoursecuActionIconSrc,
getBoursecuTransactionsIcon, getBoursecuTransactionsIcon,
isBoursecuProject,
} from '@/utils/config' } from '@/utils/config'
import type { AppNavItem } from '../types' import type { AppNavItem } from '../types'
@@ -43,21 +42,12 @@ const BOURSECU_PRIMARY_NAV: AppNavItem[] = [
}, },
] ]
const DEFAULT_PRIMARY_NAV: AppNavItem[] = [
{ id: 'home', routeName: 'home', label: 'صفحه اصلی', icon: 'vuesax-linear:home' },
{ id: 'transactions', routeName: 'transactions', label: 'تراکنش‌ها', icon: 'vuesax-linear:receipt-item' },
{ id: 'buyProduct', routeName: 'buyProduct', label: 'محصولات', icon: 'vuesax-linear:coin' },
{ id: 'wallet', routeName: 'wallet', label: 'کیف پول', icon: 'vuesax-linear:wallet-1' },
{ id: 'user', routeName: 'user', label: 'تنظیمات', icon: 'vuesax-linear:setting-2' },
]
export const SECONDARY_NAV: AppNavItem[] = [ export const SECONDARY_NAV: AppNavItem[] = [
{ id: 'notifyTrigger', routeName: 'notifyTrigger', label: 'هشدار قیمت', icon: 'vuesax-linear:bill' }, { id: 'notifyTrigger', routeName: 'notifyTrigger', label: 'هشدار قیمت', icon: 'vuesax-linear:bill' },
{ id: 'about', routeName: 'about', label: 'درباره ما', icon: 'vuesax-linear:info-circle' }, { id: 'about', routeName: 'about', label: 'درباره ما', icon: 'vuesax-linear:info-circle' },
{ id: 'rules', routeName: 'rules', label: 'قوانین و مقررات', icon: 'vuesax-linear:task' }, { id: 'rules', routeName: 'rules', label: 'قوانین و مقررات', icon: 'vuesax-linear:task' },
{ id: 'giftCard', routeName: 'giftCard', label: 'ثبت کارت هدیه', icon: 'vuesax-linear:gift' },
] ]
export function getPrimaryNavItems(): AppNavItem[] { export function getPrimaryNavItems(): AppNavItem[] {
return isBoursecuProject() ? BOURSECU_PRIMARY_NAV : DEFAULT_PRIMARY_NAV return BOURSECU_PRIMARY_NAV
} }
+2 -6
View File
@@ -1,8 +1,5 @@
<template> <template>
<aside <aside class="desktop-sidebar desktop-sidebar--boursecu">
class="desktop-sidebar"
:class="{ 'desktop-sidebar--boursecu': isBoursecu }"
>
<div class="desktop-sidebar__brand"> <div class="desktop-sidebar__brand">
<img <img
:src="navLogo" :src="navLogo"
@@ -46,14 +43,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { computed } from 'vue'
import { useConfigStore } from '@/store/configStore' import { useConfigStore } from '@/store/configStore'
import { getProjectSplashLogo, isBoursecuProject } from '@/utils/config' import { getProjectSplashLogo } from '@/utils/config'
import { getPrimaryNavItems, SECONDARY_NAV } from '../config/navigation' import { getPrimaryNavItems, SECONDARY_NAV } from '../config/navigation'
import { useAppNavigation } from '../composables/useAppNavigation' import { useAppNavigation } from '../composables/useAppNavigation'
import DesktopNavItem from './DesktopNavItem.vue' import DesktopNavItem from './DesktopNavItem.vue'
import AppCreditLink from '@/components/AppCreditLink.vue' import AppCreditLink from '@/components/AppCreditLink.vue'
const config = useConfigStore() const config = useConfigStore()
const isBoursecu = isBoursecuProject()
const navLogo = computed(() => getProjectSplashLogo(config.config?.avatar)) const navLogo = computed(() => getProjectSplashLogo(config.config?.avatar))
const primaryItems = getPrimaryNavItems() const primaryItems = getPrimaryNavItems()
const secondaryItems = SECONDARY_NAV const secondaryItems = SECONDARY_NAV
+1 -8
View File
@@ -10,8 +10,6 @@
<MobileBottomNav <MobileBottomNav
v-if="!hideMenu" v-if="!hideMenu"
:is-boursecu="isBoursecu"
:nav-logo="navLogo"
:nav-item-class="navItemClass" :nav-item-class="navItemClass"
@navigate="navigateTo" @navigate="navigateTo"
/> />
@@ -19,18 +17,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, watch } from 'vue' import { onMounted, watch } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { useConfigStore } from '@/store/configStore'
import { getProjectMobileNavLogo, isBoursecuProject } from '@/utils/config'
import { useAppNavigation } from '../composables/useAppNavigation' import { useAppNavigation } from '../composables/useAppNavigation'
import { recoverMobileViewportAfterAuth, resetMobileAppScroll } from '../composables/useMobileScrollReset' import { recoverMobileViewportAfterAuth, resetMobileAppScroll } from '../composables/useMobileScrollReset'
import MobileBottomNav from './MobileBottomNav.vue' import MobileBottomNav from './MobileBottomNav.vue'
const config = useConfigStore()
const route = useRoute() const route = useRoute()
const isBoursecu = isBoursecuProject()
const navLogo = computed(() => getProjectMobileNavLogo(config.config?.avatar))
const { const {
hideMenu, hideMenu,
+5 -61
View File
@@ -2,7 +2,6 @@
<div class="bottom-appbar w-auto mx-auto bg-white"> <div class="bottom-appbar w-auto mx-auto bg-white">
<div class="tabs bottom-0"> <div class="tabs bottom-0">
<div <div
v-if="isBoursecu"
:class="navItemClass('buyProduct')" :class="navItemClass('buyProduct')"
class="tab" class="tab"
@click="$emit('navigate', 'buyProduct')" @click="$emit('navigate', 'buyProduct')"
@@ -16,17 +15,6 @@
</div> </div>
<div class="text-xs mt-1 font-bold">خرید و فروش</div> <div class="text-xs mt-1 font-bold">خرید و فروش</div>
</div> </div>
<div
v-else
:class="navItemClass('home')"
class="tab"
@click="$emit('navigate', 'home')"
>
<div class="tab__icon">
<Icon icon="vuesax-linear:home" size="24" />
</div>
<div class="text-xs mt-1 font-bold">صفحه اصلی</div>
</div>
<div <div
:class="navItemClass('transactions')" :class="navItemClass('transactions')"
@@ -35,18 +23,15 @@
> >
<div class="tab__icon"> <div class="tab__icon">
<img <img
v-if="isBoursecu"
:src="boursecuTransactionsIcon" :src="boursecuTransactionsIcon"
alt="" alt=""
class="tab__icon-image" class="tab__icon-image"
/> />
<Icon v-else icon="vuesax-linear:receipt-item" size="24" />
</div> </div>
<div class="text-xs mt-1 font-bold">تراکنش ها</div> <div class="text-xs mt-1 font-bold">تراکنش ها</div>
</div> </div>
<div <div
v-if="isBoursecu"
:class="navItemClass('home')" :class="navItemClass('home')"
class="tab" class="tab"
@click="$emit('navigate', 'home')" @click="$emit('navigate', 'home')"
@@ -60,28 +45,6 @@
</div> </div>
<div class="text-xs mt-1 font-bold">صفحه اصلی</div> <div class="text-xs mt-1 font-bold">صفحه اصلی</div>
</div> </div>
<div v-else class="main_in" @click="$emit('navigate', 'buyProduct')">
<div
class="circle_in"
:class="isWhiteNavLogo ? 'is-white-nav-logo' : 'text-white'"
>
<div class="circle_into">
<div
v-if="projectName === 'garoosi'"
class="p-2 text-tertiary flex flex-col justify-center items-center"
>
<Icon icon="vuesax-linear:coin" />
<div class="text-[9px] font-bold">محصولات</div>
</div>
<img
v-else
class="p-2"
:src="navLogo"
alt=""
/>
</div>
</div>
</div>
<div <div
:class="navItemClass('wallet')" :class="navItemClass('wallet')"
@@ -90,47 +53,36 @@
> >
<div class="tab__icon"> <div class="tab__icon">
<img <img
v-if="isBoursecu"
:src="boursecuWalletIcon" :src="boursecuWalletIcon"
alt="" alt=""
class="tab__icon-image" class="tab__icon-image"
/> />
<Icon v-else icon="vuesax-linear:wallet-1" size="24" />
</div> </div>
<div class="text-xs mt-1 font-bold">کیف پول</div> <div class="text-xs mt-1 font-bold">کیف پول</div>
</div> </div>
<div <div
:class="navItemClass(userNavItem.routeName)" :class="navItemClass('user')"
class="tab" class="tab"
@click="$emit('navigate', userNavItem.routeName)" @click="$emit('navigate', 'user')"
> >
<div class="tab__icon"> <div class="tab__icon">
<img <img
v-if="isBoursecu && userNavItem.routeName === 'user'"
:src="boursecuSettingsIcon" :src="boursecuSettingsIcon"
alt="" alt=""
class="tab__icon-image" class="tab__icon-image"
/> />
<Icon v-else :icon="userNavItem.icon" size="24" />
</div> </div>
<div class="text-xs mt-1 font-bold">{{ userNavItem.label }}</div> <div class="text-xs mt-1 font-bold">تنظیمات</div>
</div> </div>
</div> </div>
<div v-if="!isBoursecu" class="main">
<div class="circle" />
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { getBoursecuActionIconSrc } from '@/utils/config'
import Icon from '@/components/BasicPacks/BasicIcon.vue'
import { getBoursecuActionIconSrc, getConfigApp } from '@/utils/config'
defineProps<{ defineProps<{
isBoursecu: boolean
navLogo: string
navItemClass: (routeName: string) => string navItemClass: (routeName: string) => string
}>() }>()
@@ -138,19 +90,11 @@ defineEmits<{
navigate: [routeName: string] navigate: [routeName: string]
}>() }>()
const projectName = getConfigApp('name')
const userNavItem = computed(() =>
projectName === 'abshodehrahimi'
? { routeName: 'notifyTrigger', label: 'هشدار قیمت', icon: 'vuesax-linear:bill' }
: { routeName: 'user', label: 'تنظیمات', icon: 'vuesax-linear:setting-2' },
)
const boursecuBuySellIcon = getBoursecuActionIconSrc('buySell') ?? '' const boursecuBuySellIcon = getBoursecuActionIconSrc('buySell') ?? ''
const boursecuTransactionsIcon = getBoursecuActionIconSrc('transactions') ?? '' const boursecuTransactionsIcon = getBoursecuActionIconSrc('transactions') ?? ''
const boursecuWalletIcon = getBoursecuActionIconSrc('wallet') ?? '' const boursecuWalletIcon = getBoursecuActionIconSrc('wallet') ?? ''
const boursecuHomeIcon = getBoursecuActionIconSrc('home') ?? '' const boursecuHomeIcon = getBoursecuActionIconSrc('home') ?? ''
const boursecuSettingsIcon = getBoursecuActionIconSrc('settings') ?? '' const boursecuSettingsIcon = getBoursecuActionIconSrc('settings') ?? ''
const whiteNavLogoProjects = ['goldenramin']
const isWhiteNavLogo = computed(() => whiteNavLogoProjects.includes(projectName))
</script> </script>
<style scoped> <style scoped>
-5
View File
@@ -32,13 +32,8 @@ const KATOD_DEVIATION_FACTOR = 200
/** نمودار boursecu وقتی metal_type = کاتد مس */ /** نمودار boursecu وقتی metal_type = کاتد مس */
export function shouldAmplifyKatodMesChart(options: { export function shouldAmplifyKatodMesChart(options: {
isBoursecu: boolean
metalTypeSetting?: string | number | null metalTypeSetting?: string | number | null
}): boolean { }): boolean {
if (!options.isBoursecu) {
return false
}
const metal = String(options.metalTypeSetting ?? '').trim() const metal = String(options.metalTypeSetting ?? '').trim()
return metal === 'کاتد مس' return metal === 'کاتد مس'
} }
+30 -123
View File
@@ -9,14 +9,9 @@ import boursecuBuySellIcon from '@/assets/img/boursecu-buy-sell-icon.png'
import boursecuAssetsIcon from '@/assets/img/boursecu-assets-icon.png' import boursecuAssetsIcon from '@/assets/img/boursecu-assets-icon.png'
import boursecuHomeIcon from '@/assets/img/boursecu-home-icon.png' import boursecuHomeIcon from '@/assets/img/boursecu-home-icon.png'
import boursecuSettingsIcon from '@/assets/img/boursecu-settings-icon.png' import boursecuSettingsIcon from '@/assets/img/boursecu-settings-icon.png'
import bazretalaLoginLogo from '@/assets/img/bazretala-login-logo.png'
import bazretalaNavLogo from '@/assets/img/bazretala-nav-logo.png'
import bazretalaFundBg from '@/assets/img/bazretala-fund-bg.png'
import bazretalaEmptyFund from '@/assets/img/bazretala-empty-fund.png'
import soroushgoldLoginLogo from '@/assets/img/soroushgold-login-logo.png'
export function getConfigApp(key: string) { export function getConfigApp(key: string) {
const config = window.__APP_CONFIG__ ? window.__APP_CONFIG__.user:{} const config = window.__APP_CONFIG__ ? window.__APP_CONFIG__.user : {}
if (config) { if (config) {
return config[key] return config[key]
@@ -24,32 +19,15 @@ export function getConfigApp(key: string) {
return null return null
} }
/** boursecu-only branch */
export function isBoursecuProject() { export function isBoursecuProject() {
return getConfigApp('name') === 'boursecu' return true
} }
export function isBazretalaProject() { const WALLET_DEPOSIT_QUICK_AMOUNTS_RIAL = [10_000_000, 50_000_000, 100_000_000] as const
return getConfigApp('name') === 'bazretala'
}
export function isSoroushgoldProject() {
return getConfigApp('name') === 'soroushgold'
}
export function isNiazitalaProject() {
return getConfigApp('name') === 'niazitala'
}
const DEFAULT_WALLET_DEPOSIT_QUICK_AMOUNTS_RIAL = [10_000_000, 50_000_000, 100_000_000] as const
/** niazitala: 5 / 10 / 15 میلیون تومان — ذخیره و نمایش به ریال */
const NIAZITALA_WALLET_DEPOSIT_QUICK_AMOUNTS_RIAL = [50_000_000, 100_000_000, 150_000_000] as const
export function getWalletDepositQuickAmountsRial(): readonly number[] { export function getWalletDepositQuickAmountsRial(): readonly number[] {
if (isNiazitalaProject()) { return WALLET_DEPOSIT_QUICK_AMOUNTS_RIAL
return NIAZITALA_WALLET_DEPOSIT_QUICK_AMOUNTS_RIAL
}
return DEFAULT_WALLET_DEPOSIT_QUICK_AMOUNTS_RIAL
} }
export function getBoursecuTransactionsIcon() { export function getBoursecuTransactionsIcon() {
@@ -100,7 +78,7 @@ export function getBoursecuActionIconSrc(actionId: string): string | undefined {
} }
export function hasCustomLoginLogoLayout() { export function hasCustomLoginLogoLayout() {
return isBoursecuProject() || isBazretalaProject() return true
} }
export const BOURSECU_BRAND_RED = '#bc3c3b' export const BOURSECU_BRAND_RED = '#bc3c3b'
@@ -111,22 +89,21 @@ export const BOURSECU_COPPER_BG_LIGHT = '#fdf0e6'
export const BOURSECU_COPPER_BG_GRADIENT = 'linear-gradient(135deg, #c98a4a 0%, #a86528 100%)' export const BOURSECU_COPPER_BG_GRADIENT = 'linear-gradient(135deg, #c98a4a 0%, #a86528 100%)'
export function getProjectBrandRed() { export function getProjectBrandRed() {
return isBoursecuProject() ? BOURSECU_BRAND_RED : null return BOURSECU_BRAND_RED
} }
/** رنگ accent تم از تنظیمات tenant — بدون override پروژه‌ای */ /** رنگ accent تم از تنظیمات tenant */
export function getProjectPrimaryColor() { export function getProjectPrimaryColor() {
const color = getConfigApp('color') const color = getConfigApp('color')
return color != null ? String(color) : undefined return color != null ? String(color) : undefined
} }
/** فقط رنگ خط/ناحیه نمودار قیمت در بورس مس */
export function getBoursecuChartColor() { export function getBoursecuChartColor() {
return BOURSECU_BRAND_BLUE return BOURSECU_BRAND_BLUE
} }
export function resolveProjectColor(color?: string | null) { export function resolveProjectColor(color?: string | null) {
if (!color || !isBoursecuProject()) { if (!color) {
return color return color
} }
@@ -137,131 +114,66 @@ export function resolveProjectColor(color?: string | null) {
return color return color
} }
/** لوگوی پنل برند لاگین دسکتاپ — فقط soroushgold لوگوی اختصاصی دارد */ export function getProjectDesktopLoginBrandLogo(_avatar?: string | null) {
export function getProjectDesktopLoginBrandLogo(avatar?: string | null) { return getProjectSplashLogo()
if (isSoroushgoldProject()) {
return soroushgoldLoginLogo
}
return getProjectSplashLogo(avatar)
} }
export function getProjectLoginLogo(avatar?: string | null) { export function getProjectLoginLogo(_avatar?: string | null) {
if (isBoursecuProject()) { return boursecuLoginLogo
return boursecuLoginLogo
}
if (isBazretalaProject()) {
return bazretalaLoginLogo
}
return avatar ?? getConfigApp('avatar')
} }
/** لوگوی صفحه لودینگ اولیه — جدا از لوگوی لاگین/ناوبری */ export function getProjectSplashLogo(_avatar?: string | null) {
export function getProjectSplashLogo(avatar?: string | null) { return boursecuSplashLogo
if (isBoursecuProject()) {
return boursecuSplashLogo
}
return getProjectLoginLogo(avatar)
} }
export function getProjectNavLogo(avatar?: string | null) { export function getProjectNavLogo(_avatar?: string | null) {
if (isBazretalaProject()) { return boursecuLogo
return bazretalaNavLogo
}
if (isBoursecuProject()) {
return boursecuLogo
}
return avatar ?? getConfigApp('avatar')
} }
/** لوگوی ناوبری موبایل — boursecu همان لوگوی لاگین (پس‌زمینه سفید) */ export function getProjectMobileNavLogo(_avatar?: string | null) {
export function getProjectMobileNavLogo(avatar?: string | null) { return boursecuLoginLogo
if (isBoursecuProject()) {
return boursecuLoginLogo
}
if (isSoroushgoldProject()) {
return soroushgoldLoginLogo
}
return getProjectNavLogo(avatar)
} }
export const BAZRETALA_SUPPORT_PHONE = '09121784258'
export function getProjectSupportPhone() { export function getProjectSupportPhone() {
if (isBazretalaProject()) {
return BAZRETALA_SUPPORT_PHONE
}
return '' return ''
} }
export function getProjectSupportPhoneLink() { export function getProjectSupportPhoneLink() {
const phone = getProjectSupportPhone() return 'tel:'
return phone ? `tel:${phone}` : 'tel:'
} }
export const BAZRETALA_BALE_ID = 'bazretala'
export function getProjectBaleLink() { export function getProjectBaleLink() {
if (isBazretalaProject()) {
return `https://ble.ir/${BAZRETALA_BALE_ID}`
}
return 'https://ble.ir/' return 'https://ble.ir/'
} }
export function getProjectBaleContactLink() { export function getProjectBaleContactLink() {
if (getConfigApp('name') === 'garoosi') {
return 'https://ble.ir/garoosiadmin'
}
return getProjectBaleLink() return getProjectBaleLink()
} }
/** لینک دکمه «تماس با پشتیبانی» — bazretala به بله، garoosi تماس، بقیه تلفن پشتیبانی */
export function getProjectSupportContactLink() { export function getProjectSupportContactLink() {
if (isBazretalaProject()) {
return getProjectBaleLink()
}
if (getConfigApp('name') === 'garoosi') {
return 'tel:09031265925'
}
return getProjectSupportPhoneLink() return getProjectSupportPhoneLink()
} }
export function getProjectFundHeaderBackground(defaultBackground: string) { export function getProjectFundHeaderBackground(defaultBackground: string) {
if (isBazretalaProject()) {
return bazretalaFundBg
}
return defaultBackground return defaultBackground
} }
export function getProjectFundEmptyImage(defaultImage: string) { export function getProjectFundEmptyImage(defaultImage: string) {
if (isBazretalaProject()) {
return bazretalaEmptyFund
}
return defaultImage return defaultImage
} }
export function getProjectFundEmptyText() { export function getProjectFundEmptyText() {
if (isBazretalaProject()) {
return 'یک بذر جدید بکار!'
}
return 'یک گنج جدید بساز!' return 'یک گنج جدید بساز!'
} }
export function getProjectLoginTaglineLines() { export function getProjectLoginTaglineLines() {
if (!isBoursecuProject()) { return ['مرجع هوشمند معاملات', ' و سرمایه‌گذاری در صنعت مس']
return null
}
return [
'مرجع هوشمند معاملات',
' و سرمایه‌گذاری در صنعت مس',
]
} }
export function getProjectSplashBackground() { export function getProjectSplashBackground() {
return isBoursecuProject() ? BOURSECU_BRAND_NAV : '#ffffff' return BOURSECU_BRAND_NAV
} }
/** Reset splash-time inline backgrounds so mobile overscroll shows white again. */
export function resetAppSurfaceBackground() { export function resetAppSurfaceBackground() {
const background = '#ffffff' const background = '#ffffff'
document.documentElement.style.backgroundColor = background document.documentElement.style.backgroundColor = background
@@ -274,30 +186,25 @@ export function resetAppSurfaceBackground() {
} }
export function getProjectFontFamily() { export function getProjectFontFamily() {
return getConfigApp('name') === 'boursecu' ? "'dana', sans-serif" : "'iran-sans', sans-serif" return "'dana', sans-serif"
} }
export function applyProjectFont() { export function applyProjectFont() {
document.documentElement.style.setProperty('--App-font-family', getProjectFontFamily()) document.documentElement.style.setProperty('--App-font-family', getProjectFontFamily())
if (getConfigApp('name') === 'boursecu') { document.documentElement.classList.add('font-dana')
document.documentElement.classList.add('font-dana')
}
} }
export function isTWA() { export function isTWA() {
if (sessionStorage.getItem('appMode') === 'twa') return true; if (sessionStorage.getItem('appMode') === 'twa') return true
if (document.referrer.startsWith('android-app://')) { if (document.referrer.startsWith('android-app://')) {
sessionStorage.setItem('appMode', 'twa'); sessionStorage.setItem('appMode', 'twa')
return true; return true
} }
return false; return false
} }
export function applyProjectTheme() { export function applyProjectTheme() {
applyProjectFont() applyProjectFont()
if (!isBoursecuProject()) {
return
}
document.documentElement.classList.add('project-boursecu') document.documentElement.classList.add('project-boursecu')
document.documentElement.style.setProperty('--App-brand-nav', BOURSECU_BRAND_NAV) document.documentElement.style.setProperty('--App-brand-nav', BOURSECU_BRAND_NAV)
document.documentElement.style.setProperty('--App-brand-red', BOURSECU_BRAND_RED) document.documentElement.style.setProperty('--App-brand-red', BOURSECU_BRAND_RED)
+2 -9
View File
@@ -76,14 +76,7 @@ export function resolveBoursecuAssetMetalTotals(
return ordered return ordered
} }
export function resolveMetalAssetsDisplayLabel( export function resolveMetalAssetsDisplayLabel(metal: WalletMetalTotal): string {
metal: WalletMetalTotal,
isBoursecuLayout: boolean
): string {
if (!isBoursecuLayout) {
return metal.label
}
switch (metal.metal_id) { switch (metal.metal_id) {
case METAL_COPPER: case METAL_COPPER:
return 'دارایی مس' return 'دارایی مس'
@@ -121,7 +114,7 @@ export function formatBoursecuMetalModalText(metal: WalletMetalTotal): string {
case METAL_SILVER: case METAL_SILVER:
return 'دارایی نقره = موجودی نقره' return 'دارایی نقره = موجودی نقره'
default: default:
return `${resolveMetalAssetsDisplayLabel(metal, true)} = موجودی ${metal.label}` return `${resolveMetalAssetsDisplayLabel(metal)} = موجودی ${metal.label}`
} }
} }
+6 -25
View File
@@ -1,7 +1,6 @@
import type { SelectItem } from '@/models/SelectItem' import type { SelectItem } from '@/models/SelectItem'
import type { Product } from '@/models/products' import type { Product } from '@/models/products'
import type { WalletInventoryCard } from '@/models/walletAssets' import type { WalletInventoryCard } from '@/models/walletAssets'
import { isBoursecuProject } from '@/utils/config'
import { useSettingsStore } from '@/store/settingsStore' import { useSettingsStore } from '@/store/settingsStore'
import { import {
isLegacyMaftolGroup, isLegacyMaftolGroup,
@@ -25,10 +24,6 @@ export const BOURSECU_GROUP_MAFTOL = LEGACY_GROUP_MAFTOL
export const BOURSECU_WALLET_LABEL_GENERIC = 'موجودی کیف' export const BOURSECU_WALLET_LABEL_GENERIC = 'موجودی کیف'
export function isBoursecuDealProductGroup(groupType?: string | null) { export function isBoursecuDealProductGroup(groupType?: string | null) {
if (!isBoursecuProject()) {
return false
}
const group = normalizeProductGroupType(groupType) const group = normalizeProductGroupType(groupType)
return group === BOURSECU_GROUP_MAFTOL return group === BOURSECU_GROUP_MAFTOL
|| group === BOURSECU_GROUP_KATOD || group === BOURSECU_GROUP_KATOD
@@ -38,7 +33,7 @@ export function isBoursecuDealProductGroup(groupType?: string | null) {
export function isMaftolMesGroup(groupType?: string | null) { export function isMaftolMesGroup(groupType?: string | null) {
const group = normalizeProductGroupType(groupType) const group = normalizeProductGroupType(groupType)
return group === BOURSECU_GROUP_MAFTOL || (isBoursecuProject() && group === 'مس') return group === BOURSECU_GROUP_MAFTOL || group === 'مس'
} }
export function isKatodMesGroup(groupType?: string | null) { export function isKatodMesGroup(groupType?: string | null) {
@@ -46,10 +41,6 @@ export function isKatodMesGroup(groupType?: string | null) {
} }
export function isShamshMesGroup(groupType?: string | null, title?: string | null) { export function isShamshMesGroup(groupType?: string | null, title?: string | null) {
if (!isBoursecuProject()) {
return false
}
const group = normalizeProductGroupType(groupType) const group = normalizeProductGroupType(groupType)
if (group === 'شمش') { if (group === 'شمش') {
return true return true
@@ -115,11 +106,9 @@ export function getProductWeightUnit(product?: ProductUnitSource | null) {
return unit return unit
} }
if (isBoursecuProject()) { const group = normalizeProductGroupType(product?.group_type)
const group = normalizeProductGroupType(product?.group_type) if (group === BOURSECU_GROUP_MAFTOL || group === BOURSECU_GROUP_KATOD || group === BOURSECU_GROUP_ABSHODE || group === 'مس' || isMeltedTradeGroup(group)) {
if (group === BOURSECU_GROUP_MAFTOL || group === BOURSECU_GROUP_KATOD || group === BOURSECU_GROUP_ABSHODE || group === 'مس' || isMeltedTradeGroup(group)) { return getWeightUnit()
return getWeightUnit()
}
} }
if (!isCountBasedProduct(product) || isMeltedTradeGroup(product?.group_type)) { if (!isCountBasedProduct(product) || isMeltedTradeGroup(product?.group_type)) {
@@ -209,7 +198,7 @@ export function isCountBasedProduct(product?: ProductUnitSource | null) {
} }
const group = normalizeProductGroupType(product.group_type) const group = normalizeProductGroupType(product.group_type)
if (group === BOURSECU_GROUP_MAFTOL || isLegacyMaftolGroup(group) || (isBoursecuProject() && group === 'مس')) { if (group === BOURSECU_GROUP_MAFTOL || isLegacyMaftolGroup(group) || group === 'مس') {
return true return true
} }
@@ -407,10 +396,6 @@ export function getProductDealMethodOptions(
): SelectItem[] { ): SelectItem[] {
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore()
if (!isBoursecuProject()) {
return getDefaultTypeDealOptions(context, isBuy)
}
if (isMaftolMesGroup(groupType)) { if (isMaftolMesGroup(groupType)) {
if (isBuy) { if (isBuy) {
return [ return [
@@ -518,10 +503,6 @@ export function getBoursecuMetalWalletLabel(groupType?: string | null) {
} }
export function getGoldWalletLabel(groupType?: string | null) { export function getGoldWalletLabel(groupType?: string | null) {
if (!isBoursecuProject()) {
return getMainMetalWalletLabel()
}
const group = normalizeProductGroupType(groupType) const group = normalizeProductGroupType(groupType)
if (group === BOURSECU_GROUP_MAFTOL if (group === BOURSECU_GROUP_MAFTOL
|| group === BOURSECU_GROUP_KATOD || group === BOURSECU_GROUP_KATOD
@@ -575,7 +556,7 @@ export function formatCashWalletBalanceText(cashFormatted: string | number, grou
/** برچسب کیف فلزی متناسب با نحوه پرداخت (مثلاً کاتد مس برای خرید مفتول با کیف کاتد) */ /** برچسب کیف فلزی متناسب با نحوه پرداخت (مثلاً کاتد مس برای خرید مفتول با کیف کاتد) */
export function getMetalWalletLabelForDealMethod(method?: string | null, groupType?: string | null) { export function getMetalWalletLabelForDealMethod(method?: string | null, groupType?: string | null) {
if (!isBoursecuProject() || method === 'katod') { if (method === 'katod') {
return getMainMetalWalletLabel() return getMainMetalWalletLabel()
} }
@@ -2,10 +2,8 @@
<div <div
:class="[ :class="[
variant === 'desktop' variant === 'desktop'
? (isBoursecu ? 'dabout-content--boursecu' : 'dabout-content--default') ? 'dabout-content--boursecu'
: (isBoursecuProject() : 'border-solid border-2 border-primary text-primary rounded-[12px] p-4 text-xs leading-5 mobile-content-box',
? 'border-solid border-2 border-primary text-primary rounded-[12px] p-4 text-xs leading-5 mobile-content-box'
: 'bg-primary text-white rounded-[12px] p-4 text-xs leading-5 mobile-content-box'),
'dabout-content', 'dabout-content',
]" ]"
> >
@@ -63,9 +61,5 @@ withDefaults(
{ variant: 'mobile' }, { variant: 'mobile' },
) )
const { const { aboutSections } = useAboutPageContext()
aboutSections,
isBoursecu,
isBoursecuProject,
} = useAboutPageContext()
</script> </script>
@@ -3,15 +3,12 @@ import { useConfigStore } from '@/store/configStore'
import { import {
getProjectBaleLink, getProjectBaleLink,
getProjectSupportPhoneLink, getProjectSupportPhoneLink,
isBoursecuProject,
} from '@/utils/config' } from '@/utils/config'
import { getAboutSections, type AboutSection } from '../aboutContent' import { getAboutSections, type AboutSection } from '../aboutContent'
export interface AboutPageContext { export interface AboutPageContext {
config: ReturnType<typeof useConfigStore> config: ReturnType<typeof useConfigStore>
aboutSections: ComputedRef<AboutSection[]> aboutSections: ComputedRef<AboutSection[]>
isBoursecu: ComputedRef<boolean>
isBoursecuProject: typeof isBoursecuProject
getProjectSupportPhoneLink: typeof getProjectSupportPhoneLink getProjectSupportPhoneLink: typeof getProjectSupportPhoneLink
getProjectBaleLink: typeof getProjectBaleLink getProjectBaleLink: typeof getProjectBaleLink
} }
@@ -22,15 +19,11 @@ export function useAboutPage() {
const config = useConfigStore() const config = useConfigStore()
const nameProject = config.config.name_fa const nameProject = config.config.name_fa
const isBoursecu = computed(() => isBoursecuProject()) const aboutSections = computed(() => getAboutSections(nameProject, true))
const aboutSections = computed(() => getAboutSections(nameProject, isBoursecu.value))
const context: AboutPageContext = { const context: AboutPageContext = {
config, config,
aboutSections, aboutSections,
isBoursecu,
isBoursecuProject,
getProjectSupportPhoneLink, getProjectSupportPhoneLink,
getProjectBaleLink, getProjectBaleLink,
} }
@@ -5,7 +5,6 @@ import { createAddress, getCity, updateAddress } from '@/api/address'
import type { Address } from '@/models/address' import type { Address } from '@/models/address'
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout' import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
import { loading, loadingMore } from '@/utils' import { loading, loadingMore } from '@/utils'
import { isBazretalaProject } from '@/utils/config'
import { import {
addressItems, addressItems,
addressProvinceList, addressProvinceList,
@@ -23,7 +22,6 @@ export interface AddressPageContext {
formRef: Ref<FormInstance | undefined> formRef: Ref<FormInstance | undefined>
loading: typeof loading loading: typeof loading
loadingMore: typeof loadingMore loadingMore: typeof loadingMore
isBazretalaProject: typeof isBazretalaProject
resetFields: () => void resetFields: () => void
openAdd: () => void openAdd: () => void
openEdit: (val: Address) => void openEdit: (val: Address) => void
@@ -120,7 +118,6 @@ export function useAddressPage() {
formRef, formRef,
loading, loading,
loadingMore, loadingMore,
isBazretalaProject,
resetFields, resetFields,
openAdd, openAdd,
openEdit, openEdit,
@@ -7,13 +7,6 @@
<p class="daddr-topbar__subtitle">آدرس‌های تحویل سفارش خود را مدیریت کنید</p> <p class="daddr-topbar__subtitle">آدرس‌های تحویل سفارش خود را مدیریت کنید</p>
</header> </header>
<div
v-if="isBazretalaProject()"
class="daddr-notice"
>
جهت تحویل فیزیکی محصول در تهران و قم ازطریق معرف اقدام فرمائید یا با شماره امورمالی و حسابداری 09925202099 درارتباط باشید.
</div>
<div class="daddr-grid"> <div class="daddr-grid">
<section class="daddr-surface daddr-form"> <section class="daddr-surface daddr-form">
<div class="daddr-form__header"> <div class="daddr-form__header">
@@ -78,7 +71,6 @@ const {
items, items,
loading, loading,
editId, editId,
isBazretalaProject,
openAdd, openAdd,
openEdit, openEdit,
initItems, initItems,
@@ -9,13 +9,6 @@
:class="loading ? 'h-screen' : ''" :class="loading ? 'h-screen' : ''"
class="relative z-1 px-3 mpage-content pb-2 overflow-y-scroll mpage-scroll" class="relative z-1 px-3 mpage-content pb-2 overflow-y-scroll mpage-scroll"
> >
<div
v-if="isBazretalaProject()"
class="text-justify text-sm text-primary mb-4 px-1 leading-7"
>
جهت تحویل فیزیکی محصول در تهران و قم ازطریق معرف اقدام فرمائید یا با شماره امورمالی و حسابداری 09925202099 درارتباط باشید.
</div>
<div <div
v-if="!loading" v-if="!loading"
class="pb-[calc(var(--app-bottom-chrome-height)+var(--app-add-card-height)+1rem)]" class="pb-[calc(var(--app-bottom-chrome-height)+var(--app-add-card-height)+1rem)]"
@@ -58,7 +51,6 @@ const {
items, items,
loading, loading,
addAddressVisible, addAddressVisible,
isBazretalaProject,
openAdd, openAdd,
openEdit, openEdit,
initItems, initItems,
@@ -36,8 +36,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { computed } from 'vue'
import BasicButton from '@/components/BasicPacks/BasicButton.vue' import BasicButton from '@/components/BasicPacks/BasicButton.vue'
import { getProjectBaleContactLink, getProjectSupportContactLink, isBazretalaProject } from '@/utils/config' import { getProjectBaleContactLink, getProjectSupportContactLink } from '@/utils/config'
import { useEducatePageContext } from '../composables/useEducatePage'
withDefaults( withDefaults(
defineProps<{ defineProps<{
@@ -46,11 +45,7 @@ withDefaults(
{ variant: 'mobile' }, { variant: 'mobile' },
) )
const { config } = useEducatePageContext() const showBaleSupport = computed(() => false)
const showBaleSupport = computed(
() => config.config.name === 'garoosi' || isBazretalaProject(),
)
const supportHref = computed(() => getProjectSupportContactLink()) const supportHref = computed(() => getProjectSupportContactLink())
const baleHref = computed(() => getProjectBaleContactLink()) const baleHref = computed(() => getProjectBaleContactLink())
const opensSupportInNewTab = computed(() => supportHref.value.startsWith('http')) const opensSupportInNewTab = computed(() => supportHref.value.startsWith('http'))
@@ -6,13 +6,11 @@ import { useConfigStore } from '@/store/configStore'
import { useWalletAssets } from '@/composables/useWalletAssets' import { useWalletAssets } from '@/composables/useWalletAssets'
import type { WalletMetalTotal } from '@/models/walletAssets' import type { WalletMetalTotal } from '@/models/walletAssets'
import { resolveSummaryMetalWeight, fundPageInventoryCards } from '@/utils/productAssets' import { resolveSummaryMetalWeight, fundPageInventoryCards } from '@/utils/productAssets'
import { useWalletStore } from '@/store/walletStore'
import { getLastPrices } from '@/utils/price' import { getLastPrices } from '@/utils/price'
import { import {
getProjectFundEmptyImage, getProjectFundEmptyImage,
getProjectFundEmptyText, getProjectFundEmptyText,
getProjectFundHeaderBackground, getProjectFundHeaderBackground,
isBazretalaProject,
} from '@/utils/config' } from '@/utils/config'
import img from '@/assets/img/user_ly1.svg' import img from '@/assets/img/user_ly1.svg'
import empty from '@/assets/img/cash_empty.svg' import empty from '@/assets/img/cash_empty.svg'
@@ -30,7 +28,6 @@ export interface FundPageContext {
fundHeaderBackground: string fundHeaderBackground: string
fundEmptyImage: string fundEmptyImage: string
fundEmptyText: string fundEmptyText: string
isBazretalaProject: typeof isBazretalaProject
formatMetalTotal: (metal: WalletMetalTotal) => string formatMetalTotal: (metal: WalletMetalTotal) => string
openFundInfo: (productId: number) => void openFundInfo: (productId: number) => void
goBack: () => void goBack: () => void
@@ -41,7 +38,6 @@ export const FUND_PAGE_KEY: InjectionKey<FundPageContext> = Symbol('fundPage')
export function useFundPage(isDesktop: Ref<boolean>) { export function useFundPage(isDesktop: Ref<boolean>) {
const nameProject = useConfigStore().config.name const nameProject = useConfigStore().config.name
const { assetsSummary, loadWalletAssets } = useWalletAssets() const { assetsSummary, loadWalletAssets } = useWalletAssets()
const walletStore = useWalletStore()
const fundHeaderBackground = getProjectFundHeaderBackground(img) const fundHeaderBackground = getProjectFundHeaderBackground(img)
const fundEmptyImage = getProjectFundEmptyImage(empty) const fundEmptyImage = getProjectFundEmptyImage(empty)
const fundEmptyText = getProjectFundEmptyText() const fundEmptyText = getProjectFundEmptyText()
@@ -77,9 +73,6 @@ export function useFundPage(isDesktop: Ref<boolean>) {
try { try {
loading.value = true loading.value = true
const tasks = [loadWalletAssets(true), getLastPrices()] const tasks = [loadWalletAssets(true), getLastPrices()]
if (isBazretalaProject()) {
tasks.push(walletStore.getMyWallet())
}
await Promise.all(tasks) await Promise.all(tasks)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
@@ -101,7 +94,6 @@ export function useFundPage(isDesktop: Ref<boolean>) {
fundHeaderBackground, fundHeaderBackground,
fundEmptyImage, fundEmptyImage,
fundEmptyText, fundEmptyText,
isBazretalaProject,
formatMetalTotal, formatMetalTotal,
openFundInfo, openFundInfo,
goBack, goBack,
@@ -13,15 +13,11 @@
</div> </div>
</header> </header>
<section <section class="dfund-summary">
class="dfund-summary"
:class="{ 'dfund-summary--bazretala': isBazretalaProject() }"
>
<img <img
:src="fundHeaderBackground" :src="fundHeaderBackground"
alt="" alt=""
class="dfund-summary__bg" class="dfund-summary__bg"
:class="{ 'dfund-summary__bg--cover': isBazretalaProject() }"
/> />
<div class="dfund-summary__content"> <div class="dfund-summary__content">
<h2 class="dfund-summary__title">موجودی دارایی‌ها</h2> <h2 class="dfund-summary__title">موجودی دارایی‌ها</h2>
@@ -31,17 +27,7 @@
<strong>{{ countBasedInventory }} عدد</strong> <strong>{{ countBasedInventory }} عدد</strong>
</div> </div>
<template v-if="isBazretalaProject()"> <template v-if="meta?.single_metal && primaryMetalTotal">
<div class="dfund-summary__row">
<span>دارایی نقره</span>
<strong>{{ walletStore.walletInfo?.count_silver ?? 0 }} گرم</strong>
</div>
<div class="dfund-summary__row">
<span>دارایی طلایی</span>
<strong>{{ walletStore.walletInfo?.count_gold_wallet ?? 0 }} گرم</strong>
</div>
</template>
<template v-else-if="meta?.single_metal && primaryMetalTotal">
<div class="dfund-summary__row"> <div class="dfund-summary__row">
<span>{{ primaryMetalTotal.label }}</span> <span>{{ primaryMetalTotal.label }}</span>
<strong>{{ formatMetalTotal(primaryMetalTotal) }}</strong> <strong>{{ formatMetalTotal(primaryMetalTotal) }}</strong>
@@ -59,7 +45,7 @@
</template> </template>
<div <div
v-if="meta?.has_count_based_products && !isBazretalaProject()" v-if="meta?.has_count_based_products"
class="dfund-summary__row" class="dfund-summary__row"
> >
<span>مجموع موجودی</span> <span>مجموع موجودی</span>
@@ -112,12 +98,9 @@
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue' import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import { loading } from '@/utils' import { loading } from '@/utils'
import { usePriceWithoutType } from '@/utils/usePrice' import { usePriceWithoutType } from '@/utils/usePrice'
import { useWalletStore } from '@/store/walletStore'
import Item from '../components/Item.vue' import Item from '../components/Item.vue'
import { useFundPageContext } from '../composables/useFundPage' import { useFundPageContext } from '../composables/useFundPage'
const walletStore = useWalletStore()
const { const {
nameProject, nameProject,
meta, meta,
@@ -130,7 +113,6 @@ const {
fundHeaderBackground, fundHeaderBackground,
fundEmptyImage, fundEmptyImage,
fundEmptyText, fundEmptyText,
isBazretalaProject,
formatMetalTotal, formatMetalTotal,
openFundInfo, openFundInfo,
goBack, goBack,
+6 -47
View File
@@ -9,22 +9,14 @@
<div class="mr-2">دارایی ها</div> <div class="mr-2">دارایی ها</div>
</div> </div>
<div <div
class="w-screen md:w-full h-[70vh] fixed md:absolute overflow-hidden" class="w-screen md:w-full h-[70vh] fixed md:absolute overflow-hidden text-tertiary bg-primary"
:class="isBazretalaProject() ? 'bazretala-fund-header' : 'text-tertiary bg-primary'"
> >
<img <img
:src="fundHeaderBackground" :src="fundHeaderBackground"
alt="" alt=""
:class=" class="pointer-events-none z-0 w-full absolute bottom-0 left-0 opacity-5"
isBazretalaProject()
? 'pointer-events-none z-0 absolute inset-0 w-full object-cover object-center'
: 'pointer-events-none z-0 w-full absolute bottom-0 left-0 opacity-5'
"
/> />
<div <div class="mobile-page-body--pt15 flex flex-col relative z-10 text-tertiary">
class="mobile-page-body--pt15 flex flex-col relative z-10"
:class="isBazretalaProject() ? 'text-white' : 'text-tertiary'"
>
<div class="font-bold mr-5 z-20">موجودی دارایی ها</div> <div class="font-bold mr-5 z-20">موجودی دارایی ها</div>
<div <div
v-if="meta?.has_count_based_products" v-if="meta?.has_count_based_products"
@@ -33,21 +25,7 @@
محصولات: محصولات:
<div class="text-l mr-4 font-bold">{{ countBasedInventory }} عدد</div> <div class="text-l mr-4 font-bold">{{ countBasedInventory }} عدد</div>
</div> </div>
<template v-if="isBazretalaProject()"> <template v-if="meta?.single_metal && primaryMetalTotal">
<div class="flex justify-center mt-2 w-full z-20">
دارایی نقره:
<div class="text-l mr-4 font-bold">
{{ walletStore.walletInfo?.count_silver ?? 0 }}گرم
</div>
</div>
<div class="flex justify-center mt-2 w-full z-20">
دارایی طلایی:
<div class="text-l mr-4 font-bold">
{{ walletStore.walletInfo?.count_gold_wallet ?? 0 }}گرم
</div>
</div>
</template>
<template v-else-if="meta?.single_metal && primaryMetalTotal">
<div class="flex justify-center mt-2 w-full z-20"> <div class="flex justify-center mt-2 w-full z-20">
{{ primaryMetalTotal.label }}: {{ primaryMetalTotal.label }}:
<div class="text-l mr-4 font-bold">{{ formatMetalTotal(primaryMetalTotal) }}</div> <div class="text-l mr-4 font-bold">{{ formatMetalTotal(primaryMetalTotal) }}</div>
@@ -64,7 +42,7 @@
</div> </div>
</template> </template>
<div <div
v-if="meta?.has_count_based_products && !isBazretalaProject()" v-if="meta?.has_count_based_products"
class="flex justify-center mt-2 w-full z-20" class="flex justify-center mt-2 w-full z-20"
> >
مجموع موجودی: مجموع موجودی:
@@ -93,7 +71,7 @@
<img <img
:src="fundEmptyImage" :src="fundEmptyImage"
alt="" alt=""
:class="isBazretalaProject() ? 'w-48 max-w-[11rem]' : 'w-70'" class="w-70"
/> />
<div v-if="nameProject !== 'abaeigold'" class="text-center text-lg"> <div v-if="nameProject !== 'abaeigold'" class="text-center text-lg">
{{ fundEmptyText }} {{ fundEmptyText }}
@@ -120,12 +98,9 @@
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue' import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import { loading } from '@/utils' import { loading } from '@/utils'
import { usePriceWithoutType } from '@/utils/usePrice' import { usePriceWithoutType } from '@/utils/usePrice'
import { useWalletStore } from '@/store/walletStore'
import Item from '../components/Item.vue' import Item from '../components/Item.vue'
import { useFundPageContext } from '../composables/useFundPage' import { useFundPageContext } from '../composables/useFundPage'
const walletStore = useWalletStore()
const { const {
nameProject, nameProject,
meta, meta,
@@ -138,24 +113,8 @@ const {
fundHeaderBackground, fundHeaderBackground,
fundEmptyImage, fundEmptyImage,
fundEmptyText, fundEmptyText,
isBazretalaProject,
formatMetalTotal, formatMetalTotal,
openFundInfo, openFundInfo,
goBack, goBack,
} = useFundPageContext() } = useFundPageContext()
</script> </script>
<style scoped>
.bazretala-fund-header {
background-color: #000;
}
.bazretala-fund-header::after {
content: '';
position: absolute;
inset: 0;
z-index: 1;
background: rgba(0, 0, 0, 0.45);
pointer-events: none;
}
</style>
+2 -13
View File
@@ -5,21 +5,12 @@
:class="{ 'app-header--scrolled': isHeaderScrolled }" :class="{ 'app-header--scrolled': isHeaderScrolled }"
> >
<div class="flex justify-between my-2 items-center"> <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" /> <img :src="projectLogo" alt="" class="h-10 w-auto max-w-[9rem] object-contain" />
</div> </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"> <div class="flex items-center gap-2">
<button <button
v-if="isBoursecuProject()"
type="button" type="button"
class="mhome-header-btn" class="mhome-header-btn"
@click="router.push({ name: 'user' })" @click="router.push({ name: 'user' })"
@@ -42,16 +33,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, nextTick, onActivated, onMounted, onUnmounted, ref, watch } from 'vue' import { computed, nextTick, onActivated, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { useUserStore } from '@/store/userStore'
import router from '@/router' import router from '@/router'
import Icon from '@/components/BasicPacks/BasicIcon.vue' 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 headerRef = ref<HTMLElement | null>(null)
const isHeaderScrolled = ref(false) const isHeaderScrolled = ref(false)
const route = useRoute() const route = useRoute()
const userStore = useUserStore()
const projectLogo = computed(() => getProjectMobileNavLogo()) const projectLogo = computed(() => getProjectMobileNavLogo())
let intersectionObserver: IntersectionObserver | null = null let intersectionObserver: IntersectionObserver | null = null
@@ -1,127 +1,18 @@
<template> <template>
<div :class="rootClass"> <div class="assets-section assets-section--boursecu rounded-lg px-3 py-3 mt-2">
<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>
<BoursecuAssetsSummary <BoursecuAssetsSummary
v-if="boursecuLayout"
variant="mobile" variant="mobile"
icon-color="primary" 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> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import BoursecuAssetsSummary from './BoursecuAssetsSummary.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 type { WalletMetalTotal } from '@/models/walletAssets'
import { resolveSummaryMetalWeight } from '@/utils/productAssets'
const props = withDefaults(
defineProps<{
plain?: boolean
boursecuLayout?: boolean
}>(),
{ plain: false, boursecuLayout: false }
)
defineEmits<{ defineEmits<{
openGoldModal: [metal: WalletMetalTotal, modalText?: string, modalTitle?: string, modalContent?: string] openGoldModal: [metal: WalletMetalTotal, modalText?: string, modalTitle?: string, modalContent?: string]
openRialModal: [] 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> </script>
@@ -4,12 +4,12 @@
:class="containerClass" :class="containerClass"
> >
<template v-if="displayState !== 'open'"> <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"> <div class="status-box__icon-wrap">
<Icon <Icon
:icon="displayState === 'closed' ? 'vuesax-linear:lock' : 'vuesax-linear:info-circle'" :icon="displayState === 'closed' ? 'vuesax-linear:lock' : 'vuesax-linear:info-circle'"
size="22" size="22"
:color="isBoursecu ? '#ffffff' : '#64748b'" color="#ffffff"
/> />
</div> </div>
<div class="status-box__title">{{ statusTitle }}</div> <div class="status-box__title">{{ statusTitle }}</div>
@@ -18,8 +18,8 @@
</template> </template>
<template v-else> <template v-else>
<div class="price-label" :class="isBoursecu ? 'price-label--boursecu' : 'text-gray-600 text-[12px] ml-2 w-full text-center'"> <div class="price-label price-label--boursecu">
{{ priceLabel }} قیمت لحظه‌ای کاتد مس
</div> </div>
<div class="w-full items-center flex justify-center"> <div class="w-full items-center flex justify-center">
<div v-if="price" class="flex gap-1 text-xs items-center"> <div v-if="price" class="flex gap-1 text-xs items-center">
@@ -38,11 +38,10 @@
</div> </div>
<Vue3Odometer <Vue3Odometer
:value="displayGoldVal" :value="displayGoldVal"
class="o-text !text-xl font-bold" class="o-text !text-xl font-bold price-odometer--boursecu"
:class="isBoursecu ? 'price-odometer--boursecu' : ''"
style="direction: ltr" style="direction: ltr"
/> />
<span :class="isBoursecu ? 'price-unit--boursecu' : ''">ریال</span> <span class="price-unit--boursecu">ریال</span>
</div> </div>
</div> </div>
</template> </template>
@@ -52,9 +51,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import Icon from '@/components/BasicPacks/BasicIcon.vue' import Icon from '@/components/BasicPacks/BasicIcon.vue'
import { useConfigStore } from '@/store/configStore'
import { useUserStore } from '@/store/userStore' import { useUserStore } from '@/store/userStore'
import { isBoursecuProject } from '@/utils/config'
import { getTradeBuyPrice, lastPrice, percentPriceChanged, price } from '@/utils/price' import { getTradeBuyPrice, lastPrice, percentPriceChanged, price } from '@/utils/price'
import { import {
NO_QUOTE_MESSAGE, NO_QUOTE_MESSAGE,
@@ -65,13 +62,7 @@ import Vue3Odometer from 'vue3-odometer'
import 'odometer/themes/odometer-theme-default.css' import 'odometer/themes/odometer-theme-default.css'
import { formatSignedPercentChange } from '@/utils/usePrice' import { formatSignedPercentChange } from '@/utils/usePrice'
const config = useConfigStore()
const userStore = useUserStore() const userStore = useUserStore()
const isBoursecu = isBoursecuProject()
const priceLabel = computed(() =>
isBoursecu ? 'قیمت لحظه‌ای کاتد مس' : `قیمت خرید از ${config.config.name_fa}`
)
const displayState = computed(() => getPriceDisplayState()) const displayState = computed(() => getPriceDisplayState())
@@ -91,9 +82,6 @@ const statusHint = computed(() => {
}) })
const containerClass = computed(() => { const containerClass = computed(() => {
if (!isBoursecu) {
return 'border-gray-200 border-1'
}
if (displayState.value !== 'open') { if (displayState.value !== 'open') {
return 'boursecu-price-box boursecu-price-box--closed' return 'boursecu-price-box boursecu-price-box--closed'
} }
@@ -190,18 +178,6 @@ void price
color: rgba(255, 255, 255, 0.78); 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 { .price-change-badge {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import Icon from '@/components/BasicPacks/BasicIcon.vue' import Icon from '@/components/BasicPacks/BasicIcon.vue'
import { isBoursecuProject } from '@/utils/config'
import type { HomeQuickActionItem } from '../layouts/types' import type { HomeQuickActionItem } from '../layouts/types'
defineProps<{ defineProps<{
@@ -10,15 +9,12 @@ defineProps<{
defineEmits<{ defineEmits<{
click: [] click: []
}>() }>()
const isBoursecu = isBoursecuProject()
</script> </script>
<template> <template>
<button <button
type="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 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"
:class="{ 'home-quick-action-box--boursecu': isBoursecu }"
@click="$emit('click')" @click="$emit('click')"
> >
<div class="flex h-12 w-12 shrink-0 items-center justify-center"> <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 seriesLength = computed(() => props.priceChartSeries?.[0]?.data?.length ?? 0)
const amplifyKatodChart = computed(() => const amplifyKatodChart = computed(() =>
shouldAmplifyKatodMesChart({ shouldAmplifyKatodMesChart({
isBoursecu: props.layout === 'boursecu',
metalTypeSetting: settingsStore.metalTypeSettingValue, metalTypeSetting: settingsStore.metalTypeSettingValue,
}) })
) )
@@ -141,7 +140,6 @@ const katodDayRange = computed(() => {
return resolveMetalDayRangePrices({ return resolveMetalDayRangePrices({
price: price.value, price: price.value,
metal: props.selectedMetal, metal: props.selectedMetal,
isBoursecuLayout: true,
metalRatio: selectedMetalChartRatio.value, metalRatio: selectedMetalChartRatio.value,
}) })
}) })
@@ -2,7 +2,7 @@
<div <div
class="w-full flex justify-evenly rounded-lg py-2 home_appear_fade_in" class="w-full flex justify-evenly rounded-lg py-2 home_appear_fade_in"
:class="[ :class="[
isBoursecuLayout ? 'mt-8 pt-2' : 'mt-6', 'mt-8 pt-2',
variant === 'mobile' ? 'pb-20' : 'mt-0 pb-0', variant === 'mobile' ? 'pb-20' : 'mt-0 pb-0',
]" ]"
> >
@@ -34,7 +34,6 @@
import { computed } from 'vue' import { computed } from 'vue'
import { usePriceWithoutType } from '../../../../utils/usePrice' import { usePriceWithoutType } from '../../../../utils/usePrice'
import { price } from '@/utils/price' import { price } from '@/utils/price'
import { getHomeLayoutId } from '../layouts/registry'
import { resolveMetalDayRangePrices, usePriceChart } from '../composables/usePriceChart' import { resolveMetalDayRangePrices, usePriceChart } from '../composables/usePriceChart'
withDefaults( withDefaults(
@@ -47,13 +46,11 @@ withDefaults(
) )
const { selectedChartMetal, selectedMetalChartRatio } = usePriceChart() const { selectedChartMetal, selectedMetalChartRatio } = usePriceChart()
const isBoursecuLayout = computed(() => getHomeLayoutId() === 'boursecu')
const rangePrices = computed(() => const rangePrices = computed(() =>
resolveMetalDayRangePrices({ resolveMetalDayRangePrices({
price: price.value, price: price.value,
metal: selectedChartMetal.value, metal: selectedChartMetal.value,
isBoursecuLayout: isBoursecuLayout.value,
metalRatio: selectedMetalChartRatio.value, metalRatio: selectedMetalChartRatio.value,
}) })
) )
+4 -27
View File
@@ -7,20 +7,16 @@ import { useWalletAssets } from '@/composables/useWalletAssets'
import type { WalletMetalTotal } from '@/models/walletAssets' import type { WalletMetalTotal } from '@/models/walletAssets'
import { import {
formatBoursecuMetalModalText, formatBoursecuMetalModalText,
formatMetalAssetsInfoText,
formatRialAssetsInfoText, formatRialAssetsInfoText,
resolveAssetsRialValue, resolveAssetsRialValue,
} from '@/utils/productAssets' } from '@/utils/productAssets'
import { useConfigStore } from '@/store/configStore'
import { useSettingsStore } from '@/store/settingsStore' import { useSettingsStore } from '@/store/settingsStore'
import { getTradeBuyPrice, lastPrice, stopLastPricePolling } from '@/utils/price' import { getTradeBuyPrice, lastPrice, stopLastPricePolling } from '@/utils/price'
import { getStoreClosedMessage, isStoreClosed } from '@/utils/storeHours' import { getStoreClosedMessage, isStoreClosed } from '@/utils/storeHours'
import { message, Modal } from 'ant-design-vue' import { message, Modal } from 'ant-design-vue'
import { getReferralShareText, getReferralShareTitle, shareReferralText } from '@/utils/referralShare'
import { useBanners } from './useBanners' import { useBanners } from './useBanners'
import { usePriceChart, type ChartMetalType } from './usePriceChart' import { usePriceChart, type ChartMetalType } from './usePriceChart'
import type { HomeActionItem } from '../layouts/types' import type { HomeActionItem } from '../layouts/types'
import { getHomeLayoutId } from '../layouts/registry'
import { initHomePageData, resetHomePageData, useHomePageLoading } from './useHomePageData' import { initHomePageData, resetHomePageData, useHomePageLoading } from './useHomePageData'
import { ensureAuthenticatedRoute } from '@/app/setupRouter' import { ensureAuthenticatedRoute } from '@/app/setupRouter'
import { resetWalletPageData } from '../../wallet/composables/useWalletPageData' import { resetWalletPageData } from '../../wallet/composables/useWalletPageData'
@@ -30,7 +26,6 @@ import { resetProductsPageData } from '../../products/composables/useProductsPag
export interface HomePageContext { export interface HomePageContext {
homePageLoading: ReturnType<typeof useHomePageLoading>['homePageLoading'] homePageLoading: ReturnType<typeof useHomePageLoading>['homePageLoading']
isGaroosi: ReturnType<typeof computed<boolean>>
bannerEnabled: ReturnType<typeof computed<boolean>> bannerEnabled: ReturnType<typeof computed<boolean>>
showClosedMessage: ReturnType<typeof computed<boolean>> showClosedMessage: ReturnType<typeof computed<boolean>>
closedMessage: ReturnType<typeof computed> closedMessage: ReturnType<typeof computed>
@@ -41,7 +36,6 @@ export interface HomePageContext {
updateChartData: ReturnType<typeof usePriceChart>['updateChartData'] updateChartData: ReturnType<typeof usePriceChart>['updateChartData']
selectedChartMetal: ReturnType<typeof usePriceChart>['selectedChartMetal'] selectedChartMetal: ReturnType<typeof usePriceChart>['selectedChartMetal']
setChartMetal: (metal: ChartMetalType) => void setChartMetal: (metal: ChartMetalType) => void
isBoursecuHomeLayout: ReturnType<typeof computed<boolean>>
handleBannerClick: (banner: { typeClick?: string; modalText?: string; link?: string }) => void handleBannerClick: (banner: { typeClick?: string; modalText?: string; link?: string }) => void
openGoldModal: ( openGoldModal: (
metal: WalletMetalTotal, metal: WalletMetalTotal,
@@ -57,7 +51,6 @@ export const HOME_PAGE_CONTEXT_KEY: InjectionKey<HomePageContext> = Symbol('home
export function useHomePage() { export function useHomePage() {
const userStore = useUserStore() const userStore = useUserStore()
const walletStore = useWalletStore() const walletStore = useWalletStore()
const configStore = useConfigStore()
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore()
const { assetsSummary, clearWalletAssets } = useWalletAssets() const { assetsSummary, clearWalletAssets } = useWalletAssets()
@@ -76,9 +69,7 @@ export function useHomePage() {
selectedChartMetal, selectedChartMetal,
setChartMetal, setChartMetal,
} = usePriceChart() } = usePriceChart()
const isBoursecuHomeLayout = computed(() => getHomeLayoutId() === 'boursecu')
const isGaroosi = computed(() => configStore.config?.name === 'garoosi')
const bannerEnabled = computed(() => settingsStore.isSettingTruthy('banner_enabled')) const bannerEnabled = computed(() => settingsStore.isSettingTruthy('banner_enabled'))
const closedMessage = computed(() => userStore.getValueByKey('closed_message')) const closedMessage = computed(() => userStore.getValueByKey('closed_message'))
const marketClosedText = computed(() => { const marketClosedText = computed(() => {
@@ -100,7 +91,7 @@ export function useHomePage() {
{ {
name: 'deposit', name: 'deposit',
icon: 'vuesax-linear:card-add', icon: 'vuesax-linear:card-add',
label: isGaroosi.value ? 'واریز و برداشت' : 'واریز نقدی', label: 'واریز نقدی',
handler: () => router.push({ name: 'wallet', query: { state: 'deposit' } }), handler: () => router.push({ name: 'wallet', query: { state: 'deposit' } }),
}, },
{ {
@@ -114,21 +105,11 @@ export function useHomePage() {
{ {
name: 'assets', name: 'assets',
icon: 'vuesax-linear:cup', icon: 'vuesax-linear:cup',
label: isGaroosi.value ? 'دعوت از دوستان' : 'دارایی ها', label: 'دارایی ها',
handler: () => (isGaroosi.value ? shareCode() : router.push({ name: 'fund' })), handler: () => router.push({ name: 'fund' }),
}, },
]) ])
async function shareCode() {
const text = getReferralShareText({
userName: userStore.userInfo?.name,
projectName: configStore.config.name,
referralId: userStore.userInfo?.referral_id,
})
await shareReferralText(text, getReferralShareTitle(configStore.config.name))
}
function copyCode() { function copyCode() {
const code = userStore.userInfo?.referral_id const code = userStore.userInfo?.referral_id
if (code) { if (code) {
@@ -178,9 +159,7 @@ export function useHomePage() {
const title = const title =
modalTitle ?? modalTitle ??
modalText ?? modalText ??
(isBoursecuHomeLayout.value formatBoursecuMetalModalText(metal)
? formatBoursecuMetalModalText(metal)
: formatMetalAssetsInfoText(metal))
Modal.confirm({ Modal.confirm({
title, title,
@@ -230,7 +209,6 @@ export function useHomePage() {
const context: HomePageContext = { const context: HomePageContext = {
homePageLoading, homePageLoading,
isGaroosi,
bannerEnabled, bannerEnabled,
showClosedMessage, showClosedMessage,
closedMessage: marketClosedText, closedMessage: marketClosedText,
@@ -241,7 +219,6 @@ export function useHomePage() {
updateChartData, updateChartData,
selectedChartMetal, selectedChartMetal,
setChartMetal, setChartMetal,
isBoursecuHomeLayout,
handleBannerClick, handleBannerClick,
openGoldModal, openGoldModal,
openRialModal, openRialModal,
@@ -2,7 +2,6 @@ import { ref } from 'vue'
import { getLastPrices, startLastPricePolling, stopLastPricePolling } from '@/utils/price' import { getLastPrices, startLastPricePolling, stopLastPricePolling } from '@/utils/price'
import { useWalletAssets } from '@/composables/useWalletAssets' import { useWalletAssets } from '@/composables/useWalletAssets'
import { clearProductsCatalog } from '@/composables/useProductsCatalog' import { clearProductsCatalog } from '@/composables/useProductsCatalog'
import { getHomeLayoutId } from '../layouts/registry'
import { fetchHomeChartPrices, loadHomeMaftolOffset, resetHomePriceChart } from './usePriceChart' import { fetchHomeChartPrices, loadHomeMaftolOffset, resetHomePriceChart } from './usePriceChart'
import { loadHomeBanners, resetHomeBanners } from './useBanners' import { loadHomeBanners, resetHomeBanners } from './useBanners'
import { loadHomeFunds, resetHomeFunds } from './useUserFunds' import { loadHomeFunds, resetHomeFunds } from './useUserFunds'
@@ -24,10 +23,7 @@ export function isHomePageDataInitialized() {
export async function initHomePageData(force = false) { export async function initHomePageData(force = false) {
if (!force && initialized.value) { if (!force && initialized.value) {
startLastPricePolling() startLastPricePolling()
void loadHomeMaftolOffset(false)
if (getHomeLayoutId() === 'boursecu') {
void loadHomeMaftolOffset(false)
}
return return
} }
@@ -49,9 +45,7 @@ export async function initHomePageData(force = false) {
loadHomeBanners(force), loadHomeBanners(force),
]) ])
if (getHomeLayoutId() === 'boursecu') { await loadHomeMaftolOffset(force)
await loadHomeMaftolOffset(force)
}
initialized.value = true initialized.value = true
} catch (error) { } catch (error) {
@@ -2,7 +2,6 @@ import { computed, ref, watch } from 'vue'
import { getAllPrices } from '@/api/home' import { getAllPrices } from '@/api/home'
import type { Product } from '@/models/products' import type { Product } from '@/models/products'
import { getProductsCatalog, getProductsCatalogRef } from '@/composables/useProductsCatalog' import { getProductsCatalog, getProductsCatalogRef } from '@/composables/useProductsCatalog'
import { getHomeLayoutId } from '../layouts/registry'
import { getProductBuyPrice, resolvePriceOffsetRatio } from '@/utils/productPrice' import { getProductBuyPrice, resolvePriceOffsetRatio } from '@/utils/productPrice'
import { isKatodMesGroup, isMaftolMesGroup, isShamshMesGroup } from '@/utils/productDealMethod' import { isKatodMesGroup, isMaftolMesGroup, isShamshMesGroup } from '@/utils/productDealMethod'
import { getTradeBuyPrice, getTradeSellPrice, lastPrice } from '@/utils/price' import { getTradeBuyPrice, getTradeSellPrice, lastPrice } from '@/utils/price'
@@ -31,10 +30,6 @@ function resolveChartProducts(products: Product[]) {
/** نسبت مفتول به کاتد — reactive به قیمت لحظه‌ای و کاتالوگ محصولات */ /** نسبت مفتول به کاتد — reactive به قیمت لحظه‌ای و کاتالوگ محصولات */
const maftolChartRatio = computed(() => { const maftolChartRatio = computed(() => {
if (getHomeLayoutId() !== 'boursecu') {
return 0
}
void lastPrice.value?.price_last void lastPrice.value?.price_last
const { katod, maftol } = resolveChartProducts(catalogProducts.value) const { katod, maftol } = resolveChartProducts(catalogProducts.value)
@@ -43,10 +38,6 @@ const maftolChartRatio = computed(() => {
/** نسبت شمش مس به کاتد */ /** نسبت شمش مس به کاتد */
const shamshChartRatio = computed(() => { const shamshChartRatio = computed(() => {
if (getHomeLayoutId() !== 'boursecu') {
return 0
}
void lastPrice.value?.price_last void lastPrice.value?.price_last
const { katod, shamsh } = resolveChartProducts(catalogProducts.value) const { katod, shamsh } = resolveChartProducts(catalogProducts.value)
@@ -100,13 +91,12 @@ function applyChartRatio(basePrice: number, ratio: number) {
export function resolveMetalDayRangePrices(options: { export function resolveMetalDayRangePrices(options: {
price: { up?: { buy?: number; sell?: number }; buy?: number; sell?: number; price_last?: number } | null | undefined price: { up?: { buy?: number; sell?: number }; buy?: number; sell?: number; price_last?: number } | null | undefined
metal: ChartMetalType metal: ChartMetalType
isBoursecuLayout: boolean
metalRatio: number metalRatio: number
}) { }) {
const baseHigh = options.price?.up?.buy ?? getTradeBuyPrice(options.price as Parameters<typeof getTradeBuyPrice>[0]) const baseHigh = options.price?.up?.buy ?? getTradeBuyPrice(options.price as Parameters<typeof getTradeBuyPrice>[0])
const baseLow = options.price?.up?.sell ?? getTradeSellPrice(options.price as Parameters<typeof getTradeSellPrice>[0]) const baseLow = options.price?.up?.sell ?? getTradeSellPrice(options.price as Parameters<typeof getTradeSellPrice>[0])
if (!options.isBoursecuLayout || options.metal === 'katod') { if (options.metal === 'katod') {
return { high: baseHigh, low: baseLow } return { high: baseHigh, low: baseLow }
} }
@@ -176,7 +166,7 @@ function buildChartPoints(items: Array<{ timestamp: number; value: number }>): C
function syncChartSeries() { function syncChartSeries() {
const ratio = selectedMetalChartRatio.value const ratio = selectedMetalChartRatio.value
const data = const data =
getHomeLayoutId() === 'boursecu' && selectedChartMetal.value !== 'katod' selectedChartMetal.value !== 'katod'
? rawChartPoints.value.map( ? rawChartPoints.value.map(
([timestamp, value]) => ([timestamp, value]) =>
[timestamp, applyChartRatio(value, ratio)] as ChartPoint [timestamp, applyChartRatio(value, ratio)] as ChartPoint
@@ -221,11 +211,6 @@ async function fetchPrices(force = false) {
} }
async function loadMetalChartOffsets(force = false) { async function loadMetalChartOffsets(force = false) {
if (getHomeLayoutId() !== 'boursecu') {
syncChartSeries()
return
}
if (!force && metalOffsetLoaded && catalogProducts.value.length > 0) { if (!force && metalOffsetLoaded && catalogProducts.value.length > 0) {
return return
} }
@@ -258,7 +243,7 @@ function updateChartData(timeline: string) {
function setChartMetal(metal: ChartMetalType) { function setChartMetal(metal: ChartMetalType) {
selectedChartMetal.value = metal selectedChartMetal.value = metal
if (metal !== 'katod' && getHomeLayoutId() === 'boursecu' && !catalogProducts.value.length) { if (metal !== 'katod' && !catalogProducts.value.length) {
void loadMetalChartOffsets(true) void loadMetalChartOffsets(true)
} }
} }
@@ -1,17 +1,11 @@
<template> <template>
<BoursecuDesktopHome v-if="layoutId === 'boursecu'" @logout="$emit('logout')" /> <BoursecuDesktopHome @logout="$emit('logout')" />
<DefaultDesktopHome v-else @logout="$emit('logout')" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'
import { getHomeLayoutId } from '../layouts/registry'
import BoursecuDesktopHome from './layouts/BoursecuDesktopHome.vue' import BoursecuDesktopHome from './layouts/BoursecuDesktopHome.vue'
import DefaultDesktopHome from './layouts/DefaultDesktopHome.vue'
defineEmits<{ defineEmits<{
logout: [] logout: []
}>() }>()
const layoutId = computed(() => getHomeLayoutId())
</script> </script>
@@ -13,6 +13,7 @@ defineEmits<{
<DesktopHomeCard title="دارایی‌های من"> <DesktopHomeCard title="دارایی‌های من">
<AssetsSection <AssetsSection
plain plain
boursecu-layout
class="home-desktop-assets" class="home-desktop-assets"
@open-gold-modal="$emit('openGoldModal', $event)" @open-gold-modal="$emit('openGoldModal', $event)"
@open-rial-modal="$emit('openRialModal')" @open-rial-modal="$emit('openRialModal')"
@@ -1,126 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import BoursecuAssetsSummary from '../../components/BoursecuAssetsSummary.vue' import BoursecuAssetsSummary from '../../components/BoursecuAssetsSummary.vue'
import PortfolioProfitInsight from '../../components/PortfolioProfitInsight.vue'
import { formatWeightAmount, usePriceWithoutType } from '@/utils/usePrice'
import { useWalletAssets } from '@/composables/useWalletAssets'
import type { WalletMetalTotal } from '@/models/walletAssets'
import { resolveSummaryMetalWeight } from '@/utils/productAssets'
import { useHomePageContext } from '../../composables/useHomePage'
const emit = defineEmits<{
openGoldModal: [metal: WalletMetalTotal, modalText?: string, modalTitle?: string, modalContent?: string]
openRialModal: []
}>()
const { assetsSummary } = useWalletAssets()
const ctx = useHomePageContext()
const isBoursecuLayout = computed(() => ctx.isBoursecuHomeLayout.value)
const meta = computed(() => assetsSummary.value?.meta)
const metalTotals = computed(() => assetsSummary.value?.metal_totals ?? [])
const inventoryCards = computed(() => assetsSummary.value?.inventory_cards ?? [])
const cashWallet = computed(() => assetsSummary.value?.cash_wallet ?? 0)
const countBasedInventory = computed(() =>
(assetsSummary.value?.count_products ?? []).reduce((sum, item) => sum + Number(item.quantity ?? 0), 0),
)
type MetricItem = {
id: string
label: string
value: string
metal?: WalletMetalTotal
showInfo?: boolean
onInfo?: 'gold' | 'rial'
}
const metrics = computed<MetricItem[]>(() => {
const items: MetricItem[] = []
if (meta.value?.has_count_based_products) {
items.push({
id: 'products',
label: 'محصولات',
value: `${usePriceWithoutType(countBasedInventory.value, false)} عدد`,
})
}
if (meta.value?.single_metal && metalTotals.value[0]) {
const metal = metalTotals.value[0]
items.push({
id: `metal-${metal.metal_id}`,
label: metal.label,
value: formatMetalTotal(metal),
metal,
showInfo: true,
onInfo: 'gold',
})
} else {
metalTotals.value.forEach((metal, index) => {
items.push({
id: `metal-${metal.metal_id}`,
label: metal.label,
value: formatMetalTotal(metal),
metal,
showInfo: index === 0,
onInfo: 'gold',
})
})
}
items.push({
id: 'rial',
label: 'دارایی ریالی',
value: `${usePriceWithoutType(cashWallet.value)} ریال`,
showInfo: true,
onInfo: 'rial',
})
return items
})
function formatMetalTotal(metal: WalletMetalTotal) {
return `${formatWeightAmount(resolveSummaryMetalWeight(metal, inventoryCards.value))}${metal.unit}`
}
function handleInfo(metric: MetricItem) {
if (metric.onInfo === 'rial') {
emit('openRialModal')
return
}
if (metric.metal) {
emit('openGoldModal', metric.metal)
}
}
</script> </script>
<template> <template>
<BoursecuAssetsSummary <BoursecuAssetsSummary
v-if="isBoursecuLayout"
variant="desktop" variant="desktop"
icon-color="tertiary" icon-color="tertiary"
/> />
<template v-else>
<PortfolioProfitInsight variant="desktop" />
<div class="dhome-metrics">
<article v-for="metric in metrics" :key="metric.id" class="dhome-metric-tile">
<div class="dhome-metric-tile__head">
<span class="dhome-metric-tile__label">{{ metric.label }}</span>
<button
v-if="metric.showInfo"
type="button"
class="dhome-metric-tile__info"
aria-label="اطلاعات بیشتر"
@click="handleInfo(metric)"
>
<BasicIcon icon="vuesax-linear:info-circle" size="14" />
</button>
</div>
<p class="dhome-metric-tile__value">{{ metric.value }}</p>
</article>
</div>
</template>
</template> </template>
@@ -6,7 +6,7 @@ import DesktopHomeCard from '../components/DesktopHomeCard.vue'
const ctx = useHomePageContext() const ctx = useHomePageContext()
const chartLayout = computed(() => (toValue(ctx.isBoursecuHomeLayout) ? 'boursecu' : 'default')) const chartLayout = 'boursecu' as const
const priceChartSeries = computed(() => toValue(ctx.priceChartSeries)) const priceChartSeries = computed(() => toValue(ctx.priceChartSeries))
const chartSelection = computed(() => toValue(ctx.chartSelection)) const chartSelection = computed(() => toValue(ctx.chartSelection))
const selectedChartMetal = computed(() => toValue(ctx.selectedChartMetal)) const selectedChartMetal = computed(() => toValue(ctx.selectedChartMetal))
@@ -15,7 +15,7 @@ const props = withDefaults(
const ctx = useHomePageContext() const ctx = useHomePageContext()
const chartLayout = computed(() => (toValue(ctx.isBoursecuHomeLayout) ? 'boursecu' : 'default')) const chartLayout = 'boursecu' as const
const priceChartSeries = computed(() => toValue(ctx.priceChartSeries)) const priceChartSeries = computed(() => toValue(ctx.priceChartSeries))
const chartSelection = computed(() => toValue(ctx.chartSelection)) const chartSelection = computed(() => toValue(ctx.chartSelection))
const selectedChartMetal = computed(() => toValue(ctx.selectedChartMetal)) const selectedChartMetal = computed(() => toValue(ctx.selectedChartMetal))
@@ -1,30 +0,0 @@
<script setup lang="ts">
import { inject } from 'vue'
import { useUserStore } from '@/store/userStore'
import { usePriceWithoutType } from '@/utils/usePrice'
import DesktopHomeSection from './DesktopHomeSection.vue'
import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage'
const ctx = inject(HOME_PAGE_CONTEXT_KEY)!
const userStore = useUserStore()
</script>
<template>
<DesktopHomeSection v-if="ctx.isGaroosi.value" title="دارایی‌ها">
<div class="dhome-garoosi-assets">
<div class="dhome-garoosi-assets__item">
<span class="dhome-garoosi-assets__label">دارایی طلایی</span>
<span class="dhome-garoosi-assets__value">
{{ (userStore?.userInfo?.wallet?.count_gold ?? 0).toFixed(3) }} گرم
</span>
</div>
<div class="dhome-garoosi-assets__divider" />
<div class="dhome-garoosi-assets__item">
<span class="dhome-garoosi-assets__label">دارایی ریالی</span>
<span class="dhome-garoosi-assets__value">
{{ usePriceWithoutType(userStore?.userInfo?.wallet?.count_cash ?? 0) }} ریال
</span>
</div>
</div>
</DesktopHomeSection>
</template>
@@ -1,17 +0,0 @@
<script setup lang="ts">
import { inject } from 'vue'
import { isBoursecuProject } from '@/utils/config'
import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage'
const ctx = inject(HOME_PAGE_CONTEXT_KEY)!
const isBoursecu = isBoursecuProject()
</script>
<template>
<div v-if="!isBoursecu && ctx.showClosedMessage.value" class="dhome-alerts">
<div
class="dhome-alert dhome-alert--info"
v-html="ctx.closedMessage.value"
/>
</div>
</template>
@@ -1,15 +1,9 @@
<template> <template>
<div class="dhome" :class="{ 'dhome--boursecu': isBoursecu }"> <div class="dhome dhome--boursecu">
<div class="dhome__container"> <div class="dhome__container">
<slot /> <slot />
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup>
import { isBoursecuProject } from '@/utils/config'
const isBoursecu = isBoursecuProject()
</script>
<style lang="less" src="../styles/desktop-home.less"></style> <style lang="less" src="../styles/desktop-home.less"></style>
@@ -2,17 +2,17 @@
<div <div
class="dhome-surface dhome-hero-price" class="dhome-surface dhome-hero-price"
:class="{ :class="{
'dhome-hero-price--boursecu-open': isBoursecu && displayState === 'open', 'dhome-hero-price--boursecu-open': displayState === 'open',
'dhome-hero-price--boursecu-closed': isBoursecu && displayState !== 'open', 'dhome-hero-price--boursecu-closed': displayState !== 'open',
}" }"
> >
<template v-if="displayState !== 'open'"> <template v-if="displayState !== 'open'">
<div class="dhome-hero-price__closed" :class="{ 'dhome-hero-price__closed--default': !isBoursecu }"> <div class="dhome-hero-price__closed">
<div class="dhome-hero-price__closed-icon"> <div class="dhome-hero-price__closed-icon">
<BasicIcon <BasicIcon
:icon="displayState === 'closed' ? 'vuesax-linear:lock' : 'vuesax-linear:info-circle'" :icon="displayState === 'closed' ? 'vuesax-linear:lock' : 'vuesax-linear:info-circle'"
size="22" size="22"
:color="isBoursecu ? '#ffffff' : '#64748b'" color="#ffffff"
/> />
</div> </div>
<p class="dhome-hero-price__closed-title">{{ statusTitle }}</p> <p class="dhome-hero-price__closed-title">{{ statusTitle }}</p>
@@ -21,10 +21,9 @@
</template> </template>
<template v-else> <template v-else>
<span class="dhome-label" :class="{ 'dhome-label--boursecu': isBoursecu }"> <span class="dhome-label dhome-label--boursecu">
{{ isBoursecu ? 'قیمت لحظه‌ای کاتد مس' : 'قیمت لحظه‌ای' }} قیمت لحظه‌ای کاتد مس
</span> </span>
<p v-if="!isBoursecu" class="dhome-hero-price__brand">{{ config.config?.name_fa }}</p>
<div class="dhome-hero-price__value-row"> <div class="dhome-hero-price__value-row">
<span <span
v-if="percentChange !== 0" v-if="percentChange !== 0"
@@ -39,10 +38,9 @@
</span> </span>
<Vue3Odometer <Vue3Odometer
:value="displayGoldVal" :value="displayGoldVal"
class="dhome-hero-price__odometer o-text" class="dhome-hero-price__odometer o-text dhome-hero-price__odometer--boursecu"
:class="{ 'dhome-hero-price__odometer--boursecu': isBoursecu }"
/> />
<span class="dhome-hero-price__unit" :class="{ 'dhome-hero-price__unit--boursecu': isBoursecu }">ریال</span> <span class="dhome-hero-price__unit dhome-hero-price__unit--boursecu">ریال</span>
</div> </div>
</template> </template>
</div> </div>
@@ -53,9 +51,7 @@ import { computed } from 'vue'
import Vue3Odometer from 'vue3-odometer' import Vue3Odometer from 'vue3-odometer'
import 'odometer/themes/odometer-theme-default.css' import 'odometer/themes/odometer-theme-default.css'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue' import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import { useConfigStore } from '@/store/configStore'
import { useUserStore } from '@/store/userStore' import { useUserStore } from '@/store/userStore'
import { isBoursecuProject } from '@/utils/config'
import { getTradeBuyPrice, lastPrice, percentPriceChanged } from '@/utils/price' import { getTradeBuyPrice, lastPrice, percentPriceChanged } from '@/utils/price'
import { import {
NO_QUOTE_MESSAGE, NO_QUOTE_MESSAGE,
@@ -64,9 +60,7 @@ import {
} from '@/utils/storeHours' } from '@/utils/storeHours'
import { formatSignedPercentChange } from '@/utils/usePrice' import { formatSignedPercentChange } from '@/utils/usePrice'
const config = useConfigStore()
const userStore = useUserStore() const userStore = useUserStore()
const isBoursecu = isBoursecuProject()
const displayState = computed(() => getPriceDisplayState()) const displayState = computed(() => getPriceDisplayState())
@@ -152,18 +146,4 @@ const percentChange = computed(() => {
line-height: 1.7; line-height: 1.7;
color: rgba(255, 255, 255, 0.78); color: rgba(255, 255, 255, 0.78);
} }
.dhome-hero-price__closed--default {
.dhome-hero-price__closed-icon {
background: #f1f5f9;
}
.dhome-hero-price__closed-title {
color: #334155;
}
.dhome-hero-price__closed-hint {
color: #64748b;
}
}
</style> </style>
@@ -2,17 +2,14 @@
import { computed } from 'vue' import { computed } from 'vue'
import { price } from '@/utils/price' import { price } from '@/utils/price'
import { usePriceWithoutType } from '@/utils/usePrice' import { usePriceWithoutType } from '@/utils/usePrice'
import { getHomeLayoutId } from '../../layouts/registry'
import { resolveMetalDayRangePrices, usePriceChart } from '../../composables/usePriceChart' import { resolveMetalDayRangePrices, usePriceChart } from '../../composables/usePriceChart'
const { selectedChartMetal, selectedMetalChartRatio } = usePriceChart() const { selectedChartMetal, selectedMetalChartRatio } = usePriceChart()
const isBoursecuLayout = computed(() => getHomeLayoutId() === 'boursecu')
const rangePrices = computed(() => const rangePrices = computed(() =>
resolveMetalDayRangePrices({ resolveMetalDayRangePrices({
price: price.value, price: price.value,
metal: selectedChartMetal.value, metal: selectedChartMetal.value,
isBoursecuLayout: isBoursecuLayout.value,
metalRatio: selectedMetalChartRatio.value, metalRatio: selectedMetalChartRatio.value,
}) })
) )
@@ -1,14 +0,0 @@
<script setup lang="ts">
import { inject } from 'vue'
import DesktopHomeSection from './DesktopHomeSection.vue'
import ReferralSection from '../../components/ReferralSection.vue'
import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage'
const ctx = inject(HOME_PAGE_CONTEXT_KEY)!
</script>
<template>
<DesktopHomeSection v-if="ctx.isGaroosi.value" title="دعوت از دوستان">
<ReferralSection />
</DesktopHomeSection>
</template>
@@ -7,7 +7,6 @@ import { useHomePageContext } from '../../composables/useHomePage'
import HomeSkeleton from '../../components/HomeSkeleton.vue' import HomeSkeleton from '../../components/HomeSkeleton.vue'
import DesktopHomeShell from '../components/DesktopHomeShell.vue' import DesktopHomeShell from '../components/DesktopHomeShell.vue'
import DesktopHomeTopbar from '../components/DesktopHomeTopbar.vue' import DesktopHomeTopbar from '../components/DesktopHomeTopbar.vue'
import DesktopHomeAlerts from '../components/DesktopHomeAlerts.vue'
import DesktopLivePrice from '../components/DesktopLivePrice.vue' import DesktopLivePrice from '../components/DesktopLivePrice.vue'
import DesktopPriceRange from '../components/DesktopPriceRange.vue' import DesktopPriceRange from '../components/DesktopPriceRange.vue'
import DesktopAssetsMetrics from '../components/DesktopAssetsMetrics.vue' import DesktopAssetsMetrics from '../components/DesktopAssetsMetrics.vue'
@@ -29,7 +28,6 @@ const showSkeleton = computed(() => Boolean(getToken()) && !userInfo.value)
<template> <template>
<DesktopHomeShell> <DesktopHomeShell>
<DesktopHomeTopbar @logout="$emit('logout')" /> <DesktopHomeTopbar @logout="$emit('logout')" />
<DesktopHomeAlerts />
<HomeSkeleton v-if="showSkeleton" /> <HomeSkeleton v-if="showSkeleton" />
@@ -1,98 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue'
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/store/userStore'
import { getToken } from '@/utils'
import { useHomePageContext } from '../../composables/useHomePage'
import HomeSkeleton from '../../components/HomeSkeleton.vue'
import DesktopHomeShell from '../components/DesktopHomeShell.vue'
import DesktopHomeTopbar from '../components/DesktopHomeTopbar.vue'
import DesktopHomeAlerts from '../components/DesktopHomeAlerts.vue'
import DesktopLivePrice from '../components/DesktopLivePrice.vue'
import DesktopPriceRange from '../components/DesktopPriceRange.vue'
import DesktopAssetsMetrics from '../components/DesktopAssetsMetrics.vue'
import DesktopChartSection from '../components/DesktopChartSection.vue'
import DesktopDefaultActions from '../components/DesktopDefaultActions.vue'
import DesktopGaroosiAssets from '../components/DesktopGaroosiAssets.vue'
import DesktopReferralPanel from '../components/DesktopReferralPanel.vue'
import DesktopBannersPanel from '../components/DesktopBannersPanel.vue'
defineEmits<{
logout: []
}>()
const userStore = useUserStore()
const { userInfo } = storeToRefs(userStore)
const ctx = useHomePageContext()
const showContent = computed(() => Boolean(userInfo.value))
const showSkeleton = computed(() => Boolean(getToken()) && !userInfo.value)
</script>
<template>
<DesktopHomeShell>
<DesktopHomeTopbar @logout="$emit('logout')" />
<DesktopHomeAlerts />
<HomeSkeleton v-if="showSkeleton" />
<template v-if="showContent">
<section v-if="ctx.isGaroosi.value" class="dhome-hero-row">
<DesktopLivePrice />
<DesktopPriceRange />
</section>
<div v-if="ctx.isGaroosi.value" class="dhome-default-grid">
<div class="dhome-default-grid__main">
<DesktopDefaultActions />
<section
v-if="ctx.bannerEnabled.value && ctx.banners.value?.length"
class="dhome-surface dhome-chart-banner-combined"
>
<header class="dhome-section__header">
<h2 class="dhome-section__title">نمودار قیمت</h2>
</header>
<div class="dhome-chart-banner-combined__grid">
<DesktopChartSection embedded />
<DesktopBannersPanel embedded />
</div>
</section>
<DesktopChartSection v-else />
</div>
<aside class="dhome-default-grid__side">
<DesktopGaroosiAssets />
<DesktopReferralPanel />
</aside>
</div>
<template v-else>
<DesktopDefaultActions />
<DesktopAssetsMetrics
@open-gold-modal="ctx.openGoldModal"
@open-rial-modal="ctx.openRialModal"
/>
<section class="dhome-hero-row">
<DesktopLivePrice />
<DesktopPriceRange />
</section>
<section
v-if="ctx.bannerEnabled.value && ctx.banners.value?.length"
class="dhome-surface dhome-chart-banner-combined"
>
<header class="dhome-section__header">
<h2 class="dhome-section__title">نمودار قیمت</h2>
</header>
<div class="dhome-chart-banner-combined__grid">
<DesktopChartSection embedded />
<DesktopBannersPanel embedded />
</div>
</section>
<DesktopChartSection v-else />
</template>
</template>
</DesktopHomeShell>
</template>
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,5 @@
import type { HomeLayoutConfig } from '../types' import type { HomeLayoutConfig } from '../types'
/**
* چیدمان اختصاصی boursecu — فقط وقتی home_layout=boursecu یا پروژه boursecu باشد.
* شامل: دارایی بدون بک‌گراند، گرید ۶ باکس، نمودار کاتد/مفتول، و ...
*/
export const boursecuHomeLayoutConfig: HomeLayoutConfig = { export const boursecuHomeLayoutConfig: HomeLayoutConfig = {
id: 'boursecu', id: 'boursecu',
wrapperClass: 'rounded-lg mt-5', wrapperClass: 'rounded-lg mt-5',
@@ -13,7 +9,6 @@ export const boursecuHomeLayoutConfig: HomeLayoutConfig = {
{ id: 'boursecuQuickActions' }, { id: 'boursecuQuickActions' },
], ],
sections: [ sections: [
{ id: 'marketClosed' },
{ id: 'priceChart' }, { id: 'priceChart' },
{ id: 'priceStats' }, { id: 'priceStats' },
], ],
@@ -1,20 +0,0 @@
import type { HomeLayoutConfig } from '../types'
/** چیدمان پیش‌فرض — همه پروژه‌ها به‌جز boursecu */
export const defaultHomeLayoutConfig: HomeLayoutConfig = {
id: 'default',
wrapperClass: 'p-1 shadow-lg from-primary rounded-lg p-px to-transparent mt-5',
sections: [
{ id: 'garoosiAssets' },
{ id: 'mainActions' },
{ id: 'referral' },
{ id: 'assets' },
],
tailSections: [
{ id: 'marketClosed' },
{ id: 'currentPrice' },
{ id: 'banners' },
{ id: 'priceChart' },
{ id: 'priceStats' },
],
}
+3 -15
View File
@@ -1,13 +1,10 @@
import { defineAsyncComponent, type Component } from 'vue' import { defineAsyncComponent, type Component } from 'vue'
import { getConfigApp, isBoursecuProject } from '@/utils/config'
import type { HomeLayoutConfig, HomeLayoutId } from './types' import type { HomeLayoutConfig, HomeLayoutId } from './types'
import { defaultHomeLayoutConfig } from './configs/default'
import { boursecuHomeLayoutConfig } from './configs/boursecu' import { boursecuHomeLayoutConfig } from './configs/boursecu'
export const DEFAULT_HOME_LAYOUT_ID: HomeLayoutId = 'default' export const DEFAULT_HOME_LAYOUT_ID: HomeLayoutId = 'boursecu'
const HOME_LAYOUT_CONFIGS: Record<HomeLayoutId, HomeLayoutConfig> = { const HOME_LAYOUT_CONFIGS: Record<HomeLayoutId, HomeLayoutConfig> = {
default: defaultHomeLayoutConfig,
boursecu: boursecuHomeLayoutConfig, boursecu: boursecuHomeLayoutConfig,
} }
@@ -26,20 +23,11 @@ export function isHomeLayoutId(value: unknown): value is HomeLayoutId {
} }
export function getHomeLayoutId(): HomeLayoutId { export function getHomeLayoutId(): HomeLayoutId {
const adminLayout = getConfigApp('home_layout') return 'boursecu'
if (isHomeLayoutId(adminLayout)) {
return adminLayout
}
if (isBoursecuProject()) {
return 'boursecu'
}
return DEFAULT_HOME_LAYOUT_ID
} }
export function getHomeLayoutConfig(layoutId = getHomeLayoutId()): HomeLayoutConfig { export function getHomeLayoutConfig(layoutId = getHomeLayoutId()): HomeLayoutConfig {
return HOME_LAYOUT_CONFIGS[layoutId] ?? HOME_LAYOUT_CONFIGS[DEFAULT_HOME_LAYOUT_ID] return HOME_LAYOUT_CONFIGS[layoutId] ?? boursecuHomeLayoutConfig
} }
export function resolveHomeLayoutComponent(_layoutId = getHomeLayoutId()): Component { export function resolveHomeLayoutComponent(_layoutId = getHomeLayoutId()): Component {
@@ -1,16 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject } from 'vue' import { inject } from 'vue'
import AssetsSection from '../../components/AssetsSection.vue' import AssetsSection from '../../components/AssetsSection.vue'
import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage' import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage'
const ctx = inject(HOME_PAGE_CONTEXT_KEY)! const ctx = inject(HOME_PAGE_CONTEXT_KEY)!
const isPlainAssets = computed(() => ctx.isBoursecuHomeLayout.value)
</script> </script>
<template> <template>
<AssetsSection <AssetsSection
v-if="!ctx.isGaroosi.value" plain
:plain="isPlainAssets" boursecu-layout
:boursecu-layout="isPlainAssets"
@open-gold-modal="ctx.openGoldModal" @open-gold-modal="ctx.openGoldModal"
@open-rial-modal="ctx.openRialModal" @open-rial-modal="ctx.openRialModal"
/> />
@@ -1,23 +0,0 @@
<script setup lang="ts">
import { inject } from 'vue'
import { useUserStore } from '@/store/userStore'
import { usePriceWithoutType } from '@/utils/usePrice'
import AssetItem from '../../components/AssetItem.vue'
import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage'
const ctx = inject(HOME_PAGE_CONTEXT_KEY)!
const userStore = useUserStore()
</script>
<template>
<div v-if="ctx.isGaroosi.value" class="flex justify-between px-2 mb-2">
<AssetItem
label="دارایی طلایی:"
:value="`${(userStore?.userInfo?.wallet?.count_gold ?? 0).toFixed(3)} گرم`"
/>
<AssetItem
label="دارایی ریالی:"
:value="`${usePriceWithoutType(userStore?.userInfo?.wallet?.count_cash ?? 0)} ریال`"
/>
</div>
</template>
@@ -1,15 +0,0 @@
<script setup lang="ts">
import { inject } from 'vue'
import { isBoursecuProject } from '@/utils/config'
import MarketClosedMessage from '../../components/MarketClosedMessage.vue'
import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage'
const ctx = inject(HOME_PAGE_CONTEXT_KEY)!
</script>
<template>
<MarketClosedMessage
v-if="!isBoursecuProject() && ctx.showClosedMessage.value"
:message="ctx.closedMessage.value as string"
/>
</template>
@@ -4,7 +4,7 @@ import PriceChart from '../../components/PriceChart.vue'
import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage' import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage'
const ctx = inject(HOME_PAGE_CONTEXT_KEY)! const ctx = inject(HOME_PAGE_CONTEXT_KEY)!
const layout = computed(() => (toValue(ctx.isBoursecuHomeLayout) ? 'boursecu' : 'default')) const layout = 'boursecu' as const
const priceChartSeries = computed(() => toValue(ctx.priceChartSeries)) const priceChartSeries = computed(() => toValue(ctx.priceChartSeries))
const chartSelection = computed(() => toValue(ctx.chartSelection)) const chartSelection = computed(() => toValue(ctx.chartSelection))
const selectedChartMetal = computed(() => toValue(ctx.selectedChartMetal)) const selectedChartMetal = computed(() => toValue(ctx.selectedChartMetal))
@@ -1,11 +0,0 @@
<script setup lang="ts">
import { inject } from 'vue'
import ReferralSection from '../../components/ReferralSection.vue'
import { HOME_PAGE_CONTEXT_KEY } from '../../composables/useHomePage'
const ctx = inject(HOME_PAGE_CONTEXT_KEY)!
</script>
<template>
<ReferralSection v-if="ctx.isGaroosi.value" />
</template>
@@ -1,23 +1,17 @@
import type { Component } from 'vue' import type { Component } from 'vue'
import type { HomeSectionId } from '../types' import type { HomeSectionId } from '../types'
import GaroosiAssetsSection from './GaroosiAssetsSection.vue'
import MainActionsSection from './MainActionsSection.vue' import MainActionsSection from './MainActionsSection.vue'
import BoursecuQuickActionsSection from './BoursecuQuickActionsSection.vue' import BoursecuQuickActionsSection from './BoursecuQuickActionsSection.vue'
import ReferralSectionBlock from './ReferralSectionBlock.vue'
import AssetsSectionBlock from './AssetsSectionBlock.vue' import AssetsSectionBlock from './AssetsSectionBlock.vue'
import MarketClosedSection from './MarketClosedSection.vue'
import CurrentPriceSection from './CurrentPriceSection.vue' import CurrentPriceSection from './CurrentPriceSection.vue'
import BannersSection from './BannersSection.vue' import BannersSection from './BannersSection.vue'
import PriceChartSection from './PriceChartSection.vue' import PriceChartSection from './PriceChartSection.vue'
import PriceStatsSection from './PriceStatsSection.vue' import PriceStatsSection from './PriceStatsSection.vue'
export const HOME_SECTION_COMPONENTS: Record<HomeSectionId, Component> = { export const HOME_SECTION_COMPONENTS: Record<HomeSectionId, Component> = {
garoosiAssets: GaroosiAssetsSection,
mainActions: MainActionsSection, mainActions: MainActionsSection,
boursecuQuickActions: BoursecuQuickActionsSection, boursecuQuickActions: BoursecuQuickActionsSection,
referral: ReferralSectionBlock,
assets: AssetsSectionBlock, assets: AssetsSectionBlock,
marketClosed: MarketClosedSection,
currentPrice: CurrentPriceSection, currentPrice: CurrentPriceSection,
banners: BannersSection, banners: BannersSection,
priceChart: PriceChartSection, priceChart: PriceChartSection,
+2 -6
View File
@@ -1,13 +1,10 @@
/** چیدمان‌های صفحه اصلی — هر پروژه/ادمین می‌تواند یکی را انتخاب کند */ /** چیدمان صفحه اصلی boursecu */
export type HomeLayoutId = 'default' | 'boursecu' export type HomeLayoutId = 'boursecu'
export type HomeSectionId = export type HomeSectionId =
| 'garoosiAssets'
| 'mainActions' | 'mainActions'
| 'boursecuQuickActions' | 'boursecuQuickActions'
| 'referral'
| 'assets' | 'assets'
| 'marketClosed'
| 'currentPrice' | 'currentPrice'
| 'banners' | 'banners'
| 'priceChart' | 'priceChart'
@@ -31,7 +28,6 @@ export interface HomeLayoutConfig {
wrapperClass?: string wrapperClass?: string
outerSections?: HomeLayoutSectionConfig[] outerSections?: HomeLayoutSectionConfig[]
sections: HomeLayoutSectionConfig[] sections: HomeLayoutSectionConfig[]
/** بعد از wrapper — مثل قیمت و نمودار در UI قدیمی */
tailSections?: HomeLayoutSectionConfig[] tailSections?: HomeLayoutSectionConfig[]
} }
+1 -11
View File
@@ -15,10 +15,6 @@
v-if="usesQuantityTrade(selectedProduct)" v-if="usesQuantityTrade(selectedProduct)"
:ProductItem="selectedProduct" :ProductItem="selectedProduct"
/> />
<CopperSilver
v-else-if="isMetalCommodityGroup(selectedProduct?.group_type) && !isBoursecuDealProductGroup(selectedProduct?.group_type)"
:ProductItem="selectedProduct"
/>
<DealProduct v-else :ProductItem="selectedProduct" @ordered="onProductOrdered" /> <DealProduct v-else :ProductItem="selectedProduct" @ordered="onProductOrdered" />
</div> </div>
</BasicActionSheet> </BasicActionSheet>
@@ -36,10 +32,6 @@
v-if="usesQuantityTrade(selectedProduct)" v-if="usesQuantityTrade(selectedProduct)"
:ProductItem="selectedProduct" :ProductItem="selectedProduct"
/> />
<CopperSilver
v-else-if="isMetalCommodityGroup(selectedProduct?.group_type) && !isBoursecuDealProductGroup(selectedProduct?.group_type)"
:ProductItem="selectedProduct"
/>
<DealProduct v-else :ProductItem="selectedProduct" @ordered="onProductOrdered" /> <DealProduct v-else :ProductItem="selectedProduct" @ordered="onProductOrdered" />
</div> </div>
</BasicModal> </BasicModal>
@@ -78,13 +70,11 @@
<script setup lang="ts"> <script setup lang="ts">
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue' import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
import indexGold from '@/views/base/gold/index.vue' import indexGold from '@/views/base/gold/index.vue'
import CopperSilver from '@/views/base/copper&silver/index.vue'
import DealProduct from '@/views/base/products/components/DealProduct.vue' import DealProduct from '@/views/base/products/components/DealProduct.vue'
import BasicModal from '@/components/BasicPacks/BasicModal.vue' import BasicModal from '@/components/BasicPacks/BasicModal.vue'
import ResponsiveViewPair from '@/layouts/components/ResponsiveViewPair.vue' import ResponsiveViewPair from '@/layouts/components/ResponsiveViewPair.vue'
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout' import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
import { isMetalCommodityGroup, usesQuantityTrade } from '@/utils/productCategories' import { usesQuantityTrade } from '@/utils/productCategories'
import { isBoursecuDealProductGroup } from '@/utils/productDealMethod'
import { useProductsPageProvider } from './composables/useProductsPage' import { useProductsPageProvider } from './composables/useProductsPage'
import MobileProductsView from './mobile/MobileProductsView.vue' import MobileProductsView from './mobile/MobileProductsView.vue'
import DesktopProductsView from './desktop/DesktopProductsView.vue' import DesktopProductsView from './desktop/DesktopProductsView.vue'
@@ -1,18 +1,16 @@
import { computed, inject, provide, type ComputedRef, type InjectionKey } from 'vue' import { computed, inject, provide, type InjectionKey } from 'vue'
import { useConfigStore } from '@/store/configStore' import { useConfigStore } from '@/store/configStore'
import { import {
getProjectBaleLink, getProjectBaleLink,
getProjectSupportPhoneLink, getProjectSupportPhoneLink,
isBoursecuProject,
} from '@/utils/config' } from '@/utils/config'
import { getRulesBlocks, getRulesTitle, type RulesBlock } from '../rulesContent' import { getRulesBlocks, getRulesTitle, type RulesBlock } from '../rulesContent'
export interface RulesPageContext { export interface RulesPageContext {
config: ReturnType<typeof useConfigStore> config: ReturnType<typeof useConfigStore>
nameProject: ComputedRef<string> nameProject: ReturnType<typeof computed<string>>
isBoursecu: ComputedRef<boolean> rulesTitle: ReturnType<typeof computed<string>>
rulesTitle: ComputedRef<string> rulesBlocks: ReturnType<typeof computed<RulesBlock[]>>
rulesBlocks: ComputedRef<RulesBlock[]>
getProjectSupportPhoneLink: typeof getProjectSupportPhoneLink getProjectSupportPhoneLink: typeof getProjectSupportPhoneLink
getProjectBaleLink: typeof getProjectBaleLink getProjectBaleLink: typeof getProjectBaleLink
} }
@@ -26,16 +24,13 @@ export function useRulesPage() {
() => config.config?.name_fa || config.config?.name || 'بورس مس', () => config.config?.name_fa || config.config?.name || 'بورس مس',
) )
const isBoursecu = computed(() => isBoursecuProject()) const rulesTitle = computed(() => getRulesTitle(nameProject.value, true))
const rulesTitle = computed(() => getRulesTitle(nameProject.value, isBoursecu.value)) const rulesBlocks = computed(() => getRulesBlocks(nameProject.value, true))
const rulesBlocks = computed(() => getRulesBlocks(nameProject.value, isBoursecu.value))
const context: RulesPageContext = { const context: RulesPageContext = {
config, config,
nameProject, nameProject,
isBoursecu,
rulesTitle, rulesTitle,
rulesBlocks, rulesBlocks,
getProjectSupportPhoneLink, getProjectSupportPhoneLink,
-44
View File
@@ -1,5 +1,3 @@
import { isBazretalaProject } from '@/utils/config'
export interface RulesBlock { export interface RulesBlock {
title?: string title?: string
items: string[] items: string[]
@@ -41,40 +39,6 @@ function getDefaultRulesItems(nameProject: string): string[] {
] ]
} }
function getBazretalaRulesBlocks(): RulesBlock[] {
return [
{
items: [
'کاربران موظفند قبل از ثبت‌ نام، تمامی مفاد این تعهدنامه را مطالعه نموده و صرفاً در صورت پذیرش آن اقدام به ثبت‌نام نمایند.',
'کاربران بذر طلا می‌پذیرند که کلیه‌ی فعالیت‌های آن‌ها در بذر طلا، در چارچوب قوانین جمهوری اسلامی ایران بوده و هیچ گونه فعالیتی خارج از این چارچوب انجام نخواهد شد.',
'احراز هویت برای استفاده از خدمات بذر طلا ضروری بوده و کاربران موظفند اطلاعات صحیح خود را بارگذاری نمایند؛ بدیهی است در صورت وجود هرگونه تخلف در احراز هویت، مسئولیت به عهده‌ی فرد متخلف بوده و بذر طلا حق توقف ارائه خدمات به کاربر مذکور و ارجاع موارد تخلف به مراجع ذی‌صلاح را خواهد داشت.',
'بذر طلا خود را ملزم به حفظ اطلاعات شخصی کاربران در چارچوب قوانین قضایی کشور می‌داند.',
'کاربران بذر طلا تعهد می‌دهند که از قرار دادن حساب خود در اختیار دیگران خودداری نمایند. از خدمات سایت، تنها برای حساب شخصی خود استفاده نموده و مسئولیت استفاده از خدمات بذر طلا برای فردی دیگر، به عهده کاربر خواهد بود. شماره حساب اعلام شده به سایت باید متعلق به کاربر بوده و کاربران مجاز به دادن شماره حساب متعلق به اشخاص دیگر نیستند.',
'کاربران می‌پذیرند در صورت تشخیص کارشناسان پشتیبانی، جهت حفظ امنیت دارایی حساب کاربری‌شان با اطلاع قبلی نسبت به برقراری تماس تصویری با تیم پشتیبان همکاری نمایند.',
'هرگونه سهل‌انگاری کاربران در حفظ اطلاعات شخصی و امنیتی از جمله: رمز عبور کاربر، سرقت گوشی کاربر، سرقت کارت ملی و مدارک شناسایی کاربر، سپردن مدارک شخصی و موبایل به فرد ثالث و ... به عهده‌ی شخص کاربر بوده و بذر طلا هیچ گونه مسئولیتی در این موارد نخواهد داشت.',
'اکیداً توصیه می‌شود از رمز عبوری پیچیده، امن و همچنین کد شناسایی دو عاملی استفاده شود.',
'بذر طلا این اطمینان را می‌دهد که دارایی‌های کاربران را نزد خود به امانت و به بهترین شکل و با بالاترین استانداردهای امنیتی ممکن حفظ نماید؛ در صورت بروز هرگونه مشکل امنیتی، بذر طلا متعهد به جبران خسارت خواهد بود.',
'به عنوان پلتفرم معاملات برخط فلزات گران‌بها، هیچ‌گونه مسئولیتی در قبال تصمیمات اقتصادی فی مابین کاربران و سود و زیان حاصل از آن بر عهده بذر طلا نمی‌باشد.',
'بذر طلا حق اصلاح و تغییر قوانین و مقررات ذکر شده در این صفحه را چه به صورت کلی و چه به صورت جزئی دارد و نسخه نهایی را در این صفحه در معرض دید قرار می‌دهد. استفاده از خدمات بذر طلا پس از اعمال تغییرات، به معنای پذیرش همه تغییرات می‌باشد.',
'از لحظه خرید کاربران تا زمان تحویل طلا و نقره ی خریداری شده، مسئولیت حفظ و نگهداری اصل طلا و نقره بر عهده بذر طلا می‌باشد.',
'کاربران بذر طلا اذعان می‌دارند که از ریسک‌های اقتصادی مربوط به سرمایه‌گذاری در حوزه طلا و نقره مطلع بوده و با علم بر این موضوع اقدام به تبادل و سرمایه‌گذاری می‌کنند.',
'بذر طلا متعهد می‌شود در حد توان از حریم شخصی شما محافظت کند و در این راستا، تکنولوژی مورد نیاز در سایت و پلتفرم را برای هر چه مطمئن‌تر و امن‌تر شدن استفاده شما، توسعه دهد.',
'در صورت بروز نوسان شدید در قیمت طلا و نقره هرگونه رویداد برهم‌زننده نظم حاکم بر بازار طلا و نقره، بذر طلا می‌تواند برای خرید و فروش طلا و نقره توسط کاربر سقف تعیین کند تا دو طرف متضرر نشوند.',
'بذر طلا مجاز است بدون اطلاع قبلی و به صلاح‌دید شخصی نسبت به توقف سفارش‌گیری جدید، معاملات (از جمله سفارش‌های باز) را متوقف کند؛ بدیهی است که کلیه سفارش‌های تکمیل‌شده قبل از توقف سفارش‌گیری، پردازش و تحویل داده خواهد شد.',
'در صورت بروز هرگونه اختلال در اینترنت و خدمات زیرساخت به دلایل حاکمیتی، هیچ‌گونه مسئولیتی به عهده بذر طلا نمی‌باشد.',
'درخواست برداشت ریال از حساب کاربری در سریع‌ترین زمان ممکن پس از ثبت، بررسی خواهد شد. زمان واریز پول به حساب کاربران بر اساس محدودیت‌های انتقال وجه بین بانکی، متفاوت خواهد بود. برای اطلاعات بیشتر، به قوانین انتقال وجه بین بانکی (پایا، ساتنا) مراجعه فرمایید.',
'کاربر می‌پذیرد که به جز در موارد مورد تعهد، حق هیچ‌گونه داعیه، طلب و شکایت از سایت، مدیران، کارمندان و افراد مرتبط با این سایت را نخواهد داشت.',
'اگر کاربر تحت هر عنوان اشتباهاً یا من غیر حق، وجوهی را به حساب کاربر منظور یا در محاسبات خود هر نوع اشتباهی نماید، بذر طلا هر زمان مجاز و مختار است راساً و مستقلاً و بدون انجام هیچ‌گونه تشریفات اداری و قضایی و دریافت اجازه کتبی از متعهد (صاحب حساب) در رفع اشتباه و برداشت از حساب‌های وی اقدام نماید و تشخیص نسبت به وقوع اشتباه یا پرداخت بدون حق و لزوم برداشت از حساب معتبر خواهد بود و کاربر حق هرگونه اعتراض و ادعایی را در خصوص نحوه عملکرد بذر طلا از هر جهت از خود ساقط می‌نماید.',
'در صورت بروز هرگونه مشکل یا ابهام در هر یک از معاملات (مانند دستکاری قیمت، اختلال در وب‌سرویس‌ها، انجام معامله خارج از دامنه نوسان و ...)، بذر طلا حق دارد مستقلاً آن معامله را ابطال کرده و دارایی‌های هر یک از طرفین را به حساب خودشان عودت دهد. بدیهی است در صورتی که اشکال از سمت بذر طلا باشد، شرکت موظف خواهد بود که جبران خسارت نماید و در غیر این صورت، کاربر حق هرگونه اعتراض و ادعایی در خصوص نحوه عملکرد از هر جهت را از خود ساقط می‌نماید.',
'در صورت فوت یا حجر کاربر، بذر طلا پیرو قوانین جمهوری اسلامی ایران می‌باشد. کاربر با علم و اطلاع از مقررات این بند نسبت به افتتاح حساب کاربری در بذر طلا اقدام نموده و کاربر، اشخاص ثالث، ورثه یا قائم‌مقام قانونی ایشان حق اعتراض و یا ادعایی در آینده در این خصوص نخواهند داشت.',
'هزینه خدمات و کارمزد تراکنش‌های مالی انجام شده در مطابق با توضیحات ارائه شده در صفحه راهنمای استفاده از بذر طلا می‌باشد.',
'تمامی علائم تجاری، طرح‌های گرافیکی، رابط کاربری، رابط بصری، عکس، لوگو، کدهای نرم‌افزار، ساختار، نحوه بیان، تنظیم مطالب و محتوای موجود در بذر طلا و شبکه‌های متعلق به بذر طلا و به‌طور کلی، سایر حقوق و امتیازات مربوطه منحصراً در مالکیت بذر طلا می‌باشد و هیچ شخصی حق استفاده از آن را بدون رضایت بذر طلا ندارد.',
'اگر هر یک از مفاد این موافقت‌نامه بر اساس هر قانون لازم‌الاجرا، نامعتبر یا غیرقابل اجرا تشخیص داده شود، این امر بر اعتبار هیچ ماده دیگری تأثیری نخواهد داشت و بقیه مقررات اجرا می‌شود.',
],
},
]
}
function getBoursecuRulesBlocks(nameProject: string): RulesBlock[] { function getBoursecuRulesBlocks(nameProject: string): RulesBlock[] {
const brand = resolveProjectName(nameProject) const brand = resolveProjectName(nameProject)
@@ -141,10 +105,6 @@ export function getRulesTitle(nameProject: string, isBoursecu: boolean) {
return `قوانین و مقررات معاملات مس در بستر ${brand}` return `قوانین و مقررات معاملات مس در بستر ${brand}`
} }
if (isBazretalaProject()) {
return 'قوانین و مقررات معاملات طلا و نقره در بستر بذر طلا'
}
return `قوانین و مقررات معاملات طلا در بستر ${brand}` return `قوانین و مقررات معاملات طلا در بستر ${brand}`
} }
@@ -153,10 +113,6 @@ export function getRulesBlocks(nameProject: string, isBoursecu: boolean): RulesB
return getBoursecuRulesBlocks(nameProject) return getBoursecuRulesBlocks(nameProject)
} }
if (isBazretalaProject()) {
return getBazretalaRulesBlocks()
}
return [{ items: getDefaultRulesItems(nameProject) }] return [{ items: getDefaultRulesItems(nameProject) }]
} }
@@ -1,72 +0,0 @@
<template>
<div class="relative">
<div
class="mobile-page-bg mobile-page-bg--cover opacity-60"
aria-hidden="true"
:style="{ backgroundImage: `url(${img})` }"
></div>
<MobilePageHeader title="تراکنش ها">
<template #trailing>
<BasicIcon icon="vuesax-linear:filter" @click="showFilter = true" />
</template>
</MobilePageHeader>
<!-- <div ref="el" :class="loading ? 'h-screen' : ''" class="px-3 mpage-content pb-10">-->
<div ref="el" class="transactions-page-body relative z-1 px-3 mpage-content pb-24">
<list-anim v-if="!loading">
<Item v-for="item in items" :key="item.id" :item="item" />
<div :ref="bindMobileObserver" v-show="!loading" class="w-full opacity-0 relative">
در حال بارگذاری
</div>
<TransactionSkeleton v-if="loadingMore" class="mt-1 mb-8" />
</list-anim>
<TransactionSkeleton v-for="i in 13" v-else :key="i" class="mt-1" />
<div v-if="items.length <= 0 && !loading" class="flex flex-col items-center justify-center h-80vh">
<DotLottieVue
class="w-11rem h-11rem"
autoplay
src="https://lottie.host/51d3315a-cf88-4c18-bb4d-a31511fd4325/KjSBzcMbi6.json"
/>
<div class="font-bold">{{ emptyText }}</div>
</div>
</div>
<BasicActionSheet v-model:show="showFilter">
<div class="font-bold text-lg">فیلتر</div>
<div class="pb-10 mt-5">
<a-form-item class="w-full gap-2 !flex-col" label="تاریخ" name="password">
<BaseDatePicker v-model:value="dateFilter" class="w-full" size="large" />
</a-form-item>
<BasicButton class="w-full mt-5" type="primary" @click="applyFilter">اعمال فیلتر</BasicButton>
<BasicButton class="w-full mt-2" @click="showFilter = false">بازگشت</BasicButton>
</div>
</BasicActionSheet>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { loading, loadingMore } from '@/utils'
import Item from './components/Item.vue'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
import BaseDatePicker from '@/components/BasicPacks/BaseDatePicker/src/BaseDatePicker.vue'
import ListAnim from '@/components/ListAnim.vue'
import TransactionSkeleton from '@/components/TransactionSkeleton.vue'
import { DotLottieVue } from '@lottiefiles/dotlottie-vue'
import img from '@/assets/img/user_ly1.svg'
import { useTransactionsPageContext } from './composables/useTransactionsPage'
const el = ref<HTMLElement | null>(null)
const {
items,
showFilter,
dateFilter,
emptyText,
applyFilter,
bindMobileObserver,
} = useTransactionsPageContext()
</script>
@@ -15,9 +15,6 @@
<p class="dtxn-row__title">{{ props.item.title }}</p> <p class="dtxn-row__title">{{ props.item.title }}</p>
<div class="dtxn-row__meta"> <div class="dtxn-row__meta">
<span>{{ toJalaliOutOfUTC(props.item.expire_at) }}</span> <span>{{ toJalaliOutOfUTC(props.item.expire_at) }}</span>
<span v-if="!isBoursecu" :class="goldAmount >= 0 ? 'text-green-600' : 'text-red-600'">
{{ formatTransactionGoldAmount(props.item, goldAmount) }} {{ props.item.unit }}
</span>
<span v-if="isGold"> <span v-if="isGold">
قیمت معامله: قیمت معامله:
{{ usePriceWithoutType(tradePrice) }} {{ usePriceWithoutType(tradePrice) }}
@@ -54,15 +51,7 @@
<div>{{ props.item.title }}</div> <div>{{ props.item.title }}</div>
<div>{{ toJalaliOutOfUTC(props.item.expire_at) }}</div> <div>{{ toJalaliOutOfUTC(props.item.expire_at) }}</div>
</div> </div>
<div :class="['flex mt-2', isBoursecu ? 'justify-end' : 'justify-between']"> <div class="flex mt-2 justify-end">
<div
v-if="!isBoursecu"
:class="goldAmount >= 0 ? 'text-green-600' : 'text-red-600'"
class="text-gray-500 text-xs number-ltr"
>
{{ formatTransactionGoldAmount(props.item, goldAmount) }}
<div>{{ props.item.unit }}</div>
</div>
<div class="text-gray-500 flex gap-1 text-xs items-center number-ltr"> <div class="text-gray-500 flex gap-1 text-xs items-center number-ltr">
<div :class="props.item.cash > 0 ? 'text-green-600' : 'text-red-600'" class="text-base"> <div :class="props.item.cash > 0 ? 'text-green-600' : 'text-red-600'" class="text-base">
{{ usePriceWithoutType(props.item.cash ?? 0) }} {{ usePriceWithoutType(props.item.cash ?? 0) }}
@@ -116,23 +105,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import Icon from '@/components/BasicPacks/BasicIcon.vue' import Icon from '@/components/BasicPacks/BasicIcon.vue'
import { import {
getTransactionGoldAmount,
getTransactionTradePrice, getTransactionTradePrice,
resolveTransactionStatusLabel, resolveTransactionStatusLabel,
usePriceWithoutType, usePriceWithoutType,
} from '@/utils/usePrice' } from '@/utils/usePrice'
import { formatTransactionGoldAmount } from '@/utils/productDealMethod'
import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue' import BasicActionSheet from '@/components/BasicPacks/BasicActionSheet.vue'
import BasicModal from '@/components/BasicPacks/BasicModal.vue' import BasicModal from '@/components/BasicPacks/BasicModal.vue'
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import BasicButton from '@/components/BasicPacks/BasicButton.vue' import BasicButton from '@/components/BasicPacks/BasicButton.vue'
import type { Transaction } from '@/models/transaction' import type { Transaction } from '@/models/transaction'
import { toJalaliOutOfUTC } from '@/utils/useDateTime' import { toJalaliOutOfUTC } from '@/utils/useDateTime'
import { isBoursecuProject } from '@/utils/config'
import TransactionDetailPanel from './TransactionDetailPanel.vue' import TransactionDetailPanel from './TransactionDetailPanel.vue'
const isBoursecu = isBoursecuProject()
const infoVisible = ref(false) const infoVisible = ref(false)
const props = withDefaults( const props = withDefaults(
@@ -143,7 +127,6 @@ const props = withDefaults(
{ variant: 'mobile' }, { variant: 'mobile' },
) )
const goldAmount = computed(() => getTransactionGoldAmount(props.item))
const tradePrice = computed(() => getTransactionTradePrice(props.item)) const tradePrice = computed(() => getTransactionTradePrice(props.item))
const statusLabel = computed(() => resolveTransactionStatusLabel(props.item)) const statusLabel = computed(() => resolveTransactionStatusLabel(props.item))
@@ -28,7 +28,6 @@ import {
transactionProductFilter, transactionProductFilter,
transactionTableOutput, transactionTableOutput,
transactionTradeSide, transactionTradeSide,
transactionsBoursecuMode,
} from './useTransactionsPageData' } from './useTransactionsPageData'
export interface TransactionsPageContext { export interface TransactionsPageContext {
@@ -64,8 +63,7 @@ export interface TransactionsPageContext {
export const TRANSACTIONS_PAGE_KEY: InjectionKey<TransactionsPageContext> = export const TRANSACTIONS_PAGE_KEY: InjectionKey<TransactionsPageContext> =
Symbol('transactionsPage') Symbol('transactionsPage')
export function useTransactionsPageProvider(options?: { boursecu?: boolean }) { export function useTransactionsPageProvider() {
const isBoursecu = options?.boursecu ?? false
const isDesktop = useDesktopBreakpoint() const isDesktop = useDesktopBreakpoint()
const showFilter = ref(false) const showFilter = ref(false)
const mobileObserverEl = ref<HTMLElement | null>(null) const mobileObserverEl = ref<HTMLElement | null>(null)
@@ -93,14 +91,10 @@ export function useTransactionsPageProvider(options?: { boursecu?: boolean }) {
) )
const showProductFilter = computed( const showProductFilter = computed(
() => transactionsBoursecuMode.value && boursecuTabSupportsProductFilter(activeTab.value), () => boursecuTabSupportsProductFilter(activeTab.value),
) )
const emptyText = computed(() => { const emptyText = computed(() => {
if (!transactionsBoursecuMode.value) {
return 'تراکنشی موجود نیست'
}
if (activeTab.value.id === 'trades' && transactionTradeSide.value === 'sell') { if (activeTab.value.id === 'trades' && transactionTradeSide.value === 'sell') {
return 'معامله فروشی برای شما به ثبت نرسیده است' return 'معامله فروشی برای شما به ثبت نرسیده است'
} }
@@ -183,10 +177,8 @@ export function useTransactionsPageProvider(options?: { boursecu?: boolean }) {
) )
onMounted(async () => { onMounted(async () => {
if (isBoursecu) { await getProductsCatalog()
await getProductsCatalog() await initTransactionsPageData()
}
await initTransactionsPageData(isBoursecu)
}) })
onActivated(async () => { onActivated(async () => {
@@ -236,6 +228,6 @@ export function useTransactionsPageContext() {
} }
/** @deprecated Use useTransactionsPageProvider in index + useTransactionsPageContext in views. */ /** @deprecated Use useTransactionsPageProvider in index + useTransactionsPageContext in views. */
export function useTransactionsPage(options?: { boursecu?: boolean }) { export function useTransactionsPage() {
return useTransactionsPageProvider(options) return useTransactionsPageProvider()
} }
@@ -14,7 +14,7 @@ import {
/** Shared transactions page state — refetched on every page visit (no list cache). */ /** Shared transactions page state — refetched on every page visit (no list cache). */
let activeFetchId = 0 let activeFetchId = 0
export const transactionsBoursecuMode = ref(false) export const transactionsBoursecuMode = ref(true)
export const transactionPagination = ref<TableEngineInput>({ filters: [], limit: 7, page: 0 }) export const transactionPagination = ref<TableEngineInput>({ filters: [], limit: 7, page: 0 })
export const transactionItems = ref<Transaction[]>([]) export const transactionItems = ref<Transaction[]>([])
export const transactionTableOutput = ref<TableEngineOutput<Transaction> | null>(null) export const transactionTableOutput = ref<TableEngineOutput<Transaction> | null>(null)
@@ -34,37 +34,19 @@ export function getActiveBoursecuTab(): BoursecuTransactionTab {
} }
export function resetTransactionsPagination() { export function resetTransactionsPagination() {
if (transactionsBoursecuMode.value) { transactionPagination.value = buildBoursecuTransactionPagination(
transactionPagination.value = buildBoursecuTransactionPagination( getActiveBoursecuTab(),
getActiveBoursecuTab(), transactionTradeSide.value,
transactionTradeSide.value, transactionDateFromFilter.value,
transactionDateFromFilter.value, transactionDateToFilter.value,
transactionDateToFilter.value, transactionStatusFilter.value,
transactionStatusFilter.value, transactionProductFilter.value,
transactionProductFilter.value, )
)
return
}
transactionPagination.value = { filters: [], limit: 7, page: 0 }
if (transactionDateFilter.value) {
transactionPagination.value.filters = [
{
field: 'created_at',
cr: 'and',
type: 'date',
op: 'eq',
value: transactionDateFilter.value,
},
]
}
} }
function resolveTransactionFetcher() { function resolveTransactionFetcher() {
const activeTab = getActiveBoursecuTab() const activeTab = getActiveBoursecuTab()
return transactionsBoursecuMode.value && activeTab.fetchMode === 'deposit' return activeTab.fetchMode === 'deposit' ? getTransactionTable2 : getTransactionTable
? getTransactionTable2
: getTransactionTable
} }
async function fetchTransactionsPage(append: boolean): Promise<boolean> { async function fetchTransactionsPage(append: boolean): Promise<boolean> {
@@ -134,8 +116,8 @@ export async function loadMoreTransactions() {
} }
/** هر بار ورود به صفحه تراکنش‌ها */ /** هر بار ورود به صفحه تراکنش‌ها */
export async function initTransactionsPageData(boursecu: boolean) { export async function initTransactionsPageData() {
transactionsBoursecuMode.value = boursecu transactionsBoursecuMode.value = true
await reloadTransactionsTable() await reloadTransactionsTable()
} }
@@ -143,7 +125,7 @@ export function resetTransactionsPageData() {
activeFetchId += 1 activeFetchId += 1
loading.value = false loading.value = false
loadingMore.value = false loadingMore.value = false
transactionsBoursecuMode.value = false transactionsBoursecuMode.value = true
transactionPagination.value = { filters: [], limit: 7, page: 0 } transactionPagination.value = { filters: [], limit: 7, page: 0 }
transactionItems.value = [] transactionItems.value = []
transactionTableOutput.value = null transactionTableOutput.value = null
@@ -1,84 +0,0 @@
<template>
<div class="dtxn">
<div class="dtxn__container">
<DesktopPageTopbar title="تراکنش‌ها" />
<div class="dtxn-filters">
<div class="dtxn-filters__label">
<BasicIcon icon="vuesax-linear:filter" size="16" />
<span>فیلتر تاریخ:</span>
</div>
<BaseDatePicker
v-model:value="dateFilter"
class="dtxn-filters__date"
placeholder="تاریخ"
size="large"
/>
<BasicButton type="primary" @click="applyFilter">اعمال فیلتر</BasicButton>
<BasicButton v-if="dateFilter" @click="clearFilter">حذف فیلتر</BasicButton>
</div>
<div ref="el" :class="loading ? 'min-h-[40vh]' : ''" class="mt-5">
<div v-if="!loading" class="dtxn-list">
<list-anim>
<Item
v-for="item in items"
:key="item.id"
:item="item"
variant="desktop"
/>
</list-anim>
<div :ref="bindDesktopObserver" class="w-full opacity-0 relative">
در حال بارگذاری
</div>
<TransactionSkeleton v-if="loadingMore" class="mt-1 mb-8" />
</div>
<template v-else>
<TransactionSkeleton v-for="i in 8" :key="i" class="mt-1" />
</template>
<div v-if="items.length <= 0 && !loading" class="dtxn-empty">
<DotLottieVue
class="w-44 h-44"
autoplay
src="https://lottie.host/51d3315a-cf88-4c18-bb4d-a31511fd4325/KjSBzcMbi6.json"
/>
<p class="dtxn-empty__text">{{ emptyText }}</p>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { loading, loadingMore } from '@/utils'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import DesktopPageTopbar from '@/layouts/components/DesktopPageTopbar.vue'
import BasicButton from '@/components/BasicPacks/BasicButton.vue'
import BaseDatePicker from '@/components/BasicPacks/BaseDatePicker/src/BaseDatePicker.vue'
import ListAnim from '@/components/ListAnim.vue'
import TransactionSkeleton from '@/components/TransactionSkeleton.vue'
import { DotLottieVue } from '@lottiefiles/dotlottie-vue'
import Item from '../components/Item.vue'
import { useTransactionsPageContext } from '../composables/useTransactionsPage'
const el = ref<HTMLElement | null>(null)
const {
items,
dateFilter,
emptyText,
applyFilter,
reloadTable,
bindDesktopObserver,
} = useTransactionsPageContext()
async function clearFilter() {
dateFilter.value = undefined
await reloadTable()
}
</script>
<style lang="less" src="./styles/desktop-transactions.less"></style>
@@ -39,11 +39,13 @@
transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease; transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
&:hover { &:hover {
border-color: #d1d5db; background: @App-primary-color;
color: #374151; border-color: @App-primary-color;
color: #fff;
} }
&--active { &--active,
&--active:hover {
background: @App-primary-color; background: @App-primary-color;
border-color: @App-primary-color; border-color: @App-primary-color;
color: #fff; color: #fff;
+2 -15
View File
@@ -1,6 +1,6 @@
<template> <template>
<div class="transactions-page-root"> <div class="transactions-page-root">
<ResponsiveViewPair v-if="isBoursecu"> <ResponsiveViewPair>
<template #mobile> <template #mobile>
<BoursecuTransactionsPage /> <BoursecuTransactionsPage />
</template> </template>
@@ -8,29 +8,16 @@
<DesktopBoursecuTransactionsView /> <DesktopBoursecuTransactionsView />
</template> </template>
</ResponsiveViewPair> </ResponsiveViewPair>
<ResponsiveViewPair v-else>
<template #mobile>
<DefaultTransactionsPage />
</template>
<template #desktop>
<DesktopDefaultTransactionsView />
</template>
</ResponsiveViewPair>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import ResponsiveViewPair from '@/layouts/components/ResponsiveViewPair.vue' import ResponsiveViewPair from '@/layouts/components/ResponsiveViewPair.vue'
import { isBoursecuProject } from '@/utils/config'
import { useTransactionsPageProvider } from './composables/useTransactionsPage' import { useTransactionsPageProvider } from './composables/useTransactionsPage'
import BoursecuTransactionsPage from './BoursecuTransactionsPage.vue' import BoursecuTransactionsPage from './BoursecuTransactionsPage.vue'
import DefaultTransactionsPage from './DefaultTransactionsPage.vue'
import DesktopBoursecuTransactionsView from './desktop/DesktopBoursecuTransactionsView.vue' import DesktopBoursecuTransactionsView from './desktop/DesktopBoursecuTransactionsView.vue'
import DesktopDefaultTransactionsView from './desktop/DesktopDefaultTransactionsView.vue'
const isBoursecu = isBoursecuProject() useTransactionsPageProvider()
useTransactionsPageProvider({ boursecu: isBoursecu })
</script> </script>
<style scoped> <style scoped>
+6 -24
View File
@@ -14,7 +14,7 @@ import { updateMe, updatePass } from '@/api/me'
import { useUserStore } from '@/store/userStore' import { useUserStore } from '@/store/userStore'
import { getUserAvatarUrl } from '@/utils/file' import { getUserAvatarUrl } from '@/utils/file'
import { useConfigStore } from '@/store/configStore' import { useConfigStore } from '@/store/configStore'
import { getConfigApp, isBoursecuProject, getBoursecuActionIconSrc } from '@/utils/config' import { getBoursecuActionIconSrc } from '@/utils/config'
import { getReferralShareText, getReferralShareTitle, shareReferralText } from '@/utils/referralShare' import { getReferralShareText, getReferralShareTitle, shareReferralText } from '@/utils/referralShare'
import { normalizeAuthFormFields } from '@/utils/persianDigits' import { normalizeAuthFormFields } from '@/utils/persianDigits'
@@ -95,13 +95,9 @@ export function useUserPage() {
return result return result
}) })
const shareButtonLabel = computed(() => const shareButtonLabel = computed(() => 'اشتراک گذاری')
getConfigApp('name') === 'garoosi' ? 'دعوت از دوستان' : 'اشتراک گذاری',
)
const menuActions = computed<UserMenuAction[]>(() => { const menuActions = computed<UserMenuAction[]>(() => [
const projectName = getConfigApp('name')
return [
{ {
id: 'password', id: 'password',
label: 'تغییر رمز عبور', label: 'تغییر رمز عبور',
@@ -130,20 +126,13 @@ export function useUserPage() {
onClick: () => router.push({ name: 'about' }), onClick: () => router.push({ name: 'about' }),
visible: true, visible: true,
}, },
{
id: 'giftCard',
label: 'ثبت کارت هدیه',
icon: 'vuesax-linear:gift',
onClick: () => router.push({ name: 'giftCard' }),
visible: projectName === 'garoosi',
},
{ {
id: 'physicalReceive', id: 'physicalReceive',
label: 'تحویل فیزیکی', label: 'تحویل فیزیکی',
icon: 'vuesax-linear:direct-inbox', icon: 'vuesax-linear:direct-inbox',
iconSrc: isBoursecuProject() ? getBoursecuActionIconSrc('physicalReceive') : undefined, iconSrc: getBoursecuActionIconSrc('physicalReceive'),
onClick: () => router.push({ name: 'physicalReceive' }), onClick: () => router.push({ name: 'physicalReceive' }),
visible: projectName !== 'bazretala', visible: true,
}, },
{ {
id: 'rules', id: 'rules',
@@ -152,15 +141,8 @@ export function useUserPage() {
onClick: () => router.push({ name: 'rules' }), onClick: () => router.push({ name: 'rules' }),
visible: true, visible: true,
}, },
{
id: 'permissions',
label: 'مجوز ها',
icon: 'vuesax-linear:archive-book',
onClick: () => router.push({ name: 'permissions' }),
visible: projectName === 'bazretala',
},
].filter((action) => action.visible) ].filter((action) => action.visible)
}) )
function setForm() { function setForm() {
userFormState.value = { ...userStore.userInfo } as User userFormState.value = { ...userStore.userInfo } as User
@@ -13,10 +13,7 @@ import type { Card } from '@/models/wallet'
import { useUserStore } from '@/store/userStore' import { useUserStore } from '@/store/userStore'
import { useConfigStore } from '@/store/configStore' import { useConfigStore } from '@/store/configStore'
import { import {
getProjectBaleContactLink,
getWalletDepositQuickAmountsRial, getWalletDepositQuickAmountsRial,
isBazretalaProject,
isBoursecuProject,
} from '@/utils/config' } from '@/utils/config'
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout' import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
import { import {
@@ -60,7 +57,6 @@ export interface WalletPageContext {
depositGuideSupportLink: ReturnType<typeof computed<string>> depositGuideSupportLink: ReturnType<typeof computed<string>>
depositSmsSupportLink: ReturnType<typeof computed<string>> depositSmsSupportLink: ReturnType<typeof computed<string>>
depositQuickAmounts: ReturnType<typeof computed<readonly number[]>> depositQuickAmounts: ReturnType<typeof computed<readonly number[]>>
isBazretalaProject: typeof isBazretalaProject
usePriceWithoutType: typeof usePriceWithoutType usePriceWithoutType: typeof usePriceWithoutType
formatCashWalletBalanceText: typeof formatCashWalletBalanceText formatCashWalletBalanceText: typeof formatCashWalletBalanceText
setIsDeposit: (val: boolean) => void setIsDeposit: (val: boolean) => void
@@ -100,16 +96,12 @@ export function useWalletPage() {
const depositQuickAmounts = computed(() => getWalletDepositQuickAmountsRial()) const depositQuickAmounts = computed(() => getWalletDepositQuickAmountsRial())
const depositGuideSupportLink = computed(() => { const depositGuideSupportLink = computed(() =>
if (config.config?.name === 'garoosi' || isBazretalaProject()) { buildSupportMessengerUrl(
return getProjectBaleContactLink()
}
return buildSupportMessengerUrl(
supportMessengerSettings.value.messenger, supportMessengerSettings.value.messenger,
supportMessengerSettings.value.messengerId, supportMessengerSettings.value.messengerId,
) ),
}) )
const depositSmsSupportLink = computed(() => const depositSmsSupportLink = computed(() =>
buildDepositSmsUrl(supportMessengerSettings.value.depositSmsSupportNumber), buildDepositSmsUrl(supportMessengerSettings.value.depositSmsSupportNumber),
@@ -132,7 +124,7 @@ export function useWalletPage() {
}) })
const disableButton = computed(() => { const disableButton = computed(() => {
const minWithdrawAmount = isBoursecuProject() ? 200_000 : 1_000_000 const minWithdrawAmount = 200_000
const minDepositAmount = 1_000_000 const minDepositAmount = 1_000_000
if (!loading.value) { if (!loading.value) {
@@ -297,7 +289,6 @@ export function useWalletPage() {
depositGuideSupportLink, depositGuideSupportLink,
depositSmsSupportLink, depositSmsSupportLink,
depositQuickAmounts, depositQuickAmounts,
isBazretalaProject,
usePriceWithoutType, usePriceWithoutType,
formatCashWalletBalanceText, formatCashWalletBalanceText,
setIsDeposit, setIsDeposit,
@@ -55,9 +55,6 @@
<Transition mode="out-in" name="fade"> <Transition mode="out-in" name="fade">
<div v-if="!isDeposit" key="withdraw" class="dwallet-form__extras"> <div v-if="!isDeposit" key="withdraw" class="dwallet-form__extras">
<p v-if="!isBoursecu" class="dwallet-form__hint dwallet-form__hint--warn">
حداقل مبلغ برداشتی 100 هزار تومان می باشد.
</p>
<p class="dwallet-form__balance-text"> <p class="dwallet-form__balance-text">
{{ formatCashWalletBalanceText(usePriceWithoutType(walletStore?.walletInfo?.cash ?? 0)) }} {{ formatCashWalletBalanceText(usePriceWithoutType(walletStore?.walletInfo?.cash ?? 0)) }}
</p> </p>
@@ -81,7 +78,7 @@
</a-form-item> </a-form-item>
</div> </div>
<div v-else key="deposit" class="dwallet-form__extras"> <div v-else key="deposit" class="dwallet-form__extras">
<p v-if="!isBazretalaProject()" class="dwallet-form__hint dwallet-form__hint--warn"> <p class="dwallet-form__hint dwallet-form__hint--warn">
حداقل مبلغ واریزی 100 هزار تومان می باشد. حداقل مبلغ واریزی 100 هزار تومان می باشد.
</p> </p>
<div class="dwallet-quick-amounts"> <div class="dwallet-quick-amounts">
@@ -108,7 +105,7 @@
<span v-else>{{ isDeposit ? 'واریز' : 'برداشت' }}</span> <span v-else>{{ isDeposit ? 'واریز' : 'برداشت' }}</span>
</BasicButton> </BasicButton>
<BoursecuWithdrawGuide v-if="!isDeposit && isBoursecu" /> <BoursecuWithdrawGuide v-if="!isDeposit" />
</a-form> </a-form>
</section> </section>
@@ -136,53 +133,34 @@
<template v-else> <template v-else>
<div class="dwallet-side__header"> <div class="dwallet-side__header">
<h3 class="dwallet-side__title">{{ isBoursecu ? 'حساب‌های بانکی' : 'کارت‌های بانکی' }}</h3> <h3 class="dwallet-side__title">حساب‌های بانکی</h3>
<button <button
v-if="isBoursecu"
type="button" type="button"
class="dwallet-side__add-link" class="dwallet-side__add-link"
@click="showAddCard = true" @click="showAddCard = true"
> >
+ افزودن شبا + افزودن شبا
</button> </button>
<button
v-else
type="button"
class="dwallet-side__add-btn"
@click="showAddCard = true"
>
<BasicIcon icon="vuesax-linear:add" size="16" />
<span>افزودن کارت</span>
</button>
</div> </div>
<p v-if="isBoursecu && cardItems.length > 0" class="dwallet-side__subtitle">واریز به</p> <p v-if="cardItems.length > 0" class="dwallet-side__subtitle">واریز به</p>
<div v-if="!loading && cardItems.length === 0" class="dwallet-side__empty dwallet-side__empty--cards"> <div v-if="!loading && cardItems.length === 0" class="dwallet-side__empty dwallet-side__empty--cards">
<BasicIcon icon="vuesax-linear:card" size="32" /> <BasicIcon icon="vuesax-linear:card" size="32" />
<p>هیچ کارتی یافت نشد</p> <p>هیچ کارتی یافت نشد</p>
<button type="button" class="dwallet-side__add-btn dwallet-side__add-btn--primary" @click="showAddCard = true"> <button type="button" class="dwallet-side__add-btn dwallet-side__add-btn--primary" @click="showAddCard = true">
{{ isBoursecu ? 'افزودن شبا' : 'افزودن کارت جدید' }} افزودن شبا
</button> </button>
</div> </div>
<BoursecuBankAccountPicker <BoursecuBankAccountPicker
v-else-if="isBoursecu" v-else
v-model:id="form.card_id" v-model:id="form.card_id"
variant="desktop" variant="desktop"
:items="cardItems" :items="cardItems"
@add-account="showAddCard = true" @add-account="showAddCard = true"
@card-deleted="getCards" @card-deleted="getCards"
/> />
<div v-else-if="cardItems.length > 0" class="dwallet-cards">
<CardSlider
v-model:id="form.card_id"
:items="cardItems"
class="dwallet-cards__slider"
@cardDeleted="getCards"
/>
</div>
</template> </template>
</section> </section>
</div> </div>
@@ -199,15 +177,11 @@ import ListAnim from '@/components/ListAnim.vue'
import TransactionSkeleton from '@/components/TransactionSkeleton.vue' import TransactionSkeleton from '@/components/TransactionSkeleton.vue'
import { useConfigStore } from '@/store/configStore' import { useConfigStore } from '@/store/configStore'
import { required } from '@/utils/inputRules' import { required } from '@/utils/inputRules'
import CardSlider from '../components/UISlider/index.vue'
import BoursecuBankAccountPicker from '../components/BoursecuBankAccountPicker.vue' import BoursecuBankAccountPicker from '../components/BoursecuBankAccountPicker.vue'
import BoursecuWithdrawGuide from '../components/BoursecuWithdrawGuide.vue' import BoursecuWithdrawGuide from '../components/BoursecuWithdrawGuide.vue'
import Item from '../components/Item.vue' import Item from '../components/Item.vue'
import { isBoursecuProject } from '@/utils/config'
import { useWalletPageContext } from '../composables/useWalletPage' import { useWalletPageContext } from '../composables/useWalletPage'
const isBoursecu = isBoursecuProject()
const { const {
isDeposit, isDeposit,
walletStore, walletStore,
@@ -220,7 +194,6 @@ const {
loadingMore, loadingMore,
desktopDepositScrollEl, desktopDepositScrollEl,
depositQuickAmounts, depositQuickAmounts,
isBazretalaProject,
usePriceWithoutType, usePriceWithoutType,
formatCashWalletBalanceText, formatCashWalletBalanceText,
setIsDeposit, setIsDeposit,
+1 -18
View File
@@ -83,22 +83,7 @@
:support-link="depositGuideSupportLink" :support-link="depositGuideSupportLink"
:sms-link="depositSmsSupportLink" :sms-link="depositSmsSupportLink"
> >
<template v-if="config.config.name === 'garoosi'"> <template>
<p>جهت شارژ حساب کاربری خود، مبلغ موردنظر را به شماره کارت زیر واریز نموده و از طریق لینک زیر به پشتیبان اطلاع دهید.</p>
<p class="deposit-guide-note deposit-guide-note--warn">در صورت عدم همخوانی کارت ملی و فیش واریزی کیف پول شارژ نمیشود!</p>
<p class="deposit-guide-note">(سیده زهرا موسوی)</p>
<p class="deposit-guide-note">شماره کارت: 6037701900311389</p>
<p class="deposit-guide-note">شماره شبا: IR 080160000000001143804581</p>
</template>
<template v-else-if="isBazretalaProject()">
<p>جهت شارژ حساب کاربری خود، مبلغ موردنظر را به شماره کارت زیر واریز نموده و به شماره پشتیبانی 09121784258 در برنامه بله اطلاع دهید.</p>
<p class="deposit-guide-note">(گنجينه طلاي حاجي ابوالفتح - بانک ملی)</p>
<p class="deposit-guide-note">شماره کارت: 6037998800496928</p>
<p class="deposit-guide-note">شماره شبا: IR 110170000000372468150002</p>
</template>
<template v-else>
<p>{{ supportMessengerText }}</p> <p>{{ supportMessengerText }}</p>
</template> </template>
</WalletDepositGuideModal> </WalletDepositGuideModal>
@@ -138,7 +123,6 @@ import WalletDepositGuideModal from './components/WalletDepositGuideModal.vue'
const isDesktop = useDesktopBreakpoint() const isDesktop = useDesktopBreakpoint()
const { const {
config,
showAddCard, showAddCard,
chargeAccountModal, chargeAccountModal,
showConfirmCode, showConfirmCode,
@@ -149,7 +133,6 @@ const {
supportMessengerText, supportMessengerText,
depositGuideSupportLink, depositGuideSupportLink,
depositSmsSupportLink, depositSmsSupportLink,
isBazretalaProject,
handleOnComplete, handleOnComplete,
loading, loading,
addCard, addCard,
@@ -30,11 +30,11 @@
</div> </div>
<div <div
class="px-5 mpage-content mpage-content--with-subheader" class="px-5 mpage-content mpage-content--with-subheader"
:class="isDeposit ? 'pb-2' : isBoursecu ? 'pb-[calc(var(--app-bottom-chrome-height)+1rem)]' : 'pb-[calc(var(--app-bottom-chrome-height)+var(--app-add-card-height)+1rem)]'" :class="isDeposit ? 'pb-2' : 'pb-[calc(var(--app-bottom-chrome-height)+1rem)]'"
> >
<a-form :model="form" class="!mt-5" name="basic" @finish="ok()"> <a-form :model="form" class="!mt-5" name="basic" @finish="ok()">
<BoursecuBankAccountPicker <BoursecuBankAccountPicker
v-if="!isDeposit && isBoursecu" v-if="!isDeposit"
v-model:id="form.card_id" v-model:id="form.card_id"
variant="mobile" variant="mobile"
:items="cardItems" :items="cardItems"
@@ -59,9 +59,6 @@
</a-form-item> </a-form-item>
<Transition mode="out-in" name="fade"> <Transition mode="out-in" name="fade">
<div v-if="!isDeposit"> <div v-if="!isDeposit">
<div v-if="!isBoursecu" class="text-red text-xs font-bold mb-2">
حداقل مبلغ برداشتی 100 هزار تومان می باشد.
</div>
<div class="font-bold text-xs"> <div class="font-bold text-xs">
{{ formatCashWalletBalanceText(usePriceWithoutType(walletStore?.walletInfo?.cash ?? 0)) }} {{ formatCashWalletBalanceText(usePriceWithoutType(walletStore?.walletInfo?.cash ?? 0)) }}
</div> </div>
@@ -85,7 +82,7 @@
</a-form-item> </a-form-item>
</div> </div>
<div v-else class="mt-3"> <div v-else class="mt-3">
<div v-if="!isBazretalaProject()" class="text-red text-xs font-bold mb-2"> <div class="text-red text-xs font-bold mb-2">
حداقل مبلغ واریزی 100 هزار تومان می باشد. حداقل مبلغ واریزی 100 هزار تومان می باشد.
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
@@ -118,7 +115,7 @@
</a-form> </a-form>
<div <div
v-if="!isDeposit && isBoursecu" v-if="!isDeposit"
class="wallet-withdraw-guide-panel relative z-10" class="wallet-withdraw-guide-panel relative z-10"
> >
<BoursecuWithdrawGuide /> <BoursecuWithdrawGuide />
@@ -137,44 +134,12 @@
</list-anim> </list-anim>
<TransactionSkeleton v-for="i in 13" v-else :key="i" class="mt-1" /> <TransactionSkeleton v-for="i in 13" v-else :key="i" class="mt-1" />
</div> </div>
<Transition mode="out-in" name="fade">
<template v-if="!isDeposit">
<div
v-if="!loading && cardItems.length === 0 && !isBoursecu"
class="h-30 flex items-center justify-center"
>
هیچ کارتی یافت نشد
</div>
</template>
</Transition>
</div> </div>
<Teleport to="body">
<CardSlider
v-if="!isDeposit && !isBoursecu && cardItems.length > 0 && !showAddCard"
v-model:id="form.card_id"
:items="cardItems"
class="card-slider-wrap wallet-card-slider"
@cardDeleted="getCards"
/>
</Teleport>
<Teleport to="body">
<div
v-if="!isDeposit && !isBoursecu && !showAddCard"
class="add-card app-dock-add-bar"
@click="showAddCard = true"
>
<div>افزودن کارت جدید</div>
<BasicIcon icon="vuesax-linear:add" />
</div>
</Teleport>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import BasicButton from '@/components/BasicPacks/BasicButton.vue' import BasicButton from '@/components/BasicPacks/BasicButton.vue'
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue' import MobilePageHeader from '@/layouts/components/MobilePageHeader.vue'
import BasicInput from '@/components/BasicPacks/BasicInput.vue' import BasicInput from '@/components/BasicPacks/BasicInput.vue'
import Bg_main from '@/components/bg_main.vue' import Bg_main from '@/components/bg_main.vue'
@@ -183,15 +148,11 @@ import TransactionSkeleton from '@/components/TransactionSkeleton.vue'
import { useConfigStore } from '@/store/configStore' import { useConfigStore } from '@/store/configStore'
import { required } from '@/utils/inputRules' import { required } from '@/utils/inputRules'
import img from '@/assets/img/user_ly1.svg' import img from '@/assets/img/user_ly1.svg'
import CardSlider from '../components/UISlider/index.vue'
import BoursecuBankAccountPicker from '../components/BoursecuBankAccountPicker.vue' import BoursecuBankAccountPicker from '../components/BoursecuBankAccountPicker.vue'
import BoursecuWithdrawGuide from '../components/BoursecuWithdrawGuide.vue' import BoursecuWithdrawGuide from '../components/BoursecuWithdrawGuide.vue'
import Item from '../components/Item.vue' import Item from '../components/Item.vue'
import { isBoursecuProject } from '@/utils/config'
import { useWalletPageContext } from '../composables/useWalletPage' import { useWalletPageContext } from '../composables/useWalletPage'
const isBoursecu = isBoursecuProject()
const { const {
isDeposit, isDeposit,
walletStore, walletStore,
@@ -204,7 +165,6 @@ const {
loadingMore, loadingMore,
mobileDepositScrollEl, mobileDepositScrollEl,
depositQuickAmounts, depositQuickAmounts,
isBazretalaProject,
usePriceWithoutType, usePriceWithoutType,
formatCashWalletBalanceText, formatCashWalletBalanceText,
setIsDeposit, setIsDeposit,
+3 -51
View File
@@ -23,10 +23,7 @@
:src="getProjectLoginLogo(config.config?.avatar)" :src="getProjectLoginLogo(config.config?.avatar)"
alt="" alt=""
:class="[ :class="[
'z-100 self-center w-36', 'z-100 self-center w-36 rounded aspect-square',
config.config?.name === 'garoosi'
? 'h-auto object-contain'
: 'rounded aspect-square',
]" ]"
style="margin-bottom: 60px" style="margin-bottom: 60px"
> >
@@ -190,27 +187,7 @@
ورود با اثر انگشت ورود با اثر انگشت
</BasicButton> </BasicButton>
<template v-if="config.config?.name === 'garoosi'"> <div v-if="!isTWA() && !!setting.getSettingByKey('apk_download_link')" class="mt-1">
<div v-if="!isTWA()" class="dauth-actions-row">
<a
class="dauth-link-btn dauth-link-btn--primary"
download="Garoosi.apk"
href="https://garoosigholaktala.ir/apk/Garoosi.apk"
>
دانلود اپ اندروید
</a>
<button type="button" class="dauth-link-btn" @click="installWebApp">
نصب نسخه وب اپ
</button>
</div>
<button type="button" class="dauth-link-btn" @click="goToEducate">
آموزش کار با نرم افزار
</button>
<button type="button" class="dauth-link-btn" @click="goToAboutUs">
درباره ما
</button>
</template>
<div v-else-if="!isTWA() && !!setting.getSettingByKey('apk_download_link')" class="mt-1">
<a <a
class="dauth-link-btn dauth-link-btn--primary !hover:text-primary " class="dauth-link-btn dauth-link-btn--primary !hover:text-primary "
:href="setting.getSettingByKey('apk_download_link')" :href="setting.getSettingByKey('apk_download_link')"
@@ -254,29 +231,7 @@
</div> </div>
</BasicButton> </BasicButton>
<template v-if="config.config?.name === 'garoosi'"> <div v-if="!isTWA() && !!setting.getSettingByKey('apk_download_link')" class=" mt-1">
<div v-if="!isTWA()" class="flex w-full gap-2 mt-2">
<a
class="flex w-full items-center justify-center rounded py-2 text-white bg-primary border-none cursor-pointer no-underline"
download="Garoosi.apk"
href="https://garoosigholaktala.ir/apk/Garoosi.apk"
>
<div>دانلود اپ اندروید</div>
</a>
<BasicButton type="primary" class="w-full" @click="installWebApp">
<div>نصب نسخه وب اپ</div>
</BasicButton>
</div>
<BasicButton class="w-full !font-bold mt-2" @click="goToEducate">
<div class="flex gap-2 items-center">آموزش کار با نرم افزار</div>
</BasicButton>
<BasicButton class="mt-2 w-full" icon="" iconSize="18" @click="goToAboutUs">
<div class="flex">
<div>درباره ما</div>
</div>
</BasicButton>
</template>
<div v-else-if="!isTWA() && !!setting.getSettingByKey('apk_download_link')" class=" mt-1">
<a <a
class="dauth-link-btn dauth-link-btn--primary !hover:text-primary " class="dauth-link-btn dauth-link-btn--primary !hover:text-primary "
:href="setting.getSettingByKey('apk_download_link')" :href="setting.getSettingByKey('apk_download_link')"
@@ -294,9 +249,6 @@
ثبت نام کنید. ثبت نام کنید.
</div> </div>
</a-form-item> </a-form-item>
<!-- <BasicButton v-if="config.config.name === 'garoosi'" class="w-full !font-bold" @click="goToEducate">
<div class="flex gap-2 items-center">آموزش کار با نرم افزار</div>
</BasicButton>-->
</a-form> </a-form>
<!-- <div v-if="URLUserType() === 'single'" class="fixed bottom-5 left-0 right-0 text-center"> <!-- <div v-if="URLUserType() === 'single'" class="fixed bottom-5 left-0 right-0 text-center">
+1 -24
View File
@@ -22,12 +22,7 @@
v-else-if="variant !== 'desktop'" v-else-if="variant !== 'desktop'"
:src="getProjectLoginLogo(config.config?.avatar)" :src="getProjectLoginLogo(config.config?.avatar)"
alt="" alt=""
:class="[ class="z-100 self-center w-36 rounded aspect-square"
'z-100 self-center w-36',
config.config?.name === 'garoosi'
? 'h-auto object-contain mb-[60px]'
: 'rounded aspect-square',
]"
> >
<div v-if="variant !== 'desktop'" class="font-bold text-center text-xl">ثبت نام</div> <div v-if="variant !== 'desktop'" class="font-bold text-center text-xl">ثبت نام</div>
<a-form <a-form
@@ -104,14 +99,6 @@
<a-spin v-if="loading" size="small" /> <a-spin v-if="loading" size="small" />
<span v-else>ثبت نام</span> <span v-else>ثبت نام</span>
</BasicButton> </BasicButton>
<template v-if="config.config?.name === 'garoosi'">
<button type="button" class="dauth-link-btn" @click="goToEducate">
آموزش کار با نرم افزار
</button>
<button type="button" class="dauth-link-btn" @click="goToAboutUs">
درباره ما
</button>
</template>
</div> </div>
<div v-if="variant === 'desktop'" class="dauth-switch"> <div v-if="variant === 'desktop'" class="dauth-switch">
@@ -133,16 +120,6 @@
<div v-else>ثبت نام</div> <div v-else>ثبت نام</div>
</div> </div>
</BasicButton> </BasicButton>
<template v-if="config.config?.name === 'garoosi'">
<BasicButton class="w-full mt-2 !font-bold" @click="goToEducate">
<div class="flex gap-2 items-center">آموزش کار با نرم افزار</div>
</BasicButton>
<BasicButton class="mt-2 w-full" icon="" iconSize="18" @click="goToAboutUs">
<div class="flex">
<div>درباره ما</div>
</div>
</BasicButton>
</template>
<div <div
class="cursor-pointer flex gap-1 justify-center mt-5 text-xs" class="cursor-pointer flex gap-1 justify-center mt-5 text-xs"
@click="$emit('register', false)" @click="$emit('register', false)"
@@ -1,20 +1,13 @@
<template> <template>
<div class="dauth" :class="{ 'dauth--boursecu': isBoursecu }"> <div class="dauth dauth--boursecu">
<div class="dauth__layout"> <div class="dauth__layout">
<aside <aside class="dauth-brand dauth-brand--boursecu">
class="dauth-brand"
:class="{
'dauth-brand--boursecu': isBoursecu,
'dauth-brand--soroushgold': isSoroushgold,
}"
>
<div class="dauth-brand__pattern" /> <div class="dauth-brand__pattern" />
<div class="dauth-brand__content"> <div class="dauth-brand__content">
<img <img
:src="logo" :src="logo"
alt="" alt=""
class="dauth-brand__logo" class="dauth-brand__logo"
:class="{ 'dauth-brand__logo--round': !hasCustomLayout }"
> >
<h1 class="dauth-brand__title">{{ brandTitle }}</h1> <h1 class="dauth-brand__title">{{ brandTitle }}</h1>
<p v-if="tagline" class="dauth-brand__tagline">{{ tagline }}</p> <p v-if="tagline" class="dauth-brand__tagline">{{ tagline }}</p>
@@ -46,15 +39,9 @@ import { useConfigStore } from '@/store/configStore'
import { import {
getProjectDesktopLoginBrandLogo, getProjectDesktopLoginBrandLogo,
getProjectLoginTaglineLines, getProjectLoginTaglineLines,
hasCustomLoginLogoLayout,
isBoursecuProject,
isSoroushgoldProject,
} from '@/utils/config' } from '@/utils/config'
const config = useConfigStore() const config = useConfigStore()
const isBoursecu = isBoursecuProject()
const isSoroushgold = isSoroushgoldProject()
const hasCustomLayout = hasCustomLoginLogoLayout() || isSoroushgold
const version = getDisplayAppVersion(pkg.version) const version = getDisplayAppVersion(pkg.version)
const logo = computed(() => getProjectDesktopLoginBrandLogo(config.config?.avatar)) const logo = computed(() => getProjectDesktopLoginBrandLogo(config.config?.avatar))
@@ -69,20 +56,11 @@ const tagline = computed(() => {
const brandTitle = computed(() => config.config?.name_fa ?? 'خوش آمدید') const brandTitle = computed(() => config.config?.name_fa ?? 'خوش آمدید')
const features = computed(() => { const features = [
if (isBoursecu) { 'قیمت لحظه‌ای و نمودار بازار',
return [ 'مدیریت دارایی و کیف پول',
'قیمت لحظه‌ای و نمودار بازار', 'معاملات سریع و امن',
'مدیریت دارایی و کیف پول', ]
'معاملات سریع و امن',
]
}
return [
'ورود امن با پیامک یا رمز عبور',
'مدیریت یکپارچه دارایی‌ها',
'دسترسی سریع به خدمات',
]
})
</script> </script>
<style lang="less" src="../styles/desktop-auth.less"></style> <style lang="less" src="../styles/desktop-auth.less"></style>
@@ -173,27 +173,7 @@ function goToAboutUs() {
ورود با اثر انگشت ورود با اثر انگشت
</BasicButton> </BasicButton>
<template v-if="config.config?.name === 'garoosi'"> <div v-if="!isTWA() && !!setting.getSettingByKey('apk_download_link')" class="dauth-actions-row ">
<div v-if="!isTWA()" class="dauth-actions-row">
<a
class="dauth-link-btn dauth-link-btn--primary"
download="Garoosi.apk"
href="https://garoosigholaktala.ir/apk/Garoosi.apk"
>
دانلود اپ اندروید
</a>
<button type="button" class="dauth-link-btn" @click="installWebApp">
نصب نسخه وب اپ
</button>
</div>
<button type="button" class="dauth-link-btn" @click="goToEducate">
آموزش کار با نرم افزار
</button>
<button type="button" class="dauth-link-btn" @click="goToAboutUs">
درباره ما
</button>
</template>
<div v-else-if="!isTWA() && !!setting.getSettingByKey('apk_download_link')" class="dauth-actions-row ">
<a <a
class="dauth-link-btn dauth-link-btn--primary !hover:text-primary " class="dauth-link-btn dauth-link-btn--primary !hover:text-primary "
:href="setting.getSettingByKey('apk_download_link')" :href="setting.getSettingByKey('apk_download_link')"
@@ -114,15 +114,6 @@ function handleSuccessDismiss() {
<a-spin v-if="loading" size="small" /> <a-spin v-if="loading" size="small" />
<span v-else>ثبت نام</span> <span v-else>ثبت نام</span>
</BasicButton> </BasicButton>
<template v-if="config.config?.name === 'garoosi'">
<button type="button" class="dauth-link-btn" @click="goToEducate">
آموزش کار با نرم افزار
</button>
<button type="button" class="dauth-link-btn" @click="goToAboutUs">
درباره ما
</button>
</template>
</div> </div>
<div class="dauth-switch"> <div class="dauth-switch">
-47
View File
@@ -21,58 +21,23 @@
<!-- Mobile: original layout --> <!-- Mobile: original layout -->
<div v-show="!isDesktop" class="login-page flex flex-col items-center"> <div v-show="!isDesktop" class="login-page flex flex-col items-center">
<template v-if="!isBoursecu">
<div class="fixed top-0 right-0 left-0 h-1/3 w-screen flex">
<img :src="img_topGradiant" alt="" class="absolute z-10 w-screen h-full object-cover" />
<img :src="img_pattern" alt="" class="absolute w-screen h-full object-none" />
</div>
<div class="fixed bottom-0 right-0 left-0 h-1/3 w-screen">
<img :src="img_bottomGradiant" alt="" class="absolute z-10 w-screen h-full object-cover" />
<img :src="img_pattern" alt="" class="absolute w-screen h-full object-none" />
</div>
</template>
<div class="login-page__content z-1000 w-full p-5"> <div class="login-page__content z-1000 w-full p-5">
<Login v-if="!loginPage.enterType" @register="loginPage.setEnterType" /> <Login v-if="!loginPage.enterType" @register="loginPage.setEnterType" />
<Register v-else @register="loginPage.setEnterType" /> <Register v-else @register="loginPage.setEnterType" />
</div> </div>
<div v-if="isGaroosi" class="login-page__enamad z-1000">
<a
referrerpolicy="origin"
target="_blank"
href="https://trustseal.enamad.ir/?id=710578&Code=DdltBYWJgvc7LJs9eM46SEliXRRU0kSW"
>
<img
referrerpolicy="origin"
src="https://trustseal.enamad.ir/logo.aspx?id=710578&Code=DdltBYWJgvc7LJs9eM46SEliXRRU0kSW"
alt="enamad"
style="cursor: pointer"
code="DdltBYWJgvc7LJs9eM46SEliXRRU0kSW"
>
</a>
</div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'
import { useLoginPage } from './composables/useLoginPage' import { useLoginPage } from './composables/useLoginPage'
import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout' import { useDesktopBreakpoint } from '@/layouts/composables/useAppLayout'
import { isBoursecuProject } from '@/utils/config'
import { useConfigStore } from '@/store/configStore'
import Login from './components/Login.vue' import Login from './components/Login.vue'
import Register from './components/Register.vue' import Register from './components/Register.vue'
import DesktopAuthShell from './desktop/components/DesktopAuthShell.vue' import DesktopAuthShell from './desktop/components/DesktopAuthShell.vue'
import img_topGradiant from './assets/img/gr_top_white.svg'
import img_bottomGradiant from './assets/img/gr_bottom_white.svg'
import img_pattern from './assets/img/pattern.svg'
const isDesktop = useDesktopBreakpoint() const isDesktop = useDesktopBreakpoint()
const isBoursecu = isBoursecuProject()
const loginPage = useLoginPage() const loginPage = useLoginPage()
const config = useConfigStore()
const isGaroosi = computed(() => config.config?.name === 'garoosi')
</script> </script>
<style scoped> <style scoped>
@@ -98,16 +63,4 @@ const isGaroosi = computed(() => config.config?.name === 'garoosi')
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.login-page__enamad {
display: flex;
justify-content: center;
padding: 0 20px 16px;
margin-top: auto;
}
.login-page__enamad img {
max-width: 96px;
height: auto;
}
</style> </style>
@@ -1,15 +1,5 @@
<template> <template>
<div class="login-page flex items-center justify-center"> <div class="login-page flex items-center justify-center">
<template v-if="!isBoursecu">
<div class="fixed top-0 right-0 left-0 h-1/3 w-screen flex">
<img :src="img_topGradiant" alt="" class="absolute z-10 w-screen h-full object-cover" />
<img :src="img_pattern" alt="" class="absolute w-screen h-full object-none" />
</div>
<div class="fixed bottom-0 right-0 left-0 h-1/3 w-screen">
<img :src="img_bottomGradiant" alt="" class="absolute z-10 w-screen h-full object-cover" />
<img :src="img_pattern" alt="" class="absolute w-screen h-full object-none" />
</div>
</template>
<div class="z-1000 w-full p-5"> <div class="z-1000 w-full p-5">
<Login v-if="!page.enterType" key="login" @register="page.setEnterType" /> <Login v-if="!page.enterType" key="login" @register="page.setEnterType" />
<Register v-else key="register" @register="page.setEnterType" /> <Register v-else key="register" @register="page.setEnterType" />
@@ -21,13 +11,8 @@
import Login from '../components/Login.vue' import Login from '../components/Login.vue'
import Register from '../components/Register.vue' import Register from '../components/Register.vue'
import { useLoginPageContext } from '../composables/useLoginPage' import { useLoginPageContext } from '../composables/useLoginPage'
import { isBoursecuProject } from '@/utils/config'
import img_topGradiant from '../assets/img/gr_top_white.svg'
import img_bottomGradiant from '../assets/img/gr_bottom_white.svg'
import img_pattern from '../assets/img/pattern.svg'
const page = useLoginPageContext() const page = useLoginPageContext()
const isBoursecu = isBoursecuProject()
</script> </script>
<style scoped> <style scoped>