Add support chat, piggy funds, PWA banner, notification/SSE sync, and messenger legacy guards while keeping boursecu theme; also balance login panels and unblock login navigation. Co-authored-by: Cursor <cursoragent@cursor.com>
281 lines
8.7 KiB
Vue
281 lines
8.7 KiB
Vue
<template>
|
||
<div class="piggy-page">
|
||
<div class="piggy-page__inner">
|
||
<header class="piggy-bar">
|
||
<button type="button" class="piggy-bar__btn" aria-label="بازگشت" @click="goBack">
|
||
<BasicIcon icon="vuesax-linear:arrow-right-1" size="22" />
|
||
</button>
|
||
<h1 class="piggy-bar__title">قلکها</h1>
|
||
<button type="button" class="piggy-bar__btn piggy-bar__btn--primary" aria-label="قلک جدید" @click="goAdd">
|
||
<BasicIcon icon="vuesax-linear:add" size="20" />
|
||
</button>
|
||
</header>
|
||
|
||
<a-spin :spinning="busy">
|
||
<section class="piggy-summary">
|
||
<div class="piggy-summary__card">
|
||
<div class="piggy-summary__row">
|
||
<div class="min-w-0">
|
||
<p class="piggy-summary__label">جمع موجودی داخل قلکها</p>
|
||
<p class="piggy-summary__value" dir="ltr">
|
||
{{ formatPiggyAmount(totalGold) }}
|
||
<span class="piggy-summary__unit">گرم</span>
|
||
</p>
|
||
</div>
|
||
<div class="piggy-summary__badge">
|
||
<BasicIcon icon="vuesax-linear:safe-home" size="22" />
|
||
</div>
|
||
</div>
|
||
<div class="piggy-summary__meta">
|
||
<span>{{ rows.length }} قلک</span>
|
||
<span class="piggy-summary__dot" />
|
||
<span>{{ groups.length }} محصول</span>
|
||
</div>
|
||
</div>
|
||
|
||
<BasicButton class="!w-full mt-3" type="primary" size="middle" block @click="goAdd">
|
||
<div class="flex items-center justify-center gap-2">
|
||
<BasicIcon icon="vuesax-linear:add-circle" size="18" />
|
||
<span>{{ rows.length ? 'افزودن قلک' : 'ساخت اولین قلک' }}</span>
|
||
</div>
|
||
</BasicButton>
|
||
</section>
|
||
|
||
<section class="piggy-body">
|
||
<div v-if="!rows.length" class="piggy-empty">
|
||
<div class="piggy-empty__icon">
|
||
<BasicIcon icon="vuesax-linear:safe-home" size="36" />
|
||
</div>
|
||
<p class="piggy-empty__title">قلکی ندارید</p>
|
||
<p class="piggy-empty__desc">
|
||
برای جدا کردن بخشی از موجودی هر محصول (مثلاً آبشده یا سکه) یک قلک بسازید.
|
||
</p>
|
||
</div>
|
||
|
||
<div v-else class="piggy-groups">
|
||
<div v-for="group in groups" :key="group.productId" class="piggy-group">
|
||
<div class="piggy-group__head">
|
||
<div class="min-w-0">
|
||
<p class="piggy-group__name">{{ group.productTitle }}</p>
|
||
<p class="piggy-group__count">{{ group.funds.length }} قلک</p>
|
||
</div>
|
||
<p class="piggy-group__total" dir="ltr">
|
||
{{ formatPiggyAmount(group.totalGold) }}
|
||
<span>گرم</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="piggy-group__list">
|
||
<button
|
||
v-for="item in group.funds"
|
||
:key="item.id"
|
||
type="button"
|
||
class="piggy-row"
|
||
@click="openInfo(item.id!)"
|
||
>
|
||
<div class="piggy-row__icon">
|
||
<BasicIcon :icon="piggyIconName(item.title)" size="20" />
|
||
</div>
|
||
<div class="piggy-row__main">
|
||
<div class="piggy-row__title">{{ item.title || 'بدون عنوان' }}</div>
|
||
<div class="piggy-row__sub">کد {{ item.id }}</div>
|
||
</div>
|
||
<div class="piggy-row__amount" dir="ltr">
|
||
<strong>{{ formatPiggyAmount(fundWalletGold(item)) }}</strong>
|
||
<span>گرم</span>
|
||
</div>
|
||
<BasicIcon class="piggy-row__chevron" icon="vuesax-linear:arrow-left-1" size="16" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</a-spin>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, onMounted, ref } from 'vue'
|
||
import router from '@/router'
|
||
import { useFundUserStore } from '@/store/fundStore'
|
||
import { useSettingsStore } from '@/store/settingsStore'
|
||
import { message } from 'ant-design-vue'
|
||
import type { Fund } from '@/models/fund'
|
||
import {
|
||
formatPiggyAmount,
|
||
fundWalletGold,
|
||
groupFundsByProduct,
|
||
piggyIconName,
|
||
} from './piggyIcons'
|
||
|
||
const fundStore = useFundUserStore()
|
||
const settingsStore = useSettingsStore()
|
||
const busy = ref(false)
|
||
|
||
const rows = computed(() => (fundStore.myPiggyFunds?.rows ?? []) as Fund[])
|
||
const groups = computed(() => groupFundsByProduct(rows.value))
|
||
const totalGold = computed(() => rows.value.reduce((sum, item) => sum + fundWalletGold(item), 0))
|
||
|
||
function goBack() {
|
||
router.back()
|
||
}
|
||
function goAdd() {
|
||
router.push({ name: 'piggy_add' })
|
||
}
|
||
function openInfo(id: number) {
|
||
router.push({ name: 'piggy_info', params: { id: String(id) } })
|
||
}
|
||
|
||
onMounted(async () => {
|
||
await settingsStore.fetchSettings()
|
||
if (!settingsStore.isSettingTruthy('enable_product_funds')) {
|
||
message.warning('قابلیت قلک غیرفعال است')
|
||
router.replace({ name: 'user' })
|
||
return
|
||
}
|
||
try {
|
||
busy.value = true
|
||
await fundStore.getMyPiggyFunds()
|
||
} finally {
|
||
busy.value = false
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.piggy-page {
|
||
box-sizing: border-box;
|
||
min-height: 100%;
|
||
width: 100%;
|
||
background: #f9fafb;
|
||
padding: 0 12px calc(var(--app-bottom-chrome-height, 108px) + 1rem);
|
||
}
|
||
.piggy-page__inner {
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
max-width: 26rem;
|
||
margin-inline: auto;
|
||
}
|
||
.piggy-bar {
|
||
@apply sticky top-0 z-20 flex items-center justify-between bg-white px-3 py-3;
|
||
box-shadow: 0 1px 0 rgba(15, 23, 42, 0.06);
|
||
}
|
||
.piggy-bar__title {
|
||
@apply m-0 truncate px-2 text-base font-bold text-gray-900;
|
||
}
|
||
.piggy-bar__btn {
|
||
@apply flex h-9 w-9 shrink-0 items-center justify-center rounded-full border-none bg-gray-100 text-gray-800;
|
||
}
|
||
.piggy-bar__btn--primary {
|
||
@apply bg-primary text-white;
|
||
}
|
||
.piggy-summary {
|
||
@apply px-3 pt-4 sm:px-4;
|
||
}
|
||
.piggy-summary__card {
|
||
@apply rounded-2xl bg-primary p-4 text-white;
|
||
}
|
||
.piggy-summary__row {
|
||
@apply flex items-start justify-between gap-3;
|
||
}
|
||
.piggy-summary__label {
|
||
@apply m-0 text-sm text-white/80;
|
||
}
|
||
.piggy-summary__value {
|
||
@apply m-0 mt-1 flex flex-wrap items-baseline gap-2 text-2xl font-bold sm:text-3xl;
|
||
}
|
||
.piggy-summary__unit {
|
||
@apply text-sm font-medium text-white/75;
|
||
}
|
||
.piggy-summary__badge {
|
||
@apply flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-white/15;
|
||
}
|
||
.piggy-summary__meta {
|
||
@apply mt-4 flex flex-wrap items-center gap-2 text-xs text-white/75;
|
||
}
|
||
.piggy-summary__dot {
|
||
@apply h-1 w-1 rounded-full bg-white/50;
|
||
}
|
||
.piggy-body {
|
||
@apply px-3 pt-4 sm:px-4;
|
||
}
|
||
.piggy-groups {
|
||
@apply flex flex-col gap-4;
|
||
}
|
||
.piggy-group {
|
||
@apply overflow-hidden rounded-2xl bg-white;
|
||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||
}
|
||
.piggy-group__head {
|
||
@apply flex items-center justify-between gap-3 border-0 border-b border-solid border-gray-100 px-3 py-3 sm:px-4;
|
||
}
|
||
.piggy-group__name {
|
||
@apply m-0 truncate text-sm font-bold text-gray-900;
|
||
}
|
||
.piggy-group__count {
|
||
@apply m-0 mt-0.5 text-[11px] text-gray-500;
|
||
}
|
||
.piggy-group__total {
|
||
@apply m-0 flex shrink-0 items-baseline gap-1 text-sm font-bold text-primary;
|
||
}
|
||
.piggy-group__total span {
|
||
@apply text-[11px] font-medium text-gray-500;
|
||
}
|
||
.piggy-group__list {
|
||
@apply flex flex-col;
|
||
}
|
||
.piggy-row {
|
||
@apply flex w-full min-w-0 items-center gap-2 border-0 border-b border-solid border-gray-50 bg-transparent px-2.5 py-3 text-right last:border-b-0 sm:gap-3 sm:px-3;
|
||
}
|
||
.piggy-row__icon {
|
||
@apply flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-gray-50 text-primary sm:h-10 sm:w-10;
|
||
}
|
||
.piggy-row__main {
|
||
@apply min-w-0 flex-1;
|
||
}
|
||
.piggy-row__title {
|
||
@apply truncate text-sm font-bold text-gray-900;
|
||
}
|
||
.piggy-row__sub {
|
||
@apply mt-0.5 text-[11px] text-gray-400;
|
||
}
|
||
.piggy-row__amount {
|
||
@apply flex shrink-0 flex-col items-end leading-tight;
|
||
}
|
||
.piggy-row__amount strong {
|
||
@apply text-sm font-bold text-gray-900;
|
||
}
|
||
.piggy-row__amount span {
|
||
@apply text-[11px] text-gray-500;
|
||
}
|
||
.piggy-row__chevron {
|
||
@apply shrink-0 text-gray-300;
|
||
}
|
||
.piggy-empty {
|
||
@apply rounded-2xl bg-white px-5 py-10 text-center;
|
||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05);
|
||
}
|
||
.piggy-empty__icon {
|
||
@apply mx-auto mb-3 flex h-16 w-16 items-center justify-center rounded-2xl bg-gray-50 text-primary;
|
||
}
|
||
.piggy-empty__title {
|
||
@apply m-0 text-base font-bold text-gray-900;
|
||
}
|
||
.piggy-empty__desc {
|
||
@apply m-0 mt-2 text-sm leading-6 text-gray-500;
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
.piggy-page {
|
||
padding: 16px 16px 2rem;
|
||
}
|
||
.piggy-page__inner {
|
||
max-width: 28rem;
|
||
}
|
||
.piggy-bar {
|
||
border-radius: 12px;
|
||
}
|
||
}
|
||
</style>
|