feat: initial commit
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
<!--
|
||||
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>
|
||||
Reference in New Issue
Block a user