diff --git a/public/images/customer-CORP-avartar-female.png b/public/images/customer-CORP-avartar-female.png index 446ef866..4cbb894f 100644 Binary files a/public/images/customer-CORP-avartar-female.png and b/public/images/customer-CORP-avartar-female.png differ diff --git a/public/images/customer-CORP-avartar-male.png b/public/images/customer-CORP-avartar-male.png index 6ad95d7d..ae177f60 100644 Binary files a/public/images/customer-CORP-avartar-male.png and b/public/images/customer-CORP-avartar-male.png differ diff --git a/public/images/customer-PERS-avartar-female.png b/public/images/customer-PERS-avartar-female.png index ca0a2bf1..c3ba574e 100644 Binary files a/public/images/customer-PERS-avartar-female.png and b/public/images/customer-PERS-avartar-female.png differ diff --git a/public/images/customer-PERS-avartar-male.png b/public/images/customer-PERS-avartar-male.png index e9fd15fe..ce0ab20c 100644 Binary files a/public/images/customer-PERS-avartar-male.png and b/public/images/customer-PERS-avartar-male.png differ diff --git a/public/images/employee-avatar-female.png b/public/images/employee-avatar-female.png index 66ace3a0..ce9370f1 100644 Binary files a/public/images/employee-avatar-female.png and b/public/images/employee-avatar-female.png differ diff --git a/public/images/employee-avatar-male.png b/public/images/employee-avatar-male.png index a8daa8ff..aaf5fb1f 100644 Binary files a/public/images/employee-avatar-male.png and b/public/images/employee-avatar-male.png differ diff --git a/public/no-img-female.png b/public/no-img-female.png index 4e177dca..95f959ff 100644 Binary files a/public/no-img-female.png and b/public/no-img-female.png differ diff --git a/public/no-img-man.png b/public/no-img-man.png index 861f356a..f0ccba15 100644 Binary files a/public/no-img-man.png and b/public/no-img-man.png differ diff --git a/src/components/03_customer-management/DialogEmployee.vue b/src/components/03_customer-management/DialogEmployee.vue new file mode 100644 index 00000000..db428f83 --- /dev/null +++ b/src/components/03_customer-management/DialogEmployee.vue @@ -0,0 +1,1656 @@ + + diff --git a/src/components/03_customer-management/DrawerEmployee.vue b/src/components/03_customer-management/DrawerEmployee.vue new file mode 100644 index 00000000..1c1d39bf --- /dev/null +++ b/src/components/03_customer-management/DrawerEmployee.vue @@ -0,0 +1,1772 @@ + + + diff --git a/src/components/03_customer-management/FormEmployeePassport.vue b/src/components/03_customer-management/FormEmployeePassport.vue index 040c9676..06373538 100644 --- a/src/components/03_customer-management/FormEmployeePassport.vue +++ b/src/components/03_customer-management/FormEmployeePassport.vue @@ -20,8 +20,8 @@ const issuePlace = defineModel('issuePlace'); const issueCountry = defineModel('issueCountry'); const issueDate = defineModel('issueDate'); const type = defineModel('type'); -const expireDate = defineModel('expireDate'); -const birthDate = defineModel('birthDate'); +const expireDate = defineModel('expireDate'); +const birthDate = defineModel('birthDate'); const workerStatus = defineModel('workerStatus'); const nationality = defineModel('nationality'); const gender = defineModel('gender'); diff --git a/src/components/03_customer-management/FormEmployeeVisa.vue b/src/components/03_customer-management/FormEmployeeVisa.vue index a5f0f292..7c9085c4 100644 --- a/src/components/03_customer-management/FormEmployeeVisa.vue +++ b/src/components/03_customer-management/FormEmployeeVisa.vue @@ -28,12 +28,12 @@ const arrivalAt = defineModel('arrivalAt'); const arrivalTMNo = defineModel('arrivalTmNo'); const arrivalTM = defineModel('arrivalTm'); const mrz = defineModel('mrz'); -const entryCount = defineModel('entryCount'); +const entryCount = defineModel('entryCount'); const issuePlace = defineModel('issuePlace'); const issueCountry = defineModel('issueCountry'); const issueDate = defineModel('visaIssueDate'); const type = defineModel('type'); -const expireDate = defineModel('expireDate'); +const expireDate = defineModel('expireDate'); const remark = defineModel('remark'); const workerType = defineModel('workerType'); const number = defineModel('number'); diff --git a/src/components/03_customer-management/TableEmpoloyee.vue b/src/components/03_customer-management/TableEmpoloyee.vue index d93c6085..000c701a 100644 --- a/src/components/03_customer-management/TableEmpoloyee.vue +++ b/src/components/03_customer-management/TableEmpoloyee.vue @@ -141,8 +141,9 @@ defineEmits<{ +import { CustomerBranch } from 'src/stores/customer'; + import SelectCustomer from '../shared/select/SelectCustomer.vue'; import SelectBranch from '../shared/select/SelectBranch.vue'; @@ -7,6 +9,10 @@ const customerBranchId = defineModel('customerBranchId'); const agentPrice = defineModel('agentPrice'); const special = defineModel('special'); +const customerBranchOption = defineModel( + 'customerBranchOption', +); + defineProps<{ outlined?: boolean; readonly?: boolean; @@ -71,6 +77,7 @@ defineEmits<{ id="about-select-customer-branch-id" for="about-select-customer-branch-id" v-model:value="customerBranchId" + v-model:value-option="customerBranchOption" :label="$t('quotation.customer')" :creatable-disabled-text="`(${$t('form.error.selectField', { field: $t('quotation.branchVirtual'), diff --git a/src/components/05_quotation/ProductItem.vue b/src/components/05_quotation/ProductItem.vue index b1996d03..cfa2ad8e 100644 --- a/src/components/05_quotation/ProductItem.vue +++ b/src/components/05_quotation/ProductItem.vue @@ -58,16 +58,15 @@ const currentBtnOpen = ref<{ title: string; opened: boolean[] }[]>([ function calcPrice(c: (typeof rows.value)[number]) { const originalPrice = c.pricePerUnit; - const finalPriceWithVat = precisionRound( - originalPrice + originalPrice * (config.value?.vat || 0.07), + const finalPricePerUnit = precisionRound( + originalPrice + + (c.product[props.agentPrice ? 'agentPriceCalcVat' : 'calcVat'] + ? originalPrice * (config.value?.vat || 0.07) + : 0), ); - const finalPriceNoVat = finalPriceWithVat / (1 + (config.value?.vat || 0.07)); + const price = finalPricePerUnit * c.amount - c.discount; - const price = finalPriceNoVat * c.amount - c.discount; - const vat = c.product[props.agentPrice ? 'agentPriceCalcVat' : 'calcVat'] - ? (finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07) - : 0; - return precisionRound(price + vat); + return precisionRound(price); } const discount4Show = ref([]); @@ -435,8 +434,20 @@ watch( {{ formatNumberDecimal( - props.row.pricePerUnit * props.row.amount - - props.row.discount, + props.row.product[ + agentPrice ? 'agentPriceCalcVat' : 'calcVat' + ] + ? precisionRound( + (props.row.pricePerUnit * + (1 + (config?.vat || 0.07)) * + props.row.amount - + props.row.discount) / + (1 + (config?.vat || 0.07)), + ) + : precisionRound( + props.row.pricePerUnit * props.row.amount - + props.row.discount, + ), 2, ) }} @@ -448,9 +459,12 @@ watch( agentPrice ? 'agentPriceCalcVat' : 'calcVat' ] ? precisionRound( - (props.row.pricePerUnit * props.row.amount - - props.row.discount) * - (config?.vat || 0.07), + ((props.row.pricePerUnit * + (1 + (config?.vat || 0.07)) * + props.row.amount - + props.row.discount) / + (1 + (config?.vat || 0.07))) * + 0.07, ) : 0, 2, diff --git a/src/components/DrawerInfo.vue b/src/components/DrawerInfo.vue index 0ce76c7b..defcb6d5 100644 --- a/src/components/DrawerInfo.vue +++ b/src/components/DrawerInfo.vue @@ -8,7 +8,7 @@ import { UndoButton, } from 'components/button'; -withDefaults( +const props = withDefaults( defineProps<{ title: string; category?: string; @@ -42,6 +42,11 @@ const drawerOpen = defineModel('drawerOpen', { const myForm = ref(); function reset() { + if (props.beforeClose) { + drawerOpen.value = props.beforeClose + ? props.beforeClose() + : !drawerOpen.value; + } if (myForm.value) { myForm.value.resetValidation(); } @@ -62,7 +67,6 @@ async function onValidationError(ref: any) { @show="show" @before-hide="reset" @hide="close" - @update:model-value="(v) => (drawerOpen = beforeClose ? beforeClose() : v)" :width="$q.screen.gt.xs ? windowSize * 0.85 : windowSize" v-model="drawerOpen" behavior="mobile" diff --git a/src/components/index.ts b/src/components/index.ts index ecca1c1e..4f82d6a8 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -16,3 +16,4 @@ export { default as SideMenu } from './SideMenu.vue'; export { default as StatCardComponent } from './StatCardComponent.vue'; export { default as TooltipComponent } from './TooltipComponent.vue'; export { default as TreeComponent } from './TreeComponent.vue'; +export { default as PaginationPageSize } from './PaginationPageSize.vue'; diff --git a/src/components/shared/select/SelectBusinessType.vue b/src/components/shared/select/SelectBusinessType.vue index 211ed07e..12b6172e 100644 --- a/src/components/shared/select/SelectBusinessType.vue +++ b/src/components/shared/select/SelectBusinessType.vue @@ -158,6 +158,39 @@ function setDefaultValue() { + + + + diff --git a/src/components/upload-file/UploadFileCard.vue b/src/components/upload-file/UploadFileCard.vue index 2e269d8b..b299c51d 100644 --- a/src/components/upload-file/UploadFileCard.vue +++ b/src/components/upload-file/UploadFileCard.vue @@ -54,10 +54,11 @@ onMounted(() => { @click="$emit('click')" > -
- +
+
{{ name }} - +
+ {{ uploading.loaded @@ -79,7 +80,7 @@ onMounted(() => { /> {{ idle ? `Pending` : progress !== 1 ? `Uploading...` : 'Completed' }} -
+ void | Promise<{ + ) => Promise<{ status: boolean; group: string; meta: { name: string; value: string }[]; diff --git a/src/css/quasar.variables.scss b/src/css/quasar.variables.scss index 7bd226ac..eb2b877b 100644 --- a/src/css/quasar.variables.scss +++ b/src/css/quasar.variables.scss @@ -198,3 +198,10 @@ i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-i .q-focus-helper { visibility: hidden; } + +.clear-btn { + opacity: 0.6; + &:hover { + opacity: 1; + } +} diff --git a/src/i18n/eng.ts b/src/i18n/eng.ts index 5546d0b8..9afb6737 100644 --- a/src/i18n/eng.ts +++ b/src/i18n/eng.ts @@ -507,6 +507,7 @@ export default { miss: 'MISS.', }, + taxpayyerNo: 'Taxpayer Identification Number', citizenId: 'Citizen ID', religion: 'Religion', issueDate: 'Issue Date', @@ -777,6 +778,8 @@ export default { seller: 'Seller', paymentChannels: 'Payment Channels', channelsThat: 'Channels That', + refNo: 'Reference Number', + bankAccount: 'Bank Account', bankAccountNumber: 'Bank Account Number', bankAccountName: 'Bank Account Name', inTheNameOf: 'In The Name Of', diff --git a/src/i18n/tha.ts b/src/i18n/tha.ts index 64411146..3430d2f6 100644 --- a/src/i18n/tha.ts +++ b/src/i18n/tha.ts @@ -508,6 +508,7 @@ export default { religion: 'ศาสนา', issueDate: 'วันที่ออกหนังสือ', passportExpiryDate: 'วันหiมดอายุหนังสือเดินทาง', + taxpayyerNo: 'เลขที่ประจำตัวผู้เสียภาษี', ownerName: 'ชื่อนายจ้าง', firstName: 'ชื่อ ', @@ -775,6 +776,8 @@ export default { seller: 'ผู้ขาย', paymentChannels: 'ช่องทางชำระเงิน', channelsThat: 'ช่องทางที่', + refNo: 'เลขที่อ้างอิง', + bankAccount: 'บัญชีธนาคาร', bankAccountNumber: 'เลขบัญชีธนาคาร', bankAccountName: 'ชื่อบัญชี', inTheNameOf: 'ในนาม', diff --git a/src/pages/03_customer-management/BranchPage.vue b/src/pages/03_customer-management/BranchPage.vue index 3c2e07bb..dd8c4737 100644 --- a/src/pages/03_customer-management/BranchPage.vue +++ b/src/pages/03_customer-management/BranchPage.vue @@ -18,6 +18,8 @@ import { CustomerBranch, CustomerType } from 'stores/customer/types'; import { columnsEmployee } from './constant'; import { useCustomerBranchForm, useEmployeeForm } from './form'; +import DialogEmployee from 'src/components/03_customer-management/DialogEmployee.vue'; +import DrawerEmployee from 'src/components/03_customer-management/DrawerEmployee.vue'; import EmployerFormAuthorized from './components/employer/EmployerFormAuthorized.vue'; import FloatingActionButton from 'components/FloatingActionButton.vue'; import SideMenu from 'components/SideMenu.vue'; @@ -89,6 +91,11 @@ const prop = withDefaults( currentCitizenId?: string; gender: string; selectedImage: string; + fetchImageList: ( + id: string, + selectedName: string, + type: 'customer' | 'employee', + ) => Promise; }>(), { color: 'green', @@ -96,7 +103,6 @@ const prop = withDefaults( ); const currentBranchEmployee = ref(''); const listEmployee = ref([]); - const customerId = defineModel('customerId', { required: true }); defineEmits<{ @@ -106,16 +112,6 @@ defineEmits<{ (e: 'dialog'): void; }>(); -onMounted(async () => { - customerBranchFormState.value.currentCustomerId = route.params - .customerId as string; - await fetchList(); - - branch.value?.forEach((v) => { - currentBtnOpen.value.push(false); - }); -}); - const columns = [ { name: 'branchName', @@ -257,10 +253,6 @@ async function fetchEmployee(opts: { branchId: string; pageSize?: number }) { } } -onMounted(async () => { - await fetchList(); -}); - watch([customerId, inputSearch, currentStatus, pageSizeBranch], async () => { await fetchList(); }); @@ -280,6 +272,16 @@ watch( } }, ); + +onMounted(async () => { + customerBranchFormState.value.currentCustomerId = route.params + .customerId as string; + await fetchList(); + + branch.value?.forEach((v) => { + currentBtnOpen.value.push(false); + }); +}); + + + + diff --git a/src/pages/03_customer-management/TabEmployee.vue b/src/pages/03_customer-management/TabEmployee.vue new file mode 100644 index 00000000..81348622 --- /dev/null +++ b/src/pages/03_customer-management/TabEmployee.vue @@ -0,0 +1,514 @@ + + + + diff --git a/src/pages/03_customer-management/components/employer/EmployerFormAbout.vue b/src/pages/03_customer-management/components/employer/EmployerFormAbout.vue index 0d19e8e9..7353db3c 100644 --- a/src/pages/03_customer-management/components/employer/EmployerFormAbout.vue +++ b/src/pages/03_customer-management/components/employer/EmployerFormAbout.vue @@ -336,6 +336,7 @@ watch( (v) => (typeof v === 'string' ? (prefixName = v) : '') " @clear="prefixName = ''" + :rules="[(val: string) => !!val || $t('form.error.required')]" > diff --git a/src/pages/03_customer-management/components/employer/EmployerFormContact.vue b/src/pages/03_customer-management/components/employer/EmployerFormContact.vue index 4a58439c..7f56742c 100644 --- a/src/pages/03_customer-management/components/employer/EmployerFormContact.vue +++ b/src/pages/03_customer-management/components/employer/EmployerFormContact.vue @@ -9,8 +9,6 @@ const contactName = defineModel('contactName'); const email = defineModel('email'); const contactTel = defineModel('contactTel'); const officeTel = defineModel('officeTel'); -const agent = defineModel('agent'); - const agentUserId = defineModel('agentUserId'); @@ -109,7 +107,6 @@ const agentUserId = defineModel('agentUserId'); /> - { const customerStore = useCustomerStore(); + const onCreateImageList = ref<{ + selectedImage: string; + list: { url: string; imgFile: File | null; name: string }[]; + }>({ selectedImage: '', list: [] }); const { t } = useI18n(); const flowStore = useFlowStore(); @@ -30,6 +34,8 @@ export const useCustomerForm = defineStore('form-customer', () => { const registerAbleBranchOption = ref<{ id: string; name: string }[]>(); + const currentBranchRootId = ref(''); + const tabFieldRequired = ref<{ [key: string]: (keyof CustomerBranchCreate)[]; }>({ @@ -82,6 +88,7 @@ export const useCustomerForm = defineStore('form-customer', () => { formDataOcr: Record; isImageEdit: boolean; currentCustomerId?: string; + imageList: { list: string[]; selectedImage: string }; }>({ dialogType: 'info', dialogOpen: false, @@ -98,6 +105,7 @@ export const useCustomerForm = defineStore('form-customer', () => { treeFile: [], formDataOcr: {}, isImageEdit: false, + imageList: { list: [], selectedImage: '' }, }); watch( @@ -160,6 +168,7 @@ export const useCustomerForm = defineStore('form-customer', () => { state.value.editCustomerCode = data.code; state.value.customerImageUrl = `${baseUrl}/customer/${id}/image/${data.selectedImage}`; state.value.defaultCustomerImageUrl = `${baseUrl}/customer/${id}/image/${data.selectedImage}`; + currentBranchRootId.value = data.branch[0].id || ''; resetFormData.registeredBranchId = data.registeredBranchId; resetFormData.status = data.status; @@ -255,7 +264,6 @@ export const useCustomerForm = defineStore('form-customer', () => { async function addCurrentCustomerBranch() { if (currentFormData.value.customerBranch?.some((v) => !v.id)) return; currentFormData.value.customerBranch?.push({ - id: '', customerId: '', branchCode: currentFormData.value.customerBranch.length !== 0 @@ -495,11 +503,14 @@ export const useCustomerForm = defineStore('form-customer', () => { } return { + onCreateImageList, tabFieldRequired, registerAbleBranchOption, state, resetFormData, currentFormData, + currentBranchRootId, + isFormDataDifferent, resetForm, assignFormData, @@ -779,6 +790,7 @@ export const useEmployeeForm = defineStore('form-employee', () => { } | undefined; ocr: boolean; + imageList: { list: string[]; selectedImage: string }; }>({ currentBranchId: '', isImageEdit: false, @@ -803,6 +815,7 @@ export const useEmployeeForm = defineStore('form-employee', () => { infoEmployeePersonCard: [], formDataEmployeeOwner: undefined, ocr: false, + imageList: { list: [], selectedImage: '' }, }); const defaultFormData: EmployeeCreate & { image?: File } = { @@ -955,6 +968,7 @@ export const useEmployeeForm = defineStore('form-employee', () => { state.value.currentIndexVisa = -1; state.value.currentIndexCheckup = -1; state.value.currentIndexWorkHistory = -1; + state.value.imageList = { list: [], selectedImage: '' }; // state.value.currentTab = 'personalInfo'; if (clean) { state.value.formDataEmployeeOwner = undefined; @@ -1384,12 +1398,10 @@ export const useEmployeeForm = defineStore('form-employee', () => { statusSave: true, })), ), - employeeOtherInfo: structuredClone( - { - ...payload.employeeOtherInfo, - statusSave: !!payload.employeeOtherInfo?.id ? true : false, - } || {}, - ), + employeeOtherInfo: structuredClone({ + ...(payload.employeeOtherInfo ?? {}), + statusSave: true, + }), employeeWork: structuredClone( payload.employeeWork?.length === 0 ? state.value.dialogModal @@ -1663,6 +1675,7 @@ export const useEmployeeForm = defineStore('form-employee', () => { state, currentFromDataEmployee, resetEmployeeData, + addPassport, addVisa, addCheckup, diff --git a/src/pages/05_quotation/MainPage.vue b/src/pages/05_quotation/MainPage.vue index b9b57354..1f2f073d 100644 --- a/src/pages/05_quotation/MainPage.vue +++ b/src/pages/05_quotation/MainPage.vue @@ -60,7 +60,6 @@ const flowStore = useFlowStore(); const userBranch = useMyBranch(); const navigatorStore = useNavigator(); const customerStore = useCustomerStore(); - const { fetchListOfOptionBranch, customerFormUndo, @@ -76,6 +75,7 @@ const { const { state: customerFormState, currentFormData: customerFormData, + currentBranchRootId, registerAbleBranchOption, tabFieldRequired, } = storeToRefs(customerFormStore); @@ -89,6 +89,8 @@ const fieldSelectedOption = computed(() => { value: v.name, })); }); + +const keyAddDialog = ref(0); const special = ref(false); const branchId = ref(''); const agentPrice = ref(false); @@ -865,6 +867,7 @@ async function filterBySellerId() { @@ -999,6 +1003,7 @@ async function filterBySellerId() { () => { customerFormState.dialogModal = false; onCreateImageList = { selectedImage: '', list: [] }; + keyAddDialog++; } " > @@ -1190,7 +1195,7 @@ async function filterBySellerId() { customerFormData.customerBranch[0].legalPersonNo " v-model:business-type=" - customerFormData.customerBranch[0].businessType + customerFormData.customerBranch[0].businessTypeId " v-model:job-position=" customerFormData.customerBranch[0].jobPosition @@ -1271,7 +1276,6 @@ async function filterBySellerId() { res = await customerStore.createBranch({ ...customerFormData.customerBranch[idx], customerId: customerFormState.editCustomerId || '', - id: undefined, }); } if (res) { diff --git a/src/pages/05_quotation/PaymentForm.vue b/src/pages/05_quotation/PaymentForm.vue index 64f57560..2d5f1579 100644 --- a/src/pages/05_quotation/PaymentForm.vue +++ b/src/pages/05_quotation/PaymentForm.vue @@ -3,7 +3,8 @@ import { baseUrl, dialog } from 'stores/utils'; import { storeToRefs } from 'pinia'; import { useConfigStore } from 'stores/config'; import { formatNumberDecimal } from 'stores/utils'; -import { MainButton } from 'components/button'; +import { SaveButton, EditButton, UndoButton } from 'components/button'; +import SelectInput from 'src/components/shared/SelectInput.vue'; import { useI18n } from 'vue-i18n'; @@ -38,8 +39,15 @@ const firstCodePayment = defineModel('firstCodePayment'); const refQFile = ref[]>([]); const refQMenu = ref[]>([]); const paymentData = ref([]); -const submitPaymentData = ref([]); -const payAll = ref(false); +const formPaymentMethod = ref< + { + id: string; + channel: string | null; + reference: string | null; + account: string | null; + isEdit?: boolean; + }[] +>([]); const paymentStatusOpts = [ { status: 'PaymentInProcess', @@ -188,18 +196,29 @@ async function selectStatus( refQMenu.value[index].hide(); } -async function triggerSubmit() { - submitPaymentData.value.forEach(async (p) => { - const payload: PaymentPayload = { - paymentStatus: p.paymentStatus, - date: new Date(p.date), - amount: p.amount, - }; - await quotationPayment.updateQuotationPayment(p.id, payload); - }); +async function triggerSubmit(id: string) { + const index = paymentData.value.findIndex((p) => p.id === id); + if (index === -1) return; + + const p = paymentData.value[index]; + + const payload: PaymentPayload = { + paymentStatus: p.paymentStatus, + date: new Date(p.date), + amount: p.amount, + account: formPaymentMethod.value[index].account, + channel: formPaymentMethod.value[index].channel, + reference: formPaymentMethod.value[index].reference, + }; + + await quotationPayment.updateQuotationPayment(p.id, payload); + formPaymentMethod.value[index].isEdit = false; + setTimeout(async () => { + await fetchData(); + }, 300); } -onMounted(async () => { +async function fetchData() { if (!prop.data) return; const ret = await quotationPayment.getQuotationPayment({ quotationId: prop.isDebitNote === true ? undefined : prop.data.id, @@ -209,6 +228,13 @@ onMounted(async () => { }); if (ret) { paymentData.value = ret.result; + formPaymentMethod.value = ret.result.map((p, i) => ({ + id: p.id, + channel: p.channel, + reference: p.reference, + account: p.account, + isEdit: formPaymentMethod.value[i]?.isEdit || false, + })); slipFile.value = paymentData.value.map((v, i) => { if (i === 0) { firstCodePayment.value = v.code; @@ -219,474 +245,564 @@ onMounted(async () => { }; }); } +} + +onMounted(async () => { + await fetchData(); });