Files
tehran_taj/apps/user-app/src/views/user/TransactionsView.vue
T
2026-09-09 06:46:27 +03:30

169 lines
6.9 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
Ported from legacy henza_pwa/src/views/user/src/components/transactions.vue
("اسناد" — financial documents). Replaces the English AccountView scaffold
that had no legacy counterpart.
Data source: legacy listed `Cash` rows from POST /get-my-findoc. The new
system's equivalent is LedgerEntry, already exposed by
GET /customers/:id/ledger-entries, so the list is real data.
Legacy's colour/icon logic is preserved exactly: `hasFactor(item)` was
`item.type != 9` (type 9 = سفارش, the debt-creating order entry) → green
#F0FDF4/#22C55E + add-square for everything else, red #FEF2F2/#F44336 +
minus-square for orders. That maps 1:1 onto our CREDIT/DEBIT direction,
since an ORDER_STOCK entry is exactly the DEBIT case.
DROPPED: the "دریافت فایل" per-document download — legacy's getFile() hit the
old Laravel host; there is no document-file endpoint in the new API.
-->
<template>
<div class="fixed top-0 z-10 w-full">
<AppHeader>
<div class="flex justify-between items-center">
<Icon icon="vuesax-linear:arrow-right-1" class="cursor-pointer text-white" size="25" @click="router.push({ name: 'userPage::hasSubRoute' })" />
<h3 class="text-white bold mb-0">اسناد</h3>
<div></div>
</div>
</AppHeader>
</div>
<div class="h-24 bg-[#F9FAFB]"></div>
<container class="overflow-y-auto bg-[#F9FAFB]">
<Spinner :spinning="loading" tip="درحال دریافت اطلاعات" cls="no-scrollbar overflow-auto">
<div
v-if="entries.length"
v-for="item in entries"
:key="item.id"
@click="openEntry(item)"
class="rounded-[10px] border-[#E8EAEE] py-[16px] px-[14px] w-full mb-[16px] grid grid-cols-8 items-center cursor-pointer bg-white"
>
<div class="col-span-1 items-end h-full">
<Icon
:icon="getIcon(item)"
:style="{ background: getColor(item), color: getColor2(item) }"
class="rounded-full p-[10px] ml-2 h-[40px] w-[40px]"
size="20"
/>
</div>
<div class="col-span-7 pr-1 text-primary mt-2">
<div class="flex justify-between items-center">
<div class="font-bold mb-2">{{ item.note || '- -' }}</div>
<div
class="rounded-[5px] px-[20px] py-[4px] w-fit text-[16px] mb-2 font-semibold"
:style="{ background: getColor(item), color: getColor2(item) }"
>
{{ SOURCE_LABELS[item.sourceType] ?? item.sourceType }}
</div>
</div>
<div class="flex justify-between items-center">
<div class="text-[14px] font-semibold mb-2">{{ formatAmount(item) }}</div>
<div class="h-10"></div>
</div>
<div class="mb-2 text-[14px] text-grey font-semibold">{{ jdate(item.createdAt) }}</div>
<div class="flex justify-between pt-1">
<div class="text-[12px] font-bold">{{ item.confirmed ? 'تایید شده' : 'در انتظار تایید' }}</div>
<Icon icon="vuesax-linear:arrow-left-1" class="cursor-pointer text-primary" size="20" />
</div>
</div>
</div>
<EmptyState v-else :image="transactionEmpty" />
</Spinner>
<Drawer
v-model:visible="showPopUp"
placement="bottom"
v-if="activeEntry"
:closable="false"
height="450px"
:class="['henza-rounded-drawer rtl', 'bg-white', 'text-primary']"
:bodyStyle="{ display: 'flex', flexDirection: 'column' }"
>
<div class="w-full h-full flex flex-col">
<div class="pb-[10px] border-b border-solid border-[#E5E7EB] flex text-[16px] font-bold">جزئیات سند</div>
<div class="flex-1 py-[10px] overflow-auto no-scrollbar">
<div class="flex justify-between mb-4">
<div class="text-grey">نوع سند :</div>
<div class="font-semibold">{{ SOURCE_LABELS[activeEntry.sourceType] ?? activeEntry.sourceType }}</div>
</div>
<div class="flex justify-between mb-4">
<div class="text-grey">مبلغ :</div>
<div class="font-semibold">{{ formatAmount(activeEntry) }}</div>
</div>
<div class="flex justify-between mb-4">
<div class="text-grey">وضعیت :</div>
<div class="font-semibold">{{ activeEntry.confirmed ? 'تایید شده' : 'در انتظار تایید' }}</div>
</div>
<div class="flex justify-between mb-4">
<div class="text-grey">تاریخ :</div>
<div class="font-semibold">{{ jdate(activeEntry.createdAt) }}</div>
</div>
<div class="flex justify-between mb-4">
<div class="text-grey">توضیحات :</div>
<div class="font-semibold">{{ activeEntry.note || '- -' }}</div>
</div>
</div>
</div>
</Drawer>
</container>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { Drawer } from 'ant-design-vue'
import Icon from '../../components/icon/Icon.vue'
import AppHeader from '../../components/AppHeader.vue'
import Spinner from '../../components/Spinner.vue'
import EmptyState from '../../components/EmptyState.vue'
import transactionEmpty from '../../assets/img/empty/transactionEmpty.svg'
import { jdate, price } from '../../utils/filters'
import { useSessionStore } from '../../stores/session'
import { api, type LedgerEntrySummary } from '../../api/client'
const SOURCE_LABELS: Record<string, string> = {
ORDER_STOCK: 'سفارش',
ORDER_PRE_ORDER: 'سفارش ساخت',
REPAYMENT_MELTED_GOLD: 'طلای آب‌شده',
REPAYMENT_TRANSFER: 'حواله',
REPAYMENT_CHEQUE: 'چک',
REPAYMENT_CASH: 'پولی',
WALLET_BONUS: 'پاداش',
ADJUSTMENT: 'اصلاحیه',
}
const router = useRouter()
const session = useSessionStore()
const loading = ref(false)
const entries = ref<LedgerEntrySummary[]>([])
const showPopUp = ref(false)
const activeEntry = ref<LedgerEntrySummary | null>(null)
// legacy hasFactor(item) === item.type != 9 (9 = سفارش/order). Our DEBIT
// entries are exactly that case.
const isCredit = (item: LedgerEntrySummary) => item.direction === 'CREDIT'
const getIcon = (item: LedgerEntrySummary) => (isCredit(item) ? 'vuesax-linear:add-square' : 'vuesax-linear:minus-square')
const getColor = (item: LedgerEntrySummary) => (isCredit(item) ? '#F0FDF4' : '#FEF2F2')
const getColor2 = (item: LedgerEntrySummary) => (isCredit(item) ? '#22C55E' : '#F44336')
const formatAmount = (item: LedgerEntrySummary) =>
item.currency === 'GOLD_GRAM' ? `${item.amount} گرم` : `${price(item.amount)} ریال`
const openEntry = (item: LedgerEntrySummary) => {
activeEntry.value = item
showPopUp.value = true
}
onMounted(async () => {
if (!session.customerId) return
loading.value = true
try {
entries.value = await api.getLedgerEntries(session.customerId)
} finally {
loading.value = false
}
})
</script>