feat: quotation stats

This commit is contained in:
Methapon Metanipat 2024-10-07 16:58:45 +07:00
parent c5b3e4dbd7
commit 1d73d8084e
4 changed files with 89 additions and 179 deletions

View file

@ -2,6 +2,7 @@
const props = withDefaults(
defineProps<{
branch: {
hidden?: boolean;
icon: string;
count: number;
label: string;
@ -32,7 +33,7 @@ const props = withDefaults(
<template>
<div class="row no-wrap" style="gap: var(--size-4)" :class="{ dark }">
<div
v-for="v in props.branch"
v-for="v in branch.filter((v) => !v.hidden)"
class="no-padding stat-card"
:class="{ [`stat-card__${v.color}`]: true }"
:key="v.label"

View file

@ -97,20 +97,6 @@ const tabFieldRequired = ref<{ [key: string]: (keyof CustomerBranchCreate)[] }>(
const productServiceStore = useProductServiceStore();
const router = useRouter();
type ProductGroupId = string;
const productGroup = ref<ProductGroup[]>([]);
const productList = ref<Partial<Record<ProductGroupId, Product[]>>>({});
const serviceList = ref<Partial<Record<ProductGroupId, Service[]>>>({});
type Id = string;
const product = ref<Record<Id, Product>>({});
const service = ref<Record<Id, Service>>({});
const selectedGroup = ref<ProductGroup | null>(null);
const selectedGroupSub = ref<'product' | 'service' | null>(null);
const selectedProductServiceId = ref('');
const selectedEmployee = ref<Employee[]>([]);
const pageState = reactive({
@ -127,58 +113,11 @@ const pageState = reactive({
productServiceModal: false,
});
const statData = ref<
{
icon: string;
count: number;
label: string;
color:
| 'pink'
| 'purple'
| 'green'
| 'orange'
| 'cyan'
| 'yellow'
| 'red'
| 'magenta'
| 'blue'
| 'lime'
| 'light-purple';
}[]
>([
{
icon: 'mdi-cash',
count: 0,
label: 'quotation.type.fullAmountCash',
color: 'red',
},
{
icon: 'mdi-hand-coin-outline',
count: 0,
label: 'quotation.type.installmentsCash',
color: 'blue',
},
{
icon: 'mdi-receipt-text-outline',
count: 0,
label: 'quotation.type.fullAmountBill',
color: 'lime',
},
{
icon: 'mdi-receipt-text-send-outline',
count: 0,
label: 'quotation.type.installmentsBill',
color: 'light-purple',
},
]);
const formDataCustomerBranch = ref<
CustomerBranchCreate & {
id?: string;
branchCode?: string;
codeCustomer?: string;
}
>({
const CUSTOMER_BRANCH_DEFAULT: CustomerBranchCreate & {
id?: string;
branchCode?: string;
codeCustomer?: string;
} = {
customerCode: '',
customerId: '',
legalPersonNo: '',
@ -226,58 +165,18 @@ const formDataCustomerBranch = ref<
authorizedName: '',
authorizedNameEN: '',
code: '',
});
};
const formDataCustomerBranch = ref<
CustomerBranchCreate & {
id?: string;
branchCode?: string;
codeCustomer?: string;
}
>(structuredClone(CUSTOMER_BRANCH_DEFAULT));
function setDefaultCustomerd() {
formDataCustomerBranch.value = {
customerCode: '',
customerId: '',
legalPersonNo: '',
citizenId: '',
namePrefix: '',
firstName: '',
lastName: '',
firstNameEN: '',
lastNameEN: '',
telephoneNo: '',
gender: '',
birthDate: new Date().toString(),
businessType: '',
jobPosition: '',
jobDescription: '',
payDate: '',
payDateEN: '',
wageRate: 0,
wageRateText: '',
homeCode: '',
employmentOffice: '',
employmentOfficeEN: '',
address: '',
addressEN: '',
street: '',
streetEN: '',
moo: '',
mooEN: '',
soi: '',
soiEN: '',
provinceId: '',
districtId: '',
subDistrictId: '',
contactName: '',
email: '',
contactTel: '',
officeTel: '',
agent: '',
status: 'CREATED',
customerName: '',
registerName: '',
registerNameEN: '',
registerDate: new Date(),
authorizedCapital: '',
authorizedName: '',
authorizedNameEN: '',
code: '',
};
formDataCustomerBranch.value = structuredClone(CUSTOMER_BRANCH_DEFAULT);
}
async function submitCustomer() {
@ -335,70 +234,21 @@ function triggerProductServiceDialog() {
// TODO: form and state controll
}
async function getAllProduct(
groupId: string,
opts?: { force?: false; page?: number; pageSize?: number },
) {
selectedGroupSub.value = 'product';
if (!opts?.force && productList.value[groupId] !== undefined) return;
const ret = await productServiceStore.fetchListProduct({
page: opts?.page ?? 1,
pageSize: opts?.pageSize ?? 9999,
productGroupId: groupId,
});
if (ret) productList.value[groupId] = ret.result;
}
async function getAllService(
groupId: string,
opts?: { force?: false; page?: number; pageSize?: number },
) {
selectedGroupSub.value = 'service';
if (!opts?.force && serviceList.value[groupId] !== undefined) return;
const ret = await productServiceStore.fetchListService({
page: opts?.page ?? 1,
pageSize: opts?.pageSize ?? 9999,
productGroupId: groupId,
fullDetail: true,
});
if (ret) serviceList.value[groupId] = ret.result;
}
async function getProduct(id: string, force = false) {
selectedGroupSub.value = 'product';
selectedProductServiceId.value = id;
if (!force && product.value[id] !== undefined) return;
const ret = await productServiceStore.fetchListProductById(id);
if (ret) product.value[id] = ret;
}
async function getService(id: string, force = false) {
selectedGroupSub.value = 'service';
selectedProductServiceId.value = id;
if (!force && service.value[id] !== undefined) return;
const ret = await productServiceStore.fetchListServiceById(id);
if (ret) service.value[id] = ret;
}
function convertToTree() {
// TODO: convert quotation product service data to tree
}
const quotationStore = useQuotationStore();
const {
data: quotationData,
page: quotationPage,
pageSize: quotationPageSize,
pageMax: quotationPageMax,
stats: quotationStats,
} = storeToRefs(quotationStore);
onMounted(async () => {
{
const ret = await productServiceStore.fetchListProductService({
page: 1,
pageSize: 9999,
});
if (ret) productGroup.value = ret.result;
const ret = await quotationStore.getQuotationStats();
if (ret) {
quotationStats.value = Object.assign(quotationStats.value, ret);
}
}
{
@ -517,13 +367,44 @@ watch(() => pageState.currentTab, fetchQuotationList);
<div style="display: inline-block">
<StatCardComponent
labelI18n
:branch="
pageState.currentTab === 'all'
? statData
: statData.filter((i) =>
i.label.split('.').pop()?.includes(pageState.currentTab),
)
"
:branch="[
{
icon: 'mdi-cash',
count: quotationStats.full,
label: 'quotation.type.fullAmountCash',
color: 'red',
hidden:
pageState.currentTab !== 'all' &&
pageState.currentTab !== 'fullAmountCash',
},
{
icon: 'mdi-hand-coin-outline',
count: quotationStats.split,
label: 'quotation.type.installmentsCash',
color: 'blue',
hidden:
pageState.currentTab !== 'all' &&
pageState.currentTab !== 'installmentsCash',
},
{
icon: 'mdi-receipt-text-outline',
count: quotationStats.billFull,
label: 'quotation.type.fullAmountBill',
color: 'lime',
hidden:
pageState.currentTab !== 'all' &&
pageState.currentTab !== 'fullAmountBill',
},
{
icon: 'mdi-receipt-text-send-outline',
count: quotationStats.billSplit,
label: 'quotation.type.installmentsBill',
color: 'light-purple',
hidden:
pageState.currentTab !== 'all' &&
pageState.currentTab !== 'installmentsBill',
},
]"
:dark="$q.dark.isActive"
/>
</div>

View file

@ -3,7 +3,12 @@ import { defineStore } from 'pinia';
import { api } from 'src/boot/axios';
import { Quotation, QuotationFull, QuotationPayload } from './types';
import {
Quotation,
QuotationFull,
QuotationPayload,
QuotationStats,
} from './types';
import { PaginationResult } from 'src/types';
export const useQuotationStore = defineStore('quotation-store', () => {
@ -11,6 +16,20 @@ export const useQuotationStore = defineStore('quotation-store', () => {
const page = ref<number>(1);
const pageMax = ref<number>(1);
const pageSize = ref<number>(30);
const stats = ref<QuotationStats>({
full: 0,
split: 0,
billFull: 0,
billSplit: 0,
});
async function getQuotationStats() {
const res = await api.get<QuotationStats>(`/quotation/stats`);
if (res.status < 400) {
return res.data;
}
return null;
}
async function getQuotation(id: string) {
const res = await api.get<QuotationFull>(`/quotation/${id}`);
@ -92,6 +111,8 @@ export const useQuotationStore = defineStore('quotation-store', () => {
page,
pageSize,
pageMax,
stats,
getQuotationStats,
getQuotation,
getQuotationList,
createQuotation,

View file

@ -151,6 +151,13 @@ type ServiceRelation = {
updatedByUserId: string;
};
export type QuotationStats = {
full: number;
split: number;
billFull: number;
billSplit: number;
};
export type Quotation = {
_count: { worker: number };
id: string;