refactor: quotation form & product service

This commit is contained in:
puriphatt 2024-10-03 15:33:20 +07:00
parent 8698268836
commit c51f664beb
2 changed files with 90 additions and 92 deletions

View file

@ -140,6 +140,7 @@ function mapCard() {
attributes: s.attributes, attributes: s.attributes,
createAt: s.createdAt, createAt: s.createdAt,
selectedImage: s.selectedImage, selectedImage: s.selectedImage,
raw: s,
})) || [], })) || [],
product: product:
productList.value[selectedProductGroup.value]?.map((p) => ({ productList.value[selectedProductGroup.value]?.map((p) => ({
@ -154,6 +155,7 @@ function mapCard() {
serviceCharge: p.serviceCharge, serviceCharge: p.serviceCharge,
process: p.process, process: p.process,
selectedImage: p.selectedImage, selectedImage: p.selectedImage,
raw: p,
})) || [], })) || [],
}; };
@ -165,15 +167,12 @@ function mapNode() {
const node = selectedItems.value.map((v: Node) => { const node = selectedItems.value.map((v: Node) => {
if (v.type === 'service') { if (v.type === 'service') {
return { return {
type: 'type',
id: v.id, id: v.id,
title: v.name, title: v.name,
subtitle: v.code, subtitle: v.code,
detail: v.detail, raw: v.raw,
attributes: {
additional: v.attributes.additional,
},
opened: v.work.length > 0, opened: v.work.length > 0,
type: 'type',
icon: 'mdi-server-outline', icon: 'mdi-server-outline',
bg: 'hsla(var(--orange-5-hsl)/0.1)', bg: 'hsla(var(--orange-5-hsl)/0.1)',
fg: 'var(--orange-5)', fg: 'var(--orange-5)',
@ -182,38 +181,30 @@ function mapNode() {
.filter((w: Work) => !w.name) .filter((w: Work) => !w.name)
.flatMap((w: Work) => .flatMap((w: Work) =>
w.productOnWork.map((p) => ({ w.productOnWork.map((p) => ({
type: 'product',
id: p.product.id, id: p.product.id,
title: p.product.name, title: p.product.name,
subtitle: p.product.code || ' ', subtitle: p.product.code || ' ',
detail: p.product.detail, raw: p.product,
remark: p.product.remark,
price: p.product.price,
agentPrice: p.product.agentPrice,
serviceCharge: p.product.serviceCharge,
icon: 'mdi-shopping-outline', icon: 'mdi-shopping-outline',
bg: 'hsla(var(--teal-10-hsl)/0.1)', bg: 'hsla(var(--teal-10-hsl)/0.1)',
fg: 'var(--teal-10)', fg: 'var(--teal-10)',
type: 'product',
})), })),
); );
const withNameObjects = v.work const withNameObjects = v.work
.filter((w: Work) => w.name) .filter((w: Work) => w.name)
.flatMap((w: Work) => ({ .flatMap((w: Work) => ({
type: 'work',
id: w.id, id: w.id,
title: w.name, title: w.name,
subtitle: ' ', subtitle: ' ',
attributes: w.attributes, raw: w,
type: 'work',
opened: w.productOnWork.length > 0, opened: w.productOnWork.length > 0,
children: w.productOnWork.map((p) => ({ children: w.productOnWork.map((p) => ({
id: p.product.id, id: p.product.id,
title: p.product.name, title: p.product.name,
subtitle: p.product.code || ' ', subtitle: p.product.code || ' ',
detail: p.product.detail, raw: p.product,
remark: p.product.remark,
price: p.product.price,
agentPrice: p.product.agentPrice,
serviceCharge: p.product.serviceCharge,
type: 'product', type: 'product',
})), })),
})); }));
@ -225,11 +216,7 @@ function mapNode() {
id: v.id, id: v.id,
title: v.name, title: v.name,
subtitle: v.code, subtitle: v.code,
detail: v.detail, raw: v.raw,
remark: v.remark,
price: v.price,
agentPrice: v.agentPrice,
serviceCharge: v.serviceCharge,
type: 'product', type: 'product',
icon: 'mdi-shopping-outline', icon: 'mdi-shopping-outline',
bg: 'hsla(var(--teal-10-hsl)/0.1)', bg: 'hsla(var(--teal-10-hsl)/0.1)',
@ -525,7 +512,7 @@ watch(
<div <div
class="row text-capitalize" class="row text-capitalize"
:class=" :class="
pageState.infoTab === 'detail' pageState.infoTab === '1'
? 'text-bold' ? 'text-bold'
: 'app-text-muted' : 'app-text-muted'
" "
@ -537,7 +524,7 @@ watch(
<div <div
class="row text-capitalize" class="row text-capitalize"
:class=" :class="
pageState.infoTab === 'remark' pageState.infoTab === '2'
? 'text-bold' ? 'text-bold'
: 'app-text-muted' : 'app-text-muted'
" "
@ -568,7 +555,7 @@ watch(
}} }}
</span> </span>
<aside class="surface-3 q-pa-md"> <aside class="surface-3 q-pa-md">
{{ selectedNode[0].detail || '-' }} {{ selectedNode[0].raw.detail || '-' }}
</aside> </aside>
</span> </span>
@ -593,16 +580,17 @@ watch(
" "
class="surface-3 q-pa-md" class="surface-3 q-pa-md"
> >
{{ selectedNode[0].remark || '-' }} {{ selectedNode[0].raw.remark || '-' }}
</aside> </aside>
<aside v-else class="q-pt-md row q-gutter-sm"> <aside v-else class="q-pt-md row q-gutter-sm">
<template <template
v-if=" v-if="
selectedNode[0].attributes.additional.length > 0 selectedNode[0].raw.attributes.additional
.length > 0
" "
> >
<div <div
v-for="att in selectedNode[0].attributes v-for="att in selectedNode[0].raw.attributes
.additional" .additional"
:key="att" :key="att"
class="bordered q-py-xs q-px-sm rounded" class="bordered q-py-xs q-px-sm rounded"

View file

@ -1,8 +1,21 @@
<script lang="ts" setup> <script lang="ts" setup>
import { setLocale } from 'src/utils/datetime'; import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import { QSelect, useQuasar } from 'quasar'; import { QSelect, useQuasar } from 'quasar';
import { onMounted, reactive, ref } from 'vue'; import { onMounted, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { setLocale } from 'src/utils/datetime';
import { dateFormat } from 'src/utils/datetime';
import useOptionStore from 'stores/options';
import { useQuotationForm } from './form';
import { Employee } from 'src/stores/employee/types';
import {
ProductGroup,
ProductList,
Service,
} from 'src/stores/product-service/types';
import { QuotationPayload } from 'src/stores/quotations/types';
import ProductServiceForm from './ProductServiceForm.vue'; import ProductServiceForm from './ProductServiceForm.vue';
import WorkerItem from 'components/05_quotation/WorkerItem.vue'; import WorkerItem from 'components/05_quotation/WorkerItem.vue';
@ -14,15 +27,6 @@ import DialogForm from 'src/components/DialogForm.vue';
import SelectZone from 'src/components/shared/SelectZone.vue'; import SelectZone from 'src/components/shared/SelectZone.vue';
import PersonCard from 'src/components/shared/PersonCard.vue'; import PersonCard from 'src/components/shared/PersonCard.vue';
import { AddButton, SaveButton } from 'components/button'; import { AddButton, SaveButton } from 'components/button';
import { storeToRefs } from 'pinia';
import { useQuotationForm } from './form';
import { dateFormat } from 'src/utils/datetime';
import { Employee } from 'src/stores/employee/types';
import {
ProductGroup,
ProductList,
Service,
} from 'src/stores/product-service/types';
import useProductServiceStore from 'src/stores/product-service'; import useProductServiceStore from 'src/stores/product-service';
type Node = { type Node = {
@ -39,6 +43,7 @@ defineProps<{
readonly?: boolean; readonly?: boolean;
}>(); }>();
const optionStore = useOptionStore();
const productServiceStore = useProductServiceStore(); const productServiceStore = useProductServiceStore();
const quotationForm = useQuotationForm(); const quotationForm = useQuotationForm();
const { locale } = useI18n(); const { locale } = useI18n();
@ -131,20 +136,12 @@ function triggerProductServiceDialog() {
// TODO: form and state controll // TODO: form and state controll
} }
function convertToTable(nodes: Node[]): Node[] { function toggleDeleteProduct(index: number) {
const products: Node[] = []; console.log(index);
}
nodes.forEach((node) => { function convertToTable(nodes: Node[]) {
if (node.type === 'product' && node.checked) { console.log(nodes);
products.push(node);
}
if (node.children && node.children.length !== 0) {
products.push(...convertToTable(node.children)); // Correctly push the converted children
}
});
return products;
} }
function changeMode(mode: string) { function changeMode(mode: string) {
@ -188,6 +185,11 @@ onMounted(async () => {
quotationFormData.value.customerBranchId = quotationFormData.value.customerBranchId =
urlParams.get('customerBranchId') || ''; urlParams.get('customerBranchId') || '';
const resultOption = await fetch('/option/option.json');
const rawOption = await resultOption.json();
if (locale.value === 'eng') optionStore.globalOption = rawOption.eng;
if (locale.value === 'tha') optionStore.globalOption = rawOption.tha;
const getCurLang = localStorage.getItem('currentLanguage'); const getCurLang = localStorage.getItem('currentLanguage');
if (getCurLang === 'English') { if (getCurLang === 'English') {
locale.value = 'eng'; locale.value = 'eng';
@ -270,23 +272,28 @@ onMounted(async () => {
header-class="surface-1" header-class="surface-1"
> >
<template v-slot:header> <template v-slot:header>
<div class="row full-width items-center q-pr-md q-py-sm"> <section class="row items-center full-width">
<span <div class="row items-center q-pr-md q-py-sm">
class="text-weight-medium q-mr-md" <span
style="font-size: 18px" class="text-weight-medium q-mr-md"
> style="font-size: 18px"
{{ $t('quotation.employeeList') }} >
</span> {{ $t('quotation.employeeList') }}
<ToggleButton class="q-mr-sm" v-model="toggleWorker" /> </span>
{{ <ToggleButton class="q-mr-sm" v-model="toggleWorker" />
toggleWorker ? $t('general.specify') : $t('general.noSpecify') {{
}} toggleWorker
</div> ? $t('general.specify')
<AddButton : $t('general.noSpecify')
icon-only }}
class="q-ml-auto" </div>
@click.stop="triggerSelectEmployeeDialog" <nav class="q-ml-auto">
/> <AddButton
icon-only
@click.stop="triggerSelectEmployeeDialog"
/>
</nav>
</section>
</template> </template>
<div class="surface-1 q-pa-md full-width"> <div class="surface-1 q-pa-md full-width">
@ -320,27 +327,23 @@ onMounted(async () => {
header-class="surface-1" header-class="surface-1"
> >
<template v-slot:header> <template v-slot:header>
<div class="row full-width items-center q-pr-md q-py-sm"> <section class="row items-center full-width">
<span class="text-weight-medium" style="font-size: 18px"> <div class="row items-center q-pr-md q-py-sm">
{{ $t('quotation.productList') }} <span class="text-weight-medium" style="font-size: 18px">
</span> {{ $t('quotation.productList') }}
</div> </span>
<AddButton </div>
icon-only <nav class="q-ml-auto">
class="q-ml-auto" <AddButton
@click.stop="triggerProductServiceDialog" icon-only
/> class="q-ml-auto"
@click.stop="triggerProductServiceDialog"
/>
</nav>
</section>
</template> </template>
<div class="surface-1 q-pa-md full-width"> <div class="surface-1 q-pa-md full-width">
<ProductItem <ProductItem @delete="toggleDeleteProduct" :rows="[]" />
:rows="
rows.map((p) => ({
code: p.subtitle,
name: p.title,
}))
"
/>
{{ console.log(rows) }}
</div> </div>
</q-expansion-item> </q-expansion-item>
@ -358,7 +361,7 @@ onMounted(async () => {
<template v-slot:header> <template v-slot:header>
<div class="row full-width items-center q-pr-md q-py-sm"> <div class="row full-width items-center q-pr-md q-py-sm">
<span class="text-weight-medium" style="font-size: 18px"> <span class="text-weight-medium" style="font-size: 18px">
{{ $t('productService.group.title') }} {{ $t('general.remark') }}
</span> </span>
</div> </div>
</template> </template>
@ -501,8 +504,8 @@ onMounted(async () => {
v-model:service-list="serviceList" v-model:service-list="serviceList"
@submit=" @submit="
(nodes) => { (nodes) => {
rows = convertToTable(nodes); convertToTable(nodes);
pageState.productServiceModal = false; // pageState.productServiceModal = false;
} }
" "
@select-group=" @select-group="
@ -597,4 +600,11 @@ onMounted(async () => {
:deep(.q-editor__toolbar) { :deep(.q-editor__toolbar) {
border-color: var(--surface-3) !important; border-color: var(--surface-3) !important;
} }
:deep(
.q-item.q-item-type.row.no-wrap.q-item--dense.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable.surface-1
.q-focus-helper
) {
visibility: hidden;
}
</style> </style>