105 lines
3.9 KiB
Vue
105 lines
3.9 KiB
Vue
<script setup lang="ts">
|
||||
|
|
import { computed, onMounted, ref } from 'vue';
|
|||
|
|
import { RouterLink } from 'vue-router';
|
|||
|
|
import { api } from '@/lib/api';
|
|||
|
|
import { formatMoney } from '@/lib/money';
|
|||
|
|
import { formatJalaliNumeric, toFaDigits } from '@/lib/jalali';
|
|||
|
|
import UiButton from '@/components/ui/UiButton.vue';
|
|||
|
|
import type { CommissionEntryDto } from '@mento/shared';
|
|||
|
|
|
|||
|
|
const loading = ref(false);
|
|||
|
|
const error = ref('');
|
|||
|
|
const items = ref<CommissionEntryDto[]>([]);
|
|||
|
|
|
|||
|
|
const posted = computed(() => items.value.filter((r) => r.status === 'posted'));
|
|||
|
|
const total = computed(() => posted.value.reduce((sum, r) => sum + r.amount, 0));
|
|||
|
|
|
|||
|
|
async function load() {
|
|||
|
|
loading.value = true;
|
|||
|
|
error.value = '';
|
|||
|
|
try {
|
|||
|
|
const { data } = await api.get<CommissionEntryDto[]>('/commissions');
|
|||
|
|
items.value = data;
|
|||
|
|
} catch {
|
|||
|
|
error.value = 'بارگذاری پورسانتها ناموفق بود.';
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
onMounted(load);
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<section class="page" dir="rtl">
|
|||
|
|
<header class="head">
|
|||
|
|
<div>
|
|||
|
|
<h1>پورسانت فروش</h1>
|
|||
|
|
<p class="sub">با صدور هر فاکتور برای کارشناس مالک فاکتور ثبت میشود</p>
|
|||
|
|
</div>
|
|||
|
|
<UiButton variant="ghost" type="button" :disabled="loading" @click="load">
|
|||
|
|
بروزرسانی
|
|||
|
|
</UiButton>
|
|||
|
|
</header>
|
|||
|
|
|
|||
|
|
<article class="kpi">
|
|||
|
|
<span>جمع پورسانت ثبتشده</span>
|
|||
|
|
<strong>{{ formatMoney(total) }}</strong>
|
|||
|
|
<small>{{ toFaDigits(String(posted.length)) }} فاکتور</small>
|
|||
|
|
</article>
|
|||
|
|
|
|||
|
|
<p v-if="error" class="error">{{ error }}</p>
|
|||
|
|
<p v-else-if="loading && !items.length" class="muted">در حال بارگذاری…</p>
|
|||
|
|
<p v-else-if="!items.length" class="muted">هنوز پورسانتی ثبت نشده.</p>
|
|||
|
|
<div v-else class="table-wrap">
|
|||
|
|
<table>
|
|||
|
|
<thead>
|
|||
|
|
<tr>
|
|||
|
|
<th>کارشناس</th>
|
|||
|
|
<th>فاکتور</th>
|
|||
|
|
<th>درصد</th>
|
|||
|
|
<th>مبلغ</th>
|
|||
|
|
<th>وضعیت</th>
|
|||
|
|
<th>تاریخ</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr v-for="row in items" :key="row.id">
|
|||
|
|
<td>{{ row.userName }}</td>
|
|||
|
|
<td>
|
|||
|
|
<RouterLink :to="{ name: 'invoice-detail', params: { id: row.invoiceId } }">
|
|||
|
|
{{ row.invoiceNumber }}
|
|||
|
|
</RouterLink>
|
|||
|
|
<small>{{ row.invoiceTitle }}</small>
|
|||
|
|
</td>
|
|||
|
|
<td>{{ toFaDigits(String(row.percent)) }}٪</td>
|
|||
|
|
<td>{{ formatMoney(row.amount, row.currency) }}</td>
|
|||
|
|
<td>{{ row.status === 'posted' ? 'ثبتشده' : 'باطل' }}</td>
|
|||
|
|
<td>{{ formatJalaliNumeric(new Date(row.createdAt)) }}</td>
|
|||
|
|
</tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.page { display: grid; gap: 1rem; }
|
|||
|
|
.head { display: flex; justify-content: space-between; gap: 1rem; align-items: flex-start; }
|
|||
|
|
h1 { margin: 0; font-size: 1.25rem; }
|
|||
|
|
.sub, .muted { margin: 0.25rem 0 0; color: var(--muted); font-size: 0.88rem; }
|
|||
|
|
.error { color: var(--danger, #dc2626); font-weight: 600; }
|
|||
|
|
.kpi {
|
|||
|
|
background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
|
|||
|
|
padding: 1rem 1.1rem; width: fit-content; min-width: 16rem;
|
|||
|
|
}
|
|||
|
|
.kpi span, .kpi small { display: block; color: var(--muted); font-size: 0.8rem; }
|
|||
|
|
.kpi strong { display: block; margin: 0.25rem 0; font-size: 1.15rem; }
|
|||
|
|
.table-wrap { overflow: auto; background: #fff; border: 1px solid var(--line); border-radius: var(--radius); }
|
|||
|
|
table { width: 100%; border-collapse: collapse; min-width: 720px; }
|
|||
|
|
th, td { padding: 0.7rem 0.65rem; border-bottom: 1px solid #eef2f7; text-align: right; vertical-align: top; }
|
|||
|
|
th { background: #f8fafc; color: #64748b; font-size: 0.82rem; }
|
|||
|
|
td small { display: block; color: var(--muted); font-size: 0.78rem; }
|
|||
|
|
a { color: #0f766e; font-weight: 700; }
|
|||
|
|
</style>
|