diff --git a/.github/workflows/local-build-dev.yaml b/.github/workflows/local-build-dev.yaml new file mode 100644 index 00000000..43d852cf --- /dev/null +++ b/.github/workflows/local-build-dev.yaml @@ -0,0 +1,31 @@ +name: local-build-dev + +# Intended for local network use. +# Remote access is possible if the host has a public IP address. + +on: + workflow_dispatch: + +env: + REGISTRY: ${{ vars.DOCKER_REGISTRY }} + +jobs: + local-build-dev: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + config-inline: | + [registry."${{ env.REGISTRY }}"] + http = true + insecure = true + - name: Build and Push Docker Image + uses: docker/build-push-action@v3 + with: + context: . + platforms: linux/amd64 + push: true + tags: ${{ env.REGISTRY }}/jws/jws-frontend:dev + allow: security.insecure diff --git a/.github/workflows/local-build-release.yaml b/.github/workflows/local-build-release.yaml new file mode 100644 index 00000000..a14cbadf --- /dev/null +++ b/.github/workflows/local-build-release.yaml @@ -0,0 +1,31 @@ +name: local-build-release + +# Intended for local network use. +# Remote access is possible if the host has a public IP address. + +on: + workflow_dispatch: + +env: + REGISTRY: ${{ vars.DOCKER_REGISTRY }} + +jobs: + local-build-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + config-inline: | + [registry."${{ env.REGISTRY }}"] + http = true + insecure = true + - name: Build and Push Docker Image + uses: docker/build-push-action@v3 + with: + context: . + platforms: linux/amd64 + push: true + tags: ${{ env.REGISTRY }}/jws/jws-frontend:latest + allow: security.insecure diff --git a/Dockerfile b/Dockerfile index 7438b0eb..1b6602f8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-slim as build-stage +FROM node:20-slim AS build-stage ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" @@ -19,7 +19,7 @@ ENV VITE_API_BASE_URL_OCR=ENV_API_BASE_URL_OCR RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile RUN pnpm run build -FROM alpine as production-stage +FROM alpine AS production-stage WORKDIR /app diff --git a/public/images/customer-CORP-avartar-female.png b/public/images/customer-CORP-avartar-female.png new file mode 100644 index 00000000..446ef866 Binary files /dev/null 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 new file mode 100644 index 00000000..6ad95d7d Binary files /dev/null and b/public/images/customer-CORP-avartar-male.png differ diff --git a/public/images/customer-CORP-avartar.png b/public/images/customer-CORP-avartar.png deleted file mode 100644 index 1c2f0dab..00000000 Binary files a/public/images/customer-CORP-avartar.png and /dev/null differ diff --git a/public/images/customer-PERS-avartar-female.png b/public/images/customer-PERS-avartar-female.png new file mode 100644 index 00000000..ca0a2bf1 Binary files /dev/null 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 new file mode 100644 index 00000000..e9fd15fe Binary files /dev/null and b/public/images/customer-PERS-avartar-male.png differ diff --git a/public/images/customer-PERS-avartar.png b/public/images/customer-PERS-avartar.png deleted file mode 100644 index 18f65135..00000000 Binary files a/public/images/customer-PERS-avartar.png and /dev/null differ diff --git a/public/images/employee-avatar.png b/public/images/employee-avatar.png index a68cc4d7..66ace3a0 100644 Binary files a/public/images/employee-avatar.png and b/public/images/employee-avatar.png differ diff --git a/public/images/quotation-avatar.png b/public/images/quotation-avatar.png new file mode 100644 index 00000000..754e33a2 Binary files /dev/null and b/public/images/quotation-avatar.png differ diff --git a/public/images/quotation-banner.png b/public/images/quotation-banner.png new file mode 100644 index 00000000..48219642 Binary files /dev/null and b/public/images/quotation-banner.png differ diff --git a/public/images/quotation-bg-avatar.png b/public/images/quotation-bg-avatar.png new file mode 100644 index 00000000..1b9b172e Binary files /dev/null and b/public/images/quotation-bg-avatar.png differ diff --git a/public/option/option.json b/public/option/option.json index a8eea85a..03336dad 100644 --- a/public/option/option.json +++ b/public/option/option.json @@ -854,6 +854,21 @@ "label": "Certificate of Identity", "value": "ci" } + ], + + "expenseType": [ + { + "label": "Service Fee", + "value": "serviceFee" + }, + { + "label": "Fee", + "value": "fee" + }, + { + "label": "Processing Fee", + "value": "processingFee" + } ] }, @@ -1712,6 +1727,21 @@ "label": "เอกสารสำคัญประจำตัวเพื่อใช้แทนหนังสือเดินทาง", "value": "ci" } + ], + + "expenseType": [ + { + "label": "ค่าบริการ", + "value": "serviceFee" + }, + { + "label": "ค่าธรรมเนียม", + "value": "fee" + }, + { + "label": "ค่าดำเนินการ", + "value": "processingFee" + } ] } } diff --git a/reports.json b/reports.json new file mode 100644 index 00000000..697c1be6 --- /dev/null +++ b/reports.json @@ -0,0 +1,167 @@ +{ + "config": { + "configFile": "/Users/linping/Desktop/Chamomind&FrappeT/JWS_TestScript/playwright.config.ts", + "rootDir": "/Users/linping/Desktop/Chamomind&FrappeT/JWS_TestScript/tests", + "forbidOnly": false, + "fullyParallel": true, + "globalSetup": null, + "globalTeardown": null, + "globalTimeout": 0, + "grep": {}, + "grepInvert": null, + "maxFailures": 0, + "metadata": { + "actualWorkers": 1 + }, + "preserveOutput": "always", + "reporter": [ + [ + "json", + { + "outputFile": "reports.json" + } + ] + ], + "reportSlowTests": { + "max": 5, + "threshold": 15000 + }, + "quiet": false, + "projects": [ + { + "outputDir": "/Users/linping/Desktop/Chamomind&FrappeT/JWS_TestScript/test-results", + "repeatEach": 1, + "retries": 0, + "metadata": {}, + "id": "chromium", + "name": "chromium", + "testDir": "/Users/linping/Desktop/Chamomind&FrappeT/JWS_TestScript/tests", + "testIgnore": [], + "testMatch": [ + "**/*.@(spec|test).?(c|m)[jt]s?(x)" + ], + "timeout": 30000 + } + ], + "shard": null, + "updateSnapshots": "missing", + "version": "1.44.1", + "workers": 1, + "webServer": null + }, + "suites": [ + { + "title": "01-Admin-BranchManagement/JWS_BM_001_CreateHeadquarters.spec.ts", + "file": "01-Admin-BranchManagement/JWS_BM_001_CreateHeadquarters.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "Login", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 4, + "status": "passed", + "duration": 3024, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2024-07-30T02:59:00.817Z", + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "8c5091bd59605f227965-8109f0f4a59e27330a76", + "file": "01-Admin-BranchManagement/JWS_BM_001_CreateHeadquarters.spec.ts", + "line": 16, + "column": 1 + }, + { + "title": "Create Branch Managenment", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 4, + "status": "passed", + "duration": 5091, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2024-07-30T02:59:05.659Z", + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "8c5091bd59605f227965-5a0d70f27623401a3479", + "file": "01-Admin-BranchManagement/JWS_BM_001_CreateHeadquarters.spec.ts", + "line": 27, + "column": 1 + }, + { + "title": "Create Branch Managenment Second", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 4, + "status": "passed", + "duration": 5029, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2024-07-30T02:59:10.755Z", + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "8c5091bd59605f227965-d619bd2184e7f07d4970", + "file": "01-Admin-BranchManagement/JWS_BM_001_CreateHeadquarters.spec.ts", + "line": 52, + "column": 1 + } + ] + } + ], + "errors": [], + "stats": { + "startTime": "2024-07-30T02:59:00.334Z", + "duration": 15556.794999999925, + "expected": 3, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + } +} \ No newline at end of file diff --git a/src/boot/axios.ts b/src/boot/axios.ts index 87b93766..7f8767e3 100644 --- a/src/boot/axios.ts +++ b/src/boot/axios.ts @@ -3,6 +3,7 @@ import { boot } from 'quasar/wrappers'; import { getToken } from 'src/services/keycloak'; import { dialog } from 'stores/utils'; import useLoader from 'stores/loader'; +import useFlowStore from 'src/stores/flow'; declare module '@vue/runtime-core' { interface ComponentCustomProperties { @@ -32,6 +33,7 @@ function parseError( api.interceptors.request.use(async (config) => { useLoader().show(); config.headers.Authorization = `Bearer ${await getToken()}`; + config.headers['X-Rtid'] = useFlowStore().rtid; return config; }); diff --git a/src/components/01_branch-management/BranchCard.vue b/src/components/01_branch-management/BranchCard.vue index 84dcb12b..4c5d51a9 100644 --- a/src/components/01_branch-management/BranchCard.vue +++ b/src/components/01_branch-management/BranchCard.vue @@ -3,25 +3,22 @@ import { baseUrl } from 'stores/utils'; defineProps<{ inactive?: boolean; - color?: 'none' | 'hq' | 'br'; + i18nKey?: string; + color?: 'none' | 'hq' | 'br' | 'br-virtual'; data: { - branchLabelCode: string; + branchLabelCode?: string; branchLabelName: string; - branchLabelTel: string; - branchLabelAddress: string; - branchLabelType: string; - branchImgUrl: string; + taxNo?: string; + branchLabelTel?: string; + contactName?: string; + branchLabelAddress?: string; + branchImgUrl?: string; }; - + virtualBranch: boolean; metadata?: unknown; badgeField?: string[]; - fieldSelected?: ( - | 'orderNumber' - | 'branchLabelName' - | 'branchLabelAddress' - | 'branchLabelTel' - | 'branchLabelType' - )[]; + fieldSelected?: string[]; + footer?: boolean; }>(); @@ -36,12 +33,15 @@ defineProps<{ color !== 'hq' && color !== 'br' && (!color || color === 'none'), 'branch-card__hq': color === 'hq', 'branch-card__br': color === 'br', + 'branch-card__br-virtual': color === 'br-virtual', }" @click="$emit('open')" >
+ +
{{ data.branchLabelName }} - {{ data.branchLabelCode }} + + {{ data.branchLabelCode }} +
- +
+ +
+
+ + {{ + $t( + `${i18nKey || 'branch.card'}.${virtualBranch ? 'branchVirtual' : 'branchLabel'}`, + ) + }} + +
-
-
{{ $t(`branch.card.${key}`) }}
-
{{ data[key as keyof typeof data] }}
-
+ +
@@ -189,6 +212,10 @@ defineProps<{ } } + &.branch-card__br-virtual { + --_branch-card-bg: var(--blue-6-hsl); + } + &.branch-card__inactive { --_branch-status-color: var(--red-4-hsl); --_branch-badge-bg: var(--red-4-hsl); diff --git a/src/components/01_branch-management/FormBank.vue b/src/components/01_branch-management/FormBank.vue index 219c6f6a..275b6ff3 100644 --- a/src/components/01_branch-management/FormBank.vue +++ b/src/components/01_branch-management/FormBank.vue @@ -8,10 +8,28 @@ import { QSelect } from 'quasar'; import { AddButton, DeleteButton } from 'components/button'; import ToggleButton from '../button/ToggleButton.vue'; +import ImageHover from '../ImageHover.vue'; + const optionStore = useOptionStore(); const bankBookList = defineModel('bankBookList', { default: [] }); +const bankQrUrl = ref([]); +const listIndex = ref(0); + +const reader = new FileReader(); +const inputFile = (() => { + const _element = document.createElement('input'); + _element.type = 'file'; + _element.accept = 'image/*'; + _element.addEventListener('change', change); + return _element; +})(); +reader.addEventListener('load', () => { + if (typeof reader.result === 'string') + bankQrUrl.value[listIndex.value] = reader.result; +}); + defineProps<{ title?: string; dense?: boolean; @@ -20,6 +38,11 @@ defineProps<{ view?: boolean; }>(); +defineEmits<{ + (e: 'viewQr', index: number): void; + (e: 'editQr', index: number): void; +}>(); + const bankBookOptions = ref[]>([]); let bankBoookFilter: ( value: string, @@ -43,6 +66,15 @@ function addBankBook() { }); } +function change(e: Event) { + const _element = e.target as HTMLInputElement | null; + const _file = _element?.files?.[0]; + if (_file) { + bankBookList.value[listIndex.value].bankQr = _file; + reader.readAsDataURL(_file); + } +} + onMounted(() => { if (optionStore.globalOption) { bankBoookFilter = selectFilterOptionRefMod( @@ -99,7 +131,7 @@ watch(
@@ -129,144 +161,132 @@ watch( v-if="bankBookList.length !== 1 && !readonly" id="btn-delete-bank" icon-only - @click="deleteItem(bankBookList, i)" + @click=" + () => { + deleteItem(bankBookList, i); + bankQrUrl[i] = ''; + } + " />
- - - - - - - - - - - - - + + + + +
+ + diff --git a/src/components/01_branch-management/FormBranchContact.vue b/src/components/01_branch-management/FormBranchContact.vue index 5f266b4d..b878cacd 100644 --- a/src/components/01_branch-management/FormBranchContact.vue +++ b/src/components/01_branch-management/FormBranchContact.vue @@ -32,13 +32,22 @@ defineProps<{
+import { isRoleInclude } from 'src/stores/utils'; +import DatePicker from '../shared/DatePicker.vue'; + const code = defineModel('code'); const branchCount = defineModel('branchCount', { default: 0 }); const codeSubBranch = defineModel('codeSubBranch'); @@ -7,6 +10,11 @@ const name = defineModel('name'); const abbreviation = defineModel('abbreviation'); const nameEN = defineModel('nameEN'); const typeBranch = defineModel('typeBranch'); +const virtual = defineModel('virtual'); + +const permitExpireDate = defineModel('permitExpireDate'); +const permitIssueDate = defineModel('permitIssueDate'); +const permitNo = defineModel('permitNo'); defineProps<{ title?: string; @@ -42,7 +50,6 @@ function formatCode(input: string | undefined, type: 'code' | 'number') {
- - - @@ -122,7 +115,6 @@ function formatCode(input: string | undefined, type: 'code' | 'number') {
+ + + + +
+ +
+ + + + +
diff --git a/src/components/01_branch-management/FormQr.vue b/src/components/01_branch-management/FormQr.vue index 552f0a7e..a03fc626 100644 --- a/src/components/01_branch-management/FormQr.vue +++ b/src/components/01_branch-management/FormQr.vue @@ -1,5 +1,5 @@ - (); const employeeOther = defineModel('employeeOther'); @@ -31,7 +32,7 @@ const employeeOther = defineModel('employeeOther'); style="background-color: var(--surface-3)" /> {{ $t('customerEmployee.form.group.family') }} -
+
('employeeOther');
('employeeOther');
('employeeOther'); v-model="employeeOther.fatherFirstName" /> ('employeeOther'); v-model="employeeOther.fatherLastName" /> ('employeeOther'); v-model="employeeOther.fatherFirstNameEN" /> ('employeeOther'); v-model="employeeOther.fatherLastNameEN" /> ('employeeOther');
('employeeOther'); v-model="employeeOther.motherFirstName" /> ('employeeOther'); v-model="employeeOther.motherLastName" /> ('employeeOther'); v-model="employeeOther.motherFirstNameEN" /> ('employeeOther'); v-model="employeeOther.motherLastNameEN" /> - {{ $t(`${title}`) }} + {{ title }}
@@ -111,7 +111,6 @@ watch( input-debounce="0" option-value="value" option-label="label" - lazy-rules="ondemand" v-model="passportType" class="col-12" :class="{ 'col-md-3': !ocr }" @@ -122,8 +121,11 @@ watch( :for="`${prefixId}-select-passport-type`" :label="$t('customerEmployee.form.passportType')" :rules="[ - (val: string) => - !!val || $t('selectValidate') + $t('formDialogInputPassportType'), + (val) => + (val && val.length > 0) || + $t('form.error.selectField', { + field: $t('customerEmployee.form.passportType'), + }), ]" @filter="passportTypeFilter" > @@ -136,7 +138,6 @@ watch( @@ -227,9 +221,11 @@ watch( :class="{ 'col-md-3': !ocr }" :readonly="readonly" :rules="[ - (val: string) => - !!val || - $t('selectValidate') + $t('formDialogInputPassportIssuance'), + (val) => + (val && val.length > 0) || + $t('form.error.selectField', { + field: $t('customerEmployee.form.passportIssueDate'), + }), ]" />
diff --git a/src/components/03_customer-management/FormEmployeeVisa.vue b/src/components/03_customer-management/FormEmployeeVisa.vue index 90708552..ac8296bd 100644 --- a/src/components/03_customer-management/FormEmployeeVisa.vue +++ b/src/components/03_customer-management/FormEmployeeVisa.vue @@ -112,7 +112,6 @@ watch( input-debounce="0" option-value="value" option-label="label" - lazy-rules="ondemand" :class="{ 'col-4': !ocr, 'col-6': ocr }" :dense="dense" :readonly="readonly" @@ -140,7 +139,6 @@ watch( !!val || $t('selectValidate') + $t('formDialogInputVisaType'), ]" --> import { onMounted, ref } from 'vue'; -import { dateFormat, parseAndFormatDate } from 'src/utils/datetime'; import { EmployeeWorkCreate } from 'stores/employee/types'; -import { useI18n } from 'vue-i18n'; -import { checkTabBeforeAdd, selectFilterOptionRefMod } from 'stores/utils'; +import { selectFilterOptionRefMod } from 'stores/utils'; import DatePicker from '../shared/DatePicker.vue'; import { @@ -13,7 +11,6 @@ import { SaveButton, UndoButton, } from 'components/button'; -const { locale } = useI18n(); const currentIndex = defineModel('currentIndex'); const employeeWork = defineModel('employeeWork'); @@ -38,6 +35,7 @@ defineProps<{ outlined?: boolean; readonly?: boolean; prefixId: string; + hideAction?: boolean; }>(); defineEmits<{ @@ -62,7 +60,10 @@ function addData() { ownerName: '', remark: '', }); - if (employeeWork.value) tab.value = `tab${employeeWork.value.length - 1}`; + if (employeeWork.value) { + tab.value = `tab${employeeWork.value.length - 1}`; + currentIndex.value = employeeWork.value.length - 1; + } } } @@ -116,7 +117,7 @@ const workplaceFilter = selectFilterOptionRefMod( /> {{ $t(`customerEmployee.formWorkHistory.title`) }} {{ $t('general.times', { number: index + 1 }) }} -
+
import { calculateAge, dateFormat } from 'src/utils/datetime'; +import { baseUrl } from 'src/stores/utils'; import PersonCard from 'components/shared/PersonCard.vue'; import KebabAction from '../shared/KebabAction.vue'; @@ -121,7 +122,7 @@ defineEmits<{ {
{ (val: string) => !!val || $t('form.error.selectField', { - field: $t('customer.form.employerBranch'), + field: $t('customerEmployee.branch'), }), ]" > @@ -274,7 +273,7 @@ onMounted(() => { '-' + ' ' + scope.opt.customer.lastName }} - {{ $t('address') }} + {{ $t('general.address') }} {{ $i18n.locale === 'eng' ? `${scope.opt.addressEN || ''} ${scope.opt.subDistrict.nameEN || ''} ${scope.opt.district.nameEN || ''} ${scope.opt.province.nameEN || ''}` @@ -297,7 +296,6 @@ onMounted(() => { { /> import { QSelect } from 'quasar'; -import { getRole } from 'src/services/keycloak'; +import useOptionStore from 'src/stores/options'; import { selectFilterOptionRefMod } from 'stores/utils'; -import { ref, onMounted } from 'vue'; +import { ref, onMounted, watch } from 'vue'; import { useI18n } from 'vue-i18n'; const { locale } = useI18n({ useScope: 'global' }); +const optionStore = useOptionStore(); + const remark = defineModel('remark', { default: '' }); const detail = defineModel('detail', { default: '' }); const process = defineModel('process'); const name = defineModel('name'); const code = defineModel('code'); +const expenseType = defineModel('expenseType'); -const registeredBranchId = defineModel('registeredBranchId'); const codeOption = ref<{ id: string; name: string }[]>([]); -const optionsBranch = defineModel<{ id: string; name: string }[]>( - 'optionsBranch', - { default: [] }, -); defineProps<{ dense?: boolean; @@ -42,11 +40,31 @@ onMounted(async () => { const codeOptions = ref[]>([]); const codeFilter = selectFilterOptionRefMod(codeOption, codeOptions, 'label'); -const branchOptions = ref[]>([]); -const branchFilter = selectFilterOptionRefMod( - optionsBranch, - branchOptions, - 'name', +const expenseTypeOptions = ref[]>([]); +let expenseTypeFilter: ( + value: string, + update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void, +) => void; + +onMounted(() => { + if (optionStore.globalOption) { + expenseTypeFilter = selectFilterOptionRefMod( + ref(optionStore.globalOption.expenseType), + expenseTypeOptions, + 'label', + ); + } +}); + +watch( + () => optionStore.globalOption, + () => { + expenseTypeFilter = selectFilterOptionRefMod( + ref(optionStore.globalOption.expenseType), + expenseTypeOptions, + 'label', + ); + }, ); @@ -67,7 +85,6 @@ const branchFilter = selectFilterOptionRefMod(
+ + + + + - - - -
('remark'); @@ -12,8 +13,7 @@ const code = defineModel('code'); const serviceCode = defineModel('serviceCode'); const serviceName = defineModel('serviceNameTh'); const serviceDescription = defineModel('serviceDescription'); - -const registeredBranchId = defineModel('registeredBranchId'); +const registeredBranchId = defineModel('registeredBranchId'); const optionsBranch = defineModel<{ id: string; name: string }[]>( 'optionsBranch', @@ -31,18 +31,21 @@ defineProps<{ }>(); const branchOptions = ref[]>([]); -const branchFilter = selectFilterOptionRefMod( +let branchFilter = selectFilterOptionRefMod( optionsBranch, branchOptions, 'name', ); -const serviceCodeOpts = ref([{ label: 'mou', value: 'mou' }]); -const serviceCodeOptions = ref[]>([]); -const serviceCodeFilter = selectFilterOptionRefMod( - serviceCodeOpts, - serviceCodeOptions, - 'label', +watch( + () => optionsBranch.value, + () => { + branchFilter = selectFilterOptionRefMod( + optionsBranch, + branchOptions, + 'name', + ); + }, ); @@ -60,122 +63,17 @@ const serviceCodeFilter = selectFilterOptionRefMod( {{ $t(`form.field.basicInformation`) }}
- - - - -
- -
- - - + + + + +
+ +
+ + {{ $t(`productService.service.serviceProperties`) }} - ('serviceCharge'); const agentPrice = defineModel('agentPrice'); const price = defineModel('price'); +const vatIncluded = defineModel('vatIncluded'); withDefaults( defineProps<{ @@ -38,11 +39,44 @@ withDefaults( style="background-color: var(--surface-3)" /> {{ $t('productService.product.priceInformation') }} + +
+ + {{ $t('productService.product.vatIncluded') }} + + + {{ $t('productService.product.vatExcluded') }} + +
- + diff --git a/src/components/04_product-service/ServiceProperties.vue b/src/components/04_product-service/ServiceProperties.vue index f43cdc7d..235180b1 100644 --- a/src/components/04_product-service/ServiceProperties.vue +++ b/src/components/04_product-service/ServiceProperties.vue @@ -17,25 +17,25 @@ const formServiceProperties = defineModel('formServiceProperties'); const typeOption = ref([ { - label: t('text'), + label: 'Text', value: 'string', color: 'var(--pink-6-hsl)', icon: 'mdi-alpha-t', }, { - label: t('number'), + label: 'Number', value: 'number', color: 'var(--purple-11-hsl)', icon: 'mdi-numeric', }, { - label: t('date'), + label: 'Date', value: 'date', color: 'var(--green-9-hsl)', icon: 'mdi-calendar-blank-outline', }, { - label: t('selection'), + label: 'Selection', value: 'array', color: 'var(--indigo-7-hsl)', icon: 'mdi-code-array', @@ -223,7 +223,8 @@ function confirmDelete(items: unknown[], index: number) {
- +
- + {{ $t('form.useComma') }}
{{ $t('form.decimal') }}
(import.meta.env.VITE_API_BASE_URL); const productServiceStore = useProductServiceStore(); @@ -58,10 +58,26 @@ defineEmits<{ (e: 'manageWorkName'): void; (e: 'workProperties'): void; }>(); + +watch( + () => workNameItems.value, + (c, o) => { + const list = c.map((v: { name: string }) => v.name); + const oldList = o.map((v: { name: string }) => v.name); + const index = oldList.indexOf(workName.value); + + if (list[index] !== oldList[index] && !list.includes(workName.value)) { + if (list.length - 1 === index - 1) workName.value = list[index - 1]; + else workName.value = list[index]; + } + }, +);