diff --git a/src/pages/05_quotation/MainPage.vue b/src/pages/05_quotation/MainPage.vue index 654efc25..d5e53ca1 100644 --- a/src/pages/05_quotation/MainPage.vue +++ b/src/pages/05_quotation/MainPage.vue @@ -19,6 +19,7 @@ import { CustomerBranchCreate } from 'stores/customer/types'; import { Employee } from 'src/stores/employee/types'; // NOTE: Import Components +import TreeView from 'src/components/shared/TreeView.vue'; import QuotationCard from 'src/components/05_quotation/QuotationCard.vue'; import PaginationComponent from 'src/components/PaginationComponent.vue'; import StatCardComponent from 'src/components/StatCardComponent.vue'; @@ -26,7 +27,6 @@ import ButtonAddComponent from 'components/ButtonAddCompoent.vue'; import FormAbout from 'src/components/05_quotation/FormAbout.vue'; import SelectZone from 'src/components/shared/SelectZone.vue'; import PersonCard from 'src/components/shared/PersonCard.vue'; -import ProductServiceForm from './ProductServiceForm.vue'; import CreateButton from 'src/components/AddButton.vue'; import ItemCard from 'src/components/ItemCard.vue'; import DialogForm from 'components/DialogForm.vue'; @@ -34,11 +34,6 @@ import { AddButton } from 'src/components/button'; import SideMenu from 'components/SideMenu.vue'; import { dialogCreateCustomerItem } from 'src/pages/03_customer-management/constant'; -import { - ProductGroup, - Product, - Service, -} from 'src/stores/product-service/types'; import { SaveButton } from 'components/button'; @@ -54,6 +49,7 @@ import { useEmployeeForm, } from 'src/pages/03_customer-management/form'; import QuotationView from './QuotationView.vue'; +import { quotationProductTree } from './utils'; const quotationFormStore = useQuotationForm(); const customerFormStore = useCustomerForm(); @@ -633,6 +629,38 @@ watch(() => pageState.currentTab, fetchQuotationList); /> + ( + + ) @@ -1056,33 +1084,6 @@ watch(() => pageState.currentTab, fetchQuotationList); - - diff --git a/src/pages/05_quotation/utils.ts b/src/pages/05_quotation/utils.ts new file mode 100644 index 00000000..41a6c31e --- /dev/null +++ b/src/pages/05_quotation/utils.ts @@ -0,0 +1,106 @@ +import { QuotationFull } from 'src/stores/quotations/types'; + +export type ProductTree = { + id: string; + title: string; + subtitle: string; + checked: boolean; + opened: boolean; + icon: string; + fg: string; + bg: string; + value?: { + vat: number; + pricePerUnit: number; + discount: number; + amount: number; + serviceId?: string; + service?: QuotationFull['productServiceList'][number]['service']; + workId: string; + work?: QuotationFull['productServiceList'][number]['work']; + productId: string; + product: QuotationFull['productServiceList'][number]['product']; + }; + children?: ProductTree; +}[]; + +export function quotationProductTree( + list: { + service?: QuotationFull['productServiceList'][number]['service']; + work?: QuotationFull['productServiceList'][number]['work']; + product: QuotationFull['productServiceList'][number]['product']; + }[], +): ProductTree { + const ret: ProductTree = []; + + for (let i = 0; i < list.length; i++) { + const current = list[i]; + + if (ret.find((v) => v.id === current.service?.id)) continue; + + if (current.service && current.work) { + const service = current.service; + + ret.push({ + id: service.id, + title: service.name, + subtitle: service.code, + opened: service.work.length > 0, + checked: true, + children: service.work.flatMap((work) => { + const mapper = ( + relation: (typeof work)['productOnWork'][number], + ) => ({ + id: relation.product.id, + title: relation.product.name, + subtitle: relation.product.code, + opened: true, + checked: !!list.find( + (v) => + v.service?.id === service.id && + v.work?.id === work.id && + v.product.id === relation.product.id, + ), + icon: 'mdi-shopping-outline', + bg: 'hsla(var(--teal-10-hsl)/0.1)', + fg: 'var(--teal-10)', + }); + + if (!!work.name) { + return { + id: work.id, + title: work.name, + subtitle: ' ', + opened: true, + checked: !!list.find( + (v) => v.service?.id === service.id && v.work?.id === work.id, + ), + children: work.productOnWork.map(mapper), + icon: 'mdi-briefcase-outline', + bg: 'hsla(var(--violet-11-hsl)/0.1)', + fg: 'var(--violet-11)', + }; + } + + return work.productOnWork.map(mapper); + }), + icon: 'mdi-server-outline', + bg: 'hsla(var(--orange-5-hsl)/0.1)', + fg: 'var(--orange-5)', + }); + } else { + ret.push({ + id: current.product.id, + title: current.product.name, + subtitle: current.product.code, + opened: true, + checked: true, + icon: 'mdi-shopping-outline', + bg: 'hsla(var(--teal-10-hsl)/0.1)', + fg: 'var(--teal-10)', + }); + } + } + + return ret; +} diff --git a/src/stores/quotations/types.ts b/src/stores/quotations/types.ts index a52f6663..788956c6 100644 --- a/src/stores/quotations/types.ts +++ b/src/stores/quotations/types.ts @@ -149,6 +149,13 @@ type ServiceRelation = { createdByUserId: string; updatedAt: string; updatedByUserId: string; + + work: (WorkRelation & { + productOnWork: { + order: number; + product: ProductRelation; + }[]; + })[]; }; export type QuotationStats = {