From 6f2471c33b007317d21e545467e0df2fd14a4ab5 Mon Sep 17 00:00:00 2001 From: Thanaphon Frappet Date: Fri, 4 Apr 2025 15:55:04 +0700 Subject: [PATCH] refactor: change form like page customer --- src/pages/03_customer-management/MainPage.vue | 174 ++----- src/pages/03_customer-management/form.ts | 144 +++++- src/pages/05_quotation/MainPage.vue | 487 ++++++++++++------ .../QuotationFormWorkerSelect.vue | 5 +- 4 files changed, 532 insertions(+), 278 deletions(-) diff --git a/src/pages/03_customer-management/MainPage.vue b/src/pages/03_customer-management/MainPage.vue index 45482ec4..e5103c0b 100644 --- a/src/pages/03_customer-management/MainPage.vue +++ b/src/pages/03_customer-management/MainPage.vue @@ -104,23 +104,22 @@ const ocrStore = useOcrStore(); const refFilter = ref>(); const statusEmployeeCreate = ref(false); const mrz = ref>>(); -const tabFieldRequired = ref<{ [key: string]: (keyof CustomerBranchCreate)[] }>( - { - main: [], - business: ['businessType', 'jobPosition'], - address: [ - 'address', - 'addressEN', - 'provinceId', - 'districtId', - 'subDistrictId', - ], - contact: [], - }, -); -const { state: customerFormState, currentFormData: customerFormData } = - storeToRefs(customerFormStore); +const { + fetchListOfOptionBranch, + customerFormUndo, + customerConfirmUnsave, + deleteCustomerById, + validateTabField, + deleteCustomerBranchById, +} = customerFormStore; + +const { + state: customerFormState, + currentFormData: customerFormData, + registerAbleBranchOption, + tabFieldRequired, +} = storeToRefs(customerFormStore); const { state: employeeFormState, currentFromDataEmployee } = storeToRefs(employeeFormStore); @@ -276,8 +275,6 @@ const fieldSelected = ref( ].filter((v, index, self) => self.indexOf(v) === index), ); -const registerAbleBranchOption = ref<{ id: string; name: string }[]>(); - const branch = ref(); const customerStats = [ @@ -294,81 +291,6 @@ const fieldCustomer = [ const employeeHistoryDialog = ref(false); const employeeHistory = ref(); -function validateTabField( - value: T, - fieldRequired: { [key: string]: (keyof T)[] }, -) { - const list: string[] = []; - - for (const tab in fieldRequired) { - for (const field of fieldRequired[tab]) { - if (!value[field] && !list.includes(tab)) list.push(tab); - } - } - - return list; -} - -function deleteCustomerById(id: string) { - dialog({ - color: 'negative', - icon: 'mdi-alert', - title: t('dialog.title.confirmDelete'), - actionText: t('general.delete'), - persistent: true, - message: t('dialog.message.confirmDelete'), - action: async () => { - await customerStore.deleteById(id); - - await fetchListCustomer(true, $q.screen.xs); - customerFormState.value.dialogModal = false; - flowStore.rotate(); - }, - cancel: () => {}, - }); -} - -async function deleteCustomerBranchById(id: string) { - return await new Promise((resolve) => { - dialog({ - color: 'negative', - icon: 'mdi-alert', - title: t('dialog.title.confirmDelete'), - actionText: t('general.delete'), - persistent: true, - message: t('dialog.message.confirmDelete'), - action: async () => { - await customerStore.deleteBranchById(id); - flowStore.rotate(); - resolve(true); - }, - cancel: () => { - resolve(false); - }, - }); - }); -} -async function fetchListOfOptionBranch() { - if (registerAbleBranchOption.value) return; - - const uid = getUserId(); - const role = getRole(); - - if (!uid) return; // should not possible as the system require login to be able to access resource. - - if (role?.includes('system')) { - const result = await userBranchStore.fetchListOptionBranch(); - if (result && result.total > 0) - registerAbleBranchOption.value = result.result; - } else { - const result = await userBranchStore.fetchListMyBranch(uid); - if (result && result.total > 0) - registerAbleBranchOption.value = result.result; - } - - // TODO: Assign (first) branch of the user as register branch of the data -} - async function fetchListCustomer(fetchStats = false, mobileFetch?: boolean) { const total = statsCustomerType.value.PERS + statsCustomerType.value.CORP; @@ -621,37 +543,6 @@ function employeeFormUndo(close = true) { employeeFormState.value.editReadonly = true; } -function customerConfirmUnsave(close = true) { - dialog({ - color: 'warning', - icon: 'mdi-alert', - title: t('form.warning.title'), - actionText: t('general.ok'), - persistent: true, - message: t('form.warning.unsave'), - - action: () => { - customerFormStore.resetForm(); - customerFormState.value.readonly = true; - - if (!customerFormState.value.drawerModal) { - customerFormState.value.dialogModal = !close; - } else { - customerFormState.value.drawerModal = !close; - } - }, - cancel: () => {}, - }); -} - -function customerFormUndo(close = true) { - if (customerFormStore.isFormDataDifferent()) { - return customerConfirmUnsave(close); - } - customerFormStore.resetForm(); - customerFormState.value.readonly = true; -} - async function createCustomerForm(customerType: 'CORP' | 'PERS') { customerFormState.value.dialogModal = true; customerFormState.value.dialogType = 'create'; @@ -1540,7 +1431,13 @@ const emptyCreateDialog = ref(false); customerFormState.branchIndex = 0; } " - @delete="deleteCustomerById(props.row.id)" + @delete=" + deleteCustomerById( + props.row.id, + async () => + await fetchListCustomer(true, $q.screen.xs), + ) + " @change-status=" async () => { triggerChangeStatus( @@ -1763,7 +1660,16 @@ const emptyCreateDialog = ref(false); customerFormState.branchIndex = 0; } " - @delete="deleteCustomerById(props.row.id)" + @delete=" + deleteCustomerById( + props.row.id, + async () => + await fetchListCustomer( + true, + $q.screen.xs, + ), + ) + " @change-status=" triggerChangeStatus( props.row.id, @@ -2265,7 +2171,10 @@ const emptyCreateDialog = ref(false); @cancel="() => customerFormUndo(false)" @delete=" customerFormState.editCustomerId && - deleteCustomerById(customerFormState.editCustomerId) + deleteCustomerById( + customerFormState.editCustomerId, + async () => await fetchListCustomer(true, $q.screen.xs), + ) " :customer-type="customerFormData.customerType" v-model:registered-branch-id="customerFormData.registeredBranchId" @@ -2435,7 +2344,11 @@ const emptyCreateDialog = ref(false); if (!customerFormState.editCustomerId) return; if (idx === 0) { - deleteCustomerById(customerFormState.editCustomerId); + deleteCustomerById( + customerFormState.editCustomerId, + async () => + await fetchListCustomer(true, $q.screen.xs), + ); return; } if (!!customerFormData.customerBranch?.[idx].id) { @@ -4225,7 +4138,10 @@ const emptyCreateDialog = ref(false); @cancel="() => customerFormUndo(false)" @delete=" customerFormState.editCustomerId && - deleteCustomerById(customerFormState.editCustomerId) + deleteCustomerById( + customerFormState.editCustomerId, + async () => await fetchListCustomer(true, $q.screen.xs), + ) " :customer-type="customerFormData.customerType" v-model:registered-branch-id="customerFormData.registeredBranchId" diff --git a/src/pages/03_customer-management/form.ts b/src/pages/03_customer-management/form.ts index b2ae9499..125388cc 100644 --- a/src/pages/03_customer-management/form.ts +++ b/src/pages/03_customer-management/form.ts @@ -1,5 +1,6 @@ import { ref, watch } from 'vue'; import { defineStore } from 'pinia'; +import { useI18n } from 'vue-i18n'; import { CustomerBranchCreate, CustomerCreate, @@ -7,16 +8,41 @@ import { } from 'stores/customer/types'; import { Employee, EmployeeCreate } from 'stores/employee/types'; +import { dialog } from 'stores/utils'; import useMyBranch from 'stores/my-branch'; import useCustomerStore from 'stores/customer'; import useEmployeeStore from 'stores/employee'; import useFlowStore from 'stores/flow'; +import useMyBranchStore from 'stores/my-branch'; import { baseUrl } from 'src/stores/utils'; +import { getRole, getUserId } from 'src/services/keycloak'; export const useCustomerForm = defineStore('form-customer', () => { const customerStore = useCustomerStore(); + const { t } = useI18n(); + const flowStore = useFlowStore(); + + const userBranchStore = useMyBranchStore(); + + const registerAbleBranchOption = ref<{ id: string; name: string }[]>(); + + const tabFieldRequired = ref<{ + [key: string]: (keyof CustomerBranchCreate)[]; + }>({ + main: [], + business: ['businessType', 'jobPosition'], + address: [ + 'address', + 'addressEN', + 'provinceId', + 'districtId', + 'subDistrictId', + ], + contact: [], + }); + const defaultFormData: CustomerCreate = { // code: '', // namePrefix: '', @@ -360,7 +386,118 @@ export const useCustomerForm = defineStore('form-customer', () => { } } + async function fetchListOfOptionBranch() { + if (registerAbleBranchOption.value) return; + + const uid = getUserId(); + const role = getRole(); + + if (!uid) return; // should not possible as the system require login to be able to access resource. + + if (role?.includes('system')) { + const result = await userBranchStore.fetchListOptionBranch(); + if (result && result.total > 0) + registerAbleBranchOption.value = result.result; + } else { + const result = await userBranchStore.fetchListMyBranch(uid); + if (result && result.total > 0) + registerAbleBranchOption.value = result.result; + } + + // TODO: Assign (first) branch of the user as register branch of the data + } + + function customerFormUndo(close = true) { + if (isFormDataDifferent()) { + return customerConfirmUnsave(close); + } + resetForm(); + state.value.readonly = true; + } + + function customerConfirmUnsave(close = true) { + dialog({ + color: 'warning', + icon: 'mdi-alert', + title: t('form.warning.title'), + actionText: t('general.ok'), + persistent: true, + message: t('form.warning.unsave'), + + action: () => { + resetForm(); + state.value.readonly = true; + + if (!state.value.drawerModal) { + state.value.dialogModal = !close; + } else { + state.value.drawerModal = !close; + } + }, + cancel: () => {}, + }); + } + + function deleteCustomerById( + id: string, + fetch?: (...args: unknown[]) => unknown, + ) { + dialog({ + color: 'negative', + icon: 'mdi-alert', + title: t('dialog.title.confirmDelete'), + actionText: t('general.delete'), + persistent: true, + message: t('dialog.message.confirmDelete'), + action: async () => { + await customerStore.deleteById(id); + await fetch(); + state.value.dialogModal = false; + flowStore.rotate(); + }, + cancel: () => {}, + }); + } + + function validateTabField( + value: T, + fieldRequired: { [key: string]: (keyof T)[] }, + ) { + const list: string[] = []; + + for (const tab in fieldRequired) { + for (const field of fieldRequired[tab]) { + if (!value[field] && !list.includes(tab)) list.push(tab); + } + } + + return list; + } + + async function deleteCustomerBranchById(id: string) { + return await new Promise((resolve) => { + dialog({ + color: 'negative', + icon: 'mdi-alert', + title: t('dialog.title.confirmDelete'), + actionText: t('general.delete'), + persistent: true, + message: t('dialog.message.confirmDelete'), + action: async () => { + await customerStore.deleteBranchById(id); + flowStore.rotate(); + resolve(true); + }, + cancel: () => { + resolve(false); + }, + }); + }); + } + return { + tabFieldRequired, + registerAbleBranchOption, state, resetFormData, currentFormData, @@ -370,13 +507,18 @@ export const useCustomerForm = defineStore('form-customer', () => { submitFormCustomer, addCurrentCustomerBranch, deleteAttachment, + fetchListOfOptionBranch, + customerFormUndo, + customerConfirmUnsave, + deleteCustomerById, + validateTabField, + deleteCustomerBranchById, }; }); export const useCustomerBranchForm = defineStore('form-customer-branch', () => { const customerStore = useCustomerStore(); const customerFormStore = useCustomerForm(); - const defaultFormData: CustomerBranchCreate & { id?: string; codeCustomer?: string; diff --git a/src/pages/05_quotation/MainPage.vue b/src/pages/05_quotation/MainPage.vue index 13a58c60..c525f856 100644 --- a/src/pages/05_quotation/MainPage.vue +++ b/src/pages/05_quotation/MainPage.vue @@ -2,10 +2,12 @@ import { onMounted, reactive, ref, watch, computed } from 'vue'; import { storeToRefs } from 'pinia'; import { useQuasar } from 'quasar'; +import { useI18n } from 'vue-i18n'; // NOTE: Import stores +import useCustomerStore from 'stores/customer'; import { useQuotationStore } from 'src/stores/quotations'; -import { isRoleInclude } from 'stores/utils'; +import { dialog, isRoleInclude, notify } from 'stores/utils'; import { useNavigator } from 'src/stores/navigator'; import useFlowStore from 'src/stores/flow'; import useMyBranch from 'stores/my-branch'; @@ -38,26 +40,43 @@ import { AddressForm } from 'components/form'; import { EmployerFormBusiness, EmployerFormAbout, + EmployerFormBasicInfo, + EmployerFormBranch, } from 'src/pages/03_customer-management/components'; import { useCustomerForm } from 'src/pages/03_customer-management/form'; import { Quotation } from 'src/stores/quotations/types'; import TableQuotation from 'src/components/05_quotation/TableQuotation.vue'; import PaginationPageSize from 'src/components/PaginationPageSize.vue'; +import { DialogContainer, DialogHeader } from 'src/components/dialog'; +const { t, locale } = useI18n(); const $q = useQuasar(); const quotationFormStore = useQuotationForm(); const customerFormStore = useCustomerForm(); const flowStore = useFlowStore(); const userBranch = useMyBranch(); const navigatorStore = useNavigator(); +const customerStore = useCustomerStore(); + +const { + fetchListOfOptionBranch, + customerFormUndo, + deleteCustomerById, + validateTabField, + deleteCustomerBranchById, +} = customerFormStore; const { currentFormData: quotationFormData, currentFormState: quotationFormState, } = storeToRefs(quotationFormStore); -const { state: customerFormState, currentFormData: customerFormData } = - storeToRefs(customerFormStore); +const { + state: customerFormState, + currentFormData: customerFormData, + registerAbleBranchOption, + tabFieldRequired, +} = storeToRefs(customerFormStore); const { currentMyBranch } = storeToRefs(userBranch); const fieldSelectedOption = computed(() => { @@ -170,8 +189,7 @@ async function submitCustomer() { customerFormData.value.registeredBranchId = isRoleInclude(['system']) ? branchId.value : currentMyBranch.value.id; - await customerFormStore.submitFormCustomer(); - + await customerFormStore.addCurrentCustomerBranch(); customerFormState.value.dialogModal = false; // customerFormState.value.dialogType = 'info'; } @@ -241,6 +259,16 @@ const { stats: quotationStats, } = storeToRefs(quotationStore); +const customerNameInfo = computed(() => { + if (customerFormData.value.customerBranch === undefined) return; + + const name = + locale.value === 'eng' + ? `${customerFormData.value.customerBranch[0]?.firstNameEN} ${customerFormData.value.customerBranch[0]?.lastNameEN}` + : `${customerFormData.value.customerBranch[0]?.firstName} ${customerFormData.value.customerBranch[0]?.lastName}`; + return name || '-'; +}); + onMounted(async () => { pageState.gridView = $q.screen.lt.md ? true : false; navigatorStore.current.title = 'quotation.title'; @@ -911,30 +939,49 @@ async function storeDataLocal(id: string) { - { customerFormState.dialogModal = false; - customerFormStore.resetForm(true); - setDefaultCustomer(); + onCreateImageList = { selectedImage: '', list: [] }; } " > + +
-
+
-
- +
+ + +
- -
- -
- +
+
+ + {{ $t('customer.form.group.branch') }} +
- - + +
- + diff --git a/src/pages/05_quotation/QuotationFormWorkerSelect.vue b/src/pages/05_quotation/QuotationFormWorkerSelect.vue index a3010e53..4ef1a9cb 100644 --- a/src/pages/05_quotation/QuotationFormWorkerSelect.vue +++ b/src/pages/05_quotation/QuotationFormWorkerSelect.vue @@ -444,10 +444,7 @@ watch( class="full-width" :prefix-id="'employee-' + index" :data="{ - name: - locale === Lang.English - ? `${emp.firstNameEN} ${emp.lastNameEN}` - : `${emp.firstName} ${emp.lastName}`, + name: `${emp.firstName} ${emp.lastName}`, female: emp.gender === 'female', male: emp.gender === 'male', img: `/images/employee-avatar-${emp.gender}.png`,