169 lines
6.9 KiB
Vue
169 lines
6.9 KiB
Vue
<!--
|
||
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>
|