feat: initial commit
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
<!--
|
||||
Ported from legacy henza_pwa/src/views/user/src/components/Dashboard.vue —
|
||||
the most heavily adapted view in this port so far. Legacy's original (820
|
||||
lines) is a multi-role workbench mixing plain-customer, sales-agent
|
||||
("reagent"), and "banker" (type=1, sub-account switching) views. Kept: the
|
||||
real balance card, the nav grid to Orders/Documents/Profile/Favorites, and
|
||||
the logout/login-nudge dialogs — all backed by real data.
|
||||
|
||||
DROPPED as architecturally incompatible, not merely deferred: everything
|
||||
tied to legacy's `Customer.type`/multi-account model — "انتخاب مشتری"
|
||||
(toggleUser/hasActiveChild), "سفارش مشتریان" (ChildOrders), and
|
||||
customerChoice. In the redesigned schema (see the original migration
|
||||
audit), sales agents are AdminUser records with no customer-facing session
|
||||
at all — there is no "type" field on our Customer model, so there is
|
||||
nothing to adapt here; the underlying data model this UI assumed no longer
|
||||
exists.
|
||||
|
||||
DROPPED as no backend exists: "سفارش ساخت" (custom/pre-order manufacturing)
|
||||
— PreOrder has a DB table but no controller anywhere in apps/api. Shown as
|
||||
a disabled tile rather than a broken link.
|
||||
|
||||
REPLACED, not dropped: legacy's "گزارش حساب" van-action-sheet called out to
|
||||
the external Tahesab accounting service (hesabMe()) that the very first
|
||||
migration audit explicitly decided to retire in favor of this system's own
|
||||
internal ledger — this dashboard's balance card already shows that real,
|
||||
internal balance directly, so there's no separate popup needed.
|
||||
|
||||
DROPPED: the multi-row world gold/currency price ticker (legacy's GET
|
||||
/price, ~14 rows). The new API has one single-karat quote endpoint
|
||||
(GET /pricing/latest) — shown as one line instead of fabricating rows that
|
||||
don't exist.
|
||||
-->
|
||||
<template>
|
||||
<div class="fixed top-0 z-10 w-full">
|
||||
<AppHeader>
|
||||
<div class="flex justify-between items-center">
|
||||
<div></div>
|
||||
<h3 class="text-white bold mb-0 text-center w-full">میز کار</h3>
|
||||
<Icon v-if="session.customerId" icon="vuesax-linear:logout" @click="data.notify = true" class="cursor-pointer text-white absolute left-5" size="25" />
|
||||
</div>
|
||||
</AppHeader>
|
||||
</div>
|
||||
<div class="h-16"></div>
|
||||
|
||||
<container class="overflow-y-scroll pt-4">
|
||||
<div v-if="session.customerId">
|
||||
<div class="rounded-[10px] bg-[#F9F9F9] p-[16px] w-full mb-[16px] border-[#E5E7EB] border border-solid">
|
||||
<div class="grid grid-cols-5 items-center pb-2 mb-2 border-b-2 border-solid border-[#E5E7EB]">
|
||||
<h5 class="font-bold col-span-2 mb-0">{{ customer?.fullName }}</h5>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<div>شماره تلفن همراه</div>
|
||||
<div>{{ session.mobile }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="rounded-[10px] bg-[#F9F9F9] p-[16px] w-full mb-[16px] border-[#E5E7EB] border border-solid">
|
||||
<div class="text-center font-bold">برای استفاده از امکانات نرم افزار ابتدا وارد شوید</div>
|
||||
<div class="flex w-full justify-center mt-2" @click="router.push({ name: 'login' })">
|
||||
<Button type="primary" class="w-[17rem]">ورود/ثبت نام</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-[10px] bg-light p-[16px] w-full mb-[16px] bg-[#F9F9F9]" v-if="customer">
|
||||
<div class="flex justify-between">
|
||||
<div>
|
||||
<div class="text-[12px] mb-2">{{ remainText }}</div>
|
||||
<div class="font-bold">{{ Math.abs(parseFloat(customer.goldDebtGrams)) }} گرم</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-[12px] mb-2">بدهی ریالی</div>
|
||||
<div class="font-bold">{{ priceUnit(customer.fiatDebtRial) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-4 gap-6 my-4">
|
||||
<div v-for="item in list" :key="item.id" class="grid col-span-1 cursor-pointer" @click="goToDetails(item.id)">
|
||||
<div class="border border-solid border-[#E5E7EB] bg-[#F9F9F9] rounded-[5px] flex justify-center items-center py-4" :class="{ 'opacity-40': item.disabled }">
|
||||
<Icon :icon="item.icon" />
|
||||
</div>
|
||||
<div class="text-center text-[14px] font-medium pt-2">{{ item.title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-[10px] text-secondary col-span-6 mb-24" v-if="goldPrice">
|
||||
<div class="flex items-center justify-between bg-[#1C2D56] text-[#ffffff] p-3 rounded">
|
||||
<div class="font-bold text-[16px]">نرخ طلای ۱۸ عیار</div>
|
||||
<div>{{ jdate(goldPrice.quotedAt) }}</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 mt-2">
|
||||
<div class="bg-[#F9F9F9] rounded flex justify-between items-center p-2">
|
||||
<div class="flex items-center">
|
||||
<div class="bg-white rounded w-9 h-9 flex justify-center items-center">
|
||||
<Icon class="text-primary" icon="vuesax-linear:number-18" />
|
||||
</div>
|
||||
<div class="text-[#1C2D56] text-[15px] font-semibold mr-3">هر گرم طلای ۱۸ عیار</div>
|
||||
</div>
|
||||
<div class="text-[#1C2D56] text-[18px] font-semibold flex items-center">
|
||||
{{ price(goldPrice.pricePerGramRial) }}
|
||||
<div class="text-xs text-[#8D6A46] pr-0.5 font-medium">ریال</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogVant :timeOut="-1" v-model:show="data.notify" :closeOnClickOverlay="true">
|
||||
<template v-slot:img>
|
||||
<img :src="exitImage" alt="" class="opacity-70 mb-8" />
|
||||
</template>
|
||||
<template v-slot:title>آیا می خواهید از برنامه خارج شوید؟</template>
|
||||
<template v-slot:button>
|
||||
<div class="text-center text-[#1C2D56]" @click="logOut">بله</div>
|
||||
<div class="text-center text-[#6b7280]" @click="data.notify = false">بی خیال</div>
|
||||
</template>
|
||||
</DialogVant>
|
||||
<DialogVant :timeOut="-1" v-model:show="data.loginNotify" :closeOnClickOverlay="true">
|
||||
<template v-slot:img>
|
||||
<img :src="loginDialogImage" alt="" class="opacity-80" />
|
||||
</template>
|
||||
<template v-slot:title>
|
||||
<div class="p-8 text-justify">برای دسترسی به تمامی امکانات نرم افزار هنزاگلد، ابتدا گزینه ورود/ثبت نام را لمس کنید و در صورت نداشتن حساب کاربری، ثبت نام نمایید.</div>
|
||||
</template>
|
||||
<template v-slot:button>
|
||||
<div class="text-center text-[#1C2D56]" @click="router.push({ name: 'login' })">ورود/ثبت نام</div>
|
||||
<div class="text-center text-danger" @click="data.loginNotify = false">بیخیال</div>
|
||||
</template>
|
||||
</DialogVant>
|
||||
</container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Button } from 'ant-design-vue'
|
||||
import Icon from '../../components/icon/Icon.vue'
|
||||
import AppHeader from '../../components/AppHeader.vue'
|
||||
import DialogVant from '../../components/DialogVant.vue'
|
||||
import exitImage from '../../assets/img/empty/dialogEmpty5.svg'
|
||||
import loginDialogImage from '../../assets/img/empty/dialogEmpty1.svg'
|
||||
import { price, priceUnit } from '../../utils/filters'
|
||||
import { jdate } from '../../utils/filters'
|
||||
import { useSessionStore } from '../../stores/session'
|
||||
import { api, type CustomerDetail, type GoldPriceQuote } from '../../api/client'
|
||||
|
||||
const router = useRouter()
|
||||
const session = useSessionStore()
|
||||
|
||||
const customer = ref<CustomerDetail | null>(null)
|
||||
const goldPrice = ref<GoldPriceQuote | null>(null)
|
||||
|
||||
const remainText = computed(() => (customer.value && parseFloat(customer.value.goldDebtGrams) > 0 ? 'بدهکار گرمی' : 'بستانکار گرمی'))
|
||||
|
||||
const data = reactive({ notify: false, loginNotify: false })
|
||||
|
||||
const list = [
|
||||
{ id: 0, icon: 'vuesax-linear:box', title: 'سفارشات', disabled: false },
|
||||
{ id: 1, icon: 'vuesax-linear:receipt-text', title: 'اسناد', disabled: false },
|
||||
{ id: 2, icon: 'vuesax-linear:user', title: 'پروفایل', disabled: false },
|
||||
{ id: 3, icon: 'vuesax-linear:heart', title: 'علاقمندی', disabled: false },
|
||||
]
|
||||
|
||||
function goToDetails(id: number) {
|
||||
if (!session.customerId) {
|
||||
data.loginNotify = true
|
||||
return
|
||||
}
|
||||
if (id === 0) router.push({ name: 'ordersList' })
|
||||
if (id === 1) router.push({ name: 'transactionsList' })
|
||||
if (id === 2) router.push({ name: 'profileData' })
|
||||
if (id === 3) router.push({ name: 'favoritesList' })
|
||||
}
|
||||
|
||||
function logOut() {
|
||||
session.logout()
|
||||
data.notify = false
|
||||
router.push({ name: 'home' })
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (session.customerId) {
|
||||
customer.value = await api.getCustomer(session.customerId)
|
||||
}
|
||||
try {
|
||||
goldPrice.value = await api.getLatestGoldPrice()
|
||||
} catch {
|
||||
goldPrice.value = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user