feat: dashboard data bind api
This commit is contained in:
parent
9a196b7077
commit
58b5613d2c
6 changed files with 100 additions and 59 deletions
|
|
@ -1,7 +1,8 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Icon } from '@iconify/vue/dist/iconify.js';
|
import { Icon } from '@iconify/vue/dist/iconify.js';
|
||||||
|
import { formatNumberDecimal } from 'src/stores/utils';
|
||||||
|
|
||||||
defineEmits<{ (e: 'labelClick', value: string, index: number): void }>();
|
defineEmits<{ (e: 'labelClick', value: string, index: number | null): void }>();
|
||||||
|
|
||||||
withDefaults(
|
withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
|
@ -38,7 +39,7 @@ withDefaults(
|
||||||
v-if="typeof value === 'string'"
|
v-if="typeof value === 'string'"
|
||||||
@click="$emit('labelClick', value, null)"
|
@click="$emit('labelClick', value, null)"
|
||||||
>
|
>
|
||||||
{{ value }}
|
{{ formatNumberDecimal(+value, 2) }}
|
||||||
<q-tooltip v-if="tooltip" :delay="500">{{ value }}</q-tooltip>
|
<q-tooltip v-if="tooltip" :delay="500">{{ value }}</q-tooltip>
|
||||||
</span>
|
</span>
|
||||||
<span v-else :class="{ 'link cursor-pointer': clickable }">
|
<span v-else :class="{ 'link cursor-pointer': clickable }">
|
||||||
|
|
|
||||||
|
|
@ -1362,7 +1362,7 @@ export default {
|
||||||
title: 'สรุปยอดใบกำกับภาษี',
|
title: 'สรุปยอดใบกำกับภาษี',
|
||||||
caption: 'ตามจำนวนเงินรวม',
|
caption: 'ตามจำนวนเงินรวม',
|
||||||
total: 'ยอดรวมทั้งหมด',
|
total: 'ยอดรวมทั้งหมด',
|
||||||
paid: 'ชำระไม่ครบและชำระครบ',
|
paid: 'ชำระแล้ว',
|
||||||
pending: 'รอชำระ',
|
pending: 'รอชำระ',
|
||||||
cancel: 'ยกเลิก',
|
cancel: 'ยกเลิก',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,15 @@ import ChartSales from './chart/ChartSales.vue';
|
||||||
import { useNavigator } from 'src/stores/navigator';
|
import { useNavigator } from 'src/stores/navigator';
|
||||||
import { useQuotationStore } from 'src/stores/quotations';
|
import { useQuotationStore } from 'src/stores/quotations';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useReportStore } from 'src/stores/report';
|
||||||
|
import { PaymentDataStatus } from 'src/stores/payment/types';
|
||||||
|
|
||||||
// NOTE: Variable
|
// NOTE: Variable
|
||||||
const navigatorStore = useNavigator();
|
const navigatorStore = useNavigator();
|
||||||
const quotationStore = useQuotationStore();
|
const quotationStore = useQuotationStore();
|
||||||
|
const reportStore = useReportStore();
|
||||||
const { stats: quotationStats } = storeToRefs(quotationStore);
|
const { stats: quotationStats } = storeToRefs(quotationStore);
|
||||||
|
const { dataReportPayment } = storeToRefs(reportStore);
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
role: 'admin',
|
role: 'admin',
|
||||||
|
|
@ -46,6 +50,12 @@ onMounted(async () => {
|
||||||
quotationStats.value = Object.assign(quotationStats.value, ret);
|
quotationStats.value = Object.assign(quotationStats.value, ret);
|
||||||
console.log(quotationStats.value);
|
console.log(quotationStats.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const retPayment = await reportStore.getReportPayment();
|
||||||
|
|
||||||
|
if (retPayment) {
|
||||||
|
dataReportPayment.value = retPayment;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -72,27 +82,32 @@ onMounted(async () => {
|
||||||
:dark="$q.dark.isActive"
|
:dark="$q.dark.isActive"
|
||||||
text-size="10px"
|
text-size="10px"
|
||||||
:branch="[
|
:branch="[
|
||||||
{
|
|
||||||
icon: 'mdi-account-outline',
|
|
||||||
count: 0,
|
|
||||||
label: $t('dashboard.newComer'),
|
|
||||||
color: 'pink',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
icon: 'material-symbols-light:receipt-long',
|
icon: 'material-symbols-light:receipt-long',
|
||||||
count: 0,
|
count:
|
||||||
|
(quotationStats.issued || 0) +
|
||||||
|
(quotationStats.accepted || 0) +
|
||||||
|
(quotationStats.paymentInProcess || 0) +
|
||||||
|
(quotationStats.paymentSuccess || 0) +
|
||||||
|
(quotationStats.processComplete || 0) +
|
||||||
|
(quotationStats.canceled || 0),
|
||||||
label: $t('dashboard.openQuotation'),
|
label: $t('dashboard.openQuotation'),
|
||||||
color: 'cyan',
|
color: 'cyan',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'si:wallet-detailed-line',
|
icon: 'si:wallet-detailed-line',
|
||||||
count: 0,
|
count:
|
||||||
|
(quotationStats.paymentSuccess || 0) +
|
||||||
|
(quotationStats.processComplete || 0),
|
||||||
label: $t('dashboard.paidSales'),
|
label: $t('dashboard.paidSales'),
|
||||||
color: 'light-yellow',
|
color: 'light-yellow',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'fluent:money-hand-24-regular',
|
icon: 'fluent:money-hand-24-regular',
|
||||||
count: 0,
|
count:
|
||||||
|
(quotationStats.issued || 0) +
|
||||||
|
(quotationStats.accepted || 0) +
|
||||||
|
(quotationStats.paymentInProcess || 0),
|
||||||
label: $t('dashboard.pendingSales'),
|
label: $t('dashboard.pendingSales'),
|
||||||
color: 'light-purple',
|
color: 'light-purple',
|
||||||
},
|
},
|
||||||
|
|
@ -108,34 +123,57 @@ onMounted(async () => {
|
||||||
<div class="col-sm-8 col-12">
|
<div class="col-sm-8 col-12">
|
||||||
<ChartReceipt
|
<ChartReceipt
|
||||||
class="full-height"
|
class="full-height"
|
||||||
:summary="[45, 30, 10, 5]"
|
:summary="[
|
||||||
|
dataReportPayment.reduce(
|
||||||
|
(a, c) =>
|
||||||
|
a +
|
||||||
|
(c.data[PaymentDataStatus.Success] || 0) +
|
||||||
|
(c.data[PaymentDataStatus.Wait] || 0),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
dataReportPayment.reduce(
|
||||||
|
(a, c) => a + (c.data[PaymentDataStatus.Success] || 0),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
dataReportPayment.reduce(
|
||||||
|
(a, c) => a + (c.data[PaymentDataStatus.Wait] || 0),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
]"
|
||||||
|
:categories="
|
||||||
|
dataReportPayment.map((v) => v.month + ' / ' + v.year)
|
||||||
|
"
|
||||||
:series="[
|
:series="[
|
||||||
{
|
{
|
||||||
name: $t('dashboard.receipt.total'),
|
name: $t('dashboard.receipt.total'),
|
||||||
data: [45, 78, 92, 34, 67, 89, 120, 150, 180, 200, 50],
|
data: dataReportPayment.map(
|
||||||
|
(v) =>
|
||||||
|
(v.data[PaymentDataStatus.Success] || 0) +
|
||||||
|
(v.data[PaymentDataStatus.Wait] || 0),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: $t('dashboard.receipt.paid'),
|
name: $t('dashboard.receipt.paid'),
|
||||||
data: [30, 50, 70, 20, 40, 65, 80, 130, 160, 190, 210],
|
data: dataReportPayment.map(
|
||||||
|
(v) => v.data[PaymentDataStatus.Success] || 0,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: $t('dashboard.receipt.pending'),
|
name: $t('dashboard.receipt.pending'),
|
||||||
data: [10, 25, 45, 30, 50, 60, 75, 95, 110, 130, 150],
|
data: dataReportPayment.map(
|
||||||
},
|
(v) => v.data[PaymentDataStatus.Wait] || 0,
|
||||||
{
|
),
|
||||||
name: $t('dashboard.receipt.cancel'),
|
|
||||||
data: [5, 20, 15, 25, 35, 40, 55, 70, 85, 100, 300],
|
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4 col-12">
|
<!-- <div class="col-sm-4 col-12">
|
||||||
<ChartOpportunity
|
<ChartOpportunity
|
||||||
class="full-height"
|
class="full-height"
|
||||||
:labels="['1', '2', '3', '4', '5', '6', '7', '8']"
|
:labels="['1', '2', '3', '4', '5', '6', '7', '8']"
|
||||||
:series="[581, 389, 609, 581, 603, 600, 699, 347]"
|
:series="[581, 389, 609, 581, 603, 600, 699, 347]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> -->
|
||||||
<div class="col-sm-4 col-12">
|
<div class="col-sm-4 col-12">
|
||||||
<ChartQuotationStatus
|
<ChartQuotationStatus
|
||||||
v-if="quotationStats"
|
v-if="quotationStats"
|
||||||
|
|
@ -148,23 +186,21 @@ onMounted(async () => {
|
||||||
]"
|
]"
|
||||||
:series="[
|
:series="[
|
||||||
{
|
{
|
||||||
data: true
|
data: [
|
||||||
? []
|
(quotationStats.issued || 0) +
|
||||||
: [
|
(quotationStats.accepted || 0),
|
||||||
quotationStats.issued || 0,
|
(quotationStats.paymentInProcess || 0) +
|
||||||
quotationStats.accepted +
|
(quotationStats.paymentSuccess || 0),
|
||||||
quotationStats.paymentInProcess +
|
quotationStats.processComplete || 0,
|
||||||
quotationStats.paymentSuccess || 1,
|
quotationStats.canceled || 0,
|
||||||
quotationStats.processComplete || 1,
|
],
|
||||||
quotationStats.canceled || 1,
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-8 col-12">
|
<!-- <div class="col-sm-8 col-12">
|
||||||
<ChartSales class="full-height" />
|
<ChartSales class="full-height" />
|
||||||
</div>
|
</div> -->
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,20 @@ import { computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
const { locale } = useI18n({ useScope: 'global' });
|
const { locale } = useI18n({ useScope: 'global' });
|
||||||
|
|
||||||
withDefaults(
|
const prop = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
series: { name: string; data: number[] }[];
|
series: { name: string; data: number[] }[];
|
||||||
summary: number[];
|
summary: number[];
|
||||||
|
categories: string[];
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
series: () => [
|
series: () => [
|
||||||
{ name: 'series-1', data: [0, 0] },
|
{ name: 'series-1', data: [0, 0] },
|
||||||
{ name: 'series-2', data: [1, 1] },
|
{ name: 'series-2', data: [1, 1] },
|
||||||
{ name: 'series-3', data: [2, 2] },
|
{ name: 'series-3', data: [2, 2] },
|
||||||
{ name: 'series-4', data: [3, 3] },
|
|
||||||
],
|
],
|
||||||
summary: () => [0, 0, 0, 0],
|
summary: () => [0, 0, 0, 0],
|
||||||
|
categories: () => ['Jan', 'Feb', 'Mar'],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const chartOptions = computed(() => {
|
const chartOptions = computed(() => {
|
||||||
|
|
@ -50,23 +51,7 @@ const chartOptions = computed(() => {
|
||||||
position: 'right',
|
position: 'right',
|
||||||
},
|
},
|
||||||
xaxis: {
|
xaxis: {
|
||||||
categories:
|
categories: prop.categories,
|
||||||
locale.value === 'tha'
|
|
||||||
? thaiMonths
|
|
||||||
: [
|
|
||||||
'Jan',
|
|
||||||
'Feb',
|
|
||||||
'Mar',
|
|
||||||
'Apr',
|
|
||||||
'May',
|
|
||||||
'Jun',
|
|
||||||
'Jul',
|
|
||||||
'Aug',
|
|
||||||
'Sep',
|
|
||||||
'Oct',
|
|
||||||
'Nov',
|
|
||||||
'Dec',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -87,11 +72,6 @@ const detail = [
|
||||||
color: 'var(--orange-4)',
|
color: 'var(--orange-4)',
|
||||||
icon: 'material-symbols-light:credit-card-outline',
|
icon: 'material-symbols-light:credit-card-outline',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'cancel',
|
|
||||||
color: 'var(--pink-7)',
|
|
||||||
icon: 'hugeicons:wallet-remove-02',
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,13 @@ import { ref } from 'vue';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { Pagination, Status } from '../types';
|
import { Pagination, Status } from '../types';
|
||||||
import { api } from 'src/boot/axios';
|
import { api } from 'src/boot/axios';
|
||||||
import { Report, ReportProduct, ReportQuotation, ReportSale } from './types';
|
import {
|
||||||
|
Report,
|
||||||
|
ReportPayment,
|
||||||
|
ReportProduct,
|
||||||
|
ReportQuotation,
|
||||||
|
ReportSale,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
const ENDPOINT = 'report';
|
const ENDPOINT = 'report';
|
||||||
|
|
||||||
|
|
@ -48,12 +54,21 @@ export async function getReportProduct() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getReportPayment() {
|
||||||
|
const res = await api.get<ReportPayment[]>(`/${ENDPOINT}/payment`);
|
||||||
|
if (res.status < 400) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
export const useReportStore = defineStore('report-store', () => {
|
export const useReportStore = defineStore('report-store', () => {
|
||||||
const dataReportQuotation = ref<ReportQuotation[]>([]);
|
const dataReportQuotation = ref<ReportQuotation[]>([]);
|
||||||
const dataReportInvoice = ref<Report[]>([]);
|
const dataReportInvoice = ref<Report[]>([]);
|
||||||
const dataReportReceipt = ref<Report[]>([]);
|
const dataReportReceipt = ref<Report[]>([]);
|
||||||
const dataReportSale = ref<ReportSale>();
|
const dataReportSale = ref<ReportSale>();
|
||||||
const dataReportProduct = ref<ReportProduct[]>([]);
|
const dataReportProduct = ref<ReportProduct[]>([]);
|
||||||
|
const dataReportPayment = ref<ReportPayment[]>([]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dataReportQuotation,
|
dataReportQuotation,
|
||||||
|
|
@ -61,11 +76,13 @@ export const useReportStore = defineStore('report-store', () => {
|
||||||
dataReportReceipt,
|
dataReportReceipt,
|
||||||
dataReportSale,
|
dataReportSale,
|
||||||
dataReportProduct,
|
dataReportProduct,
|
||||||
|
dataReportPayment,
|
||||||
|
|
||||||
getReportQuotation,
|
getReportQuotation,
|
||||||
getReportInvoice,
|
getReportInvoice,
|
||||||
getReportReceipt,
|
getReportReceipt,
|
||||||
getReportSale,
|
getReportSale,
|
||||||
getReportProduct,
|
getReportProduct,
|
||||||
|
getReportPayment,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { QuotationStatus } from 'src/stores/quotations/types';
|
||||||
import { ProductGroup } from '../product-service/types';
|
import { ProductGroup } from '../product-service/types';
|
||||||
import { User } from '../user';
|
import { User } from '../user';
|
||||||
import { CustomerBranch } from '../customer';
|
import { CustomerBranch } from '../customer';
|
||||||
|
import { PaymentDataStatus } from '../payment/types';
|
||||||
|
|
||||||
export type ReportQuotation = {
|
export type ReportQuotation = {
|
||||||
updatedAt: Date | null;
|
updatedAt: Date | null;
|
||||||
|
|
@ -34,6 +35,12 @@ export type ReportProduct = {
|
||||||
code: string;
|
code: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ReportPayment = {
|
||||||
|
year: string;
|
||||||
|
month: string;
|
||||||
|
data: Partial<Record<PaymentDataStatus, number>>;
|
||||||
|
};
|
||||||
|
|
||||||
export type ReportSale = {
|
export type ReportSale = {
|
||||||
byCustomer: (Omit<CustomerBranch, '_count'> & { _count: number })[];
|
byCustomer: (Omit<CustomerBranch, '_count'> & { _count: number })[];
|
||||||
bySale: (User & { _count: number })[];
|
bySale: (User & { _count: number })[];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue