Compare commits

..

No commits in common. "develop" and "version-0.11.14" have entirely different histories.

193 changed files with 8301 additions and 14106 deletions

View file

@ -22,7 +22,6 @@
"apexcharts": "^4.5.0", "apexcharts": "^4.5.0",
"axios": "^1.8.4", "axios": "^1.8.4",
"cropperjs": "^1.6.2", "cropperjs": "^1.6.2",
"dayjs": "^1.11.13",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"keycloak-js": "^25.0.6", "keycloak-js": "^25.0.6",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",

8
pnpm-lock.yaml generated
View file

@ -29,9 +29,6 @@ importers:
cropperjs: cropperjs:
specifier: ^1.6.2 specifier: ^1.6.2
version: 1.6.2 version: 1.6.2
dayjs:
specifier: ^1.11.13
version: 1.11.13
highlight.js: highlight.js:
specifier: ^11.11.1 specifier: ^11.11.1
version: 11.11.1 version: 11.11.1
@ -1476,9 +1473,6 @@ packages:
date-fns@3.6.0: date-fns@3.6.0:
resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
dayjs@1.11.13:
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
de-indent@1.0.2: de-indent@1.0.2:
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
@ -5278,8 +5272,6 @@ snapshots:
date-fns@3.6.0: {} date-fns@3.6.0: {}
dayjs@1.11.13: {}
de-indent@1.0.2: {} de-indent@1.0.2: {}
debug@2.6.9: debug@2.6.9:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 151 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Before After
Before After

View file

@ -183,15 +183,15 @@
"prefix": [ "prefix": [
{ {
"label": "MR", "label": "Mr",
"value": "mr" "value": "mr"
}, },
{ {
"label": "MRS", "label": "Mrs",
"value": "mrs" "value": "mrs"
}, },
{ {
"label": "MISS", "label": "Miss",
"value": "miss" "value": "miss"
} }
], ],

View file

@ -31,7 +31,7 @@ export default defineConfig((ctx) => {
devServer: { devServer: {
host: '0.0.0.0', host: '0.0.0.0',
open: false, open: false,
port: 5174, port: 5173,
}, },
framework: { framework: {
config: {}, config: {},

View file

@ -89,7 +89,15 @@ defineProps<{
</div> </div>
</div> </div>
</div> </div>
<q-separator /> <div
style="
display: block;
width: 100%;
height: 1px;
background: hsla(0 0% 0% / 0.1);
margin-bottom: var(--size-2);
"
/>
<slot name="data"></slot> <slot name="data"></slot>
<template v-if="!$slots.data"> <template v-if="!$slots.data">
<div <div

View file

@ -177,7 +177,7 @@ watch(
<div <div
v-if="!single" v-if="!single"
class="bordered q-mr-sm rounded col-4 text-center overflow-hidden" class="bordered q-mr-sm rounded col text-center overflow-hidden"
:class="{ 'pointer-none': readonly, 'q-my-sm': $q.screen.lt.md }" :class="{ 'pointer-none': readonly, 'q-my-sm': $q.screen.lt.md }"
> >
<ImageHover <ImageHover

View file

@ -159,6 +159,42 @@ function formatCode(input: string | undefined, type: 'code' | 'number') {
]" ]"
for="input-name-en" for="input-name-en"
/> />
<q-select
v-if="
typeBranch !== 'headOffice' &&
isRoleInclude(['head_of_admin', 'head_of_account'])
"
outlined
use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space
input-debounce="0"
option-label="label"
option-value="value"
class="col-2"
dense
for="input-branch-status"
:readonly="readonly || isRoleInclude(['head_of_account'])"
:options="['Virtual', 'Branch']"
:hide-dropdown-icon="readonly"
:label="$t('general.branchStatus')"
:model-value="virtual ? 'Virtual' : 'Branch'"
@update:model-value="(v) => (virtual = v === 'Virtual')"
:rules="[(val) => val && val.length > 0]"
:error-message="$t('form.error.required')"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
{{ $t('general.noData') }}
</q-item-section>
</q-item>
</template>
</q-select>
</div> </div>
<div class="col-12 row q-col-gutter-sm"> <div class="col-12 row q-col-gutter-sm">

View file

@ -29,18 +29,19 @@ const discountCondition = defineModel<string | null | undefined>(
const sourceNationality = defineModel<string | null | undefined>( const sourceNationality = defineModel<string | null | undefined>(
'sourceNationality', 'sourceNationality',
); );
const importNationality = defineModel<string[] | null | undefined>( const importNationality = defineModel<string | null | undefined>(
'importNationality', 'importNationality',
); );
const trainingPlace = defineModel<string | null | undefined>('trainingPlace'); const trainingPlace = defineModel<string | null | undefined>('trainingPlace');
const checkpoint = defineModel<string | null | undefined>('checkpoint'); const checkpoint = defineModel<string | null | undefined>('checkpoint');
const userFile = defineModel<File[]>('userFile'); const agencyFile = defineModel<File[]>('agencyFile');
const userFileList = const agencyFileList =
defineModel<{ name: string; url: string }[]>('userFileList'); defineModel<{ name: string; url: string }[]>('agencyFileList');
const remark = defineModel<string | null | undefined>('remark'); const remark = defineModel<string | null | undefined>('remark');
const agencyStatus = defineModel<string | null | undefined>('agencyStatus'); const agencyStatus = defineModel<string | null | undefined>('agencyStatus');
const attachmentRef = ref(); const attachmentRef = ref();
const checkpointENOption = ref([]);
defineProps<{ defineProps<{
dense?: boolean; dense?: boolean;
@ -70,12 +71,18 @@ function deleteFile(name: string) {
userStore.deleteAttachment(userId.value, payload); userStore.deleteAttachment(userId.value, payload);
const result = await userStore.fetchAttachment(userId.value); const result = await userStore.fetchAttachment(userId.value);
if (result) { if (result) {
userFileList.value = result; agencyFileList.value = result;
} }
}, },
cancel: () => {}, cancel: () => {},
}); });
} }
onMounted(async () => {
const resultOption = await fetch('/option/option.json');
const rawOption = await resultOption.json();
checkpointENOption.value = rawOption.eng.border;
});
</script> </script>
<template> <template>
<div class="row col-12"> <div class="row col-12">
@ -133,12 +140,11 @@ function deleteFile(name: string) {
/> />
<SelectOffice <SelectOffice
v-if="userType === 'MESSENGER'"
for="input-responsible-area" for="input-responsible-area"
v-model:value="responsibleArea" v-model:value="responsibleArea"
v-if="userType === 'MESSENGER'"
:readonly="readonly" :readonly="readonly"
:label="$t('personnel.form.responsibleArea')" :label="$t('personnel.form.responsibleArea')"
class="col"
/> />
</div> </div>
<div <div
@ -179,27 +185,38 @@ function deleteFile(name: string) {
(v) => (typeof v === 'string' ? (sourceNationality = v) : '') (v) => (typeof v === 'string' ? (sourceNationality = v) : '')
" "
/> />
<SelectInput <SelectInput
v-model="importNationality" :model-value="readonly ? importNationality || '-' : importNationality"
id="input-import-nationality" id="input-import-nationality"
for="input-import-nationality" for="input-import-nationality"
:option="optionStore.globalOption.nationality" :option="optionStore.globalOption.nationality"
class="col-md-3 col-6" class="col-md-3 col-6"
:readonly :readonly
multiple
:hideSelected="false"
clearable clearable
fillInput
:label="$t('personnel.form.importNationality')" :label="$t('personnel.form.importNationality')"
@update:model-value="
(v) => (typeof v === 'string' ? (importNationality = v) : '')
"
/>
<SelectInput
:model-value="readonly ? trainingPlace || '-' : trainingPlace"
id="select-trainig-place"
for="select-trainig-place"
:option="optionStore.globalOption.training"
class="col-md-6 col-12"
:readonly
:label="$t('personnel.form.trainingPlace')"
clearable
@update:model-value="
(v) => (typeof v === 'string' ? (trainingPlace = v) : '')
"
/> />
<SelectInput <SelectInput
:model-value="readonly ? checkpoint || '-' : checkpoint" :model-value="readonly ? checkpoint || '-' : checkpoint"
id="select-checkpoint" id="select-checkpoint"
for="select-checkpoint" for="select-checkpoint"
:option="optionStore.globalOption.border" :option="optionStore.globalOption.border"
class="col-md-6 col-12" class="col-6"
:readonly :readonly
:label="$t('personnel.form.checkpoint')" :label="$t('personnel.form.checkpoint')"
clearable clearable
@ -207,18 +224,17 @@ function deleteFile(name: string) {
(v) => (typeof v === 'string' ? (checkpoint = v) : '') (v) => (typeof v === 'string' ? (checkpoint = v) : '')
" "
/> />
<SelectInput <SelectInput
:model-value="readonly ? trainingPlace || '-' : trainingPlace" :model-value="readonly ? checkpoint || '-' : checkpoint"
id="select-trainig-place" id="select-checkpoint-en"
for="select-trainig-place" for="select-checkpoint-en"
:option="optionStore.globalOption.training" :option="checkpointENOption"
class="col-md-8 col-12" class="col-6"
:readonly :readonly
:label="$t('personnel.form.trainingPlace')" :label="$t('personnel.form.checkpointEN')"
clearable clearable
@update:model-value=" @update:model-value="
(v) => (typeof v === 'string' ? (trainingPlace = v) : '') (v) => (typeof v === 'string' ? (checkpoint = v) : '')
" "
/> />
@ -237,7 +253,7 @@ function deleteFile(name: string) {
value: AgencyStatus.Blacklist, value: AgencyStatus.Blacklist,
}, },
]" ]"
class="col-md-4 col-12" class="col-md-6 col-12"
:readonly :readonly
:label="$t('personnel.form.agencyStatus')" :label="$t('personnel.form.agencyStatus')"
clearable clearable
@ -259,10 +275,7 @@ function deleteFile(name: string) {
" "
@clear="remark = ''" @clear="remark = ''"
/> />
</div>
<q-file <q-file
v-if="userType && !readonly"
ref="attachmentRef" ref="attachmentRef"
for="input-attachment" for="input-attachment"
:dense="dense" :dense="dense"
@ -272,7 +285,7 @@ function deleteFile(name: string) {
append append
:label="$t('personnel.form.attachment')" :label="$t('personnel.form.attachment')"
class="col" class="col"
v-model="userFile" v-model="agencyFile"
> >
<template v-slot:prepend> <template v-slot:prepend>
<Icon <Icon
@ -299,12 +312,12 @@ function deleteFile(name: string) {
</template> </template>
</q-file> </q-file>
<div v-if="userFileList && userFileList?.length > 0" class="col-12"> <div v-if="agencyFileList && agencyFileList?.length > 0" class="col-12">
<q-list bordered separator class="rounded" style="padding: 0"> <q-list bordered separator class="rounded" style="padding: 0">
<q-item <q-item
id="attachment-file" id="attachment-file"
for="attachment-file" for="attachment-file"
v-for="item in userFileList" v-for="item in agencyFileList"
clickable clickable
:key="item.url" :key="item.url"
class="items-center row" class="items-center row"
@ -334,4 +347,5 @@ function deleteFile(name: string) {
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>

View file

@ -1,8 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { QSelect } from 'quasar';
import useOptionStore from 'stores/options'; import useOptionStore from 'stores/options';
import { selectFilterOptionRefMod } from 'stores/utils';
import { calculateAge, disabledAfterToday } from 'src/utils/datetime'; import { calculateAge, disabledAfterToday } from 'src/utils/datetime';
import { watch } from 'vue'; import { ref, onMounted, watch } from 'vue';
import SelectInput from '../shared/SelectInput.vue'; import { capitalize } from 'vue';
import DatePicker from '../shared/DatePicker.vue'; import DatePicker from '../shared/DatePicker.vue';
const optionStore = useOptionStore(); const optionStore = useOptionStore();
@ -21,8 +23,6 @@ const midNameEN = defineModel<string | null>('midNameEn');
const citizenId = defineModel<string>('citizenId'); const citizenId = defineModel<string>('citizenId');
const citizenIssue = defineModel<Date | null>('citizenIssue'); const citizenIssue = defineModel<Date | null>('citizenIssue');
const citizenExpire = defineModel<Date | null>('citizenExpire'); const citizenExpire = defineModel<Date | null>('citizenExpire');
const contactName = defineModel<string>('contactName');
const contactTel = defineModel<string>('contactTel');
const props = defineProps<{ const props = defineProps<{
dense?: boolean; dense?: boolean;
@ -30,19 +30,94 @@ const props = defineProps<{
readonly?: boolean; readonly?: boolean;
separator?: boolean; separator?: boolean;
employee?: boolean; employee?: boolean;
agency?: boolean;
title?: string; title?: string;
prefixId: string; prefixId: string;
hideNameEn?: boolean; hideNameEn?: boolean;
}>(); }>();
function matchPreFixName() { const prefixNameOptions = ref<Record<string, unknown>[]>([]);
let prefixNameFilter: (
value: string,
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
) => void;
const prefixNameOptionsEn = ref<Record<string, unknown>[]>([]);
let prefixNameFilterEn: (
value: string,
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
) => void;
const genderOptions = ref<Record<string, unknown>[]>([]);
let genderFilter: (
value: string,
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
) => void;
const nationalityOptions = ref<Record<string, unknown>[]>([]);
let nationalityFilter: (
value: string,
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
) => void;
function matPreFixName() {
if (gender.value === 'male') prefixName.value = 'mr'; if (gender.value === 'male') prefixName.value = 'mr';
if (gender.value === 'female' && prefixName.value === 'mr') { if (gender.value === 'female' && prefixName.value === 'mr') {
prefixName.value = 'mrs'; prefixName.value = 'mrs';
} }
} }
onMounted(() => {
prefixNameFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.prefix),
prefixNameOptions,
'label',
);
prefixNameFilterEn = selectFilterOptionRefMod(
ref(optionStore.rawOption?.eng.prefix),
prefixNameOptionsEn,
'label',
);
genderFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.gender),
genderOptions,
'label',
);
nationalityFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.nationality),
nationalityOptions,
'label',
);
});
watch(
() => optionStore.globalOption,
() => {
prefixNameFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption.prefix),
prefixNameOptions,
'label',
);
genderFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption.gender),
genderOptions,
'label',
);
nationalityFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption.nationality),
nationalityOptions,
'label',
);
prefixNameFilterEn = selectFilterOptionRefMod(
ref(optionStore.rawOption?.eng.prefix),
prefixNameOptionsEn,
'label',
);
},
);
watch( watch(
() => prefixName.value, () => prefixName.value,
(v) => { (v) => {
@ -56,7 +131,7 @@ watch(
() => gender.value, () => gender.value,
() => { () => {
if (props.readonly) return; if (props.readonly) return;
matchPreFixName(); matPreFixName();
}, },
); );
</script> </script>
@ -96,19 +171,41 @@ watch(
for="input-citizen-id" for="input-citizen-id"
/> />
<div class="col-12 row" style="display: flex; gap: var(--size-2)"> <div class="col-12 row" style="display: flex; gap: var(--size-2)">
<SelectInput <q-select
outlined
use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space
input-debounce="0"
option-label="label"
option-value="value"
hide-dropdown-icon hide-dropdown-icon
:readonly autocomplete="off"
:option="optionStore.globalOption?.prefix"
:id="`${prefixId}-select-prefix-name`"
:for="`${prefixId}-select-prefix-name`"
:rules="
agency ? [] : [(val: string) => !!val || $t('form.error.required')]
"
:label="$t('personnel.form.prefixName')"
class="col-md-1 col-6" class="col-md-1 col-6"
v-model="prefixName" :dense="dense"
/> :readonly="readonly"
:options="prefixNameOptions"
:for="`${prefixId}-select-prefix-name`"
:label="$t('personnel.form.prefixName')"
@filter="prefixNameFilter"
:model-value="readonly ? prefixName || '-' : prefixName"
@update:model-value="
(v) => (typeof v === 'string' ? (prefixName = v) : '')
"
@clear="prefixName = ''"
:rules="[(val: string) => !!val || $t('form.error.required')]"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
{{ $t('general.noData') }}
</q-item-section>
</q-item>
</template>
</q-select>
<q-input <q-input
:for="`${prefixId}-input-first-name`" :for="`${prefixId}-input-first-name`"
@ -120,7 +217,7 @@ watch(
:label="$t('personnel.form.firstName')" :label="$t('personnel.form.firstName')"
v-model="firstName" v-model="firstName"
:rules=" :rules="
employee || agency employee
? [] ? []
: [(val: string) => !!val || $t('form.error.required')] : [(val: string) => !!val || $t('form.error.required')]
" "
@ -158,17 +255,41 @@ watch(
class="col-12 row" class="col-12 row"
style="display: flex; gap: var(--size-2)" style="display: flex; gap: var(--size-2)"
> >
<SelectInput <q-select
outlined
use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space
input-debounce="0"
option-label="label"
option-value="value"
hide-dropdown-icon hide-dropdown-icon
:readonly autocomplete="off"
:option="optionStore.rawOption?.eng.prefix"
:id="`${prefixId}-select-prefix-name-en`"
:for="`${prefixId}-select-prefix-name-en`"
:rules="[(val: string) => !!val || $t('form.error.required')]"
label="Prefix"
class="col-md-1 col-6" class="col-md-1 col-6"
v-model="prefixName" :dense="dense"
/> :readonly="readonly"
:options="prefixNameOptionsEn"
:for="`${prefixId}-select-prefix-name-en`"
label="Prefix"
@filter="prefixNameFilter"
:model-value="readonly ? prefixName || '-' : prefixName"
@update:model-value="
(v) => (typeof v === 'string' ? (prefixName = v) : '')
"
@clear="prefixName = ''"
:rules="[(val: string) => !!val || $t('form.error.required')]"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
{{ $t('general.noData') }}
</q-item-section>
</q-item>
</template>
</q-select>
<q-input <q-input
:for="`${prefixId}-input-first-name-en`" :for="`${prefixId}-input-first-name-en`"
@ -210,7 +331,13 @@ watch(
v-model="lastNameEN" v-model="lastNameEN"
:rules=" :rules="
employee employee
? [] ? [
(val: string) => !!val || $t('form.error.required'),
(val: string) =>
!val ||
/^[A-Za-z\s]+$/.test(val) ||
$t('form.error.letterOnly'),
]
: [ : [
(val: string) => (val: string) =>
!val || !val ||
@ -253,13 +380,16 @@ watch(
hide-bottom-space hide-bottom-space
:readonly="readonly" :readonly="readonly"
:label="$t('form.email')" :label="$t('form.email')"
:rules="[ :rules="
(val) => (val && val.length > 0) || $t('form.error.required'), readonly
? undefined
: [
(v: string) => (v: string) =>
!v || !v ||
/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g.test(v) || /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g.test(v) ||
$t('form.error.invalid'), $t('form.error.invalid'),
]" ]
"
class="col-md-3 col-6" class="col-md-3 col-6"
:model-value="readonly ? email || '-' : email" :model-value="readonly ? email || '-' : email"
@update:model-value="(v) => (typeof v === 'string' ? (email = v) : '')" @update:model-value="(v) => (typeof v === 'string' ? (email = v) : '')"
@ -275,16 +405,39 @@ watch(
</template> </template>
</q-input> </q-input>
<SelectInput <q-select
v-if="!employee" v-if="!employee"
:readonly outlined
:option="optionStore.globalOption?.gender" use-input
:id="`${prefixId}-select-gender`" fill-input
emit-value
map-options
hide-selected
hide-bottom-space
input-debounce="0"
option-label="label"
option-value="value"
autocomplete="off"
class="col-md-2 col-6"
:dense="dense"
:readonly="readonly"
:options="genderOptions"
:hide-dropdown-icon="readonly"
:for="`${prefixId}-select-gender`" :for="`${prefixId}-select-gender`"
:label="$t('form.gender')" :label="$t('form.gender')"
class="col-md-2 col-6" @filter="genderFilter"
v-model="gender" :model-value="readonly ? gender || '-' : gender"
/> @update:model-value="(v) => (typeof v === 'string' ? (gender = v) : '')"
@clear="gender = ''"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
{{ $t('general.noData') }}
</q-item-section>
</q-item>
</template>
</q-select>
<DatePicker <DatePicker
v-model="birthDate" v-model="birthDate"
@ -293,11 +446,15 @@ watch(
:readonly="readonly" :readonly="readonly"
:label="$t('form.birthDate')" :label="$t('form.birthDate')"
:disabled-dates="disabledAfterToday" :disabled-dates="disabledAfterToday"
:rules="[ :rules="
employee
? []
: [
(val: string) => (val: string) =>
!!val || !!val ||
$t('form.error.selectField', { field: $t('form.birthDate') }), $t('form.error.selectField', { field: $t('form.birthDate') }),
]" ]
"
/> />
<q-input <q-input
@ -357,67 +514,70 @@ watch(
" "
/> />
<SelectInput <q-select
v-if="employee" v-if="employee"
:readonly outlined
:option="optionStore.globalOption?.gender" clearable
:id="`${prefixId}-select-gender`" use-input
fill-input
emit-value
map-options
hide-selected
autocomplete="off"
hide-bottom-space
input-debounce="0"
option-label="label"
option-value="value"
class="col-md-2 col-6"
:dense="dense"
v-model="gender"
:readonly="readonly"
:options="genderOptions"
:hide-dropdown-icon="readonly"
:for="`${prefixId}-select-gender`" :for="`${prefixId}-select-gender`"
:label="$t('form.gender')" :label="$t('form.gender')"
class="col-md-2 col-6" @filter="genderFilter"
v-model="gender" >
/> <template v-slot:no-option>
<SelectInput <q-item>
<q-item-section class="text-grey">
{{ $t('general.noData') }}
</q-item-section>
</q-item>
</template>
</q-select>
<q-select
v-if="employee" v-if="employee"
:readonly outlined
:option="optionStore.globalOption.nationality" clearable
:id="`${prefixId}-select-nationality`" use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space
autocomplete="off"
input-debounce="0"
option-label="label"
option-value="value"
v-model="nationality"
class="col-md-2 col-6"
:dense="dense"
:readonly="readonly"
:options="nationalityOptions"
:hide-dropdown-icon="readonly"
:for="`${prefixId}-select-nationality`" :for="`${prefixId}-select-nationality`"
:label="$t('general.nationality')" :label="$t('general.nationality')"
class="col-md-2 col-6" @filter="nationalityFilter"
v-model="nationality"
clearable
/>
<q-input
v-if="agency"
for="input-agencies-contact-name"
dense
outlined
:readonly="readonly"
hide-bottom-space
class="col-md-4 col-12"
:label="$t('personnel.form.contactName')"
:model-value="readonly ? contactName || '-' : contactName"
@update:model-value="
(v) => (typeof v === 'string' ? (contactName = v) : '')
"
/>
<q-input
v-if="agency"
for="input-agencies-contact-tel"
id="input-agencies-contact-tel"
dense
outlined
:readonly="readonly"
hide-bottom-space
class="col-md-4 col-12"
:label="$t('personnel.form.contactTel')"
:model-value="readonly ? contactTel || '-' : contactTel"
@update:model-value="
(v) => (typeof v === 'string' ? (contactTel = v) : '')
"
> >
<template #prepend> <template v-slot:no-option>
<q-icon <q-item>
size="xs" <q-item-section class="text-grey">
name="mdi-phone-outline" {{ $t('general.noData') }}
class="cursor-pointer" </q-item-section>
color="primary" </q-item>
/>
</template> </template>
</q-input> </q-select>
</div> </div>
</div> </div>
</template> </template>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -268,7 +268,6 @@ const insuranceCompanyFilter = selectFilterOptionRefMod(
<div class="col-md col-6"> <div class="col-md col-6">
<DatePicker <DatePicker
:label="$t('customerEmployee.formHealthCheck.coverageStartDate')"
v-model="checkup.coverageStartDate" v-model="checkup.coverageStartDate"
:id="`${prefixId}-input-coverage-start-date`" :id="`${prefixId}-input-coverage-start-date`"
:readonly="readonly || checkup.statusSave" :readonly="readonly || checkup.statusSave"

View file

@ -9,8 +9,6 @@ import useOptionStore from 'stores/options';
import DatePicker from '../shared/DatePicker.vue'; import DatePicker from '../shared/DatePicker.vue';
import { dateFormat } from 'src/utils/datetime';
const optionStore = useOptionStore(); const optionStore = useOptionStore();
const { locale } = useI18n(); const { locale } = useI18n();
@ -20,8 +18,8 @@ const issuePlace = defineModel<string>('issuePlace');
const issueCountry = defineModel<string>('issueCountry'); const issueCountry = defineModel<string>('issueCountry');
const issueDate = defineModel<Date | null | string>('issueDate'); const issueDate = defineModel<Date | null | string>('issueDate');
const type = defineModel<string>('type'); const type = defineModel<string>('type');
const expireDate = defineModel<Date | string>('expireDate'); const expireDate = defineModel<Date>('expireDate');
const birthDate = defineModel<Date | string>('birthDate'); const birthDate = defineModel<Date>('birthDate');
const workerStatus = defineModel<string>('workerStatus'); const workerStatus = defineModel<string>('workerStatus');
const nationality = defineModel<string>('nationality'); const nationality = defineModel<string>('nationality');
const gender = defineModel<string>('gender'); const gender = defineModel<string>('gender');
@ -34,8 +32,6 @@ const firstName = defineModel<string>('firstName');
const namePrefix = defineModel<string>('namePrefix'); const namePrefix = defineModel<string>('namePrefix');
const passportNumber = defineModel<string>('passportNumber'); const passportNumber = defineModel<string>('passportNumber');
const file = defineModel<File>('file');
const passportValidator = /[a-zA-Z]{1}[a-zA-Z0-9]{1}[0-9]{5,7}$/; const passportValidator = /[a-zA-Z]{1}[a-zA-Z0-9]{1}[0-9]{5,7}$/;
const genderOptions = ref<Record<string, unknown>[]>([]); const genderOptions = ref<Record<string, unknown>[]>([]);
@ -181,30 +177,6 @@ watch(
}, },
); );
function browse() {
inputFile?.click();
}
const inputFile = (() => {
const _element = document.createElement('input');
_element.type = 'file';
_element.accept = 'image/jpeg,image/png';
_element.addEventListener('change', change);
return _element;
})();
async function change(e: Event) {
const _element = e.target as HTMLInputElement | null;
const _file = _element?.files?.[0];
if (_file) {
const newFileName = `passport-${dateFormat(new Date().toISOString())}-${_file.name}`;
const renamedFile = new File([_file], newFileName, { type: _file.type });
file.value = renamedFile;
}
}
watch( watch(
() => namePrefix.value, () => namePrefix.value,
(v) => { (v) => {
@ -249,14 +221,20 @@ watch(
</div> </div>
<div class="q-col-gutter-sm" :class="{ row: $q.screen.gt.sm }"> <div class="q-col-gutter-sm" :class="{ row: $q.screen.gt.sm }">
<div v-if="!ocr"> <div
<q-btn class="col row justify-center q-col-gutter-sml"
flat style="max-height: 50%"
color="primary" v-if="!ocr"
icon="mdi-upload-box-outline" >
@click="() => browse()" <q-avatar
:disable="readonly" style="border: 1px dashed; border-color: black"
></q-btn> square
size="100px"
font-size="50px"
color="grey-4"
text-color="grey"
icon="mdi-image-outline"
/>
</div> </div>
<div <div
class="row q-col-gutter-sm" class="row q-col-gutter-sm"
@ -280,7 +258,7 @@ watch(
:options="workerStatusOptions" :options="workerStatusOptions"
:hide-dropdown-icon="readonly" :hide-dropdown-icon="readonly"
:for="`${prefixId}-select-visa-type`" :for="`${prefixId}-select-visa-type`"
:label="$t('customerEmployee.form.workerStatus')" :label="$t('customerEmployee.form.workerType')"
@filter="workerStatusFilter" @filter="workerStatusFilter"
:model-value="readonly ? workerStatus || '-' : workerStatus" :model-value="readonly ? workerStatus || '-' : workerStatus"
@update:model-value=" @update:model-value="

View file

@ -28,22 +28,20 @@ const arrivalAt = defineModel<string>('arrivalAt');
const arrivalTMNo = defineModel<string>('arrivalTmNo'); const arrivalTMNo = defineModel<string>('arrivalTmNo');
const arrivalTM = defineModel<string>('arrivalTm'); const arrivalTM = defineModel<string>('arrivalTm');
const mrz = defineModel<string>('mrz'); const mrz = defineModel<string>('mrz');
const entryCount = defineModel<number | string>('entryCount'); const entryCount = defineModel<number>('entryCount');
const issuePlace = defineModel<string>('issuePlace'); const issuePlace = defineModel<string>('issuePlace');
const issueCountry = defineModel<string>('issueCountry'); const issueCountry = defineModel<string>('issueCountry');
const issueDate = defineModel<Date | null | string>('visaIssueDate'); const issueDate = defineModel<Date | null | string>('visaIssueDate');
const type = defineModel<string>('type'); const type = defineModel<string>('visaType');
const expireDate = defineModel<Date | string>('expireDate'); const expireDate = defineModel<Date>('expireDate');
const remark = defineModel<string>('remark'); const remark = defineModel<string>('remark');
const workerType = defineModel<string>('workerType'); const workerType = defineModel<string>('workerType');
const number = defineModel<string>('number'); const number = defineModel<string>('visaNumber');
const reportDate = defineModel<Date | null | string>('reportDate');
// const calculatedVisaDate = computed(() => {
// const calculatedVisaDate = computed(() => { if (!issueDate.value) return undefined;
// if (!issueDate.value) return undefined; return calculate90DayNext(issueDate.value);
// return calculate90DayNext(issueDate.value); });
// });
defineProps<{ defineProps<{
title?: string; title?: string;
@ -80,12 +78,6 @@ onMounted(async () => {
await fetchProvince(); await fetchProvince();
}); });
const visaIssueCountryOptions = ref<Record<string, unknown>[]>([]);
let visaIssueCountryFilter: (
value: string,
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
) => void;
const visaTypeOptions = ref<Record<string, unknown>[]>([]); const visaTypeOptions = ref<Record<string, unknown>[]>([]);
let visaTypeFilter: ( let visaTypeFilter: (
value: string, value: string,
@ -105,12 +97,6 @@ onMounted(() => {
'label', 'label',
); );
visaIssueCountryFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.nationality),
visaIssueCountryOptions,
'label',
);
workerTypeFilter = selectFilterOptionRefMod( workerTypeFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.workerType), ref(optionStore.globalOption?.workerType),
workerTypeOptions, workerTypeOptions,
@ -121,14 +107,8 @@ onMounted(() => {
watch( watch(
() => optionStore.globalOption, () => optionStore.globalOption,
() => { () => {
visaIssueCountryFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.nationality),
visaIssueCountryOptions,
'label',
);
visaTypeFilter = selectFilterOptionRefMod( visaTypeFilter = selectFilterOptionRefMod(
optionStore.globalOption.visaType, optionStore.globalOption.nationality,
visaTypeOptions, visaTypeOptions,
'label', 'label',
); );
@ -140,10 +120,6 @@ watch(
); );
}, },
); );
//
// watch([() => issueDate.value], () => {
// reportDate.value = calculate90DayNext(issueDate.value);
// });
</script> </script>
<template> <template>
@ -157,7 +133,7 @@ watch(
name="mdi-passport" name="mdi-passport"
style="background-color: var(--surface-3)" style="background-color: var(--surface-3)"
/> />
{{ $t(title) }} {{ title }}
</div> </div>
<div <div
@ -377,12 +353,10 @@ watch(
<DatePicker <DatePicker
:id="`${prefixId}-date-picker-visa-issuance`" :id="`${prefixId}-date-picker-visa-issuance`"
:readonly :readonly
:disabled="!readonly"
:label="$t('customerEmployee.form.visa90Day')" :label="$t('customerEmployee.form.visa90Day')"
v-model="reportDate" :model-value="calculatedVisaDate"
clearable clearable
:rules="[
(val) => (val && val.length > 0) || $t('form.error.required'),
]"
/> />
</div> </div>
</div> </div>
@ -448,11 +422,11 @@ watch(
class="col-md-4 col-6" class="col-md-4 col-6"
:dense="dense" :dense="dense"
:readonly="readonly" :readonly="readonly"
:options="visaIssueCountryOptions" :options="visaTypeOptions"
:hide-dropdown-icon="readonly" :hide-dropdown-icon="readonly"
:for="`${prefixId}-select-issue-country`" :for="`${prefixId}-select-issue-country`"
:label="$t('customerEmployee.form.issueCountry')" :label="$t('customerEmployee.form.issueCountry')"
@filter="visaIssueCountryFilter" @filter="visaTypeFilter"
:model-value="readonly ? issueCountry || '-' : issueCountry" :model-value="readonly ? issueCountry || '-' : issueCountry"
@update:model-value=" @update:model-value="
(v) => (typeof v === 'string' ? (issueCountry = v) : '') (v) => (typeof v === 'string' ? (issueCountry = v) : '')

View file

@ -22,8 +22,6 @@ const prop = withDefaults(
inTable?: boolean; inTable?: boolean;
addButton?: boolean; addButton?: boolean;
prefixId?: string; prefixId?: string;
hideAction?: boolean;
hideDelete?: boolean;
}>(), }>(),
{ {
gridView: false, gridView: false,
@ -141,9 +139,8 @@ defineEmits<{
<q-avatar size="md"> <q-avatar size="md">
<q-img <q-img
:src=" :src="
props.row.selectedImage `${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}` ||
? `${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}` `/images/employee-avatar-${props.row.gender}.png`
: `/images/employee-avatar-${props.row.gender}.png`
" "
class="text-center" class="text-center"
:ratio="1" :ratio="1"
@ -268,10 +265,9 @@ defineEmits<{
@click.stop="$emit('view', props.row)" @click.stop="$emit('view', props.row)"
/> />
<KebabAction <KebabAction
v-if="!inTable && !hideAction" v-if="!inTable"
:id-name="props.row.firstName" :id-name="props.row.firstName"
:status="props.row.status" :status="props.row.status"
:hide-delete="hideDelete"
@view="$emit('view', props.row)" @view="$emit('view', props.row)"
@edit="$emit('edit', props.row)" @edit="$emit('edit', props.row)"
@delete="$emit('delete', props.row)" @delete="$emit('delete', props.row)"
@ -284,11 +280,9 @@ defineEmits<{
<template v-slot:item="props"> <template v-slot:item="props">
<div class="col-12 col-md-3 col-sm-6"> <div class="col-12 col-md-3 col-sm-6">
<PersonCard <PersonCard
history
:hide-delete="hideDelete"
:hide-action="hideAction"
:id="`card-${props.row.firstNameEN}`" :id="`card-${props.row.firstNameEN}`"
:field-selected="fieldSelected" :field-selected="fieldSelected"
history
:prefix-id="props.row.firstNameEN ?? props.rowIndex" :prefix-id="props.row.firstNameEN ?? props.rowIndex"
:data="{ :data="{
code: props.row.code, code: props.row.code,
@ -296,9 +290,9 @@ defineEmits<{
$i18n.locale === 'eng' $i18n.locale === 'eng'
? `${props.row.firstNameEN} ${props.row.lastNameEN} `.trim() ? `${props.row.firstNameEN} ${props.row.lastNameEN} `.trim()
: `${props.row.firstName} ${props.row.lastName} `.trim(), : `${props.row.firstName} ${props.row.lastName} `.trim(),
img: props.row.selectedImage img:
? `${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}` `${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}` ||
: `/images/employee-avatar-${props.row.gender}.png`, `/images/employee-avatar-${props.row.gender}.png`,
fallbackImg: `/images/employee-avatar-${props.row.gender}.png`, fallbackImg: `/images/employee-avatar-${props.row.gender}.png`,
male: props.row.gender === 'male', male: props.row.gender === 'male',
female: props.row.gender === 'female', female: props.row.gender === 'female',

View file

@ -54,7 +54,6 @@ defineProps<{
employeeOwnerOption?: CustomerBranch[]; employeeOwnerOption?: CustomerBranch[];
prefixId: string; prefixId: string;
showBtnSave?: boolean; showBtnSave?: boolean;
disableCustomerSelect?: boolean;
}>(); }>();
defineEmits<{ defineEmits<{
@ -118,16 +117,12 @@ defineEmits<{
<div class="col-12 row" style="gap: var(--size-2)"> <div class="col-12 row" style="gap: var(--size-2)">
<SelectCustomer <SelectCustomer
id="form-select-customer-branch-id"
for="form-select-customer-branch-id"
v-model:value="customerBranchId" v-model:value="customerBranchId"
v-model:value-option="currentCustomerBranch" v-model:value-option="currentCustomerBranch"
:label="$t('customer.form.branchCode')" :label="$t('customer.form.branchCode')"
class="col-12 field-two" class="col-12 field-two"
simple
required required
:readonly :readonly
:disabled="disableCustomerSelect && !readonly"
/> />
<q-input <q-input

View file

@ -6,14 +6,12 @@ import { useI18n } from 'vue-i18n';
import useUserStore from 'src/stores/user'; import useUserStore from 'src/stores/user';
import useOptionStore from 'src/stores/options'; import useOptionStore from 'src/stores/options';
import { useWorkflowTemplate } from 'src/stores/workflow-template';
import { baseUrl } from 'stores/utils'; import { baseUrl } from 'stores/utils';
import { getRole } from 'src/services/keycloak'; import { getRole } from 'src/services/keycloak';
import { import {
WorkflowUserInTable, WorkflowUserInTable,
WorkflowTemplatePayload, WorkflowTemplatePayload,
WorkFlowPayloadStep, WorkFlowPayloadStep,
Group,
} from 'src/stores/workflow-template/types'; } from 'src/stores/workflow-template/types';
import { User } from 'src/stores/user/types'; import { User } from 'src/stores/user/types';
@ -22,18 +20,15 @@ import ToggleButton from 'src/components/button/ToggleButton.vue';
import NoData from '../NoData.vue'; import NoData from '../NoData.vue';
import SelectBranch from '../shared/select/SelectBranch.vue'; import SelectBranch from '../shared/select/SelectBranch.vue';
import AddButton from '../button/AddButton.vue'; import AddButton from '../button/AddButton.vue';
import { QField } from 'quasar';
defineProps<{ defineProps<{
readonly?: boolean; readonly?: boolean;
onDrawer?: boolean; onDrawer?: boolean;
hideAction?: boolean;
}>(); }>();
const { t } = useI18n(); const { t } = useI18n();
const userStore = useUserStore(); const userStore = useUserStore();
const optionStore = useOptionStore(); const optionStore = useOptionStore();
const workflowStore = useWorkflowTemplate();
const userInTable = defineModel<WorkflowUserInTable[]>('userInTable', { const userInTable = defineModel<WorkflowUserInTable[]>('userInTable', {
default: [], default: [],
@ -56,9 +51,7 @@ let objectOptions = [
const options = ref(objectOptions); const options = ref(objectOptions);
const role = ref<string[]>([]); const role = ref<string[]>([]);
const userList = ref<User[]>([]); const userList = ref<User[]>([]);
const groupList = ref<Group[]>([]);
const responsiblePersonSearch = ref(''); const responsiblePersonSearch = ref('');
const responsibleMenu = ref(false);
async function getUserList(opts?: { query: string }) { async function getUserList(opts?: { query: string }) {
const resUser = await userStore.fetchList({ const resUser = await userStore.fetchList({
@ -67,10 +60,10 @@ async function getUserList(opts?: { query: string }) {
if (resUser) userList.value = resUser.result; if (resUser) userList.value = resUser.result;
} }
async function getGroupList() { // async function getUserById(responsiblePersonId: string) {
const resGroup = await workflowStore.getGroupList(); // const resUser = await userStore.fetchById(responsiblePersonId);
if (resGroup) groupList.value = resGroup; // if (resUser) userInTable.value.push(resUser);
} // }
function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) { function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) {
const currStep = flowData.value.step[stepIndex]; const currStep = flowData.value.step[stepIndex];
@ -85,7 +78,6 @@ function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) {
userInTable.value[stepIndex] = { userInTable.value[stepIndex] = {
name: flowData.value.step[stepIndex].name, name: flowData.value.step[stepIndex].name,
responsiblePerson: [], responsiblePerson: [],
responsibleGroup: [],
}; };
} }
@ -109,33 +101,6 @@ function selectResponsiblePerson(stepIndex: number, responsiblePerson: User) {
} }
} }
function selectResponsibleGroup(stepIndex: number, responsibleGroup: string) {
const currStep = flowData.value.step[stepIndex];
const existGroupIndex = currStep.responsibleGroup?.findIndex(
(p) => p === responsibleGroup,
);
if (existGroupIndex === -1) {
currStep.responsibleGroup?.push(responsibleGroup);
if (!userInTable.value[stepIndex]) {
userInTable.value[stepIndex] = {
name: flowData.value.step[stepIndex].name,
responsiblePerson: [],
responsibleGroup: [],
};
}
userInTable.value[stepIndex]?.responsibleGroup.push(responsibleGroup);
} else {
currStep.responsibleGroup?.splice(Number(existGroupIndex), 1);
userInTable.value[stepIndex]?.responsibleGroup.splice(
Number(existGroupIndex),
1,
);
}
}
function selectItem( function selectItem(
val: Record<string, unknown>, val: Record<string, unknown>,
responsibleInstitution?: string[], responsibleInstitution?: string[],
@ -177,7 +142,6 @@ watch(
onMounted(async () => { onMounted(async () => {
role.value = getRole() || []; role.value = getRole() || [];
await getUserList(); await getUserList();
await getGroupList();
await userStore.fetchHqOption(); await userStore.fetchHqOption();
}); });
</script> </script>
@ -202,7 +166,6 @@ onMounted(async () => {
:class="{ 'q-ml-lg': $q.screen.gt.xs, 'q-mt-sm': $q.screen.lt.sm }" :class="{ 'q-ml-lg': $q.screen.gt.xs, 'q-mt-sm': $q.screen.lt.sm }"
> >
<ToggleButton <ToggleButton
:disable="hideAction"
class="q-mr-sm" class="q-mr-sm"
two-way two-way
:model-value="flowData.status !== 'INACTIVE'" :model-value="flowData.status !== 'INACTIVE'"
@ -504,29 +467,33 @@ onMounted(async () => {
</div> </div>
<!-- RESPONSIBLE-PERSON --> <!-- RESPONSIBLE-PERSON -->
<q-field <q-select
v-if="step.responsiblePersonId" v-if="step.responsiblePersonId"
behavior="menu"
:for="`select-responsible-person-${index}-${onDrawer ? 'drawer' : 'dialog'}`" :for="`select-responsible-person-${index}-${onDrawer ? 'drawer' : 'dialog'}`"
:bg-color="readonly ? 'transparent' : ''" :bg-color="readonly ? 'transparent' : ''"
:readonly :readonly
outlined outlined
:stack-label=" dense
userInTable[index]?.responsiblePerson.length > 0 || v-model="step.responsiblePersonId"
userInTable[index]?.responsibleGroup.length > 0 multiple
" :options="[1, 2, 3]"
hide-bottom-space
option-label="label"
option-value="value"
emit-value
:label="$t('flow.responsiblePerson')" :label="$t('flow.responsiblePerson')"
dense
class="col-md-6 col-12" class="col-md-6 col-12"
:class="{ 'cursor-pointer': !readonly }" :hide-dropdown-icon="readonly"
> >
<template #control> <template v-slot:selected-item="scope">
<q-item <div class="column full-width">
dense <div
class="items-center full-width no-padding" class="row items-center no-wrap"
v-for="person in userInTable[ v-for="person in userInTable[
index index
]?.responsiblePerson.filter((p) => ]?.responsiblePerson.filter(
step.responsiblePersonId.includes(p.id), (p) => p.id === scope.opt,
)" )"
:key="person.id" :key="person.id"
> >
@ -580,52 +547,12 @@ onMounted(async () => {
{{ person.code }} {{ person.code }}
</span> </span>
</div> </div>
</q-item>
<div
v-if="step.responsibleGroup.length > 0"
class="full-width app-text-muted text-weight-medium"
style="font-size: 10px"
>
{{ $t('general.group') }}
</div> </div>
<q-item </div>
class="items-center full-width no-padding"
v-for="group in userInTable[
index
]?.responsibleGroup.filter((g) =>
step.responsibleGroup.includes(g),
)"
:key="group"
dense
>
<q-avatar class="q-ml-sm" size="md">
<q-img
class="text-center"
:ratio="1"
:src="`/img-group.png`"
/>
</q-avatar>
<span class="q-pl-md">
{{ group }}
</span>
</q-item>
</template> </template>
<template v-if="!readonly" #append>
<q-icon <template v-slot:option></template>
name="mdi-menu-down" <q-menu v-if="!readonly" :offset="[0, 4]">
:class="{ rotated: responsibleMenu }"
class="transition-rotate"
/>
</template>
<q-menu
v-if="!readonly"
no-focus
no-refocus
:offset="[0, 4]"
@before-show="() => (responsibleMenu = true)"
@before-hide="() => (responsibleMenu = false)"
>
<q-list> <q-list>
<q-item> <q-item>
<q-input <q-input
@ -654,7 +581,6 @@ onMounted(async () => {
{{ $t('general.noData') }} {{ $t('general.noData') }}
</q-item> </q-item>
<q-item <q-item
v-else
v-for="(person, i) in userList" v-for="(person, i) in userList"
dense dense
:key="i" :key="i"
@ -729,7 +655,6 @@ onMounted(async () => {
{{ $t('personnel.MESSENGER') }} {{ $t('personnel.MESSENGER') }}
</span> </span>
<q-item <q-item
dense
clickable clickable
@click="step.messengerByArea = !step.messengerByArea" @click="step.messengerByArea = !step.messengerByArea"
class="column" class="column"
@ -745,49 +670,9 @@ onMounted(async () => {
</div> </div>
</div> </div>
</q-item> </q-item>
<span class="text-caption app-text-muted-2 q-px-md">
{{ $t('general.group') }}
</span>
<q-item
v-if="groupList.length === 0"
class="app-text-muted q-px-lg"
>
{{ $t('general.noData') }}
</q-item>
<q-item
v-else
v-for="(group, i) in groupList"
dense
clickable
@click="selectResponsibleGroup(index, group.name)"
class="column"
>
<div class="row items-center">
<q-checkbox
size="xs"
:model-value="
step.responsibleGroup.includes(group.name)
"
@click.stop="
selectResponsibleGroup(index, group.name)
"
/>
<q-avatar class="q-ml-sm" size="md">
<q-img
class="text-center"
:ratio="1"
:src="`/img-group.png`"
/>
</q-avatar>
<div class="column q-pl-md">
<span>{{ group.name }}</span>
</div>
</div>
</q-item>
</q-list> </q-list>
</q-menu> </q-menu>
</q-field> </q-select>
<!-- RESPONSIBLE-AGENCIES, RESPONSIBLE-INSTITUTION --> <!-- RESPONSIBLE-AGENCIES, RESPONSIBLE-INSTITUTION -->
<q-select <q-select
@ -930,11 +815,4 @@ onMounted(async () => {
:deep(.q-dialog.fullscreen.no-pointer-events.q-dialog--modal) { :deep(.q-dialog.fullscreen.no-pointer-events.q-dialog--modal) {
visibility: hidden; visibility: hidden;
} }
.transition-rotate {
transition: transform 0.3s ease;
}
.rotated {
transform: rotate(180deg);
}
</style> </style>

View file

@ -167,28 +167,26 @@ withDefaults(
<div class="col-12 full-width"> <div class="col-12 full-width">
<q-table <q-table
:rows-per-page-options="[0]" :rows-per-page-options="[0]"
:rows=" :rows="[
[ {
priceDisplay.price && {
label: $t('productService.product.salePrice'), label: $t('productService.product.salePrice'),
pricePerUnit: price, pricePerUnit: price,
calcVat, calcVat,
vatIncluded, vatIncluded,
}, },
priceDisplay.agentPrice && { {
label: $t('productService.product.agentPrice'), label: $t('productService.product.agentPrice'),
calcVat: agentPriceCalcVat, calcVat: agentPriceCalcVat,
vatIncluded: agentPriceVatIncluded, vatIncluded: agentPriceVatIncluded,
pricePerUnit: agentPrice, pricePerUnit: agentPrice,
}, },
priceDisplay.serviceCharge && { {
label: $t('productService.product.processingPrice'), label: $t('productService.product.processingPrice'),
calcVat: serviceChargeCalcVat, calcVat: serviceChargeCalcVat,
vatIncluded: serviceChargeVatIncluded, vatIncluded: serviceChargeVatIncluded,
pricePerUnit: serviceCharge, pricePerUnit: serviceCharge,
}, },
].filter(Boolean) ]"
"
:columns :columns
hide-bottom hide-bottom
bordered bordered
@ -246,19 +244,16 @@ withDefaults(
<template v-if="col.name === '#calcVat'"> <template v-if="col.name === '#calcVat'">
<q-checkbox <q-checkbox
v-if="priceDisplay?.price && props.rowIndex === 0" v-if="priceDisplay?.price && props.rowIndex === 0"
:disable="readonly"
v-model="calcVat" v-model="calcVat"
size="xs" size="xs"
/> />
<q-checkbox <q-checkbox
v-if="priceDisplay?.agentPrice && props.rowIndex === 1" v-if="priceDisplay?.agentPrice && props.rowIndex === 1"
:disable="readonly"
v-model="agentPriceCalcVat" v-model="agentPriceCalcVat"
size="xs" size="xs"
/> />
<q-checkbox <q-checkbox
v-if="priceDisplay?.serviceCharge && props.rowIndex === 2" v-if="priceDisplay?.serviceCharge && props.rowIndex === 2"
:disable="readonly"
v-model="serviceChargeCalcVat" v-model="serviceChargeCalcVat"
size="xs" size="xs"
/> />
@ -276,8 +271,6 @@ withDefaults(
flat flat
outlined outlined
dense dense
:readonly
:hide-dropdown-icon="readonly"
v-model="vatIncluded" v-model="vatIncluded"
></q-select> ></q-select>
<q-select <q-select
@ -292,8 +285,6 @@ withDefaults(
flat flat
outlined outlined
dense dense
:readonly
:hide-dropdown-icon="readonly"
v-model="agentPriceVatIncluded" v-model="agentPriceVatIncluded"
></q-select> ></q-select>
<q-select <q-select
@ -308,8 +299,6 @@ withDefaults(
flat flat
outlined outlined
dense dense
:readonly
:hide-dropdown-icon="readonly"
v-model="serviceChargeVatIncluded" v-model="serviceChargeVatIncluded"
></q-select> ></q-select>
</template> </template>

View file

@ -98,8 +98,7 @@ watch(
(c, o) => { (c, o) => {
const list = c.map((v: { name: string }) => v.name); const list = c.map((v: { name: string }) => v.name);
const oldList = o.map((v: { name: string }) => v.name); const oldList = o.map((v: { name: string }) => v.name);
const index = workName.value ? oldList.indexOf(workName.value) : -1; const index = oldList.indexOf(workName.value || '');
if (index === -1) return;
if ( if (
list[index] !== oldList[index] && list[index] !== oldList[index] &&
@ -706,7 +705,7 @@ watch(
:deep( :deep(
.q-item__section.column.q-item__section--side.justify-center.q-item__section--avatar.q-focusable.relative-position.cursor-pointer .q-item__section.column.q-item__section--side.justify-center.q-item__section--avatar.q-focusable.relative-position.cursor-pointer
) { ) {
justify-content: start !important; justify-content: start !important;
padding-right: 8px !important; padding-right: 8px !important;
padding-top: 16px; padding-top: 16px;
@ -737,7 +736,7 @@ watch(
:deep( :deep(
i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated
) { ) {
color: var(--brand-1); color: var(--brand-1);
} }

View file

@ -1,6 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { nextTick, onMounted, ref, watch } from 'vue'; import { nextTick, onMounted, ref, watch } from 'vue';
import { DeleteButton, EditButton, SaveButton, UndoButton } from '../button'; import { DeleteButton, EditButton, SaveButton, UndoButton } from '../button';
import { scrollToElement } from 'stores/utils';
// import { useI18n } from 'vue-i18n'; // import { useI18n } from 'vue-i18n';
// import { storeToRefs } from 'pinia'; // import { storeToRefs } from 'pinia';
@ -137,9 +138,12 @@ watch(
@click=" @click="
() => { () => {
assignClone(); assignClone();
if (nameList[nameList.length - 1].name === '') {
$emit('delete', cloneList[cloneList.length - 1].id, true);
cloneList = cloneList.filter((item) => item.name !== ''); cloneList = cloneList.filter((item) => item.name !== '');
nameList = nameList.filter((item) => item.name !== ''); nameList = nameList.filter((item) => item.name !== '');
} }
}
" "
/> />
</q-form> </q-form>

View file

@ -48,7 +48,6 @@ defineProps<{
readonly?: boolean; readonly?: boolean;
onDrawer?: boolean; onDrawer?: boolean;
inputOnly?: boolean; inputOnly?: boolean;
disableToggle?: boolean;
}>(); }>();
defineEmits<{ defineEmits<{
@ -77,7 +76,6 @@ defineEmits<{
<ToggleButton <ToggleButton
class="q-mr-sm" class="q-mr-sm"
two-way two-way
:disable="disableToggle"
:model-value="status !== 'INACTIVE'" :model-value="status !== 'INACTIVE'"
@click=" @click="
() => { () => {
@ -198,7 +196,7 @@ defineEmits<{
:deep( :deep(
.q-item__section.column.q-item__section--side.justify-center.q-item__section--avatar.q-focusable.relative-position.cursor-pointer .q-item__section.column.q-item__section--side.justify-center.q-item__section--avatar.q-focusable.relative-position.cursor-pointer
) { ) {
justify-content: start !important; justify-content: start !important;
padding-right: 8px !important; padding-right: 8px !important;
padding-top: 16px; padding-top: 16px;
@ -211,14 +209,14 @@ defineEmits<{
:deep( :deep(
i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated
) { ) {
color: var(--brand-1); color: var(--brand-1);
} }
:deep( :deep(
.q-item.q-item-type.row.no-wrap.q-item--dense.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable.expansion-rounded.surface-2 .q-item.q-item-type.row.no-wrap.q-item--dense.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable.expansion-rounded.surface-2
.q-focus-helper .q-focus-helper
) { ) {
visibility: hidden; visibility: hidden;
} }

View file

@ -2,18 +2,11 @@
import SelectCustomer from '../shared/select/SelectCustomer.vue'; import SelectCustomer from '../shared/select/SelectCustomer.vue';
import SelectBranch from '../shared/select/SelectBranch.vue'; import SelectBranch from '../shared/select/SelectBranch.vue';
import { CustomerBranch } from 'src/stores/customer';
import { ref } from 'vue';
const branchId = defineModel<string>('branchId'); const branchId = defineModel<string>('branchId');
const customerBranchId = defineModel<string>('customerBranchId'); const customerBranchId = defineModel<string>('customerBranchId');
const agentPrice = defineModel<boolean>('agentPrice'); const agentPrice = defineModel<boolean>('agentPrice');
const special = defineModel<boolean>('special'); const special = defineModel<boolean>('special');
const customerBranchOption = defineModel<CustomerBranch>(
'customerBranchOption',
);
defineProps<{ defineProps<{
outlined?: boolean; outlined?: boolean;
readonly?: boolean; readonly?: boolean;
@ -74,12 +67,8 @@ defineEmits<{
required required
:readonly :readonly
/> />
<SelectCustomer <SelectCustomer
id="about-select-customer-branch-id"
for="about-select-customer-branch-id"
v-model:value="customerBranchId" v-model:value="customerBranchId"
v-model:value-option="customerBranchOption"
:label="$t('quotation.customer')" :label="$t('quotation.customer')"
:creatable-disabled-text="`(${$t('form.error.selectField', { :creatable-disabled-text="`(${$t('form.error.selectField', {
field: $t('quotation.branchVirtual'), field: $t('quotation.branchVirtual'),
@ -100,13 +89,13 @@ defineEmits<{
<style scoped> <style scoped>
:deep( :deep(
label.q-field.row.no-wrap.items-start.q-field--outlined.q-select.field-one label.q-field.row.no-wrap.items-start.q-field--outlined.q-select.field-one
) { ) {
padding-right: 4px; padding-right: 4px;
} }
:deep( :deep(
label.q-field.row.no-wrap.items-start.q-field--outlined.q-select.field-two label.q-field.row.no-wrap.items-start.q-field--outlined.q-select.field-two
) { ) {
padding-left: 4px; padding-left: 4px;
} }
</style> </style>

View file

@ -58,15 +58,16 @@ const currentBtnOpen = ref<{ title: string; opened: boolean[] }[]>([
function calcPrice(c: (typeof rows.value)[number]) { function calcPrice(c: (typeof rows.value)[number]) {
const originalPrice = c.pricePerUnit; const originalPrice = c.pricePerUnit;
const finalPricePerUnit = precisionRound( const finalPriceWithVat = precisionRound(
originalPrice + originalPrice + originalPrice * (config.value?.vat || 0.07),
(c.product[props.agentPrice ? 'agentPriceCalcVat' : 'calcVat']
? originalPrice * (config.value?.vat || 0.07)
: 0),
); );
const price = finalPricePerUnit * c.amount - c.discount; const finalPriceNoVat = finalPriceWithVat / (1 + (config.value?.vat || 0.07));
return precisionRound(price); 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);
} }
const discount4Show = ref<string[]>([]); const discount4Show = ref<string[]>([]);
@ -434,20 +435,8 @@ watch(
<q-td align="right"> <q-td align="right">
{{ {{
formatNumberDecimal( formatNumberDecimal(
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.pricePerUnit * props.row.amount -
props.row.discount, props.row.discount,
),
2, 2,
) )
}} }}
@ -459,12 +448,9 @@ watch(
agentPrice ? 'agentPriceCalcVat' : 'calcVat' agentPrice ? 'agentPriceCalcVat' : 'calcVat'
] ]
? precisionRound( ? precisionRound(
((props.row.pricePerUnit * (props.row.pricePerUnit * props.row.amount -
(1 + (config?.vat || 0.07)) * props.row.discount) *
props.row.amount - (config?.vat || 0.07),
props.row.discount) /
(1 + (config?.vat || 0.07))) *
0.07,
) )
: 0, : 0,
2, 2,

View file

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { QTableProps } from 'quasar'; import { QTableProps } from 'quasar';
import { dateFormat, dateFormatJS } from 'src/utils/datetime'; import { dateFormat } from 'src/utils/datetime';
import { formatNumberDecimal } from 'stores/utils'; import { formatNumberDecimal } from 'stores/utils';
@ -19,8 +19,6 @@ const props = withDefaults(
page?: number; page?: number;
pageSize?: number; pageSize?: number;
hideBtnPreview?: boolean; hideBtnPreview?: boolean;
hideAction?: boolean;
hideDelete?: boolean;
}>(), }>(),
{ {
row: () => [], row: () => [],
@ -86,11 +84,11 @@ defineEmits<{
</q-td> </q-td>
<q-td v-if="visibleColumns.includes('createdAt')"> <q-td v-if="visibleColumns.includes('createdAt')">
{{ dateFormatJS({ date: props.row.createdAt }) }} {{ dateFormat(props.row.createdAt) }}
</q-td> </q-td>
<q-td v-if="visibleColumns.includes('dueDate')"> <q-td v-if="visibleColumns.includes('dueDate')">
{{ dateFormatJS({ date: props.row.dueDate }) }} {{ dateFormat(props.row.dueDate) }}
</q-td> </q-td>
<q-td v-if="visibleColumns.includes('contactName')"> <q-td v-if="visibleColumns.includes('contactName')">
@ -149,12 +147,12 @@ defineEmits<{
flat flat
@click.stop="$emit('view', props.row)" @click.stop="$emit('view', props.row)"
/> />
<KebabAction <KebabAction
v-if="!hideAction"
:idName="`btn-kebab-${props.row.workName}`" :idName="`btn-kebab-${props.row.workName}`"
status="'ACTIVE'" status="'ACTIVE'"
hide-toggle hide-toggle
:hide-delete hide-delete
:hide-edit="hideEdit" :hide-edit="hideEdit"
@view="$emit('view', props.row)" @view="$emit('view', props.row)"
@edit="$emit('edit', props.row)" @edit="$emit('edit', props.row)"

View file

@ -1,10 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import SelectInput from '../shared/SelectInput.vue'; import SelectInput from '../shared/SelectInput.vue';
import useOptionStore from 'src/stores/options'; import useOptionStore from 'src/stores/options';
import { Icon } from '@iconify/vue/dist/iconify.js';
import { useInstitution } from 'src/stores/institution';
const optionStore = useOptionStore(); const optionStore = useOptionStore();
@ -14,11 +10,6 @@ defineProps<{
readonly?: boolean; readonly?: boolean;
onDrawer?: boolean; onDrawer?: boolean;
}>(); }>();
const emit = defineEmits<{
(e: 'deleteAttachment', name: string): void;
}>();
const attachmentRef = ref();
const group = defineModel('group', { default: '' }); const group = defineModel('group', { default: '' });
const name = defineModel('name', { default: '' }); const name = defineModel('name', { default: '' });
@ -26,19 +17,8 @@ const nameEn = defineModel('nameEn', { default: '' });
const contactName = defineModel('contactName', { default: '' }); const contactName = defineModel('contactName', { default: '' });
const email = defineModel('email', { default: '' }); const email = defineModel('email', { default: '' });
const contactTel = defineModel('contactTel', { default: '' }); const contactTel = defineModel('contactTel', { default: '' });
const attachment = defineModel<File[]>('attachment');
const attachmentList =
defineModel<{ name: string; url: string }[]>('attachmentList');
type Options = { label: string; value: string }; type Options = { label: string; value: string };
function openNewTab(url: string) {
window.open(url, '_blank');
}
function deleteAttachment(name: string) {
emit('deleteAttachment', name);
}
</script> </script>
<template> <template>
<div class="row col-12"> <div class="row col-12">
@ -88,12 +68,7 @@ function deleteAttachment(name: string) {
class="col-md-4 col-12" class="col-md-4 col-12"
:label="$t('agencies.name')" :label="$t('agencies.name')"
v-model="name" v-model="name"
:rules="[ :rules="[(val: string) => !!val || $t('form.error.required')]"
(val) => !!val || $t('form.error.required'),
(val) =>
/^[A-Za-z0-9ก-๙\s&.,'-]+$/.test(val) ||
$t('form.error.branchNameField'),
]"
/> />
<q-input <q-input
for="input-agencies-name-en" for="input-agencies-name-en"
@ -104,15 +79,6 @@ function deleteAttachment(name: string) {
class="col-md-4 col-12" class="col-md-4 col-12"
:label="'Agencies Name'" :label="'Agencies Name'"
v-model="nameEn" v-model="nameEn"
:rules="
nameEn
? [
(val) =>
/^[A-Za-z0-9ก-๙\s&.,'-]+$/.test(val) ||
$t('form.error.branchNameENField'),
]
: []
"
/> />
<q-input <q-input
@ -167,7 +133,7 @@ function deleteAttachment(name: string) {
:readonly="readonly" :readonly="readonly"
hide-bottom-space hide-bottom-space
class="col-md-4 col-12" class="col-md-4 col-12"
:label="$t('agencies.contactTel')" :label="$t('form.telephone')"
:model-value="readonly ? contactTel || '-' : contactTel" :model-value="readonly ? contactTel || '-' : contactTel"
@update:model-value=" @update:model-value="
(v) => (typeof v === 'string' ? (contactTel = v) : '') (v) => (typeof v === 'string' ? (contactTel = v) : '')
@ -182,78 +148,6 @@ function deleteAttachment(name: string) {
/> />
</template> </template>
</q-input> </q-input>
<q-file
v-if="!readonly"
ref="attachmentRef"
for="input-attachment"
dense
outlined
multiple
append
:readonly
:label="$t('personnel.form.attachment')"
class="col"
v-model="attachment"
>
<template v-slot:prepend>
<Icon
icon="material-symbols:attach-file"
width="20px"
style="color: var(--brand-1)"
/>
</template>
<template v-slot:file="file">
<div class="row full-width items-center">
<span class="col ellipsis">
{{ file.file.name }}
</span>
<q-btn
dense
rounded
flat
padding="2 2"
class="app-text-muted"
icon="mdi-close-circle"
@click.stop="attachmentRef.removeAtIndex(file.index)"
/>
</div>
</template>
</q-file>
<div v-if="attachmentList && attachmentList?.length > 0" class="col-12">
<q-list bordered separator class="rounded" style="padding: 0">
<q-item
id="attachment-file"
for="attachment-file"
v-for="item in attachmentList"
clickable
:key="item.url"
class="items-center row"
@click="() => openNewTab(item.url)"
>
<q-item-section>
<div class="row items-center justify-between">
<div class="col">
{{ item.name }}
</div>
<q-btn
v-if="!readonly"
id="delete-file"
rounded
flat
dense
unelevated
size="md"
icon="mdi-trash-can-outline"
class="app-text-negative"
@click.stop="deleteAttachment(item.name)"
/>
</div>
</q-item-section>
</q-item>
</q-list>
</div>
</div> </div>
</div> </div>
</template> </template>

View file

@ -27,38 +27,26 @@ withDefaults(
class="app-text-muted q-pr-sm" class="app-text-muted q-pr-sm"
:width="iconSize || '2rem'" :width="iconSize || '2rem'"
/> />
<span :id="`dd-wrapper-${label}`" class="row col"> <span class="row col">
<span <span class="col-12 app-text-muted-2" style="font-size: 10px">
:id="`dd-label-${label}`"
class="col-12 app-text-muted-2"
style="font-size: 10px"
>
{{ label }} {{ label }}
</span> </span>
<span :id="`dd-value-wrapper-${label}`" class="col-12 ellipsis"> <span class="col-12 ellipsis">
<slot name="value"> <slot name="value">
<span <span
:class="{ 'link cursor-pointer': clickable }" :class="{ 'link cursor-pointer': clickable }"
v-if="typeof value === 'string'" v-if="typeof value === 'string'"
@click="clickable ? $emit('labelClick', value, null) : undefined" @click="$emit('labelClick', value, null)"
:id="`link-${value}`"
:for="`link-${value}`"
> >
{{ value }} {{ value }}
<q-tooltip v-if="tooltip" :delay="500">{{ value }}</q-tooltip> <q-tooltip v-if="tooltip" :delay="500">{{ value }}</q-tooltip>
</span> </span>
<span <span v-else :class="{ 'link cursor-pointer': clickable }">
:id="`dd-value-${label}`"
v-else
:class="{ 'link cursor-pointer': clickable }"
>
<span <span
v-for="(item, index) in value" v-for="(item, index) in value"
:key="index" :key="index"
@click="$emit('labelClick', item, index)" @click="$emit('labelClick', item, index)"
class="link cursor-pointer" class="link cursor-pointer"
:id="`link-${item}`"
:for="`link-${item}`"
> >
{{ item }} {{ item }}
<span v-if="index < value.length - 1">,&nbsp;</span> <span v-if="index < value.length - 1">,&nbsp;</span>

View file

@ -8,10 +8,6 @@ defineProps<{
const quotationId = defineModel<string>('quotationId', { const quotationId = defineModel<string>('quotationId', {
required: true, required: true,
}); });
const isDebitNote = defineModel<boolean>('isDebitNote', {
required: false,
default: false,
});
</script> </script>
<template> <template>
<div class="row col-12"> <div class="row col-12">
@ -41,7 +37,6 @@ const isDebitNote = defineModel<boolean>('isDebitNote', {
cancelIncludeDebitNote: true, cancelIncludeDebitNote: true,
hasCancel: true, hasCancel: true,
}" }"
@selected="(v) => (isDebitNote = v.isDebitNote)"
/> />
</section> </section>
</div> </div>

View file

@ -42,15 +42,6 @@ defineProps<{
const modal = defineModel('modal', { default: false }); const modal = defineModel('modal', { default: false });
const currentTab = defineModel<string>('currentTab'); const currentTab = defineModel<string>('currentTab');
async function onValidationError(ref: any) {
const el = ref.$el as Element;
el.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest',
});
}
</script> </script>
<template> <template>
<q-dialog <q-dialog
@ -69,7 +60,6 @@ async function onValidationError(ref: any) {
@submit.prevent @submit.prevent
@validation-success="submit" @validation-success="submit"
class="column full-height" class="column full-height"
@validation-error="onValidationError"
> >
<!-- header --> <!-- header -->
<div <div

View file

@ -8,7 +8,7 @@ import {
UndoButton, UndoButton,
} from 'components/button'; } from 'components/button';
const props = withDefaults( withDefaults(
defineProps<{ defineProps<{
title: string; title: string;
category?: string; category?: string;
@ -42,24 +42,10 @@ const drawerOpen = defineModel<boolean>('drawerOpen', {
const myForm = ref(); const myForm = ref();
function reset() { function reset() {
if (props.beforeClose) {
drawerOpen.value = props.beforeClose
? props.beforeClose()
: !drawerOpen.value;
}
if (myForm.value) { if (myForm.value) {
myForm.value.resetValidation(); myForm.value.resetValidation();
} }
} }
async function onValidationError(ref: any) {
const el = ref.$el as Element;
el.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest',
});
}
</script> </script>
<template> <template>
<q-drawer <q-drawer
@ -67,6 +53,7 @@ async function onValidationError(ref: any) {
@show="show" @show="show"
@before-hide="reset" @before-hide="reset"
@hide="close" @hide="close"
@update:model-value="(v) => (drawerOpen = beforeClose ? beforeClose() : v)"
:width="$q.screen.gt.xs ? windowSize * 0.85 : windowSize" :width="$q.screen.gt.xs ? windowSize * 0.85 : windowSize"
v-model="drawerOpen" v-model="drawerOpen"
behavior="mobile" behavior="mobile"
@ -79,7 +66,6 @@ async function onValidationError(ref: any) {
greedy greedy
@submit.prevent @submit.prevent
@validation-success="submit" @validation-success="submit"
@validation-error="onValidationError"
> >
<div <div
class="column justify-between full-height" class="column justify-between full-height"

View file

@ -1,9 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { Icon } from '@iconify/vue/dist/iconify.js';
defineProps<{ defineProps<{
hideIcon?: boolean; hideIcon?: boolean;
icon?: string;
}>(); }>();
</script> </script>
@ -13,13 +10,10 @@ defineProps<{
v-if="!hideIcon" v-if="!hideIcon"
id="btn-add" id="btn-add"
padding="sm" padding="sm"
:icon="icon ? undefined : 'mdi-plus'" icon="mdi-plus"
direction="up" direction="up"
class="color-btn" class="color-btn"
> >
<template #icon v-if="icon">
<Icon :icon width="24" />
</template>
<slot> <slot>
<q-fab-action <q-fab-action
padding="xs" padding="xs"
@ -35,12 +29,10 @@ defineProps<{
fab fab
id="btn-add" id="btn-add"
padding="sm" padding="sm"
:icon="icon ? undefined : 'mdi-plus'" icon="mdi-plus"
direction="up" direction="up"
class="color-btn" class="color-btn"
> />
<Icon v-if="icon" :icon width="24" />
</q-btn>
</q-page-sticky> </q-page-sticky>
</template> </template>

View file

@ -46,7 +46,6 @@ defineProps<{
color="grey" color="grey"
icon="mdi-close" icon="mdi-close"
v-close-popup v-close-popup
@click="cancel"
/> />
</div> </div>

View file

@ -1,12 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
const pageSize = defineModel<number>({ required: true }); const pageSize = defineModel<number>({ required: true });
withDefaults(
defineProps<{
fetchData?: (...args: unknown[]) => void;
}>(),
{},
);
</script> </script>
<template> <template>
@ -17,12 +10,7 @@ withDefaults(
:key="v" :key="v"
clickable clickable
v-close-popup v-close-popup
@click=" @click="pageSize = v"
() => {
pageSize = v;
fetchData();
}
"
> >
<q-item-section> <q-item-section>
<q-item-label>{{ v }}</q-item-label> <q-item-label>{{ v }}</q-item-label>

View file

@ -229,7 +229,6 @@ const smallBanner = ref(false);
<ToggleButton <ToggleButton
v-if="useToggle" v-if="useToggle"
:disable="readonly"
two-way two-way
:model-value="toggleStatus !== 'INACTIVE'" :model-value="toggleStatus !== 'INACTIVE'"
@click="$emit('update:toggleStatus', toggleStatus)" @click="$emit('update:toggleStatus', toggleStatus)"
@ -266,7 +265,6 @@ const smallBanner = ref(false);
class="app-text-muted full-width" class="app-text-muted full-width"
align="left" align="left"
v-if="typeof tabsList === 'object'" v-if="typeof tabsList === 'object'"
@update:model-value="(v) => $emit('update:currentTab', v)"
> >
<q-tab <q-tab
v-for="tab in tabsList" v-for="tab in tabsList"

View file

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { BranchWithChildren } from 'stores/branch/types'; import { BranchWithChildren } from 'stores/branch/types';
import KebabAction from './shared/KebabAction.vue'; import KebabAction from './shared/KebabAction.vue';
import { isRoleInclude } from 'stores/utils';
const nodes = defineModel<(any | BranchWithChildren)[]>('nodes', { const nodes = defineModel<(any | BranchWithChildren)[]>('nodes', {
default: [], default: [],
@ -16,7 +17,6 @@ withDefaults(
labelKey?: string; labelKey?: string;
childrenKey: string; childrenKey: string;
action?: boolean; action?: boolean;
hideCreate?: boolean;
}>(), }>(),
{ {
color: 'transparent', color: 'transparent',
@ -96,9 +96,7 @@ defineEmits<{
expandedTree[expandedTree.length - 1] === node.id, expandedTree[expandedTree.length - 1] === node.id,
}" }"
> >
{{ {{ node.name }}
$i18n.locale === 'eng' ? node.nameEN || node.name : node.name
}}
</span> </span>
<span class="app-text-muted text-caption ellipsis"> <span class="app-text-muted text-caption ellipsis">
{{ node.code }} {{ node.code }}
@ -122,7 +120,11 @@ defineEmits<{
/> />
<q-btn <q-btn
v-if="node.isHeadOffice && typeTree === 'branch' && !hideCreate" v-if="
node.isHeadOffice &&
typeTree === 'branch' &&
isRoleInclude(['head_of_admin', 'admin', 'system'])
"
:id="`create-sub-branch-btn-${node.name}`" :id="`create-sub-branch-btn-${node.name}`"
@click.stop="$emit('create', node)" @click.stop="$emit('create', node)"
icon="mdi-file-plus-outline" icon="mdi-file-plus-outline"

View file

@ -1,10 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue';
import MainButton from './MainButton.vue'; import MainButton from './MainButton.vue';
const emit = defineEmits<{ defineEmits<{
(e: 'click', v: MouseEvent): void; (e: 'click', v: MouseEvent): void;
(e: 'fileSelected', v: File[]): void;
}>(); }>();
defineProps<{ defineProps<{
iconOnly?: boolean; iconOnly?: boolean;
@ -12,29 +10,15 @@ defineProps<{
outlined?: boolean; outlined?: boolean;
disabled?: boolean; disabled?: boolean;
dark?: boolean; dark?: boolean;
importFile?: boolean;
label?: string; label?: string;
icon?: string; icon?: string;
}>(); }>();
const inputRef = ref<HTMLInputElement | null>(null);
function triggerFileInput() {
inputRef.value?.click();
}
function handleFileChange(event: Event) {
const files = (event.target as HTMLInputElement).files;
if (files && files.length > 0) {
emit('fileSelected', Array.from(files));
}
}
</script> </script>
<template> <template>
<MainButton <MainButton
@click="(e) => (importFile ? triggerFileInput() : $emit('click', e))" @click="(e) => $emit('click', e)"
v-bind="{ ...$props, ...$attrs }" v-bind="{ ...$props, ...$attrs }"
:icon="icon || 'mdi-import'" :icon="icon || 'mdi-import'"
color="var(--info-bg)" color="var(--info-bg)"
@ -42,13 +26,4 @@ function handleFileChange(event: Event) {
> >
{{ label || $t('general.import') }} {{ label || $t('general.import') }}
</MainButton> </MainButton>
<input
ref="inputRef"
type="file"
@change="(e) => handleFileChange(e)"
hidden
accept=".xls, .xlsx , .csv"
multiple
/>
</template> </template>

View file

@ -5,7 +5,6 @@ defineEmits<{
(e: 'click', v: MouseEvent): void; (e: 'click', v: MouseEvent): void;
}>(); }>();
defineProps<{ defineProps<{
id?: string;
icon?: string; icon?: string;
color: string; color: string;
iconOnly?: boolean; iconOnly?: boolean;
@ -19,7 +18,6 @@ defineProps<{
<template> <template>
<button <button
:id="id"
@click="(e) => $emit('click', e)" @click="(e) => $emit('click', e)"
class="main-btn" class="main-btn"
:class="{ :class="{

View file

@ -1,32 +0,0 @@
<script lang="ts" setup>
import MainButton from './MainButton.vue';
defineEmits<{
(e: 'click', v: MouseEvent): void;
}>();
defineProps<{
iconOnly?: boolean;
solid?: boolean;
outlined?: boolean;
disabled?: boolean;
dark?: boolean;
label?: string;
icon?: string;
amount?: number;
}>();
</script>
<template>
<MainButton
@click="(e) => $emit('click', e)"
v-bind="{ ...$props, ...$attrs }"
:icon="icon || 'mdi-file-replace'"
color="207 96% 32%"
:title="iconOnly ? $t('general.paste') : undefined"
>
{{ label || $t('general.paste') }}
{{ amount && amount > 0 ? `(${amount})` : '' }}
</MainButton>
</template>

View file

@ -10,7 +10,6 @@ defineProps<{
outlined?: boolean; outlined?: boolean;
disabled?: boolean; disabled?: boolean;
dark?: boolean; dark?: boolean;
color?: string;
label?: string; label?: string;
icon?: string; icon?: string;
@ -24,7 +23,7 @@ defineProps<{
@click="(e) => $emit('click', e)" @click="(e) => $emit('click', e)"
v-bind="{ ...$props, ...$attrs }" v-bind="{ ...$props, ...$attrs }"
:icon="icon || 'mdi-content-save-outline'" :icon="icon || 'mdi-content-save-outline'"
:color="color || '207 96% 32%'" color="207 96% 32%"
:title="iconOnly ? $t('general.save') : undefined" :title="iconOnly ? $t('general.save') : undefined"
> >
{{ label || $t('general.save') }} {{ label || $t('general.save') }}

View file

@ -14,4 +14,3 @@ export { default as PrintButton } from './PrintButton.vue';
export { default as StateButton } from './StateButton.vue'; export { default as StateButton } from './StateButton.vue';
export { default as NextButton } from './NextButton.vue'; export { default as NextButton } from './NextButton.vue';
export { default as ImportButton } from './ImportButton.vue'; export { default as ImportButton } from './ImportButton.vue';
export { default as PasteButton } from './PasteButton.vue';

View file

@ -23,15 +23,6 @@ function update(value: boolean) {
} }
} }
async function onValidationError(ref: any) {
const el = ref.$el as Element;
el.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest',
});
}
const state = defineModel({ default: false }); const state = defineModel({ default: false });
</script> </script>
@ -50,7 +41,6 @@ const state = defineModel({ default: false });
}" }"
> >
<q-form <q-form
@validation-error="onValidationError"
@submit.prevent="(e) => $emit('submit', e)" @submit.prevent="(e) => $emit('submit', e)"
@reset="$emit('reset')" @reset="$emit('reset')"
greedy greedy

View file

@ -12,7 +12,7 @@ import { formatAddress } from 'src/utils/address';
import useOptionStore from 'stores/options'; import useOptionStore from 'stores/options';
const optionStore = useOptionStore(); const optionStore = useOptionStore();
const props = defineProps<{ defineProps<{
title?: string; title?: string;
addressTitle?: string; addressTitle?: string;
addressTitleEN?: string; addressTitleEN?: string;
@ -30,7 +30,6 @@ const props = defineProps<{
useEmployment?: boolean; useEmployment?: boolean;
useWorkPlace?: boolean; useWorkPlace?: boolean;
useForeignAddress?: boolean;
}>(); }>();
const addressStore = useAddressStore(); const addressStore = useAddressStore();
@ -58,25 +57,6 @@ const subDistrictId = defineModel<string | null | undefined>('subDistrictId');
const zipCode = defineModel<string | null | undefined>('zipCode'); const zipCode = defineModel<string | null | undefined>('zipCode');
const sameWithEmployer = defineModel<boolean>('sameWithEmployer'); const sameWithEmployer = defineModel<boolean>('sameWithEmployer');
const provinceTextEN = defineModel<string | null | undefined>(
'provinceTextEn',
{
default: '',
},
);
const districtTextEN = defineModel<string | null | undefined>(
'districtTextEn',
{
default: '',
},
);
const subDistrictTextEN = defineModel<string | null | undefined>(
'subDistrictTextEn',
{
default: '',
},
);
const homeCode = defineModel<string | null | undefined>('homeCode'); const homeCode = defineModel<string | null | undefined>('homeCode');
const employmentOffice = defineModel<string | null | undefined>( const employmentOffice = defineModel<string | null | undefined>(
'employmentOffice', 'employmentOffice',
@ -84,7 +64,6 @@ const employmentOffice = defineModel<string | null | undefined>(
const employmentOfficeEN = defineModel<string | null | undefined>( const employmentOfficeEN = defineModel<string | null | undefined>(
'employmentOfficeEn', 'employmentOfficeEn',
); );
const addressForeign = defineModel<boolean>('addressForeign');
const addrOptions = reactive<{ const addrOptions = reactive<{
provinceOps: Province[]; provinceOps: Province[];
@ -99,18 +78,14 @@ const addrOptions = reactive<{
const area = ref<Office[]>([]); const area = ref<Office[]>([]);
const fullAddress = computed(() => { const fullAddress = computed(() => {
const province = addressForeign.value const province = provinceOptions.value.find((v) => v.id === provinceId.value);
? { id: '1', name: provinceId.value } const district = districtOptions.value.find((v) => v.id === districtId.value);
: provinceOptions.value.find((v) => v.id === provinceId.value); const sDistrict = subDistrictOptions.value.find(
const district = addressForeign.value (v) => v.id === subDistrictId.value,
? { id: '1', name: districtId.value } );
: districtOptions.value.find((v) => v.id === districtId.value);
const sDistrict = addressForeign.value
? { id: '1', name: subDistrictId.value }
: subDistrictOptions.value.find((v) => v.id === subDistrictId.value);
if (province?.name && district?.name && sDistrict?.name) { if (province && district && sDistrict) {
const fullAddressText = formatAddress({ const fullAddress = formatAddress({
address: address.value, address: address.value,
addressEN: addressEN.value, addressEN: addressEN.value,
moo: moo.value ? moo.value : '', moo: moo.value ? moo.value : '',
@ -122,26 +97,21 @@ const fullAddress = computed(() => {
province: province as unknown as Province, province: province as unknown as Province,
district: district as unknown as District, district: district as unknown as District,
subDistrict: sDistrict as unknown as SubDistrict, subDistrict: sDistrict as unknown as SubDistrict,
zipCode: addressForeign.value ? zipCode.value || ' ' : undefined,
}); });
return fullAddressText; return fullAddress;
} }
return '-'; return '-';
}); });
const fullAddressEN = computed(() => { const fullAddressEN = computed(() => {
const province = addressForeign.value const province = provinceOptions.value.find((v) => v.id === provinceId.value);
? { nameEN: provinceTextEN.value } const district = districtOptions.value.find((v) => v.id === districtId.value);
: provinceOptions.value.find((v) => v.id === provinceId.value); const sDistrict = subDistrictOptions.value.find(
const district = addressForeign.value (v) => v.id === subDistrictId.value,
? { nameEN: districtTextEN.value } );
: districtOptions.value.find((v) => v.id === districtId.value);
const sDistrict = addressForeign.value
? { nameEN: subDistrictTextEN.value }
: subDistrictOptions.value.find((v) => v.id === subDistrictId.value);
if (province?.nameEN && district?.nameEN && sDistrict?.nameEN) { if (province && district && sDistrict) {
const fullAddressText = formatAddress({ const fullAddress = formatAddress({
address: address.value, address: address.value,
addressEN: addressEN.value, addressEN: addressEN.value,
moo: moo.value ? moo.value : '', moo: moo.value ? moo.value : '',
@ -154,9 +124,8 @@ const fullAddressEN = computed(() => {
district: district as unknown as District, district: district as unknown as District,
subDistrict: sDistrict as unknown as SubDistrict, subDistrict: sDistrict as unknown as SubDistrict,
en: true, en: true,
zipCode: addressForeign.value ? zipCode.value || ' ' : undefined,
}); });
return fullAddressText; return fullAddress;
} }
return '-'; return '-';
}); });
@ -180,7 +149,7 @@ async function fetchProvince() {
} }
async function fetchDistrict() { async function fetchDistrict() {
if (!provinceId.value || addressForeign.value) return; if (!provinceId.value) return;
const result = await addressStore.fetchDistrictByProvinceId(provinceId.value); const result = await addressStore.fetchDistrictByProvinceId(provinceId.value);
if (result) addrOptions.districtOps = result; if (result) addrOptions.districtOps = result;
@ -199,7 +168,7 @@ async function fetchDistrict() {
} }
async function fetchSubDistrict() { async function fetchSubDistrict() {
if (!districtId.value || addressForeign.value) return; if (!districtId.value) return;
const result = await addressStore.fetchSubDistrictByProvinceId( const result = await addressStore.fetchSubDistrictByProvinceId(
districtId.value, districtId.value,
); );
@ -286,16 +255,6 @@ onMounted(async () => {
await fetchSubDistrict(); await fetchSubDistrict();
}); });
function clearAddress() {
provinceId.value = null;
districtId.value = null;
subDistrictId.value = null;
provinceTextEN.value = null;
districtTextEN.value = null;
subDistrictTextEN.value = null;
zipCode.value = null;
}
watch(provinceId, fetchDistrict); watch(provinceId, fetchDistrict);
watch(districtId, fetchSubDistrict); watch(districtId, fetchSubDistrict);
@ -354,15 +313,6 @@ watchEffect(async () => {
{{ $t('customerEmployee.form.addressCustom') }} {{ $t('customerEmployee.form.addressCustom') }}
</span> </span>
</div> </div>
<div v-if="useForeignAddress" class="text-caption q-ml-md app-text-muted">
<q-checkbox
size="xs"
v-model="addressForeign"
@update:model-value="clearAddress"
/>
{{ $t('personnel.form.addressForeign') }}
</div>
</div> </div>
<div class="col-12 row q-col-gutter-y-md"> <div class="col-12 row q-col-gutter-y-md">
@ -499,24 +449,7 @@ watchEffect(async () => {
(v) => (typeof v === 'string' ? (street = v) : '') (v) => (typeof v === 'string' ? (street = v) : '')
" "
/> />
<q-input
v-if="addressForeign"
outlined
hide-bottom-space
class="col-md-3 col-6"
v-model="provinceId"
:dense="dense"
:label="$t('form.province')"
:readonly="readonly || sameWithEmployer"
:for="`${prefixId}-${indexId !== undefined ? `input-province-${indexId}` : 'input-province'}`"
:rules="
disabledRule
? []
: [(val) => (val && val.length > 0) || $t('form.error.required')]
"
/>
<q-select <q-select
v-else
autocomplete="off" autocomplete="off"
outlined outlined
clearable clearable
@ -560,24 +493,7 @@ watchEffect(async () => {
</template> </template>
</q-select> </q-select>
<q-input
v-if="addressForeign"
outlined
hide-bottom-space
class="col-md-3 col-6"
v-model="districtId"
:dense="dense"
:label="$t('form.district')"
:readonly="readonly || sameWithEmployer"
:for="`${prefixId}-${indexId !== undefined ? `input-district-${indexId}` : 'input-district'}`"
:rules="
disabledRule
? []
: [(val) => (val && val.length > 0) || $t('form.error.required')]
"
/>
<q-select <q-select
v-else
autocomplete="off" autocomplete="off"
outlined outlined
clearable clearable
@ -620,25 +536,7 @@ watchEffect(async () => {
</q-item> </q-item>
</template> </template>
</q-select> </q-select>
<q-input
v-if="addressForeign"
outlined
hide-bottom-space
class="col-md-3 col-6"
v-model="subDistrictId"
:dense="dense"
:label="$t('form.district')"
:readonly="readonly || sameWithEmployer"
:for="`${prefixId}-${indexId !== undefined ? `input-sub-district-${indexId}` : 'input-sub-district'}`"
:rules="
disabledRule
? []
: [(val) => (val && val.length > 0) || $t('form.error.required')]
"
/>
<q-select <q-select
v-else
autocomplete="off" autocomplete="off"
outlined outlined
clearable clearable
@ -682,27 +580,17 @@ watchEffect(async () => {
</template> </template>
</q-select> </q-select>
<q-input <q-input
:key="Number(addressForeign)"
hide-bottom-space
:for="`${prefixId}-${indexId !== undefined ? `input-zip-code-${indexId}` : 'input-zip-code'}`" :for="`${prefixId}-${indexId !== undefined ? `input-zip-code-${indexId}` : 'input-zip-code'}`"
:dense="dense" :dense="dense"
outlined outlined
:disable="!addressForeign && !readonly && !sameWithEmployer" :disable="!readonly && !sameWithEmployer"
:readonly="!addressForeign || readonly" readonly
:label="$t('form.zipCode')" :label="$t('form.zipCode')"
class="col-md-3 col-6" class="col-md-3 col-6"
:model-value=" :model-value="
!addressForeign addrOptions.subDistrictOps
? (addrOptions.subDistrictOps
?.filter((x) => x.id === subDistrictId) ?.filter((x) => x.id === subDistrictId)
.map((x) => x.zipCode)[0] ?? '') .map((x) => x.zipCode)[0] ?? ''
: zipCode
"
@update:model-value="(v) => (zipCode = v.toString())"
:rules="
!addressForeign
? []
: [(val) => (val && val.length > 0) || $t('form.error.required')]
" "
/> />
<q-input <q-input
@ -801,24 +689,7 @@ watchEffect(async () => {
(v) => (typeof v === 'string' ? (streetEN = v) : '') (v) => (typeof v === 'string' ? (streetEN = v) : '')
" "
/> />
<q-input
v-if="addressForeign"
outlined
hide-bottom-space
class="col-md-3 col-6"
v-model="provinceTextEN"
:dense="dense"
label="Province"
:readonly="readonly || sameWithEmployer"
:for="`${prefixId}-${indexId !== undefined ? `input-province-en-${indexId}` : 'input-province-en'}`"
:rules="
disabledRule
? []
: [(val) => (val && val.length > 0) || $t('form.error.required')]
"
/>
<q-select <q-select
v-else
autocomplete="off" autocomplete="off"
outlined outlined
clearable clearable
@ -861,25 +732,7 @@ watchEffect(async () => {
</q-item> </q-item>
</template> </template>
</q-select> </q-select>
<q-input
v-if="addressForeign"
outlined
hide-bottom-space
class="col-md-3 col-6"
v-model="districtTextEN"
:dense="dense"
label="District"
:readonly="readonly || sameWithEmployer"
:for="`${prefixId}-${indexId !== undefined ? `input-district-en-${indexId}` : 'input-district-en'}`"
:rules="
disabledRule
? []
: [(val) => (val && val.length > 0) || $t('form.error.required')]
"
/>
<q-select <q-select
v-else
autocomplete="off" autocomplete="off"
outlined outlined
clearable clearable
@ -922,25 +775,7 @@ watchEffect(async () => {
</q-item> </q-item>
</template> </template>
</q-select> </q-select>
<q-input
v-if="addressForeign"
outlined
hide-bottom-space
class="col-md-3 col-6"
v-model="subDistrictTextEN"
:dense="dense"
label="Sub-District"
:readonly="readonly || sameWithEmployer"
:for="`${prefixId}-${indexId !== undefined ? `input-sub-district-en-${indexId}` : 'input-sub-district-en'}`"
:rules="
disabledRule
? []
: [(val) => (val && val.length > 0) || $t('form.error.required')]
"
/>
<q-select <q-select
v-else
autocomplete="off" autocomplete="off"
outlined outlined
clearable clearable
@ -984,28 +819,19 @@ watchEffect(async () => {
</template> </template>
</q-select> </q-select>
<q-input <q-input
:key="Number(addressForeign)"
hide-bottom-space hide-bottom-space
:for="`${prefixId}-${indexId !== undefined ? `input-zip-code-${indexId}` : 'input-zip-code'}`" :for="`${prefixId}-${indexId !== undefined ? `input-zip-code-${indexId}` : 'input-zip-code'}`"
:dense="dense" :dense="dense"
outlined outlined
:readonly="!addressForeign || readonly" readonly
:disable="!addressForeign && !readonly && !sameWithEmployer" :disable="!readonly && !sameWithEmployer"
zip="zip-en" zip="zip-en"
label="Zip Code" label="Zip Code"
class="col-md-3 col-6" class="col-md-3 col-6"
:model-value=" :model-value="
!addressForeign addrOptions.subDistrictOps
? (addrOptions.subDistrictOps
?.filter((x) => x.id === subDistrictId) ?.filter((x) => x.id === subDistrictId)
.map((x) => x.zipCode)[0] ?? '') .map((x) => x.zipCode)[0] ?? ''
: zipCode
"
@update:model-value="(v) => (zipCode = v.toString())"
:rules="
!addressForeign
? []
: [(val) => (val && val.length > 0) || $t('form.error.required')]
" "
/> />
<q-input <q-input

View file

@ -16,4 +16,3 @@ export { default as SideMenu } from './SideMenu.vue';
export { default as StatCardComponent } from './StatCardComponent.vue'; export { default as StatCardComponent } from './StatCardComponent.vue';
export { default as TooltipComponent } from './TooltipComponent.vue'; export { default as TooltipComponent } from './TooltipComponent.vue';
export { default as TreeComponent } from './TreeComponent.vue'; export { default as TreeComponent } from './TreeComponent.vue';
export { default as PaginationPageSize } from './PaginationPageSize.vue';

View file

@ -1,190 +0,0 @@
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { dateFormatJS } from 'src/utils/datetime';
import SelectInput from './SelectInput.vue';
import VueDatePicker from '@vuepic/vue-datepicker';
import dayjs from 'dayjs';
defineProps<{
active?: boolean;
}>();
const date = defineModel<string[]>();
const dateRange = ref<string>('');
const isDateSelect = ref(false);
function mapDateRange(val: string) {
const today = dayjs();
let start: dayjs.Dayjs, end: dayjs.Dayjs;
switch (val) {
case 'toDay':
start = today.startOf('day');
end = today.endOf('day');
break;
case 'yesterday':
start = today.subtract(1, 'day').startOf('day');
end = today.subtract(1, 'day').endOf('day');
break;
case 'thisWeek':
start = today.startOf('week');
end = today.endOf('week');
break;
case 'lastWeek':
start = today.subtract(1, 'week').startOf('week');
end = today.subtract(1, 'week').endOf('week');
break;
case 'thisMonth':
start = today.startOf('month');
end = today.endOf('month');
break;
case 'lastMonth':
start = today.subtract(1, 'month').startOf('month');
end = today.subtract(1, 'month').endOf('month');
break;
case 'thisYear':
start = today.startOf('year');
end = today.endOf('year');
break;
case 'lastYear':
start = today.subtract(1, 'year').startOf('year');
end = today.subtract(1, 'year').endOf('year');
break;
case 'last7Days':
start = today.subtract(6, 'day').startOf('day');
end = today.endOf('day');
break;
case 'last30Days':
start = today.subtract(29, 'day').startOf('day');
end = today.endOf('day');
break;
case 'last90Days':
start = today.subtract(89, 'day').startOf('day');
end = today.endOf('day');
break;
case 'customDateRange':
start = today.startOf('day');
end = today.endOf('day');
break;
default:
return;
}
return [start.toDate().toISOString(), end.toDate().toISOString()];
}
watch(
() => dateRange.value,
() => {
if (!dateRange.value) return;
date.value = mapDateRange(dateRange.value);
},
);
watch(
() => date.value,
() => {
if (date.value && date.value.length === 0) dateRange.value = '';
},
);
</script>
<template>
<q-btn
size="xs"
round
dense
unelevated
icon="mdi-tune-variant"
:flat="active ? false : !dateRange"
:color="active || dateRange ? 'info' : undefined"
>
<q-menu
:offset="[5, 10]"
max-width="300px"
class="bordered"
:persistent="isDateSelect"
>
<div class="q-pa-sm">
<slot name="prepend"></slot>
<div class="text-weight-medium">
{{ $t('general.advanceSearch') }}
</div>
<SelectInput
v-model="dateRange"
:label="$t('general.period')"
:option="[
{ label: $t('dateRange.today'), value: 'toDay' },
{ label: $t('dateRange.yesterday'), value: 'yesterday' },
{ label: $t('dateRange.thisWeek'), value: 'thisWeek' },
{ label: $t('dateRange.lastWeek'), value: 'lastWeek' },
{ label: $t('dateRange.thisMonth'), value: 'thisMonth' },
{ label: $t('dateRange.lastMonth'), value: 'lastMonth' },
{ label: $t('dateRange.thisYear'), value: 'thisYear' },
{ label: $t('dateRange.lastYear'), value: 'lastYear' },
{ label: $t('dateRange.last7Days'), value: 'last7Days' },
{ label: $t('dateRange.last30Days'), value: 'last30Days' },
{ label: $t('dateRange.last90Days'), value: 'last90Days' },
{
label: $t('dateRange.customDateRange'),
value: 'customDateRange',
},
]"
clearable
@clear="() => (date = [])"
/>
<VueDatePicker
v-if="dateRange === 'customDateRange'"
utc
range
teleport
auto-apply
for="select-date-range"
class="q-mt-sm"
v-model="date"
:dark="$q.dark.isActive"
:locale="$i18n.locale === 'tha' ? 'th' : 'en'"
@open="() => (isDateSelect = true)"
@closed="() => (isDateSelect = false)"
>
<template #trigger>
<q-input
placeholder="DD/MM/YYYY"
hide-bottom-space
dense
outlined
for="select-date-range"
:model-value="
date
? dateFormatJS({ date: date[0] }) +
' - ' +
dateFormatJS({ date: date[1] })
: ''
"
>
<template #prepend>
<q-icon name="mdi-calendar-outline" class="app-text-muted" />
</template>
<q-tooltip>
{{
date
? dateFormatJS({ date: date[0] }) +
' - ' +
dateFormatJS({ date: date[1] })
: ''
}}
</q-tooltip>
</q-input>
</template>
</VueDatePicker>
<slot></slot>
<!-- <SelectInput :label="$t('general.documentStatus')" :option="[]" /> -->
</div>
</q-menu>
<q-tooltip v-if="$q.screen.gt.sm">
{{ $t('general.advanceSearch') }}
</q-tooltip>
</q-btn>
</template>

View file

@ -25,11 +25,7 @@ withDefaults(
alt="Image" alt="Image"
/> />
</div> </div>
<div <div v-if="data.length > 3" class="avatar remaining-count">
v-if="data.length > 3"
class="avatar remaining-count"
style="cursor: default"
>
<q-tooltip> <q-tooltip>
<div v-for="(person, i) in data.slice(3)" :key="i + 3"> <div v-for="(person, i) in data.slice(3)" :key="i + 3">
{{ person.name }} {{ person.name }}

View file

@ -16,7 +16,6 @@ const props = withDefaults(
useUpload?: boolean; useUpload?: boolean;
useCancel?: boolean; useCancel?: boolean;
useRejectCancel?: boolean; useRejectCancel?: boolean;
useCopy?: boolean;
disableCancel?: boolean; disableCancel?: boolean;
disableDelete?: boolean; disableDelete?: boolean;
}>(), }>(),
@ -32,7 +31,6 @@ defineEmits<{
(e: 'link'): void; (e: 'link'): void;
(e: 'upload'): void; (e: 'upload'): void;
(e: 'delete'): void; (e: 'delete'): void;
(e: 'copy'): void;
(e: 'cancel'): void; (e: 'cancel'): void;
(e: 'rejectCancel'): void; (e: 'rejectCancel'): void;
(e: 'changeStatus'): void; (e: 'changeStatus'): void;
@ -174,27 +172,6 @@ watch(
</span> </span>
</q-item> </q-item>
<q-item
v-if="useCopy"
v-close-popup
dense
clickable
class="row q-py-sm"
style="white-space: nowrap"
:id="`btn-kebab-copy-${idName}`"
@click.stop="() => $emit('copy')"
>
<q-icon
size="xs"
class="col-3"
name="mdi-content-copy"
style="color: hsl(var(--teal-5-hsl))"
/>
<span class="col-9 q-px-md flex items-center">
{{ $t('general.copy') }}
</span>
</q-item>
<q-item <q-item
v-if="useCancel" v-if="useCancel"
v-close-popup v-close-popup

View file

@ -23,8 +23,6 @@ defineProps<{
history?: boolean; history?: boolean;
prefixId?: string; prefixId?: string;
separateEnter?: boolean; separateEnter?: boolean;
hideAction?: boolean;
hideDelete?: boolean;
}>(); }>();
defineEmits<{ defineEmits<{
@ -78,10 +76,8 @@ defineEmits<{
/> />
<KebabAction <KebabAction
v-if="!hideAction"
:id-name="prefixId" :id-name="prefixId"
:status="disabled ? 'INACTIVE' : 'ACTIVE'" :status="disabled ? 'INACTIVE' : 'ACTIVE'"
:hide-delete="hideDelete"
@view=" @view="
separateEnter separateEnter
? $emit('viewCard', 'INFO') ? $emit('viewCard', 'INFO')

View file

@ -13,7 +13,6 @@ let defaultFilter: (
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
prefix?: string;
id?: string; id?: string;
label?: string; label?: string;
option: T[]; option: T[];
@ -29,7 +28,6 @@ const props = withDefaults(
disable?: boolean; disable?: boolean;
multiple?: boolean; multiple?: boolean;
hideInput?: boolean; hideInput?: boolean;
hideDropdownIcon?: boolean;
rules?: ((value: string) => string | true)[]; rules?: ((value: string) => string | true)[];
}>(), }>(),
@ -72,7 +70,6 @@ watch(
</script> </script>
<template> <template>
<q-select <q-select
:id="id"
:placeholder="placeholder" :placeholder="placeholder"
outlined outlined
:clearable :clearable
@ -85,7 +82,7 @@ watch(
:hide-selected :hide-selected
hide-bottom-space hide-bottom-space
:fill-input="fillInput && !!model" :fill-input="fillInput && !!model"
:hide-dropdown-icon="readonly || hideDropdownIcon" :hide-dropdown-icon="readonly"
input-debounce="500" input-debounce="500"
:option-value=" :option-value="
typeof props.optionValue === 'string' ? props.optionValue : 'value' typeof props.optionValue === 'string' ? props.optionValue : 'value'
@ -106,11 +103,6 @@ watch(
} }
" "
:rules :rules
@clear="
() => {
multiple ? (model = []) : (model = '');
}
"
> >
<template v-if="$slots.prepend" v-slot:prepend> <template v-if="$slots.prepend" v-slot:prepend>
<slot name="prepend"></slot> <slot name="prepend"></slot>

View file

@ -72,11 +72,7 @@ onMounted(async () => {
:option="selectOptions" :option="selectOptions"
:hide-selected="false" :hide-selected="false"
:fill-input="false" :fill-input="false"
:rules="[ :rules="[(v: string) => !!v || $t('form.error.required')]"
(v: string) => {
return !!v?.length || $t('form.error.required');
},
]"
@filter="filter" @filter="filter"
> >
<template #before-options v-if="creatable"> <template #before-options v-if="creatable">

View file

@ -75,9 +75,9 @@ function setDefaultValue() {
</script> </script>
<template> <template>
<SelectInput <SelectInput
for="select-hq-id"
v-model="value" v-model="value"
incremental incremental
id="select-hq-id"
:label :label
:placeholder :placeholder
:readonly :readonly

View file

@ -1,213 +0,0 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { createSelect, SelectProps } from './select';
import SelectInput from '../SelectInput.vue';
import { BusinessType } from 'src/stores/business-type/types';
import useStore from 'src/stores/business-type';
type SelectOption = BusinessType;
const value = defineModel<string | null | undefined>('value', {
required: true,
});
const valueOption = defineModel<SelectOption>('valueOption', {
required: false,
});
const selectOptions = ref<SelectOption[]>([]);
const { fetchList: getList, fetchById: getById } = useStore();
defineEmits<{
(e: 'create'): void;
}>();
type ExclusiveProps = {
lang?: string;
codeOnly?: boolean;
selectFirstValue?: boolean;
branchVirtual?: boolean;
checkRole?: string[];
};
const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>();
const { getOptions, setFirstValue, getSelectedOption, filter } =
createSelect<SelectOption>(
{
value,
valueOption,
selectOptions,
getList: async (query) => {
const ret = await getList({
query,
...props.params,
pageSize: 99999,
});
if (ret) return ret.result;
},
getByValue: async (id) => {
const ret = await getById(id);
if (ret) return ret;
},
},
{ valueField: 'id' },
);
onMounted(async () => {
await getOptions();
if (props.autoSelectOnSingle && selectOptions.value.length === 1) {
setFirstValue();
}
if (props.selectFirstValue) {
setDefaultValue();
} else await getSelectedOption();
});
function setDefaultValue() {
setFirstValue();
}
</script>
<template>
<SelectInput
v-model="value"
incremental
option-value="id"
:label="label || $t('menu.manage.businessType')"
:placeholder
:readonly
:disable="disabled"
:option="selectOptions"
:hide-selected="false"
:fill-input="false"
:rules="[
(v: string) => !props.required || !!v || $t('form.error.required'),
]"
@filter="filter"
>
<template #selected-item="{ opt }">
{{ (lang ?? $i18n.locale) !== 'eng' ? opt.name : opt.nameEN }}
</template>
<template #no-option v-if="creatable">
<q-item
:disable="creatableDisabled"
clickable
v-close-popup
@click.stop="$emit('create')"
>
<q-item-section>
<span class="row items-center">
<q-icon
name="mdi-plus-circle-outline"
class="q-mr-sm"
style="color: hsl(var(--positive-bg))"
/>
<b>
{{ $t('general.add', { text: $t('businessType.title') }) }}
</b>
<span
v-if="creatableDisabled && creatableDisabledText"
class="app-text-muted q-pl-xs"
style="font-size: 80%"
>
{{ creatableDisabledText }}
</span>
</span>
</q-item-section>
</q-item>
<q-separator class="q-mx-sm" />
</template>
<template #before-options v-if="creatable">
<q-item
:disable="creatableDisabled"
clickable
v-close-popup
@click.stop="$emit('create')"
for="select-biz-type-add-new"
id="select-biz-type-add-new"
>
<q-item-section>
<span class="row items-center">
<q-icon
name="mdi-plus-circle-outline"
class="q-mr-sm"
style="color: hsl(var(--positive-bg))"
/>
<b>
{{ $t('general.add', { text: $t('businessType.title') }) }}
</b>
<span
v-if="creatableDisabled && creatableDisabledText"
class="app-text-muted q-pl-xs"
style="font-size: 80%"
>
{{ creatableDisabledText }}
</span>
</span>
</q-item-section>
</q-item>
<q-separator class="q-mx-sm" />
</template>
<template #before-options v-if="creatable">
<q-item
:disable="creatableDisabled"
clickable
v-close-popup
@click.stop="$emit('create')"
for="select-business-type-add-new"
id="select-business-type-add-new"
>
<q-item-section>
<span class="row items-center">
<q-icon
name="mdi-plus-circle-outline"
class="q-mr-sm"
style="color: hsl(var(--positive-bg))"
/>
<b>
{{ $t('general.add', { text: $t('menu.manage.businessType') }) }}
</b>
<span
v-if="creatableDisabled && creatableDisabledText"
class="app-text-muted q-pl-xs"
style="font-size: 80%"
>
{{ creatableDisabledText }}
</span>
</span>
</q-item-section>
</q-item>
<q-separator class="q-mx-sm" />
</template>
<template #option="{ opt, scope }">
<q-item v-bind="scope.itemProps">
<span class="row items-center">
{{ (lang ?? $i18n.locale) !== 'eng' ? opt.name : opt.nameEN }}
</span>
</q-item>
<q-separator class="q-mx-sm" />
</template>
<template #append v-if="clearable">
<q-icon
v-if="!readonly && value"
name="mdi-close-circle"
@click.stop="value = ''"
class="cursor-pointer clear-btn"
/>
</template>
</SelectInput>
</template>

View file

@ -30,7 +30,6 @@ defineEmits<{
type ExclusiveProps = { type ExclusiveProps = {
simple?: boolean; simple?: boolean;
simpleBranchNo?: boolean; simpleBranchNo?: boolean;
selectFirstValue?: boolean;
}; };
const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>(); const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>();
@ -65,14 +64,8 @@ onMounted(async () => {
setFirstValue(); setFirstValue();
} }
if (props.selectFirstValue) { await getSelectedOption();
setDefaultValue();
} else await getSelectedOption();
}); });
function setDefaultValue() {
setFirstValue();
}
</script> </script>
<template> <template>
<SelectInput <SelectInput
@ -165,9 +158,11 @@ function setDefaultValue() {
</template> </template>
<template #option="{ opt, scope }"> <template #option="{ opt, scope }">
<q-item v-bind="scope.itemProps" class="q-mx-sm bodrder"> <q-item @click="valueOption = opt" v-bind="scope.itemProps">
<SelectCustomerItem :data="opt" :simple :simple-branch-no /> <SelectCustomerItem :data="opt" :simple :simple-branch-no />
</q-item> </q-item>
<q-separator class="q-mx-sm" />
</template> </template>
<template #append v-if="clearable"> <template #append v-if="clearable">
@ -180,11 +175,3 @@ function setDefaultValue() {
</template> </template>
</SelectInput> </SelectInput>
</template> </template>
<style scoped>
.bodrder {
border-bottom: solid;
border-bottom-width: 1px;
border-color: var(--border-color);
}
</style>

View file

@ -43,7 +43,6 @@ const { getOptions, setFirstValue, getSelectedOption, filter } =
const ret = await getList({ const ret = await getList({
query: query === '' ? undefined : query, query: query === '' ? undefined : query,
...props.params, ...props.params,
activeOnly: true,
}); });
if (ret) return ret.result; if (ret) return ret.result;
}, },

View file

@ -25,7 +25,6 @@ const { getQuotationList: getList, getQuotation: getById } = useStore();
defineEmits<{ defineEmits<{
(e: 'create'): void; (e: 'create'): void;
(e: 'selected', value: SelectOption): void;
}>(); }>();
type ExclusiveProps = { type ExclusiveProps = {
@ -118,14 +117,6 @@ function setDefaultValue() {
(v: string) => !props.required || !!v || $t('form.error.required'), (v: string) => !props.required || !!v || $t('form.error.required'),
]" ]"
@filter="filter" @filter="filter"
@update:model-value="
(v) => {
$emit(
'selected',
selectOptions.find((opt) => opt.id === v),
);
}
"
> >
<template #append v-if="clearable"> <template #append v-if="clearable">
<q-icon <q-icon

View file

@ -26,7 +26,6 @@ defineEmits<{
type ExclusiveProps = { type ExclusiveProps = {
selectFirstValue?: boolean; selectFirstValue?: boolean;
prefix?: string;
}; };
const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>(); const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>();
@ -72,7 +71,6 @@ function setDefaultValue() {
<SelectInput <SelectInput
v-model="value" v-model="value"
incremental incremental
:id="`${prefix || 'nome'}-select-user`"
:label :label
:placeholder :placeholder
:readonly :readonly
@ -94,9 +92,7 @@ function setDefaultValue() {
:hide-selected="false" :hide-selected="false"
:fill-input="false" :fill-input="false"
:rules=" :rules="
required && !readonly required ? [(v: string) => !!v || $t('form.error.required')] : undefined
? [(v: string) => !!v || $t('form.error.required')]
: undefined
" "
@filter="filter" @filter="filter"
> >

View file

@ -35,13 +35,7 @@ export const createSelect = <T extends Record<string, any>>(
let previousSearch = ''; let previousSearch = '';
watch(value, (v) => { watch(value, (v) => {
if (!v) return; if (!v || (cache && cache.find((opt) => opt[valueField] === v))) return;
if (cache && cache.find((opt) => opt[valueField] === v)) {
valueOption.value = cache.find((opt) => opt[valueField] === v);
return;
}
getSelectedOption(); getSelectedOption();
}); });
@ -69,26 +63,15 @@ export const createSelect = <T extends Record<string, any>>(
const currentValue = value.value; const currentValue = value.value;
if (!currentValue) return; if (!currentValue) return;
if (selectOptions.value.find((v) => v[valueField] === currentValue)) return;
if (valueOption.value && valueOption.value[valueField] === currentValue) { if (valueOption.value && valueOption.value[valueField] === currentValue) {
selectOptions.value.unshift(valueOption.value); return selectOptions.value.unshift(valueOption.value);
selectOptions.value = selectOptions.value.filter((curr, idx, arr) => {
return (
arr.findIndex((item) => item[valueField] === curr[valueField]) === idx
);
});
return;
} }
const ret = await getByValue(currentValue); const ret = await getByValue(currentValue);
if (ret) { if (ret) {
selectOptions.value.unshift(ret); selectOptions.value.unshift(ret);
selectOptions.value = selectOptions.value.filter((curr, idx, arr) => {
return (
arr.findIndex((item) => item[valueField] === curr[valueField]) === idx
);
});
valueOption.value = ret; valueOption.value = ret;
} }
} }

View file

@ -251,10 +251,7 @@ function selectedIndex(item: any) {
> >
<!-- NOTE: custom column will starts with # --> <!-- NOTE: custom column will starts with # -->
<template v-if="!col.name.startsWith('#')"> <template v-if="!col.name.startsWith('#')">
<span v-if="col.name === 'serviceDetail'"> <span>
{{ props.row.detail.replace(/<\/?[^>]+(>|$)/g, '') || '-' }}
</span>
<span v-else>
{{ {{
typeof col.field === 'string' typeof col.field === 'string'
? props.row[col.field as keyof (Product | Service)] ? props.row[col.field as keyof (Product | Service)]

View file

@ -145,7 +145,6 @@ function selectedIndex(item: Employee) {
<template v-if="col.name === '#check'"> <template v-if="col.name === '#check'">
<q-checkbox <q-checkbox
id="select-worker-all" id="select-worker-all"
for="select-worker-all"
v-model="props.selected" v-model="props.selected"
@update:model-value="(v) => handleUpdate()" @update:model-value="(v) => handleUpdate()"
size="sm" size="sm"
@ -201,7 +200,6 @@ function selectedIndex(item: Employee) {
v-model="props.selected" v-model="props.selected"
size="sm" size="sm"
:id="`select-worker-${props.row.firstName}`" :id="`select-worker-${props.row.firstName}`"
:for="`select-worker-${props.row.firstName}`"
/> />
</template> </template>
</q-td> </q-td>

View file

@ -188,7 +188,6 @@ function formatCode(input: string | undefined, type: 'code' | 'number') {
:label="$t('customer.form.citizenId')" :label="$t('customer.form.citizenId')"
for="input-citizen-id" for="input-citizen-id"
v-model="citizenId" v-model="citizenId"
:rules="[(val: string) => !!val || $t('form.error.required')]"
/> />
<DatePicker <DatePicker
@ -222,7 +221,6 @@ function formatCode(input: string | undefined, type: 'code' | 'number') {
:label="$t('customer.form.religion')" :label="$t('customer.form.religion')"
for="input-religion" for="input-religion"
v-model="religion" v-model="religion"
:rules="[(val: string) => !!val || $t('form.error.required')]"
/> />
<q-select <q-select
outlined outlined
@ -307,7 +305,6 @@ function formatCode(input: string | undefined, type: 'code' | 'number') {
:label="$t('customer.form.firstName')" :label="$t('customer.form.firstName')"
for="input-first-name" for="input-first-name"
v-model="firstName" v-model="firstName"
:rules="[(val: string) => !!val || $t('form.error.required')]"
/> />
<q-input <q-input
@ -319,7 +316,6 @@ function formatCode(input: string | undefined, type: 'code' | 'number') {
:label="$t('customer.form.lastName')" :label="$t('customer.form.lastName')"
for="input-last-name" for="input-last-name"
v-model="lastName" v-model="lastName"
:rules="[(val: string) => !!val || $t('form.error.required')]"
/> />
<q-input <q-input

View file

@ -22,7 +22,6 @@ type Props = {
autoSave?: boolean; autoSave?: boolean;
data?: Data; data?: Data;
hideBtn?: boolean; hideBtn?: boolean;
disabledSubmit?: boolean;
}; };
type HandleProps = { type HandleProps = {
@ -110,7 +109,6 @@ async function change(e: Event) {
hide-delete hide-delete
hide-btn hide-btn
edit edit
:disabledSubmit
:title :title
:is-edit :is-edit
:readonly :readonly

View file

@ -31,7 +31,7 @@ const currentIndexDropdownList = ref(0);
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
treeFile?: { label: string; file: { label: string }[] }[]; treeFile: { label: string; file: { label: string }[] }[];
readonly?: boolean; readonly?: boolean;
dropdownList?: { label: string; value: string }[]; dropdownList?: { label: string; value: string }[];
hideAction?: boolean; hideAction?: boolean;

View file

@ -54,11 +54,10 @@ onMounted(() => {
@click="$emit('click')" @click="$emit('click')"
> >
<q-icon :name="icon" size="lg" :style="`color: ${color}`" /> <q-icon :name="icon" size="lg" :style="`color: ${color}`" />
<div class="col column q-pl-md"> <article class="col column q-pl-md">
<div class="ellipsis full-width" style="max-width: 65vw !important"> <span class="ellipsis full-width">
{{ name }} {{ name }}
</div> </span>
<span class="text-caption app-text-muted-2"> <span class="text-caption app-text-muted-2">
{{ {{
uploading.loaded uploading.loaded
@ -80,7 +79,7 @@ onMounted(() => {
/> />
{{ idle ? `Pending` : progress !== 1 ? `Uploading...` : 'Completed' }} {{ idle ? `Pending` : progress !== 1 ? `Uploading...` : 'Completed' }}
</span> </span>
</div> </article>
<q-btn <q-btn
v-if="closeable" v-if="closeable"
icon="mdi-close" icon="mdi-close"

View file

@ -45,9 +45,9 @@ const props = withDefaults(
readonly?: boolean; readonly?: boolean;
showTitle?: boolean; showTitle?: boolean;
ocr?: ( ocr?: (
group: string, group: any,
file: File, file: File,
) => Promise<{ ) => void | Promise<{
status: boolean; status: boolean;
group: string; group: string;
meta: { name: string; value: string }[]; meta: { name: string; value: string }[];
@ -123,7 +123,7 @@ async function change(e: Event) {
...obj.value, ...obj.value,
{ {
_meta: structuredClone(toRaw(selectedMenu.value)._meta || {}), _meta: structuredClone(toRaw(selectedMenu.value)._meta || {}),
group: selectedMenu.value?.group, group: selectedMenu.value?.value,
file: renamedFile, file: renamedFile,
}, },
]; ];
@ -168,8 +168,8 @@ async function change(e: Event) {
type: map['doc_type'], type: map['doc_type'],
number: map['doc_number'], number: map['doc_number'],
gender: map['sex'], gender: map['sex'],
firstName: map['last_name'], firstName: map['first_name'],
lastName: map['first_name'], lastName: map['last_name'],
issueDate: map['issue_date'], issueDate: map['issue_date'],
expireDate: map['expire_date'], expireDate: map['expire_date'],
issuePlace: map['nationality'], issuePlace: map['nationality'],
@ -327,7 +327,7 @@ defineEmits<{
:rows=" :rows="
obj obj
.filter((v) => { .filter((v) => {
if (!autoSave && v.group !== selectedMenu?.group) { if (!autoSave && v.group !== selectedMenu?.value) {
return false; return false;
} }
return true; return true;

View file

@ -198,10 +198,3 @@ i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-i
.q-focus-helper { .q-focus-helper {
visibility: hidden; visibility: hidden;
} }
.clear-btn {
opacity: 0.6;
&:hover {
opacity: 1;
}
}

View file

@ -4,7 +4,7 @@ export default {
save: 'Save', save: 'Save',
open: 'Open', open: 'Open',
close: 'Close', close: 'Close',
edit: 'Edit{text}', edit: 'Edit',
cancel: 'Cancel', cancel: 'Cancel',
back: 'Back', back: 'Back',
undo: 'Undo', undo: 'Undo',
@ -31,7 +31,6 @@ export default {
displayField: 'Display Fields', displayField: 'Display Fields',
order: 'Order', order: 'Order',
name: '{msg} Name', name: '{msg} Name',
nameEN: 'Name (English)',
fullName: 'Full Name', fullName: 'Full Name',
detail: '{msg} Detail', detail: '{msg} Detail',
remark: '{msg} Remark', remark: '{msg} Remark',
@ -61,7 +60,7 @@ export default {
branchStatus: 'Branch Status', branchStatus: 'Branch Status',
success: 'Success', success: 'Success',
taxNo: 'Legal Person', taxNo: 'Legal Person',
contactName: 'Contact Person', contactName: 'Contact Name',
image: 'Image of ', image: 'Image of ',
apply: 'Apply', apply: 'Apply',
licenseNumber: 'License number', licenseNumber: 'License number',
@ -155,13 +154,6 @@ export default {
draw: 'Draw', draw: 'Draw',
newUpload: 'New Upload', newUpload: 'New Upload',
nativeLanguage: '{msg} Native Language', nativeLanguage: '{msg} Native Language',
copy: 'Copy',
paste: 'Paste',
period: 'Period',
documentStatus: 'Document Status',
advanceSearch: 'Advance Search',
totalPeople: '{meg} people',
price: 'Price {price} Baht',
}, },
menu: { menu: {
@ -204,14 +196,12 @@ export default {
title: 'Manage', title: 'Manage',
branch: 'Branch', branch: 'Branch',
personnel: 'Personnel', personnel: 'Personnel',
group: 'Group',
productService: 'Product and Service', productService: 'Product and Service',
workflow: 'Workflow', workflow: 'Workflow',
property: 'Property', property: 'Property',
customer: 'Customer', customer: 'Customer',
mainData: 'Main Data', mainData: 'Main Data',
agencies: 'Agencies', agencies: 'Agencies',
businessType: 'Business Type',
}, },
sales: { sales: {
@ -258,8 +248,7 @@ export default {
manual: { manual: {
title: 'Manual', title: 'Manual',
usage: 'Usage', usage: 'การใช้งาน',
troubleshooting: 'Troubleshooting',
}, },
}, },
@ -341,7 +330,7 @@ export default {
requireLength: 'Please enter {msg} character', requireLength: 'Please enter {msg} character',
branchNameField: "Only letters, numbers, or the characters . , - ' &.", branchNameField: "Only letters, numbers, or the characters . , - ' &.",
branchNameENField: branchNameENField:
"Only English letters, numbers, or the characters . , - ' &. ( )", "Only English letters, numbers, or the characters . , - ' &.",
passportFormat: 'Please enter the passport number in the correct format.', passportFormat: 'Please enter the passport number in the correct format.',
}, },
warning: { warning: {
@ -388,7 +377,7 @@ export default {
branchLabel: 'Branch', branchLabel: 'Branch',
branchHQLabel: 'Headoffice', branchHQLabel: 'Headoffice',
taxNo: 'Legal Person', taxNo: 'Legal Person',
contactName: 'Contact Person', contactName: 'Contact Name',
}, },
page: { page: {
captionManage: 'Manage', captionManage: 'Manage',
@ -409,8 +398,8 @@ export default {
code: 'Headoffice Code', code: 'Headoffice Code',
codeBranch: 'Branch Code', codeBranch: 'Branch Code',
taxNo: 'Tax Identification Number', taxNo: 'Tax Identification Number',
contactName: 'Contact Person', contactName: 'Contact Name',
contactTelephone: 'Contact Number', contactTelephone: 'Contact Telephone',
branchName: 'Branch Name', branchName: 'Branch Name',
branchNameEN: 'Branch Name (EN)', branchNameEN: 'Branch Name (EN)',
servicePointName: 'Service Point Name', servicePointName: 'Service Point Name',
@ -460,7 +449,7 @@ export default {
regisNo: 'Registration Number', regisNo: 'Registration Number',
startDate: 'Start Date', startDate: 'Start Date',
retireDate: 'Retire Date', retireDate: 'Retire Date',
responsibleArea: 'Responsible Area', responsibleArea: 'Responsibel Area',
discount: 'Discount Condition', discount: 'Discount Condition',
sourceNationality: 'Source Nationality', sourceNationality: 'Source Nationality',
importNationality: 'Import Nationality', importNationality: 'Import Nationality',
@ -475,9 +464,6 @@ export default {
normal: 'Normal', normal: 'Normal',
canceled: 'Canceled', canceled: 'Canceled',
blacklist: 'Black list', blacklist: 'Black list',
contactName: 'Contact Person',
contactTel: 'Contact Number',
addressForeign: 'Use foreign address',
}, },
}, },
customer: { customer: {
@ -491,9 +477,10 @@ export default {
powerOfAttorney: 'Power of Attorney', powerOfAttorney: 'Power of Attorney',
others: 'Others', others: 'Others',
}, },
employer: 'Employer', employer: 'Employer',
employerLegalEntity: 'Legal Entity', employerLegalEntity: 'Legal Entity',
employerNaturalPerson: 'Natural Person', employerNaturalPerson: 'Natrual Person',
employerType: 'Employer Type', employerType: 'Employer Type',
employee: 'Employee', employee: 'Employee',
form: { form: {
@ -503,22 +490,24 @@ export default {
}, },
prefix: { prefix: {
mr: 'MR.', mr: 'Mr.',
mrs: 'MRS.', mrs: 'Mrs.',
miss: 'MISS.', miss: 'Miss.',
}, },
taxpayyerNo: 'Taxpayer Identification Number',
citizenId: 'Citizen ID', citizenId: 'Citizen ID',
religion: 'Religion', religion: 'Religion',
issueDate: 'Issue Date', issueDate: 'Issue Date',
passportExpiryDate: 'Passport Expiry Date', passportExpiryDate: 'Passport Expiry Date',
ownerName: 'Customer Name', ownerName: 'Customer Name',
firstName: 'First Name ', firstName: 'First Name ',
lastName: 'Last Name ', lastName: 'Last Name ',
firstNameEN: 'First Name in English', firstNameEN: 'First Name in English',
lastNameEN: 'Last Name in English', lastNameEN: 'Last Name in English',
cardNumber: 'ID Card Number', cardNumber: 'ID Card Number',
prefixName: 'Prefix', prefixName: 'Prefix',
legalPersonNo: 'Legal Entity Registration Number', legalPersonNo: 'Legal Entity Registration Number',
registerName: 'Company Name', registerName: 'Company Name',
@ -526,6 +515,7 @@ export default {
registerDate: 'Registered On', registerDate: 'Registered On',
registerCompanyName: 'Registered Name', registerCompanyName: 'Registered Name',
authorizedCapital: 'Authorized Capital', authorizedCapital: 'Authorized Capital',
workplace: 'Workplace', workplace: 'Workplace',
workplaceEN: 'Workplace (EN)', workplaceEN: 'Workplace (EN)',
address: 'Address', address: 'Address',
@ -533,6 +523,7 @@ export default {
branchCode: 'Branch Code', branchCode: 'Branch Code',
customerCode: 'Employer Code', customerCode: 'Employer Code',
legalPersonCode: 'Legal Entity Code', legalPersonCode: 'Legal Entity Code',
codeAbbrev: 'Company Abbreviation', codeAbbrev: 'Company Abbreviation',
codeNumber: 'Company Number', codeNumber: 'Company Number',
registeredBranch: 'Registered Branch', registeredBranch: 'Registered Branch',
@ -570,7 +561,7 @@ export default {
jobPosition: 'Job Position', jobPosition: 'Job Position',
address: 'Address', address: 'Address',
workPlace: 'Workplace', workPlace: 'Workplace',
contactName: 'Contact Person', contactName: 'Contact Name',
contactPhone: 'Contact Phone', contactPhone: 'Contact Phone',
totalEmployee: 'Total Employee', totalEmployee: 'Total Employee',
officeTel: 'Headoffice Telephone', officeTel: 'Headoffice Telephone',
@ -624,7 +615,7 @@ export default {
placeOfBirth: 'Place of Birth', placeOfBirth: 'Place of Birth',
countryOfbirth: 'Country of Birth', countryOfbirth: 'Country of Birth',
issueCountry: 'Issue Country', issueCountry: 'Issue Country',
entryCount: 'Number of Days in the Country', entryCount: 'Entry Count',
employerSelect: { employerSelect: {
branchName: 'Branch Name', branchName: 'Branch Name',
customerName: 'Employer Name', customerName: 'Employer Name',
@ -774,13 +765,10 @@ export default {
}, },
quotation: { quotation: {
ownOnly: 'View Own Quotation Only',
quotationDate: 'Quotation Date', quotationDate: 'Quotation Date',
seller: 'Seller', seller: 'Seller',
paymentChannels: 'Payment Channels', paymentChannels: 'Payment Channels',
channelsThat: 'Channels That', channelsThat: 'Channels That',
refNo: 'Reference Number',
bankAccount: 'Bank Account',
bankAccountNumber: 'Bank Account Number', bankAccountNumber: 'Bank Account Number',
bankAccountName: 'Bank Account Name', bankAccountName: 'Bank Account Name',
inTheNameOf: 'In The Name Of', inTheNameOf: 'In The Name Of',
@ -811,7 +799,7 @@ export default {
employee: 'Employee', employee: 'Employee',
employeeName: 'Full Name', employeeName: 'Full Name',
workName: 'Work Name', workName: 'Work Name',
contactName: 'Contact Person', contactName: 'Contact Name',
documentReceivePoint: 'Document Drop-Off Point"', documentReceivePoint: 'Document Drop-Off Point"',
dueDate: 'Quotation Due Date', dueDate: 'Quotation Due Date',
specialCondition: 'Special Conditions', specialCondition: 'Special Conditions',
@ -929,10 +917,9 @@ export default {
code: 'Agencies Code', code: 'Agencies Code',
group: 'Agencies Group', group: 'Agencies Group',
name: 'Agencies Name', name: 'Agencies Name',
contactName: 'Contact Person', contactName: 'Contact Name',
contactTel: 'Contact Number', contactTel: 'Contact Telephone',
bankInfo: 'Bank Information', bankInfo: 'Bank Information',
attachment: 'Attachment',
}, },
requestList: { requestList: {
@ -954,9 +941,8 @@ export default {
localEmployee: 'Local Employee', localEmployee: 'Local Employee',
nonLocalEmployee: 'Non Local Employee', nonLocalEmployee: 'Non Local Employee',
noWorkflowTemplate: 'A workflow template has not been selected.', noWorkflowTemplate: 'A workflow template has not been selected.',
salesRepresentative: 'Sales Representative',
dataOffice: 'Employment Office District', salesRepresentative: 'Sales Representative',
ref: 'Reference', ref: 'Reference',
action: { action: {
title: 'Action', title: 'Action',
@ -1020,7 +1006,7 @@ export default {
issueBranch: 'Issue Branch', issueBranch: 'Issue Branch',
issueDate: 'Issue Date', issueDate: 'Issue Date',
madeBy: 'Made By', madeBy: 'Made By',
contactName: 'Contact Person', contactName: 'Contact Name',
workOrderCode: 'Work Order Code', workOrderCode: 'Work Order Code',
workOrderName: 'Work Order Name', workOrderName: 'Work Order Name',
telephone: 'Telephone', telephone: 'Telephone',
@ -1079,10 +1065,6 @@ export default {
confirmDebitNoteAccept: 'Confirm acceptance of the debit note.', confirmDebitNoteAccept: 'Confirm acceptance of the debit note.',
}, },
message: { message: {
copy: 'Copy',
warningPaste:
'Do you want to replace the data with the newly copied information?',
warningCopyEmpty: 'You have not copied any data yet',
quotationAccept: 'Once accepted, no further modifications can be made', quotationAccept: 'Once accepted, no further modifications can be made',
beingUse: '"{msg}" is being used.', beingUse: '"{msg}" is being used.',
incompleteDataEntry: 'Incomplete data entry on {tap} page', incompleteDataEntry: 'Incomplete data entry on {tap} page',
@ -1127,7 +1109,7 @@ export default {
oneOrMoreBranchMissing: oneOrMoreBranchMissing:
'One or more branch cannot be delete and is missing.', 'One or more branch cannot be delete and is missing.',
cantMakeHQAndBranchSameTime: cantMakeHQAndBranchSameTime:
'Cannot make this as headquarters and branch at the same time.', 'Cannot make this as headquaters and branch at the same time.',
unknowHowToVerify: 'Unknown how to verify identity.', unknowHowToVerify: 'Unknown how to verify identity.',
noPermission: noPermission:
'You do not have permission to access or perform with this resource.', 'You do not have permission to access or perform with this resource.',
@ -1234,9 +1216,6 @@ export default {
taskListNotPending: 'One or more task is not pending.', taskListNotPending: 'One or more task is not pending.',
reqNotMet: 'Not Match', reqNotMet: 'Not Match',
systemError: 'A system error occurred.', systemError: 'A system error occurred.',
taskOrderInvalid: 'Please select the product and the organization.',
flowAccountProductIdNotFound: 'Product not found in flow account',
}, },
}, },
@ -1506,26 +1485,4 @@ export default {
type: 'Type', type: 'Type',
}, },
}, },
dateRange: {
today: 'Today',
yesterday: 'Yesterday',
thisWeek: 'This Week',
lastWeek: 'Last Week',
thisMonth: 'This Month',
lastMonth: 'Last Month',
thisYear: 'This Year',
lastYear: 'Last Year',
last7Days: 'Last 7 Days',
last30Days: 'Last 30 Days',
last90Days: 'Last 90 Days',
customDateRange: 'Custom Date Range',
},
businessType: {
title: 'Business Type',
caption: 'Manage Business Type',
name: 'Business Type Name',
nameEn: 'Business Type Name (English)',
},
}; };

View file

@ -4,7 +4,7 @@ export default {
save: 'บันทึก', save: 'บันทึก',
open: 'เปิด', open: 'เปิด',
close: 'ปิด', close: 'ปิด',
edit: 'แก้ไข{text}', edit: 'แก้ไข',
cancel: 'ยกเลิก', cancel: 'ยกเลิก',
back: 'ย้อนกลับ', back: 'ย้อนกลับ',
undo: 'ย้อนกลับ', undo: 'ย้อนกลับ',
@ -31,7 +31,6 @@ export default {
displayField: 'ฟิลด์แสดงผล', displayField: 'ฟิลด์แสดงผล',
order: 'ลำดับ', order: 'ลำดับ',
name: 'ชื่อ{msg}', name: 'ชื่อ{msg}',
nameEN: 'ชื่อ (ภาษาอังกฤษ)',
fullName: 'ชื่อ-สกุล', fullName: 'ชื่อ-สกุล',
detail: 'รายละเอียด{msg}', detail: 'รายละเอียด{msg}',
remark: 'หมายเหตุ{msg}', remark: 'หมายเหตุ{msg}',
@ -155,13 +154,6 @@ export default {
draw: 'วาด', draw: 'วาด',
newUpload: 'อัปโหลดใหม่', newUpload: 'อัปโหลดใหม่',
nativeLanguage: '{msg} ภาษาต้นทาง', nativeLanguage: '{msg} ภาษาต้นทาง',
copy: 'คัดลอก',
paste: 'วาง',
period: 'ช่วงเวลา',
documentStatus: 'สถานะเอกสาร',
advanceSearch: 'ค้นหาขั้นสูง',
totalPeople: '{meg} คน',
price: 'ราคา {price} บาท',
}, },
menu: { menu: {
@ -204,14 +196,12 @@ export default {
title: 'จัดการ', title: 'จัดการ',
branch: 'สาขา', branch: 'สาขา',
personnel: 'บุคลากร', personnel: 'บุคลากร',
group: 'กลุ่ม',
productService: 'สินค้าและบริการ', productService: 'สินค้าและบริการ',
workflow: 'ขั้นตอนการทำงาน', workflow: 'ขั้นตอนการทำงาน',
property: 'คุณสมบัติ', property: 'คุณสมบัติ',
customer: 'ลูกค้า', customer: 'ลูกค้า',
mainData: 'ข้อมูลหลัก', mainData: 'ข้อมูลหลัก',
agencies: 'หน่วยงาน', agencies: 'หน่วยงาน',
businessType: 'ประเภทกิจการ',
}, },
sales: { sales: {
@ -259,7 +249,6 @@ export default {
manual: { manual: {
title: 'คู่มือ', title: 'คู่มือ',
usage: 'การใช้งาน', usage: 'การใช้งาน',
troubleshooting: 'การแก้ปัญหา',
}, },
}, },
@ -338,7 +327,7 @@ export default {
letterAndNumOnly: 'โปรดใช้เฉพาะ _ ตัวอักษรภาษาอังกฤษและตัวเลขเท่านั้น', letterAndNumOnly: 'โปรดใช้เฉพาะ _ ตัวอักษรภาษาอังกฤษและตัวเลขเท่านั้น',
numOnly: 'โปรดใช้เฉพาะตัวเลขเท่านั้น', numOnly: 'โปรดใช้เฉพาะตัวเลขเท่านั้น',
requireLength: 'กรุณากรอกให้ครบ {msg} หลัก', requireLength: 'กรุณากรอกให้ครบ {msg} หลัก',
branchNameField: "โปรดใช้ตัวอักษร ตัวเลข หรือ . , - ' ( ) & เท่านั้น", branchNameField: "โปรดใช้ตัวอักษร ตัวเลข หรือ . , - ' & เท่านั้น",
branchNameENField: branchNameENField:
"โปรดใช้ตัวอักษรภาษาอังกฤษ ตัวเลข หรือ . , - ' & เท่านั้น", "โปรดใช้ตัวอักษรภาษาอังกฤษ ตัวเลข หรือ . , - ' & เท่านั้น",
passportFormat: 'กรุณากรอกหมายเลขพาสปอร์ตให้ถูกต้องตามรูปแบบ', passportFormat: 'กรุณากรอกหมายเลขพาสปอร์ตให้ถูกต้องตามรูปแบบ',
@ -467,13 +456,10 @@ export default {
citizenId: 'เลขที่บัตรประชาชน', citizenId: 'เลขที่บัตรประชาชน',
citizenIssue: 'วันที่ออกบัตร', citizenIssue: 'วันที่ออกบัตร',
citizenExpire: 'วันที่หมดอายุ', citizenExpire: 'วันที่หมดอายุ',
agencyStatus: 'สถานะเอเจนซี่', agencyStatus: 'สถานะการเป็นเอเจนซี่',
normal: 'ปกติ', normal: 'ปกติ',
canceled: 'ยกเลิก', canceled: 'ยกเลิก',
blacklist: 'แบล็คลิสต์', blacklist: 'แบล็คลิสต์',
contactName: 'ชื่อผู้ติดต่อ',
contactTel: 'เบอร์โทรศัพท์ผู้ติดต่อ',
addressForeign: 'ใช้ที่อยู่ต่างประเทศ',
}, },
}, },
customer: { customer: {
@ -500,16 +486,15 @@ export default {
}, },
prefix: { prefix: {
mr: 'นาย', mr: 'Mr.',
mrs: 'นาง', mrs: 'Mrs.',
miss: 'นางสาว', miss: 'Miss.',
}, },
citizenId: 'บัตรประจำตัวประชาชน', citizenId: 'บัตรประจำตัวประชาชน',
religion: 'ศาสนา', religion: 'ศาสนา',
issueDate: 'วันที่ออกหนังสือ', issueDate: 'วันที่ออกหนังสือ',
passportExpiryDate: 'วันหiมดอายุหนังสือเดินทาง', passportExpiryDate: 'วันหiมดอายุหนังสือเดินทาง',
taxpayyerNo: 'เลขที่ประจำตัวผู้เสียภาษี',
ownerName: 'ชื่อนายจ้าง', ownerName: 'ชื่อนายจ้าง',
firstName: 'ชื่อ ', firstName: 'ชื่อ ',
@ -593,7 +578,7 @@ export default {
family: 'ข้อมูลครอบครัว', family: 'ข้อมูลครอบครัว',
}, },
workerStatus: 'สถานะคนงาน', workerStatus: 'สถานะคนงาน',
previousPassportNumber: 'หมายเลขหนังสือเดินทางเล่มเก่า', previousPassportNumber: 'หมายเลขอันเก่าหนังสือเดินทาง',
employerBranch: 'สาขานายจ้าง', employerBranch: 'สาขานายจ้าง',
employeeCode: 'รหัสลูกจ้าง', employeeCode: 'รหัสลูกจ้าง',
nrcNo: 'เลขบัตรประจำตัวคนซึ่งไม่มีสัญชาติไทย (N.R.C No.)', nrcNo: 'เลขบัตรประจำตัวคนซึ่งไม่มีสัญชาติไทย (N.R.C No.)',
@ -626,7 +611,7 @@ export default {
placeOfBirth: 'สถานที่เกิด', placeOfBirth: 'สถานที่เกิด',
countryOfbirth: 'ประเทศที่เกิด', countryOfbirth: 'ประเทศที่เกิด',
issueCountry: 'ประเทศที่ออก', issueCountry: 'ประเทศที่ออก',
entryCount: 'จำนวนวันที่เข้าประเทศ', entryCount: 'จำนวนที่เข้าประเทศ',
employerSelect: { employerSelect: {
branchName: 'ชื่อสาขา', branchName: 'ชื่อสาขา',
customerName: 'ชื่อนายจ้าง', customerName: 'ชื่อนายจ้าง',
@ -654,7 +639,7 @@ export default {
permitIssuedAt: 'สถานที่ออกใบอนุญาต', permitIssuedAt: 'สถานที่ออกใบอนุญาต',
permitIssueDate: 'วันที่ออกใบอนุญาตทำงาน', permitIssueDate: 'วันที่ออกใบอนุญาตทำงาน',
permitExpireDate: 'วันที่หมดอายุใบอนุญาตทำงาน', permitExpireDate: 'วันที่หมดอายุใบอนุญาตทำงาน',
identityNo: 'เลขประจำตัวคนต่างด้าว (13 หลัก)', identityNo: 'เลขประจำตัวคนต่างด้าว (13หลัก)',
}, },
formFamily: { formFamily: {
citizenId: citizenId:
@ -772,13 +757,10 @@ export default {
}, },
quotation: { quotation: {
ownOnly: 'เห็นเฉพาะใบเสนอราคาของตัวเอง',
quotationDate: 'วันที่ใบเสนอราคา', quotationDate: 'วันที่ใบเสนอราคา',
seller: 'ผู้ขาย', seller: 'ผู้ขาย',
paymentChannels: 'ช่องทางชำระเงิน', paymentChannels: 'ช่องทางชำระเงิน',
channelsThat: 'ช่องทางที่', channelsThat: 'ช่องทางที่',
refNo: 'เลขที่อ้างอิง',
bankAccount: 'บัญชีธนาคาร',
bankAccountNumber: 'เลขบัญชีธนาคาร', bankAccountNumber: 'เลขบัญชีธนาคาร',
bankAccountName: 'ชื่อบัญชี', bankAccountName: 'ชื่อบัญชี',
inTheNameOf: 'ในนาม', inTheNameOf: 'ในนาม',
@ -803,7 +785,7 @@ export default {
branch: 'สาขาที่ออกใบเสนอราคา', branch: 'สาขาที่ออกใบเสนอราคา',
branchVirtual: 'จุดรับบริการที่ออกใบเสนอราคา', branchVirtual: 'จุดรับบริการที่ออกใบเสนอราคา',
customer: 'ลูกค้า', customer: 'ลูกค้า',
newCustomer: 'แรงงานใหม่', newCustomer: 'ลูกค้าใหม่',
employeeList: 'รายชื่อแรงงาน', employeeList: 'รายชื่อแรงงาน',
employee: 'แรงงาน', employee: 'แรงงาน',
employeeName: 'ชื่อ-นามสกุล แรงงาน', employeeName: 'ชื่อ-นามสกุล แรงงาน',
@ -929,7 +911,6 @@ export default {
contactName: 'ชื่อผู้ติดต่อ', contactName: 'ชื่อผู้ติดต่อ',
contactTel: 'เบอร์โทรผู้ติดต่อ', contactTel: 'เบอร์โทรผู้ติดต่อ',
bankInfo: 'ข้อมูลธนาคาร', bankInfo: 'ข้อมูลธนาคาร',
attachment: 'เอกสารเพิ่มเติม',
}, },
requestList: { requestList: {
@ -951,7 +932,6 @@ export default {
nonLocalEmployee: 'พนักงานนอกพื้นที่', nonLocalEmployee: 'พนักงานนอกพื้นที่',
noWorkflowTemplate: 'คุณไม่ได้เลือกแม่แบบขั้นตอนการทำงาน', noWorkflowTemplate: 'คุณไม่ได้เลือกแม่แบบขั้นตอนการทำงาน',
salesRepresentative: 'พนักงานขาย', salesRepresentative: 'พนักงานขาย',
dataOffice: 'สำนักงานเขตจัดหางาน',
ref: 'อ้างอิง', ref: 'อ้างอิง',
action: { action: {
title: 'จัดการ', title: 'จัดการ',
@ -1070,9 +1050,6 @@ export default {
confirmDebitNoteAccept: 'ยืนยันการตอบรับใบเพิ่มหนี้', confirmDebitNoteAccept: 'ยืนยันการตอบรับใบเพิ่มหนี้',
}, },
message: { message: {
copy: 'คัดลอก',
warningPaste: 'คุณต้องการที่จะเเทนที่ข้อมูลที่คัดลอกมาใหม่ใช่หรือไม่',
warningCopyEmpty: 'คุณยังไม่ได้คัดลอกข้อมูล',
quotationAccept: 'เมื่อตอบรับเเล้วจะไม่สามารถแก้ไขได้อีก', quotationAccept: 'เมื่อตอบรับเเล้วจะไม่สามารถแก้ไขได้อีก',
beingUse: '"{msg}" มีการใช้งานอยู่', beingUse: '"{msg}" มีการใช้งานอยู่',
incompleteDataEntry: 'กรอกข้อมูลไม่ครบในหน้า {tap}', incompleteDataEntry: 'กรอกข้อมูลไม่ครบในหน้า {tap}',
@ -1220,8 +1197,6 @@ export default {
'มีงานหนึ่งงานหรือมากกว่าที่ไม่อยู่ในสถานะรอดำเนินการ', 'มีงานหนึ่งงานหรือมากกว่าที่ไม่อยู่ในสถานะรอดำเนินการ',
reqNotMet: 'ไม่ตรงกัน', reqNotMet: 'ไม่ตรงกัน',
systemError: 'ระบบเกิดข้อผิดพลาด', systemError: 'ระบบเกิดข้อผิดพลาด',
taskOrderInvalid: 'โปรดเลือก สินค้าเเละสาขา',
flowAccountProductIdNotFound: 'ไม่พบสินค้าใน flow account',
}, },
}, },
@ -1493,26 +1468,4 @@ export default {
type: 'ประเภท', type: 'ประเภท',
}, },
}, },
dateRange: {
today: 'วันนี้',
yesterday: 'เมื่อวานนี้',
thisWeek: 'สัปดาห์นี้',
lastWeek: 'สัปดาห์ที่แล้ว',
thisMonth: 'เดือนนี้',
lastMonth: 'เดือนที่แล้ว',
thisYear: 'ปีนี้',
lastYear: 'ปีที่แล้ว',
last7Days: '7 วันที่ผ่านมา',
last30Days: '30 วันที่ผ่านมา',
last90Days: '90 วันที่ผ่านมา',
customDateRange: 'กำหนดช่วงวันที่เอง',
},
businessType: {
title: 'ประเภทกิจการ',
caption: 'จัดการประเภทกิจการ',
name: 'ชื่อประเภทกิจการ',
nameEn: 'ชื่อประเภทกิจการ (ภาษาอังกฤษ)',
},
}; };

View file

@ -7,7 +7,7 @@ import useMyBranch from 'stores/my-branch';
import { getUserId, getRole } from 'src/services/keycloak'; import { getUserId, getRole } from 'src/services/keycloak';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { canAccess } from 'src/stores/utils'; import { isRoleInclude } from 'src/stores/utils';
type Menu = { type Menu = {
label: string; label: string;
@ -71,50 +71,82 @@ function initMenu() {
{ {
label: 'branch', label: 'branch',
route: '/branch-management', route: '/branch-management',
hidden: !canAccess('branch'), hidden: !isRoleInclude([
'system',
'head_of_admin',
'admin',
'branch_manager',
'head_of_accountant',
]),
}, },
{ {
label: 'personnel', label: 'personnel',
route: '/personnel-management', route: '/personnel-management',
hidden: !canAccess('personnel'), hidden: !isRoleInclude([
}, 'owner',
{ 'system',
label: 'group', 'head_of_admin',
route: '/group-management', 'admin',
hidden: !canAccess('personnel'), 'branch_manager',
]),
}, },
{ {
label: 'workflow', label: 'workflow',
route: '/workflow', route: '/workflow',
hidden: !canAccess('workflow'), hidden: !isRoleInclude(['system', 'head_of_admin', 'admin']),
}, },
{ {
label: 'property', label: 'property',
route: '/property', route: '/property',
hidden: !canAccess('workflow'), hidden: !isRoleInclude(['system', 'head_of_admin', 'admin']),
},
{
label: 'businessType',
route: '/business-type',
}, },
{ {
label: 'productService', label: 'productService',
route: '/product-service', route: '/product-service',
hidden: !isRoleInclude([
'system',
'head_of_admin',
'admin',
'branch_manager',
'head_of_accountant',
'head_of_sale',
'sale',
]),
}, },
{ {
label: 'customer', label: 'customer',
route: '/customer-management', route: '/customer-management',
hidden: !canAccess('customer'), hidden: !isRoleInclude([
'system',
'head_of_admin',
'admin',
'branch_manager',
'head_of_accountant',
'accountant',
'head_of_sale',
'sale',
]),
}, },
{ {
label: 'agencies', label: 'agencies',
route: '/agencies-management', route: '/agencies-management',
hidden: !isRoleInclude(['system', 'head_of_admin', 'admin']),
}, },
], ],
}, },
{ {
label: 'menu.sales', label: 'menu.sales',
icon: 'mdi-store-settings-outline', icon: 'mdi-store-settings-outline',
hidden: !isRoleInclude([
'system',
'head_of_admin',
'admin',
'branch_manager',
'head_of_accountant',
'accountant',
'head_of_sale',
'sale',
]),
children: [ children: [
{ label: 'quotation', route: '/quotation' }, { label: 'quotation', route: '/quotation' },
{ label: 'invoice', route: '/invoice' }, { label: 'invoice', route: '/invoice' },
@ -137,6 +169,16 @@ function initMenu() {
label: 'menu.account', label: 'menu.account',
icon: 'mdi-bank-outline', icon: 'mdi-bank-outline',
disabled: false, disabled: false,
hidden: !isRoleInclude([
'system',
'head_of_admin',
'admin',
'branch_manager',
'head_of_accountant',
'accountant',
'head_of_sale',
'sale',
]),
children: [ children: [
{ label: 'receipt', route: '/receipt' }, { label: 'receipt', route: '/receipt' },
{ label: 'creditNote', route: '/credit-note' }, { label: 'creditNote', route: '/credit-note' },
@ -158,13 +200,10 @@ function initMenu() {
{ {
label: 'menu.overall', label: 'menu.overall',
icon: 'mdi-monitor-dashboard', icon: 'mdi-monitor-dashboard',
hidden: !isRoleInclude(['system', 'head_of_admin', 'admin', 'executive']),
children: [ children: [
{ label: 'report', route: '/report' }, { label: 'report', route: '/report' },
{ { label: 'dashboard', route: '/dash-board' },
label: 'dashboard',
route: '/dash-board',
hidden: !canAccess('dashBoard'),
},
], ],
}, },
@ -176,10 +215,6 @@ function initMenu() {
label: 'usage', label: 'usage',
route: '/manual', route: '/manual',
}, },
{
label: 'troubleshooting',
route: '/troubleshooting',
},
], ],
}, },
]; ];

View file

@ -2,7 +2,7 @@
import { ref, onMounted, computed, reactive } from 'vue'; import { ref, onMounted, computed, reactive } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { getUserId, getUsername, getName, logout, getRole } from 'src/services/keycloak'; import { getUserId, getUsername, logout, getRole } from 'src/services/keycloak';
import { Icon } from '@iconify/vue'; import { Icon } from '@iconify/vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import moment from 'moment'; import moment from 'moment';
@ -39,7 +39,7 @@ const configStore = useConfigStore();
const { data: notificationData } = storeToRefs(notificationStore); const { data: notificationData } = storeToRefs(notificationStore);
const { visible } = storeToRefs(loaderStore); const { visible } = storeToRefs(loaderStore);
const { t, locale } = useI18n({ useScope: 'global' }); const { t } = useI18n({ useScope: 'global' });
const userStore = useUserStore(); const userStore = useUserStore();
const canvasModal = ref(false); const canvasModal = ref(false);
@ -52,14 +52,8 @@ const unread = computed<number>(
); );
const userImage = ref<string>(); const userImage = ref<string>();
const userGender = ref(''); const userGender = ref('');
const userName = ref({ th: '', en: '' });
const canvasRef = ref(); const canvasRef = ref();
const displayName = computed(() => {
if (!userName.value.th && !userName.value.en) return getName() || 'Guest';
return locale.value === 'eng' ? userName.value.en : userName.value.th;
});
const language: { const language: {
value: Lang; value: Lang;
label: string; label: string;
@ -167,15 +161,10 @@ onMounted(async () => {
if (user === 'admin') return; if (user === 'admin') return;
if (uid) { if (uid) {
const res = await userStore.fetchById(uid); const res = await userStore.fetchById(uid);
if (res) { if (res && res.gender) {
if (res.gender) {
userGender.value = res.gender; userGender.value = res.gender;
userImage.value = `${baseUrl}/user/${uid}/profile-image/${res.selectedImage}`; userImage.value = `${baseUrl}/user/${uid}/profile-image/${res.selectedImage}`;
} }
//
userName.value.th = `${res.firstName || ''} ${res.lastName || ''}`.trim();
userName.value.en = `${res.firstNameEN || ''} ${res.lastNameEN || ''}`.trim();
}
} }
}); });
</script> </script>
@ -495,7 +484,6 @@ onMounted(async () => {
<!-- User --> <!-- User -->
<ProfileMenu <ProfileMenu
id="btn-profile-menu" id="btn-profile-menu"
:user-name="displayName"
@logout="doLogout" @logout="doLogout"
@edit-personal-info="console.log('edit')" @edit-personal-info="console.log('edit')"
@signature=" @signature="

View file

@ -12,7 +12,6 @@ const filterRole = ref<string[]>();
defineProps<{ defineProps<{
userImage?: string; userImage?: string;
gender?: string; gender?: string;
userName?: string;
}>(); }>();
const inputFile = document.createElement('input'); const inputFile = document.createElement('input');
@ -148,9 +147,9 @@ onMounted(async () => {
class="text-weight-bold ellipsis" class="text-weight-bold ellipsis"
style="max-width: 9vw" style="max-width: 9vw"
> >
{{ userName || getName() }} {{ getName() }}
<q-tooltip> <q-tooltip>
{{ userName || getName() }} {{ getName() }}
</q-tooltip> </q-tooltip>
</span> </span>
<span <span
@ -235,12 +234,12 @@ onMounted(async () => {
style="margin-top: 58px" style="margin-top: 58px"
> >
<span v-if="isLoggedIn()"> <span v-if="isLoggedIn()">
{{ userName || getName() }} {{ getName() }}
</span> </span>
<span v-else>{{ 'Guest' }}</span> <span v-else>{{ 'Guest' }}</span>
<q-tooltip> <q-tooltip>
<span v-if="isLoggedIn()"> <span v-if="isLoggedIn()">
{{ userName || getName() }} {{ getName() }}
</span> </span>
<span v-else>{{ 'Guest' }}</span> <span v-else>{{ 'Guest' }}</span>
</q-tooltip> </q-tooltip>

View file

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
// NOTE: Library // NOTE: Library
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { onMounted, watch } from 'vue'; import { onMounted } from 'vue';
// NOTE: Components // NOTE: Components
@ -10,44 +10,22 @@ import { onMounted, watch } from 'vue';
import { useManualStore } from 'src/stores/manual'; import { useManualStore } from 'src/stores/manual';
import { useNavigator } from 'src/stores/navigator'; import { useNavigator } from 'src/stores/navigator';
import { Icon } from '@iconify/vue/dist/iconify.js'; import { Icon } from '@iconify/vue/dist/iconify.js';
import { useRoute, useRouter } from 'vue-router';
// NOTE: Variable // NOTE: Variable
const route = useRoute();
const router = useRouter();
const manualStore = useManualStore(); const manualStore = useManualStore();
const navigatorStore = useNavigator(); const navigatorStore = useNavigator();
const { dataManual, dataTroubleshooting } = storeToRefs(manualStore); const { dataManual } = storeToRefs(manualStore);
async function fetchManual() {
const res = await manualStore.getManual();
dataManual.value = res ? res : [];
}
onMounted(async () => { onMounted(async () => {
navigatorStore.current.title = 'menu.manual.title'; navigatorStore.current.title = 'menu.manual.title';
navigatorStore.current.path = [{ text: '' }]; navigatorStore.current.path = [{ text: '' }];
await fetchManual();
}); });
watch(
() => route.name,
async () => {
if (route.name === 'Manual') {
const res = await manualStore.getManual();
dataManual.value = res ? res : [];
}
if (route.name === 'Troubleshooting') {
const res = await manualStore.getTroubleshooting();
dataTroubleshooting.value = res ? res : [];
if (
res.length &&
res.length === 1 &&
res[0].page &&
res[0].page.length === 1
) {
router.replace(
`/troubleshooting/${res[0].category}/${res[0].page[0].name}`,
);
}
}
},
{ immediate: true },
);
</script> </script>
<template> <template>
@ -56,7 +34,7 @@ watch(
> >
<section class="scroll q-gutter-y-sm"> <section class="scroll q-gutter-y-sm">
<q-expansion-item <q-expansion-item
v-for="v in $route.name === 'Manual' ? dataManual : dataTroubleshooting" v-for="v in dataManual"
:key="v.labelEN" :key="v.labelEN"
:content-inset-level="0.5" :content-inset-level="0.5"
class="rounded overflow-hidden bordered" class="rounded overflow-hidden bordered"
@ -80,11 +58,7 @@ watch(
clickable clickable
dense dense
class="dot items-center rounded q-my-xs" class="dot items-center rounded q-my-xs"
:to=" :to="`/manual/${v.category}/${x.name}`"
$route.name === 'Manual'
? `/manual/${v.category}/${x.name}`
: `/troubleshooting/${v.category}/${x.name}`
"
> >
<Icon <Icon
v-if="!!x.icon" v-if="!!x.icon"

View file

@ -56,8 +56,6 @@ onUnmounted(() => {
async function getContent() { async function getContent() {
if (!category.value || !page.value) return; if (!category.value || !page.value) return;
if (ROUTE.name === 'ManualView') {
const res = await manualStore.getManualByPage({ const res = await manualStore.getManualByPage({
category: category.value, category: category.value,
pageName: page.value, pageName: page.value,
@ -67,18 +65,6 @@ async function getContent() {
content.value = text; content.value = text;
contentParsed.value = md.parse(text, {}); contentParsed.value = md.parse(text, {});
} }
}
if (ROUTE.name === 'TroubleshootingView') {
const res = await manualStore.getTroubleshootingByPage({
category: category.value,
pageName: page.value,
});
if (res && res.ok) {
const text = await res.text();
content.value = text;
contentParsed.value = md.parse(text, {});
}
}
} }
function onScroll() { function onScroll() {
@ -116,8 +102,8 @@ async function scrollTo(id: string) {
<template> <template>
<main <main
class="full-height q-gutter-sm no-wrap" class="full-height q-gutter-sm"
:class="{ column: !toc && $q.screen.lt.md, 'row reverse': $q.screen.gt.sm }" :class="{ 'row reverse': $q.screen.gt.xs, column: $q.screen.xs }"
> >
<section <section
v-if="toc" v-if="toc"
@ -168,7 +154,7 @@ async function scrollTo(id: string) {
</q-list> </q-list>
</section> </section>
<section v-if="!toc && $q.screen.lt.md"> <section v-if="!toc && $q.screen.xs">
<q-btn <q-btn
dense dense
class="full-width text-capitalize" class="full-width text-capitalize"
@ -181,7 +167,7 @@ async function scrollTo(id: string) {
</section> </section>
<section <section
v-if="$q.screen.gt.xs || (!toc && $q.screen.xs)" v-if="content || (!toc && $q.screen.xs)"
ref="wrapper" ref="wrapper"
class="markdown col scroll full-height rounded" class="markdown col scroll full-height rounded"
> >
@ -198,9 +184,7 @@ async function scrollTo(id: string) {
md.render( md.render(
content.replaceAll( content.replaceAll(
'assets/', 'assets/',
$route.name === 'ManualView' `${baseUrl}/manual/${category}/assets/`,
? `${baseUrl}/manual/${category}/assets/`
: `${baseUrl}/troubleshooting/${category}/assets/`,
), ),
) )
" "
@ -328,31 +312,7 @@ async function scrollTo(id: string) {
padding: 0px 16px; padding: 0px 16px;
} }
.markdown :deep(h4) {
text-align: left;
margin-block: 0;
font-size: 16px;
font-weight: 600;
padding: 0px 16px;
}
.markdown :deep(video) { .markdown :deep(video) {
width: 100%; width: 100%;
} }
.markdown :deep(table) {
width: 100%;
border-collapse: collapse;
margin-bottom: 1.5rem;
}
.markdown :deep(:where(table th)) {
background: var(--surface-2);
border: 1px solid var(--border-color);
}
.markdown :deep(:where(table td, table th)) {
border: 1px solid var(--border-color);
padding: 0.25rem 1rem;
}
</style> </style>

View file

@ -94,22 +94,21 @@ onMounted(async () => {
<template> <template>
<main class="column full-height no-wrap"> <main class="column full-height no-wrap">
<div class="surface-1 col bordered rounded column"> <div class="surface-1 col bordered rounded column">
<div class="q-py-xs row items-center" style="padding-inline: 38px"> <div class="q-px-lg q-py-xs row items-center">
<q-checkbox <q-checkbox
size="xs" size="xs"
:model-value="noti.length > 0 && selectedNoti.length === noti.length" :model-value="selectedNoti.length === noti.length"
:disable="noti.length === 0" class="q-px-sm"
@click="toggleSelection('', true)" @click="toggleSelection('', true)"
/> />
<q-separator vertical inset spaced="md" />
<q-btn <q-btn
v-if="selectedNoti.length === 0" v-if="selectedNoti.length === 0"
icon="mdi-refresh" icon="mdi-refresh"
rounded rounded
flat flat
dense dense
size="sm" size="xs"
class="app-text-muted-2 q-mt-xs" class="app-text-muted-2 q-ml-sm q-mt-xs"
@click="async () => await fetchNoti()" @click="async () => await fetchNoti()"
> >
<q-tooltip>Refresh</q-tooltip> <q-tooltip>Refresh</q-tooltip>
@ -120,8 +119,8 @@ onMounted(async () => {
rounded rounded
flat flat
dense dense
size="sm" size="xs"
class="app-text-muted-2" class="app-text-muted-2 q-ml-sm"
@click="async () => await deleteNoti()" @click="async () => await deleteNoti()"
> >
<q-tooltip>{{ $t('general.delete') }}</q-tooltip> <q-tooltip>{{ $t('general.delete') }}</q-tooltip>
@ -132,7 +131,7 @@ onMounted(async () => {
rounded rounded
flat flat
dense dense
size="sm" size="xs"
class="app-text-muted-2 q-mx-sm" class="app-text-muted-2 q-mx-sm"
@click="async () => await markAsRead()" @click="async () => await markAsRead()"
> >
@ -178,13 +177,7 @@ onMounted(async () => {
<q-badge <q-badge
rounded rounded
class="q-ml-md" class="q-ml-md"
:color=" style="background: hsl(var(--info-bg))"
(tab.value === 'all'
? noti.length
: noti.filter((v) => !v.read).length) > 0
? 'info'
: 'grey'
"
> >
{{ {{
tab.value === 'all' tab.value === 'all'

View file

@ -6,11 +6,11 @@ import { Icon } from '@iconify/vue';
import { BranchContact } from 'stores/branch-contact/types'; import { BranchContact } from 'stores/branch-contact/types';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import type { QTableProps, QTableSlots } from 'quasar'; import type { QSelect, QTableProps, QTableSlots } from 'quasar';
import { resetScrollBar } from 'src/stores/utils'; import { resetScrollBar } from 'src/stores/utils';
import useBranchStore from 'stores/branch'; import useBranchStore from 'stores/branch';
import useFlowStore from 'stores/flow'; import useFlowStore from 'stores/flow';
import { isRoleInclude, canAccess } from 'stores/utils'; import { isRoleInclude } from 'stores/utils';
import { import {
BranchWithChildren, BranchWithChildren,
BranchCreate, BranchCreate,
@ -52,7 +52,6 @@ import {
UndoButton, UndoButton,
} from 'components/button'; } from 'components/button';
import { useNavigator } from 'src/stores/navigator'; import { useNavigator } from 'src/stores/navigator';
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const $q = useQuasar(); const $q = useQuasar();
const { t } = useI18n(); const { t } = useI18n();
@ -73,6 +72,7 @@ const typeBranchItem = [
color: 'var(--blue-6-hsl)', color: 'var(--blue-6-hsl)',
}, },
]; ];
const refFilter = ref<InstanceType<typeof QSelect>>();
const holdDialog = ref(false); const holdDialog = ref(false);
const isSubCreate = ref(false); const isSubCreate = ref(false);
const columns = [ const columns = [
@ -175,8 +175,6 @@ const qrCodeDialog = ref(false);
const qrCodeimageUrl = ref<string>(''); const qrCodeimageUrl = ref<string>('');
const formLastSubBranch = ref<number>(0); const formLastSubBranch = ref<number>(0);
const searchDate = ref<string[]>([]);
const branchStore = useBranchStore(); const branchStore = useBranchStore();
const flowStore = useFlowStore(); const flowStore = useFlowStore();
const { locale } = useI18n(); const { locale } = useI18n();
@ -717,20 +715,12 @@ async function fetchList(opts: {
tree?: boolean; tree?: boolean;
withHead?: boolean; withHead?: boolean;
filter?: 'head' | 'sub'; filter?: 'head' | 'sub';
startDate?: string;
endDate?: string;
}) { }) {
await branchStore.fetchList(opts); await branchStore.fetchList(opts);
} }
watch([inputSearch, searchDate], () => { watch(inputSearch, () => {
fetchList({ fetchList({ tree: true, query: inputSearch.value, withHead: true });
tree: true,
query: inputSearch.value,
withHead: true,
startDate: searchDate.value[0],
endDate: searchDate.value[1],
});
currentSubBranch.value = undefined; currentSubBranch.value = undefined;
}); });
@ -791,8 +781,6 @@ async function onSubmit(submitSelectedItem?: boolean) {
); );
if (!res) return; if (!res) return;
formType.value = 'view';
formData.value.codeHeadOffice = formData.value.code = res.code; formData.value.codeHeadOffice = formData.value.code = res.code;
imageUrl.value = `${baseUrl}/branch/${res.id}/image/${res.selectedImage}`; imageUrl.value = `${baseUrl}/branch/${res.id}/image/${res.selectedImage}`;
@ -867,9 +855,7 @@ async function onSubmit(submitSelectedItem?: boolean) {
actionText: t('dialog.action.ok'), actionText: t('dialog.action.ok'),
persistent: true, persistent: true,
title: t('form.warning.title'), title: t('form.warning.title'),
cancel: () => { cancel: () => {},
formType.value = 'create';
},
action: async () => { action: async () => {
await createBranch(); await createBranch();
}, },
@ -1050,7 +1036,7 @@ watch(currentHq, () => {
{{ $t('branch.allBranch') }} {{ $t('branch.allBranch') }}
</div> </div>
<q-btn <q-btn
v-if="isRoleInclude(['system'])" v-if="isRoleInclude(['head_of_admin', 'admin', 'system'])"
round round
flat flat
size="md" size="md"
@ -1074,7 +1060,6 @@ watch(currentHq, () => {
<div class="col full-width scroll"> <div class="col full-width scroll">
<div class="q-pa-md"> <div class="q-pa-md">
<TreeComponent <TreeComponent
:hide-create="!canAccess('branch', 'create')"
v-model:nodes="treeData" v-model:nodes="treeData"
v-model:expanded-tree="expandedTree" v-model:expanded-tree="expandedTree"
node-key="id" node-key="id"
@ -1185,49 +1170,26 @@ watch(currentHq, () => {
<template v-slot:prepend> <template v-slot:prepend>
<q-icon name="mdi-magnify" /> <q-icon name="mdi-magnify" />
</template> </template>
<template v-slot:append> <template v-if="$q.screen.lt.md" v-slot:append>
<q-separator vertical inset class="q-mr-xs" /> <span class="row">
<q-separator vertical />
<AdvanceSearch <q-btn
v-model="searchDate" icon="mdi-filter-variant"
:active="$q.screen.lt.md && statusFilter !== 'all'" unelevated
> class="q-ml-sm"
<div padding="4px"
v-if="$q.screen.lt.md" size="sm"
class="q-mt-sm text-weight-medium" rounded
> @click="refFilter?.showPopup"
{{ $t('general.status') }}
</div>
<q-select
v-if="$q.screen.lt.md"
v-model="statusFilter"
outlined
dense
autocomplete="off"
option-value="value"
option-label="label"
map-options
emit-value
:for="'field-select-status'"
:options="[
{ label: $t('general.all'), value: 'all' },
{
label: $t('status.ACTIVE'),
value: 'statusACTIVE',
},
{
label: $t('status.INACTIVE'),
value: 'statusINACTIVE',
},
]"
/> />
</AdvanceSearch> </span>
</template> </template>
</q-input> </q-input>
<div class="row col-md-6 justify-end"> <div class="row col-md-6 justify-end">
<q-select <q-select
v-if="$q.screen.gt.sm" v-show="$q.screen.gt.sm"
ref="refFilter"
v-model="statusFilter" v-model="statusFilter"
outlined outlined
dense dense
@ -1562,18 +1524,8 @@ watch(currentHq, () => {
</q-td> </q-td>
<q-td> <q-td>
<KebabAction <KebabAction
v-if="
!currentHq.id
? canAccess('branch', 'create')
: true
"
:status="props.row.status" :status="props.row.status"
:idName="props.row.name" :idName="props.row.name"
:hide-delete="
!currentHq.id
? !isRoleInclude(['system'])
: !canAccess('branch', 'create')
"
@view=" @view="
if (props.row.isHeadOffice) { if (props.row.isHeadOffice) {
triggerEdit( triggerEdit(
@ -1713,18 +1665,8 @@ watch(currentHq, () => {
> >
<template v-slot:action> <template v-slot:action>
<KebabAction <KebabAction
v-if="
!currentHq.id
? canAccess('branch', 'create')
: true
"
:status="props.row.status" :status="props.row.status"
:idName="props.row.name" :idName="props.row.name"
:hide-delete="
!currentHq.id
? !isRoleInclude(['system'])
: !canAccess('branch', 'create')
"
@view=" @view="
if (props.row.isHeadOffice) { if (props.row.isHeadOffice) {
triggerEdit( triggerEdit(
@ -2272,14 +2214,6 @@ watch(currentHq, () => {
@click="drawerEdit()" @click="drawerEdit()"
type="button" type="button"
/> />
<template
v-if="
formType !== 'edit' && formTypeBranch === 'headOffice'
? isRoleInclude(['system'])
: canAccess('branch', 'create')
"
>
<DeleteButton <DeleteButton
v-if="formType !== 'edit'" v-if="formType !== 'edit'"
id="btn-info-basic-delete" id="btn-info-basic-delete"
@ -2287,7 +2221,6 @@ watch(currentHq, () => {
@click="triggerDelete(currentEdit.id)" @click="triggerDelete(currentEdit.id)"
type="button" type="button"
/> />
</template>
</div> </div>
</div> </div>
<div <div

View file

@ -1,67 +0,0 @@
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue';
import { getInstance } from 'src/services/keycloak';
import { useNavigator } from 'src/stores/navigator';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import { initLang } from 'src/utils/ui';
const $q = useQuasar();
const { locale } = useI18n();
const navigatorStore = useNavigator();
const EDM_SERVICE = import.meta.env.VITE_EDM_MICRO_FRONTEND_URL;
const kc = getInstance();
const at = ref(kc.token);
const rt = ref(kc.refreshToken);
const iframe = ref<InstanceType<typeof HTMLIFrameElement>>();
function sendMessage() {
iframe?.value?.contentWindow?.postMessage(
{
i18n: locale.value,
darkMode: $q.dark.isActive,
},
'*',
);
}
onMounted(() => {
initLang();
currentLocale.value = locale.value;
navigatorStore.current.title = 'menu.dms';
navigatorStore.current.path = [
{
text: '',
i18n: true,
handler: () => {},
},
];
sendMessage();
});
watch(
() => kc.token,
() => {
at.value = kc.token;
rt.value = kc.refreshToken;
},
);
watch([locale, $q.dark], () => {
sendMessage();
});
const currentLocale = ref(locale.value);
</script>
<template>
<iframe
ref="iframe"
:src="`${EDM_SERVICE}/user?at=${at}&rt=${rt}&lang=${currentLocale}`"
frameborder="0"
class="full-width full-height rounded"
allowtransparency="true"
></iframe>
</template>
<style scoped></style>

View file

@ -10,8 +10,8 @@ import useOptionStore from 'stores/options';
import useAddressStore from 'stores/address'; import useAddressStore from 'stores/address';
import useMyBranch from 'src/stores/my-branch'; import useMyBranch from 'src/stores/my-branch';
import { calculateAge } from 'src/utils/datetime'; import { calculateAge } from 'src/utils/datetime';
import { useQuasar, type QTableProps } from 'quasar'; import { QSelect, useQuasar, type QTableProps } from 'quasar';
import { dialog, baseUrl, setPrefixName } from 'stores/utils'; import { dialog, baseUrl } from 'stores/utils';
import { useNavigator } from 'src/stores/navigator'; import { useNavigator } from 'src/stores/navigator';
import { isRoleInclude, resetScrollBar } from 'src/stores/utils'; import { isRoleInclude, resetScrollBar } from 'src/stores/utils';
import { BranchUserStats } from 'stores/branch/types'; import { BranchUserStats } from 'stores/branch/types';
@ -49,7 +49,6 @@ import FormPerson from 'components/02_personnel-management/FormPerson.vue';
import FormByType from 'components/02_personnel-management/FormByType.vue'; import FormByType from 'components/02_personnel-management/FormByType.vue';
import FormInformation from 'components/02_personnel-management/FormInformation.vue'; import FormInformation from 'components/02_personnel-management/FormInformation.vue';
import PaginationPageSize from 'src/components/PaginationPageSize.vue'; import PaginationPageSize from 'src/components/PaginationPageSize.vue';
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const { locale, t } = useI18n(); const { locale, t } = useI18n();
const $q = useQuasar(); const $q = useQuasar();
@ -74,6 +73,7 @@ const isImageEdit = ref(false);
const imageDialog = ref(false); const imageDialog = ref(false);
const infoDrawerEdit = ref(false); const infoDrawerEdit = ref(false);
const refreshImageState = ref(false); const refreshImageState = ref(false);
const refFilter = ref<InstanceType<typeof QSelect>>();
const firstScroll = ref(false); const firstScroll = ref(false);
const inputSearch = ref(''); const inputSearch = ref('');
@ -93,14 +93,12 @@ const currentUser = ref<User>();
const userCode = ref<string>(); const userCode = ref<string>();
const statusToggle = ref(true); const statusToggle = ref(true);
const userFile = ref<File[]>([]); const agencyFile = ref<File[]>([]);
const userFileList = ref<{ name: string; url: string }[]>([]); const agencyFileList = ref<{ name: string; url: string }[]>([]);
const typeStats = ref<UserTypeStats>(); const typeStats = ref<UserTypeStats>();
const userStats = ref<BranchUserStats[]>(); const userStats = ref<BranchUserStats[]>();
const searchDate = ref<[]>([]);
const urlProfile = ref<string>(); const urlProfile = ref<string>();
const profileFileImg = ref<File | null>(null); const profileFileImg = ref<File | null>(null);
const imageList = ref<{ selectedImage: string; list: string[] }>(); const imageList = ref<{ selectedImage: string; list: string[] }>();
@ -126,7 +124,7 @@ const defaultFormData = {
streetEN: '', streetEN: '',
street: '', street: '',
trainingPlace: null, trainingPlace: null,
importNationality: [], importNationality: null,
sourceNationality: null, sourceNationality: null,
licenseExpireDate: null, licenseExpireDate: null,
licenseIssueDate: null, licenseIssueDate: null,
@ -153,18 +151,8 @@ const defaultFormData = {
citizenExpire: null, citizenExpire: null,
citizenIssue: null, citizenIssue: null,
citizenId: '', citizenId: '',
contactName: '',
contactTel: '',
remark: '', remark: '',
agencyStatus: '', agencyStatus: null,
addressForeign: false,
provinceText: null,
districtText: null,
subDistrictText: null,
provinceTextEN: null,
districtTextEN: null,
subDistrictTextEN: null,
zipCodeText: null,
}; };
const formData = ref<UserCreate>({ const formData = ref<UserCreate>({
@ -186,7 +174,7 @@ const formData = ref<UserCreate>({
streetEN: '', streetEN: '',
street: '', street: '',
trainingPlace: null, trainingPlace: null,
importNationality: [], importNationality: null,
sourceNationality: null, sourceNationality: null,
licenseExpireDate: null, licenseExpireDate: null,
licenseIssueDate: null, licenseIssueDate: null,
@ -213,18 +201,8 @@ const formData = ref<UserCreate>({
citizenExpire: null, citizenExpire: null,
citizenIssue: null, citizenIssue: null,
citizenId: '', citizenId: '',
contactName: '',
contactTel: '',
remark: '', remark: '',
agencyStatus: '', agencyStatus: null,
addressForeign: false,
provinceText: null,
districtText: null,
subDistrictText: null,
provinceTextEN: null,
districtTextEN: null,
subDistrictTextEN: null,
zipCodeText: null,
}); });
const fieldSelectedOption = ref<{ label: string; value: string }[]>([ const fieldSelectedOption = ref<{ label: string; value: string }[]>([
@ -353,7 +331,7 @@ function onClose(excludeDialog?: boolean) {
urlProfile.value = ''; urlProfile.value = '';
profileFileImg.value = null; profileFileImg.value = null;
infoDrawerEdit.value = false; infoDrawerEdit.value = false;
userFile.value = []; agencyFile.value = [];
isEdit.value = false; isEdit.value = false;
statusToggle.value = true; statusToggle.value = true;
isImageEdit.value = false; isImageEdit.value = false;
@ -362,8 +340,6 @@ function onClose(excludeDialog?: boolean) {
mapUserType(currentTab.value); mapUserType(currentTab.value);
imageList.value = { selectedImage: '', list: [] }; imageList.value = { selectedImage: '', list: [] };
onCreateImageList.value = { selectedImage: '', list: [] }; onCreateImageList.value = { selectedImage: '', list: [] };
userFileList.value = [];
userFile.value = [];
flowStore.rotate(); flowStore.rotate();
} }
@ -384,10 +360,12 @@ async function openDialog(
isEdit.value = true; isEdit.value = true;
await assignFormData(id); await assignFormData(id);
if (formData.value.userType === 'AGENCY') {
const result = await userStore.fetchAttachment(id); const result = await userStore.fetchAttachment(id);
if (result) { if (result) {
userFileList.value = result; agencyFileList.value = result;
}
} }
} }
if (userStore.userOption.hqOpts.length !== 0 && !id) { if (userStore.userOption.hqOpts.length !== 0 && !id) {
@ -447,35 +425,14 @@ async function onSubmit(excludeDialog?: boolean) {
...formData.value, ...formData.value,
checkpointEN: formData.value.checkpoint, checkpointEN: formData.value.checkpoint,
status: !statusToggle.value ? 'INACTIVE' : 'ACTIVE', status: !statusToggle.value ? 'INACTIVE' : 'ACTIVE',
provinceId: formData.value.addressForeign
? null
: formData.value.provinceId,
districtId: formData.value.addressForeign
? null
: formData.value.districtId,
subDistrictId: formData.value.addressForeign
? null
: formData.value.subDistrictId,
provinceText: formData.value.addressForeign
? formData.value.provinceId
: null,
districtText: formData.value.addressForeign
? formData.value.districtId
: null,
subDistrictText: formData.value.addressForeign
? formData.value.subDistrictId
: null,
zipCodeText: formData.value.addressForeign
? formData.value.zipCode
: null,
} as const; } as const;
await userStore.editById(currentUser.value.id, formDataEdit); await userStore.editById(currentUser.value.id, formDataEdit);
if (userFile.value) { if (currentUser.value.id && formDataEdit.userType === 'AGENCY') {
if (!agencyFile.value) return;
const payload: UserAttachmentCreate = { const payload: UserAttachmentCreate = {
file: userFile.value, file: agencyFile.value,
}; };
if (payload?.file) { if (payload?.file) {
@ -500,37 +457,15 @@ async function onSubmit(excludeDialog?: boolean) {
: ''; : '';
formData.value.checkpointEN = formData.value.checkpoint; formData.value.checkpointEN = formData.value.checkpoint;
const result = await userStore.create( const result = await userStore.create(
{ formData.value,
...formData.value,
provinceId: formData.value.addressForeign
? null
: formData.value.provinceId,
districtId: formData.value.addressForeign
? null
: formData.value.districtId,
subDistrictId: formData.value.addressForeign
? null
: formData.value.subDistrictId,
provinceText: formData.value.addressForeign
? formData.value.provinceId
: null,
districtText: formData.value.addressForeign
? formData.value.districtId
: null,
subDistrictText: formData.value.addressForeign
? formData.value.subDistrictId
: null,
zipCodeText: formData.value.addressForeign
? formData.value.zipCode
: null,
},
onCreateImageList.value, onCreateImageList.value,
); );
if (userFile.value && result) { if (result && formData.value.userType === 'AGENCY') {
if (!agencyFile.value) return;
const payload: UserAttachmentCreate = { const payload: UserAttachmentCreate = {
file: userFile.value, file: agencyFile.value,
}; };
if (payload?.file) { if (payload?.file) {
@ -622,20 +557,12 @@ async function assignFormData(idEdit: string) {
currentUser.value = foundUser; currentUser.value = foundUser;
formData.value = { formData.value = {
branchId: foundUser.branch[0]?.id, branchId: foundUser.branch[0]?.id,
provinceId: foundUser.addressForeign provinceId: foundUser.provinceId,
? foundUser.provinceText districtId: foundUser.districtId,
: foundUser.provinceId, subDistrictId: foundUser.subDistrictId,
districtId: foundUser.addressForeign
? foundUser.districtText
: foundUser.districtId,
subDistrictId: foundUser.addressForeign
? foundUser.subDistrictText
: foundUser.subDistrictId,
telephoneNo: foundUser.telephoneNo, telephoneNo: foundUser.telephoneNo,
email: foundUser.email, email: foundUser.email,
zipCode: foundUser.addressForeign zipCode: foundUser.zipCode,
? foundUser.zipCodeText
: foundUser.zipCode,
gender: foundUser.gender, gender: foundUser.gender,
addressEN: foundUser.addressEN, addressEN: foundUser.addressEN,
address: foundUser.address, address: foundUser.address,
@ -646,10 +573,7 @@ async function assignFormData(idEdit: string) {
street: foundUser.street, street: foundUser.street,
streetEN: foundUser.streetEN, streetEN: foundUser.streetEN,
trainingPlace: foundUser.trainingPlace, trainingPlace: foundUser.trainingPlace,
importNationality: importNationality: foundUser.importNationality,
typeof foundUser.importNationality === 'string'
? [foundUser.importNationality]
: foundUser.importNationality,
sourceNationality: foundUser.sourceNationality, sourceNationality: foundUser.sourceNationality,
licenseNo: foundUser.licenseNo, licenseNo: foundUser.licenseNo,
discountCondition: foundUser.discountCondition, discountCondition: foundUser.discountCondition,
@ -669,8 +593,6 @@ async function assignFormData(idEdit: string) {
responsibleArea: foundUser.responsibleArea, responsibleArea: foundUser.responsibleArea,
status: foundUser.status, status: foundUser.status,
selectedImage: foundUser.selectedImage, selectedImage: foundUser.selectedImage,
contactName: foundUser.contactName || '',
contactTel: foundUser.contactTel || '',
licenseExpireDate: licenseExpireDate:
(foundUser.licenseExpireDate && (foundUser.licenseExpireDate &&
new Date(foundUser.licenseExpireDate)) || new Date(foundUser.licenseExpireDate)) ||
@ -689,10 +611,6 @@ async function assignFormData(idEdit: string) {
(foundUser.citizenExpire && new Date(foundUser.citizenExpire)) || null, (foundUser.citizenExpire && new Date(foundUser.citizenExpire)) || null,
remark: foundUser.remark || '', remark: foundUser.remark || '',
agencyStatus: foundUser.agencyStatus || '', agencyStatus: foundUser.agencyStatus || '',
addressForeign: foundUser.addressForeign || false,
provinceTextEN: foundUser.provinceTextEN,
districtTextEN: foundUser.districtTextEN,
subDistrictTextEN: foundUser.subDistrictTextEN,
}; };
formData.value.status === 'ACTIVE' || 'CREATED' formData.value.status === 'ACTIVE' || 'CREATED'
@ -751,8 +669,6 @@ async function fetchUserList(mobileFetch?: boolean) {
: statusFilter.value === 'statusACTIVE' : statusFilter.value === 'statusACTIVE'
? 'ACTIVE' ? 'ACTIVE'
: 'INACTIVE', : 'INACTIVE',
startDate: searchDate.value[0],
endDate: searchDate.value[1],
}); });
if (ret) { if (ret) {
@ -819,17 +735,7 @@ watch(
watch( watch(
() => formData.value.userType, () => formData.value.userType,
async (type) => { async () => {
if (type !== 'AGENCY') {
formData.value.addressForeign = false;
formData.value.provinceId = null;
formData.value.districtId = null;
formData.value.subDistrictId = null;
formData.value.provinceTextEN = null;
formData.value.districtTextEN = null;
formData.value.subDistrictTextEN = null;
formData.value.zipCodeText = null;
}
if (!infoDrawerEdit.value) return; if (!infoDrawerEdit.value) return;
formData.value.registrationNo = null; formData.value.registrationNo = null;
formData.value.startDate = null; formData.value.startDate = null;
@ -837,11 +743,11 @@ watch(
formData.value.responsibleArea = null; formData.value.responsibleArea = null;
formData.value.discountCondition = null; formData.value.discountCondition = null;
formData.value.sourceNationality = null; formData.value.sourceNationality = null;
formData.value.importNationality = []; formData.value.importNationality = null;
formData.value.trainingPlace = null; formData.value.trainingPlace = null;
formData.value.checkpoint = null; formData.value.checkpoint = null;
formData.value.checkpointEN = null; formData.value.checkpointEN = null;
userFile.value = []; agencyFile.value = [];
}, },
); );
@ -852,7 +758,7 @@ watch(
}, },
); );
watch([inputSearch, statusFilter, pageSize, searchDate], async () => { watch([inputSearch, statusFilter, pageSize], async () => {
if (userData.value) userData.value.result = []; if (userData.value) userData.value.result = [];
currentPage.value = 1; currentPage.value = 1;
@ -974,45 +880,26 @@ watch(
<template #prepend> <template #prepend>
<q-icon name="mdi-magnify" /> <q-icon name="mdi-magnify" />
</template> </template>
<template v-slot:append> <template v-if="$q.screen.lt.md" v-slot:append>
<q-separator vertical inset class="q-mr-xs" /> <span class="row">
<AdvanceSearch <q-separator vertical />
v-model="searchDate" <q-btn
:active="$q.screen.lt.md && statusFilter !== 'all'" icon="mdi-filter-variant"
> unelevated
<div class="q-ml-sm"
v-if="$q.screen.lt.md" padding="4px"
class="q-mt-sm text-weight-medium" size="sm"
> rounded
{{ $t('general.status') }} @click="refFilter?.showPopup"
</div>
<q-select
v-if="$q.screen.lt.md"
v-model="statusFilter"
outlined
dense
option-value="value"
option-label="label"
map-options
emit-value
autocomplete="off"
:for="'field-select-status'"
:options="[
{ label: $t('general.all'), value: 'all' },
{ label: $t('general.active'), value: 'statusACTIVE' },
{
label: $t('general.inactive'),
value: 'statusINACTIVE',
},
]"
/> />
</AdvanceSearch> </span>
</template> </template>
</q-input> </q-input>
<div class="row col-md-5" style="white-space: nowrap"> <div class="row col-md-5" style="white-space: nowrap">
<q-select <q-select
v-if="$q.screen.gt.sm" v-show="$q.screen.gt.sm"
ref="refFilter"
v-model="statusFilter" v-model="statusFilter"
outlined outlined
dense dense
@ -1362,7 +1249,7 @@ watch(
{{ {{
locale === 'eng' locale === 'eng'
? `${props.row.firstNameEN} ${props.row.lastNameEN}`.trim() ? `${props.row.firstNameEN} ${props.row.lastNameEN}`.trim()
: `${props.row.firstName || props.row.firstNameEN} ${props.row.lastName || props.row.lastNameEN}`.trim() : `${props.row.firstName} ${props.row.lastName}`.trim()
}} }}
<q-tooltip <q-tooltip
anchor="bottom left" anchor="bottom left"
@ -1372,7 +1259,7 @@ watch(
{{ {{
locale === 'eng' locale === 'eng'
? `${props.row.firstNameEN} ${props.row.lastNameEN}`.trim() ? `${props.row.firstNameEN} ${props.row.lastNameEN}`.trim()
: `${props.row.firstName || props.row.firstNameEN} ${props.row.lastName || props.row.lastNameEN}`.trim() : `${props.row.firstName} ${props.row.lastName}`.trim()
}} }}
</q-tooltip> </q-tooltip>
@ -1638,12 +1525,9 @@ watch(
hide-action hide-action
:is-edit="infoDrawerEdit" :is-edit="infoDrawerEdit"
:title=" :title="
(currentUser.namePrefix locale === 'eng'
? $t('customer.form.prefix.' + currentUser.namePrefix) + ' '
: '') +
(locale === 'eng'
? `${currentUser.firstNameEN} ${currentUser.lastNameEN}` ? `${currentUser.firstNameEN} ${currentUser.lastNameEN}`
: `${currentUser.firstName || currentUser.firstNameEN} ${currentUser.lastName || currentUser.lastNameEN}`) : `${currentUser.firstName} ${currentUser.lastName}`
" "
v-model:drawerOpen="infoDrawer" v-model:drawerOpen="infoDrawer"
:submit="() => onSubmit()" :submit="() => onSubmit()"
@ -1675,18 +1559,7 @@ watch(
v-model:toggle-status="formData.status" v-model:toggle-status="formData.status"
hideFade hideFade
:toggle-title="$t('status.title')" :toggle-title="$t('status.title')"
:title=" :title="`${locale === 'eng' ? `${formData.firstNameEN} ${formData.lastNameEN}` : `${formData.firstName} ${formData.lastName}`}`"
setPrefixName(
{
namePrefix: formData.namePrefix,
firstName: formData.firstName || formData.firstNameEN,
lastName: formData.lastName || formData.lastNameEN,
firstNameEN: formData.firstNameEN,
lastNameEN: formData.lastNameEN,
},
{ locale },
)
"
:caption="userCode" :caption="userCode"
:img=" :img="
`${baseUrl}/user/${currentUser.id}/profile-image/${formData.selectedImage}`.concat( `${baseUrl}/user/${currentUser.id}/profile-image/${formData.selectedImage}`.concat(
@ -1871,15 +1744,12 @@ watch(
v-model:citizen-id="formData.citizenId" v-model:citizen-id="formData.citizenId"
v-model:citizen-issue="formData.citizenIssue" v-model:citizen-issue="formData.citizenIssue"
v-model:citizen-expire="formData.citizenExpire" v-model:citizen-expire="formData.citizenExpire"
v-model:contact-name="formData.contactName"
v-model:contact-tel="formData.contactTel"
:title="'personnel.form.personalInformation'" :title="'personnel.form.personalInformation'"
prefix-id="drawer-info-personnel" prefix-id="drawer-info-personnel"
dense dense
outlined outlined
separator separator
:readonly="!infoDrawerEdit" :readonly="!infoDrawerEdit"
:agency="formData.userType === 'AGENCY'"
class="q-mb-xl" class="q-mb-xl"
/> />
@ -1897,15 +1767,10 @@ watch(
v-model:district-id="formData.districtId" v-model:district-id="formData.districtId"
v-model:sub-district-id="formData.subDistrictId" v-model:sub-district-id="formData.subDistrictId"
v-model:zip-code="formData.zipCode" v-model:zip-code="formData.zipCode"
v-model:address-foreign="formData.addressForeign"
v-model:province-text-en="formData.provinceTextEN"
v-model:district-text-en="formData.districtTextEN"
v-model:sub-district-text-en="formData.subDistrictTextEN"
:readonly="!infoDrawerEdit" :readonly="!infoDrawerEdit"
prefix-id="drawer-info-personnel" prefix-id="drawer-info-personnel"
:title="'personnel.form.addressInformation'" :title="'personnel.form.addressInformation'"
dense dense
:use-foreign-address="formData.userType === 'AGENCY'"
class="q-mb-xl" class="q-mb-xl"
/> />
<FormByType <FormByType
@ -1924,8 +1789,8 @@ watch(
v-model:import-nationality="formData.importNationality" v-model:import-nationality="formData.importNationality"
v-model:training-place="formData.trainingPlace" v-model:training-place="formData.trainingPlace"
v-model:checkpoint="formData.checkpoint" v-model:checkpoint="formData.checkpoint"
v-model:user-file="userFile" v-model:agency-file="agencyFile"
v-model:user-file-list="userFileList" v-model:agency-file-list="agencyFileList"
v-model:user-id="currentUser.id" v-model:user-id="currentUser.id"
v-model:remark="formData.remark" v-model:remark="formData.remark"
v-model:agency-status="formData.agencyStatus" v-model:agency-status="formData.agencyStatus"
@ -1972,18 +1837,7 @@ watch(
}[formData.gender] }[formData.gender]
" "
:toggleTitle="$t('status.title')" :toggleTitle="$t('status.title')"
:title=" :title="`${locale === 'eng' ? `${formData.firstNameEN} ${formData.lastNameEN}` : `${formData.firstName} ${formData.lastName}`}`"
setPrefixName(
{
namePrefix: formData.namePrefix,
firstName: formData.firstName,
lastName: formData.lastName,
firstNameEN: formData.firstNameEN,
lastNameEN: formData.lastNameEN,
},
{ locale },
)
"
:fallbackImg=" :fallbackImg="
{ {
male: '/no-img-man.png', male: '/no-img-man.png',
@ -2094,7 +1948,6 @@ watch(
id="dialog-form-personal" id="dialog-form-personal"
prefix-id="form-dialog-personnel" prefix-id="form-dialog-personnel"
dense dense
:agency="formData.userType === 'AGENCY'"
outlined outlined
separator separator
:title="'personnel.form.personalInformation'" :title="'personnel.form.personalInformation'"
@ -2113,8 +1966,6 @@ watch(
v-model:citizen-id="formData.citizenId" v-model:citizen-id="formData.citizenId"
v-model:citizen-issue="formData.citizenIssue" v-model:citizen-issue="formData.citizenIssue"
v-model:citizen-expire="formData.citizenExpire" v-model:citizen-expire="formData.citizenExpire"
v-model:contact-name="formData.contactName"
v-model:contact-tel="formData.contactTel"
class="q-mb-xl" class="q-mb-xl"
/> />
<AddressForm <AddressForm
@ -2131,13 +1982,8 @@ watch(
v-model:district-id="formData.districtId" v-model:district-id="formData.districtId"
v-model:sub-district-id="formData.subDistrictId" v-model:sub-district-id="formData.subDistrictId"
v-model:zip-code="formData.zipCode" v-model:zip-code="formData.zipCode"
v-model:address-foreign="formData.addressForeign"
v-model:province-text-en="formData.provinceTextEN"
v-model:district-text-en="formData.districtTextEN"
v-model:sub-district-text-en="formData.subDistrictTextEN"
prefix-id="drawer-info-personnel" prefix-id="drawer-info-personnel"
dense dense
:use-foreign-address="formData.userType === 'AGENCY'"
class="q-mb-xl" class="q-mb-xl"
/> />
<FormByType <FormByType
@ -2157,8 +2003,7 @@ watch(
v-model:checkpoint="formData.checkpoint" v-model:checkpoint="formData.checkpoint"
v-model:agency-status="formData.agencyStatus" v-model:agency-status="formData.agencyStatus"
v-model:remark="formData.remark" v-model:remark="formData.remark"
v-model:user-file="userFile" v-model:agency-file="agencyFile"
v-model:user-file-list="userFileList"
/> />
</div> </div>
</div> </div>

View file

@ -9,7 +9,7 @@ import { baseUrl } from 'src/stores/utils';
import useCustomerStore from 'stores/customer'; import useCustomerStore from 'stores/customer';
import useFlowStore from 'stores/flow'; import useFlowStore from 'stores/flow';
import useOptionStore from 'stores/options'; import useOptionStore from 'stores/options';
import { dialog, canAccess } from 'stores/utils'; import { dialog } from 'stores/utils';
import { Status } from 'stores/types'; import { Status } from 'stores/types';
import { Employee } from 'stores/employee/types'; import { Employee } from 'stores/employee/types';
@ -18,8 +18,6 @@ import { CustomerBranch, CustomerType } from 'stores/customer/types';
import { columnsEmployee } from './constant'; import { columnsEmployee } from './constant';
import { useCustomerBranchForm, useEmployeeForm } from './form'; 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 EmployerFormAuthorized from './components/employer/EmployerFormAuthorized.vue';
import FloatingActionButton from 'components/FloatingActionButton.vue'; import FloatingActionButton from 'components/FloatingActionButton.vue';
import SideMenu from 'components/SideMenu.vue'; import SideMenu from 'components/SideMenu.vue';
@ -91,11 +89,6 @@ const prop = withDefaults(
currentCitizenId?: string; currentCitizenId?: string;
gender: string; gender: string;
selectedImage: string; selectedImage: string;
fetchImageList: (
id: string,
selectedName: string,
type: 'customer' | 'employee',
) => Promise<void>;
}>(), }>(),
{ {
color: 'green', color: 'green',
@ -103,6 +96,7 @@ const prop = withDefaults(
); );
const currentBranchEmployee = ref<string>(''); const currentBranchEmployee = ref<string>('');
const listEmployee = ref<Employee[]>([]); const listEmployee = ref<Employee[]>([]);
const customerId = defineModel<string>('customerId', { required: true }); const customerId = defineModel<string>('customerId', { required: true });
defineEmits<{ defineEmits<{
@ -112,6 +106,16 @@ defineEmits<{
(e: 'dialog'): void; (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 = [ const columns = [
{ {
name: 'branchName', name: 'branchName',
@ -253,6 +257,10 @@ async function fetchEmployee(opts: { branchId: string; pageSize?: number }) {
} }
} }
onMounted(async () => {
await fetchList();
});
watch([customerId, inputSearch, currentStatus, pageSizeBranch], async () => { watch([customerId, inputSearch, currentStatus, pageSizeBranch], async () => {
await fetchList(); await fetchList();
}); });
@ -272,22 +280,12 @@ watch(
} }
}, },
); );
onMounted(async () => {
customerBranchFormState.value.currentCustomerId = route.params
.customerId as string;
await fetchList();
branch.value?.forEach((v) => {
currentBtnOpen.value.push(false);
});
});
</script> </script>
<template> <template>
<FloatingActionButton <FloatingActionButton
style="z-index: 999" style="z-index: 999"
v-if="$route.name !== 'CustomerManagement' && canAccess('customer', 'edit')" v-if="$route.name !== 'CustomerManagement'"
@click="openEmployerBranchForm('create')" @click="openEmployerBranchForm('create')"
hide-icon hide-icon
></FloatingActionButton> ></FloatingActionButton>
@ -475,6 +473,7 @@ onMounted(async () => {
<q-tr <q-tr
:class="{ :class="{
'app-text-muted': props.row.status === 'INACTIVE', 'app-text-muted': props.row.status === 'INACTIVE',
'cursor-pointer': props.row._count?.branch !== 0,
}" }"
:props="props" :props="props"
@click="$emit('viewDetail', props.row, props.rowIndex)" @click="$emit('viewDetail', props.row, props.rowIndex)"
@ -548,13 +547,7 @@ onMounted(async () => {
v-if="branchFieldSelected.includes('businessTypePure')" v-if="branchFieldSelected.includes('businessTypePure')"
class="text-left" class="text-left"
> >
{{ {{ useOptionStore().mapOption(props.row.businessType) || '-' }}
props.row.businessType
? props.row.businessType[
$i18n.locale === 'eng' ? 'nameEN' : 'name'
]
: '-'
}}
</q-td> </q-td>
<q-td <q-td
v-if="branchFieldSelected.includes('totalEmployee')" v-if="branchFieldSelected.includes('totalEmployee')"
@ -573,6 +566,8 @@ onMounted(async () => {
await fetchEmployee({ await fetchEmployee({
branchId: currentBranchEmployee, branchId: currentBranchEmployee,
pageSize: 999, pageSize: 999,
passport: true,
visa: true,
}); });
currentBtnOpen.map((v, i) => { currentBtnOpen.map((v, i) => {
@ -620,7 +615,7 @@ onMounted(async () => {
<div class="text-center"> <div class="text-center">
<TableEmpoloyee <TableEmpoloyee
:prefix-id="props.row.registerName || props.row.firstName" :prefix-id="props.row.registerName || props.row.firstName"
:add-button="canAccess('customer', 'edit')" add-button
in-table in-table
:list-employee="listEmployee" :list-employee="listEmployee"
:columns-employee="columnsEmployee" :columns-employee="columnsEmployee"
@ -643,15 +638,10 @@ onMounted(async () => {
" "
@history="(item) => {}" @history="(item) => {}"
@view=" @view="
async (item) => { (item) => {
employeeFormState.drawerModal = true; employeeFormState.drawerModal = true;
//employeeFormState.isEmployeeEdit = true; //employeeFormState.isEmployeeEdit = true;
employeeFormStore.assignFormDataEmployee(item.id); employeeFormStore.assignFormDataEmployee(item.id);
await fetchImageList(
item.id,
item.selectedImage || '',
'employee',
);
} }
" "
/> />
@ -678,11 +668,9 @@ onMounted(async () => {
? `${props.row.addressEN || ''} ${props.row.subDistrict?.nameEN || ''} ${props.row.district?.nameEN || ''} ${props.row.province?.nameEN || ''}` ? `${props.row.addressEN || ''} ${props.row.subDistrict?.nameEN || ''} ${props.row.district?.nameEN || ''} ${props.row.province?.nameEN || ''}`
: `${props.row.address || ''} ${props.row.subDistrict?.name || ''} ${props.row.district?.name || ''} ${props.row.province?.name || ''}`, : `${props.row.address || ''} ${props.row.subDistrict?.name || ''} ${props.row.district?.name || ''} ${props.row.province?.name || ''}`,
telephone: props.row.telephoneNo, telephone: props.row.telephoneNo,
businessTypePure: props.row.businessType businessTypePure: useOptionStore().mapOption(
? props.row.businessType[ props.row.businessType,
$i18n.locale === 'eng' ? 'nameEN' : 'name' ),
]
: '-',
totalEmployee: props.row._count?.employee, totalEmployee: props.row._count?.employee,
}" }"
:visible-columns="branchFieldSelected" :visible-columns="branchFieldSelected"
@ -771,10 +759,7 @@ onMounted(async () => {
/> />
<DeleteButton <DeleteButton
icon-only icon-only
v-if=" v-if="customerBranchFormState.dialogType === 'info'"
customerBranchFormState.dialogType === 'info' &&
canAccess('customer', 'edit')
"
@click=" @click="
() => { () => {
deleteBranchById(customerBranchFormData.id || ''); deleteBranchById(customerBranchFormData.id || '');
@ -868,6 +853,7 @@ onMounted(async () => {
v-model:last-name-en="customerBranchFormData.lastNameEN" v-model:last-name-en="customerBranchFormData.lastNameEN"
v-model:gender="customerBranchFormData.gender" v-model:gender="customerBranchFormData.gender"
v-model:birth-date="customerBranchFormData.birthDate" v-model:birth-date="customerBranchFormData.birthDate"
v-model:customer-name="customerBranchFormData.customerName"
v-model:legal-person-no="customerBranchFormData.legalPersonNo" v-model:legal-person-no="customerBranchFormData.legalPersonNo"
v-model:branch-code="customerBranchFormData.code" v-model:branch-code="customerBranchFormData.code"
v-model:register-name="customerBranchFormData.registerName" v-model:register-name="customerBranchFormData.registerName"
@ -897,14 +883,15 @@ onMounted(async () => {
</div> </div>
<EmployerFormBusiness <EmployerFormBusiness
dense dense
class="q-mb-xl"
outlined outlined
prefix-id="employer-branch" prefix-id="employer-branch"
:readonly="customerBranchFormState.dialogType === 'info'" :readonly="customerBranchFormState.dialogType === 'info'"
v-model:business-type-id="customerBranchFormData.businessTypeId" v-model:bussiness-type="customerBranchFormData.businessType"
v-model:job-position="customerBranchFormData.jobPosition" v-model:job-position="customerBranchFormData.jobPosition"
v-model:job-description="customerBranchFormData.jobDescription" v-model:job-description="customerBranchFormData.jobDescription"
v-model:pay-date="customerBranchFormData.payDate" v-model:pay-date="customerBranchFormData.payDate"
v-model:pay-date-en="customerBranchFormData.payDateEN" v-model:pay-date-e-n="customerBranchFormData.payDateEN"
v-model:wage-rate="customerBranchFormData.wageRate" v-model:wage-rate="customerBranchFormData.wageRate"
v-model:wage-rate-text="customerBranchFormData.wageRateText" v-model:wage-rate-text="customerBranchFormData.wageRateText"
/> />
@ -995,7 +982,7 @@ onMounted(async () => {
v-model:email="customerBranchFormData.email" v-model:email="customerBranchFormData.email"
v-model:contact-tel="customerBranchFormData.contactTel" v-model:contact-tel="customerBranchFormData.contactTel"
v-model:office-tel="customerBranchFormData.officeTel" v-model:office-tel="customerBranchFormData.officeTel"
v-model:agent-user-id="customerBranchFormData.agentUserId" v-model:agent="customerBranchFormData.agent"
/> />
</div> </div>
</div> </div>
@ -1011,18 +998,6 @@ onMounted(async () => {
/> />
</template> </template>
</DialogFormContainer> </DialogFormContainer>
<DialogEmployee
:fetch-list-employee="fetchEmployee"
:fetch-image-list="fetchImageList"
current-tab="employer"
/>
<DrawerEmployee
:fetch-list-employee="fetchEmployee"
:fetch-image-list="fetchImageList"
current-tab="employer"
/>
</template> </template>
<style scoped> <style scoped>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,514 +0,0 @@
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import {
PaginationComponent,
PaginationPageSize,
ImageUploadDialog,
DialogForm,
NoData,
} from 'src/components';
import DialogEmployee from 'src/components/03_customer-management/DialogEmployee.vue';
import HistoryEditComponent from 'src/components/03_customer-management/HistoryEditComponent.vue';
import TableEmpoloyee from 'src/components/03_customer-management/TableEmpoloyee.vue';
import DrawerEmployee from 'src/components/03_customer-management/DrawerEmployee.vue';
import useFlowStore from 'src/stores/flow';
import useEmployeeStore from 'src/stores/employee';
import { Status } from 'src/stores/types';
import { Employee, EmployeeHistory } from 'src/stores/employee/types';
import { baseUrl, dialog, canAccess } from 'src/stores/utils';
import { calculateAge, toISOStringWithTimezone } from 'src/utils/datetime';
import { useCustomerForm, useEmployeeForm } from './form';
import { columnsEmployee } from './constant';
const $q = useQuasar();
const flowStore = useFlowStore();
const employeeStore = useEmployeeStore();
const employeeFormStore = useEmployeeForm();
const customerFormStore = useCustomerForm();
const { t } = useI18n();
const { state: employeeFormState, currentFromDataEmployee } =
storeToRefs(employeeFormStore);
const { deleteEmployeeById } = employeeFormStore;
const { state: customerFormState } = storeToRefs(customerFormStore);
const employeeStats = defineModel('employeeStats', { default: 0 });
const props = defineProps<{
currentTab: 'employee' | 'employer';
currentStatus: Status | 'All';
gridView: boolean;
inputSearch: string;
searchDate: string[];
fieldSelected: string[];
fetchImageList: (
id: string,
selectedName: string,
type: 'customer' | 'employee',
) => Promise<void>;
triggerChangeStatus: (
id: string,
status: string,
employeeName?: string,
) => void;
}>();
defineExpose({
openSpecificEmployee,
fetchListEmployee,
toggleStatusEmployee,
});
const listEmployee = ref<Employee[]>([]);
const currentPageEmployee = ref<number>(1);
const maxPageEmployee = ref<number>(1);
const pageSize = ref<number>(30);
const employeeHistoryDialog = ref(false);
const employeeHistory = ref<EmployeeHistory[]>();
const splitPercent = computed(() => ($q.screen.lt.md ? 0 : 15));
async function fetchListEmployee(opt?: {
fetchStats?: boolean;
page?: number;
pageSize?: number;
customerId?: string;
mobileFetch?: boolean;
}) {
const resultListEmployee = await employeeStore.fetchList({
customerId: opt?.customerId,
page: opt
? opt.mobileFetch
? 1
: opt.page || currentPageEmployee.value
: currentPageEmployee.value,
pageSize: opt
? opt.mobileFetch
? listEmployee.value.length +
(employeeStats.value === listEmployee.value.length ? 1 : 0)
: opt.pageSize || pageSize.value
: pageSize.value,
status:
props.currentStatus === 'All'
? undefined
: props.currentStatus === 'ACTIVE'
? 'ACTIVE'
: 'INACTIVE',
query: props.inputSearch,
passport: true,
visa: true,
startDate: props.searchDate[0],
endDate: props.searchDate[1],
});
if (resultListEmployee) {
maxPageEmployee.value = Math.ceil(
resultListEmployee.total / pageSize.value,
);
$q.screen.xs && !(opt && opt.mobileFetch)
? listEmployee.value.push(...resultListEmployee.result)
: (listEmployee.value = resultListEmployee.result);
}
if (opt && opt.fetchStats)
employeeStats.value = await employeeStore.getStatsEmployee();
}
async function openHistory(id: string) {
const res = await employeeStore.getEditHistory(id);
employeeHistory.value = res.reverse();
employeeHistoryDialog.value = true;
}
async function editEmployeeFormPersonal(id: string) {
await employeeFormStore.assignFormDataEmployee(id);
await props.fetchImageList(
id,
currentFromDataEmployee.value.selectedImage || '',
'employee',
);
employeeFormState.value.isEmployeeEdit = true;
employeeFormState.value.dialogType = 'edit';
employeeFormState.value.drawerModal = true;
}
async function openSpecificEmployee(id: string) {
await employeeFormStore.assignFormDataEmployee(id);
await props.fetchImageList(
id,
currentFromDataEmployee.value.selectedImage || '',
'employee',
);
employeeFormState.value.dialogType = 'info';
employeeFormState.value.drawerModal = true;
}
async function toggleStatusEmployee(
id: string,
status: boolean,
employeeName: string,
) {
const res = await employeeStore.editById(id, {
status: !status ? 'ACTIVE' : 'INACTIVE',
firstNameEN: employeeName,
});
if (res && employeeFormState.value.drawerModal) {
currentFromDataEmployee.value.status = res.status;
}
await employeeFormStore.assignFormDataEmployee(id);
await fetchListEmployee({ mobileFetch: $q.screen.xs });
flowStore.rotate();
}
watch(
() => [
props.inputSearch,
props.searchDate,
props.currentStatus,
pageSize.value,
],
async () => {
currentPageEmployee.value = 1;
listEmployee.value = [];
await fetchListEmployee({ fetchStats: true });
customerFormState.value.currentCustomerId = undefined;
flowStore.rotate();
},
);
watch(
() => employeeFormState.value.currentCustomerBranch,
(e) => {
if (!e) return;
if (employeeFormState.value.formDataEmployeeSameAddr) {
currentFromDataEmployee.value.address = e.address;
currentFromDataEmployee.value.addressEN = e.addressEN;
currentFromDataEmployee.value.provinceId = e.provinceId;
currentFromDataEmployee.value.districtId = e.districtId;
currentFromDataEmployee.value.subDistrictId = e.subDistrictId;
}
currentFromDataEmployee.value.customerBranchId = e.id;
},
);
watch(
() => employeeFormState.value.formDataEmployeeSameAddr,
(isSame) => {
if (!employeeFormState.value.currentCustomerBranch) return;
if (isSame) {
currentFromDataEmployee.value.address =
employeeFormState.value.currentCustomerBranch.address;
currentFromDataEmployee.value.addressEN =
employeeFormState.value.currentCustomerBranch.addressEN;
currentFromDataEmployee.value.provinceId =
employeeFormState.value.currentCustomerBranch.provinceId;
currentFromDataEmployee.value.districtId =
employeeFormState.value.currentCustomerBranch.districtId;
currentFromDataEmployee.value.subDistrictId =
employeeFormState.value.currentCustomerBranch.subDistrictId;
}
currentFromDataEmployee.value.customerBranchId =
employeeFormState.value.currentCustomerBranch.id;
},
);
watch(
() => currentFromDataEmployee.value.dateOfBirth,
(v) => {
const isEdit =
employeeFormState.value.drawerModal &&
employeeFormState.value.isEmployeeEdit;
let currentFormDate = v && toISOStringWithTimezone(new Date(v));
let currentDate: string = '';
if (isEdit && employeeFormState.value.currentEmployee) {
currentDate = toISOStringWithTimezone(
new Date(employeeFormState.value.currentEmployee.dateOfBirth),
);
}
if (
employeeFormState.value.dialogModal ||
(isEdit && currentFormDate !== currentDate)
) {
const age = calculateAge(
currentFromDataEmployee.value.dateOfBirth,
'year',
);
if (currentFromDataEmployee.value.dateOfBirth && Number(age) < 15) {
dialog({
color: 'warning',
icon: 'mdi-alert',
title: t('dialog.title.youngWorker15'),
cancelText: t('general.edit'),
persistent: true,
message: t('dialog.message.youngWorker15'),
cancel: async () => {
currentFromDataEmployee.value.dateOfBirth = null;
return;
},
});
}
if (
currentFromDataEmployee.value.dateOfBirth &&
Number(age) > 15 &&
Number(age) <= 18
) {
dialog({
color: 'warning',
icon: 'mdi-alert',
title: t('dialog.title.youngWorker18'),
cancelText: t('general.cancel'),
actionText: t('general.confirm'),
persistent: true,
message: t('dialog.message.youngWorker18'),
action: () => {},
cancel: async () => {
currentFromDataEmployee.value.dateOfBirth = null;
return;
},
});
}
}
},
);
watch(
() => currentFromDataEmployee.value.image,
() => {
if (currentFromDataEmployee.value.image !== null)
employeeFormState.value.isImageEdit = true;
},
);
onMounted(async () => {
currentPageEmployee.value = 1;
listEmployee.value = [];
await fetchListEmployee({ fetchStats: true });
});
</script>
<template>
<q-splitter
v-model="splitPercent"
:limits="[0, 100]"
class="col full-width"
before-class="overflow-hidden"
after-class="overflow-hidden"
:disable="$q.screen.lt.sm"
>
<template v-slot:before>
<div
class="column q-pa-md surface-1 full-height full-width"
style="gap: var(--size-1)"
>
<q-item
active
dense
active-class="employer-active"
class="no-padding items-center rounded full-width"
v-close-popup
clickable
>
<span class="q-px-md ellipsis">
{{ $t('general.all') }}
</span>
</q-item>
</div>
</template>
<template v-slot:after>
<div class="column full-height no-wrap">
<!-- employee -->
<template
v-if="listEmployee && employeeStats > 0 && currentTab === 'employee'"
>
<div
v-if="listEmployee.length === 0"
class="row col full-width items-center justify-center"
style="min-height: 250px"
>
<NoData :not-found="!!inputSearch" />
</div>
<article
v-if="listEmployee.length !== 0"
class="column scroll q-pa-md col"
>
<q-infinite-scroll
:offset="10"
@load="
(_, done) => {
if (
$q.screen.gt.xs ||
currentPageEmployee === maxPageEmployee
)
return;
currentPageEmployee = currentPageEmployee + 1;
fetchListEmployee().then(() =>
done(currentPageEmployee >= maxPageEmployee),
);
}
"
>
<TableEmpoloyee
:hide-delete="!canAccess('customer', 'edit')"
v-model:page-size="pageSize"
v-model:current-page="currentPageEmployee"
:grid-view="gridView"
:list-employee="listEmployee"
:columns-employee="columnsEmployee"
:field-selected="fieldSelected"
@history="
(item: any) => {
openHistory(item.id);
}
"
@view="
async (item: any) => {
employeeFormState.drawerModal = true;
employeeFormState.isEmployeeEdit = false;
employeeFormStore.assignFormDataEmployee(item.id);
await fetchImageList(
item.id,
item.selectedImage || '',
'employee',
);
}
"
@edit="(item: any) => editEmployeeFormPersonal(item.id)"
@delete="
(item: any) => {
deleteEmployeeById({
id: item.id,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId: customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
});
}
"
@toggle-status="
async (item: any) => {
triggerChangeStatus(item.id, item.status, item.firstNameEN);
}
"
/>
<template v-slot:loading>
<div
v-if="
$q.screen.lt.sm && currentPageEmployee !== maxPageEmployee
"
class="row justify-center"
>
<q-spinner-dots color="primary" size="40px" />
</div>
</template>
</q-infinite-scroll>
</article>
<footer
v-if="listEmployee.length !== 0 && $q.screen.gt.xs"
class="row justify-between items-center q-px-md q-py-sm"
>
<div class="row col-4 items-center">
<div
class="app-text-muted"
style="width: 80px"
v-if="$q.screen.gt.sm"
>
{{ $t('general.recordPerPage') }}
</div>
<div><PaginationPageSize v-model="pageSize" /></div>
</div>
<div class="col-4 flex justify-center app-text-muted">
{{
$q.screen.gt.sm
? $t('general.recordsPage', {
resultcurrentPage: listEmployee.length,
total: employeeStats,
})
: $t('general.ofPage', {
current: listEmployee.length,
total: employeeStats,
})
}}
</div>
<div class="col-4 flex justify-end">
<PaginationComponent
v-model:current-page="currentPageEmployee"
v-model:max-page="maxPageEmployee"
:fetch-data="
async () => {
await fetchListEmployee();
flowStore.rotate();
}
"
/>
</div>
</footer>
</template>
</div>
</template>
</q-splitter>
<!-- add employee -->
<DialogEmployee
:fetch-list-employee="fetchListEmployee"
:fetch-image-list="fetchImageList"
:current-tab="currentTab"
/>
<!-- กจาง edit employee -->
<DrawerEmployee
:fetch-list-employee="fetchListEmployee"
:fetch-image-list="fetchImageList"
:current-tab="currentTab"
@change-status="
(s) =>
triggerChangeStatus(
currentFromDataEmployee.id,
s,
currentFromDataEmployee.firstNameEN,
)
"
/>
<DialogForm
:title="$t('general.historyEdit')"
hide-footer
v-model:modal="employeeHistoryDialog"
>
<div class="q-pa-md">
<HistoryEditComponent
v-if="employeeHistory"
v-model:history-list="employeeHistory"
/>
</div>
</DialogForm>
</template>
<style scoped>
.employer-active {
background-color: hsla(var(--info-bg) / 0.1);
color: hsl(var(--info-bg));
}
</style>

View file

@ -137,12 +137,7 @@ watch(
@update:model-value=" @update:model-value="
(v) => (typeof v === 'string' ? (registerName = v.trim()) : '') (v) => (typeof v === 'string' ? (registerName = v.trim()) : '')
" "
:rules="[ :rules="[(val: string) => !!val || $t('form.error.required')]"
(val) => !!val || $t('form.error.required'),
(val) =>
/^[A-Za-z0-9ก-๙\s&.,'()-]+$/.test(val) ||
$t('form.error.branchNameField'),
]"
/> />
<q-input <q-input
@ -158,15 +153,29 @@ watch(
(v) => (typeof v === 'string' ? (registerNameEN = v.trim()) : '') (v) => (typeof v === 'string' ? (registerNameEN = v.trim()) : '')
" "
:rules="[ :rules="[
(val) => !!val || $t('form.error.required'), (val: string) =>
(val) => val === '' ||
/^[A-Za-z0-9\s&.,'-]+$/.test(val) || /^[0-9A-Za-z\s.,]+$/.test(val) ||
$t('form.error.branchNameENField'), $t('form.error.letterOnly'),
]" ]"
/> />
</div> </div>
<div class="col-12 row q-col-gutter-sm"> <div class="col-12 row q-col-gutter-sm">
<q-input
dense
outlined
:readonly="readonly"
hide-bottom-space
class="col-12 col-md-5"
:label="$t('customer.form.employerName')"
for="input-legal-person-no"
:model-value="customerName"
@update:model-value="
(v) => (typeof v === 'string' ? (customerName = v.trim()) : '')
"
/>
<q-input <q-input
dense dense
outlined outlined
@ -336,7 +345,6 @@ watch(
(v) => (typeof v === 'string' ? (prefixName = v) : '') (v) => (typeof v === 'string' ? (prefixName = v) : '')
" "
@clear="prefixName = ''" @clear="prefixName = ''"
:rules="[(val: string) => !!val || $t('form.error.required')]"
> >
<template v-slot:no-option> <template v-slot:no-option>
<q-item> <q-item>
@ -385,11 +393,11 @@ watch(
:readonly="readonly" :readonly="readonly"
:disable="!readonly" :disable="!readonly"
class="col-md-2 col-6" class="col-md-2 col-6"
label="Prefix" label="Title"
:model-value=" :model-value="
readonly readonly
? prefixName.toUpperCase() || '-' ? capitalize(prefixName || '') || '-'
: prefixName.toUpperCase() || '' : capitalize(prefixName || '')
" "
@update:model-value=" @update:model-value="
(v) => (typeof v === 'string' ? (prefixName = v) : '') (v) => (typeof v === 'string' ? (prefixName = v) : '')

View file

@ -2,7 +2,6 @@
import useOptionStore from 'stores/options'; import useOptionStore from 'stores/options';
import SelectBranch from 'src/components/shared/select/SelectBranch.vue'; import SelectBranch from 'src/components/shared/select/SelectBranch.vue';
import { isRoleInclude } from 'src/stores/utils'; import { isRoleInclude } from 'src/stores/utils';
import SelectBusinessType from 'src/components/shared/select/SelectBusinessType.vue';
withDefaults( withDefaults(
defineProps<{ defineProps<{
@ -103,13 +102,7 @@ const telephoneNo = defineModel<string>('telephoneNo', { default: '' });
class="col-md-6" class="col-md-6"
:readonly :readonly
:disabled=" :disabled="
!isRoleInclude([ !isRoleInclude(['admin', 'system', 'head_of_admin']) && !readonly
'admin',
'system',
'head_of_admin',
'executive',
'accountant',
]) && !readonly
" "
:label="$t('customer.form.registeredBranch')" :label="$t('customer.form.registeredBranch')"
select-first-value select-first-value
@ -143,10 +136,15 @@ const telephoneNo = defineModel<string>('telephoneNo', { default: '' });
for="input-tax" for="input-tax"
v-model="legalPersonNo" v-model="legalPersonNo"
/> />
<SelectBusinessType <q-input
dense
outlined
:readonly="readonly"
hide-bottom-space
class="col-6 col-md-3" class="col-6 col-md-3"
v-model:value="businessType" :label="$t('customer.table.businessTypePure')"
:readonly for="input-business-type"
:model-value="optionStore.mapOption(businessType)"
/> />
</div> </div>
@ -175,10 +173,15 @@ const telephoneNo = defineModel<string>('telephoneNo', { default: '' });
:label="$t('personnel.form.citizenId')" :label="$t('personnel.form.citizenId')"
for="input-citizen-id" for="input-citizen-id"
/> />
<SelectBusinessType <q-input
dense
outlined
:readonly="readonly"
hide-bottom-space
class="col-6 col-md-3" class="col-6 col-md-3"
v-model:value="businessType" :label="$t('customer.table.businessTypePure')"
:readonly for="input-first-name-en"
:model-value="optionStore.mapOption(businessType)"
/> />
</div> </div>

View file

@ -7,8 +7,6 @@ import { CustomerCreate } from 'stores/customer/types';
import EmployerFormAbout from './EmployerFormAbout.vue'; import EmployerFormAbout from './EmployerFormAbout.vue';
import EmployerFormAuthorized from './EmployerFormAuthorized.vue'; import EmployerFormAuthorized from './EmployerFormAuthorized.vue';
import { waitAll } from 'src/stores/utils'; import { waitAll } from 'src/stores/utils';
import FormEmployeePassport from 'src/components/03_customer-management/FormEmployeePassport.vue';
import FormEmployeeVisa from 'src/components/03_customer-management/FormEmployeeVisa.vue';
import { import {
FormCitizen, FormCitizen,
CorpFormBusinessRegistration, CorpFormBusinessRegistration,
@ -53,7 +51,6 @@ withDefaults(
actionDisabled?: boolean; actionDisabled?: boolean;
customerType?: 'CORP' | 'PERS'; customerType?: 'CORP' | 'PERS';
hideAction?: boolean; hideAction?: boolean;
hideDelete?: boolean;
}>(), }>(),
{ {
hideAction: false, hideAction: false,
@ -84,7 +81,7 @@ withDefaults(
/> />
<DeleteButton <DeleteButton
icon-only icon-only
v-if="readonly && !hideDelete" v-if="readonly"
@click="$emit('delete')" @click="$emit('delete')"
type="button" type="button"
:disabled="actionDisabled" :disabled="actionDisabled"
@ -144,6 +141,7 @@ withDefaults(
v-model:last-name-en="item.lastNameEN" v-model:last-name-en="item.lastNameEN"
v-model:gender="item.gender" v-model:gender="item.gender"
v-model:birth-date="item.birthDate" v-model:birth-date="item.birthDate"
v-model:customer-name="item.customerName"
v-model:legal-person-no="item.legalPersonNo" v-model:legal-person-no="item.legalPersonNo"
v-model:branch-code="item.code" v-model:branch-code="item.code"
v-model:register-name="item.registerName" v-model:register-name="item.registerName"
@ -160,7 +158,7 @@ withDefaults(
outlined outlined
:prefix-id="prefixId || 'employer'" :prefix-id="prefixId || 'employer'"
:readonly="readonly" :readonly="readonly"
v-model:business-type-id="item.businessTypeId" v-model:bussiness-type="item.businessType"
v-model:job-position="item.jobPosition" v-model:job-position="item.jobPosition"
v-model:job-description="item.jobDescription" v-model:job-description="item.jobDescription"
v-model:pay-date="item.payDate" v-model:pay-date="item.payDate"
@ -222,6 +220,7 @@ withDefaults(
hide-action hide-action
:ocr=" :ocr="
async (group, file) => { async (group, file) => {
console.log(group);
if (group !== 'attachment') { if (group !== 'attachment') {
const res = await ocrStore.sendOcr({ const res = await ocrStore.sendOcr({
file: file, file: file,
@ -246,20 +245,12 @@ withDefaults(
:auto-save="item.id !== ''" :auto-save="item.id !== ''"
:download=" :download="
(obj) => { (obj) => {
if (obj.group === 'citizen') {
customerStore.getFile({ customerStore.getFile({
parentId: item.id || '', parentId: item.id || '',
group: obj.group, group: obj.group,
fileId: obj._meta.id, fileId: obj._meta.id,
download: true, download: true,
}); });
} else {
customerStore.getAttachment({
parentId: item.id || '',
name: obj._meta.id,
download: true,
});
}
} }
" "
:delete-item=" :delete-item="
@ -288,7 +279,7 @@ withDefaults(
_meta: any, _meta: any,
file: File | undefined, file: File | undefined,
) => { ) => {
if (group === 'citizen') { if (group !== 'attachment') {
if (file !== undefined && item.id) { if (file !== undefined && item.id) {
const res = await customerStore.postMeta({ const res = await customerStore.postMeta({
parentId: item.id || '', parentId: item.id || '',
@ -356,6 +347,7 @@ withDefaults(
}); });
const tempValue = resMeta.map(async (v: any) => { const tempValue = resMeta.map(async (v: any) => {
console.log(v.expireDate);
return { return {
_meta: { ...v }, _meta: { ...v },
name: `${group}-${dateFormat(v.expireDate)}`, name: `${group}-${dateFormat(v.expireDate)}`,
@ -376,6 +368,7 @@ withDefaults(
}); });
const tempValue = (res as string[]).map(async (i: any) => { const tempValue = (res as string[]).map(async (i: any) => {
console.log(i);
return { return {
_meta: { id: i, name: i }, _meta: { id: i, name: i },
name: i || '', name: i || '',

View file

@ -8,28 +8,12 @@ import { onMounted, watch } from 'vue';
import { ref } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import ThaiBahtText from 'thai-baht-text'; import ThaiBahtText from 'thai-baht-text';
import SelectBusinessType from 'src/components/shared/select/SelectBusinessType.vue';
import BusinessTypeDialog from 'src/pages/16_business-type-management/BusinessTypeDialog.vue';
import useBusinessTypeStore from 'src/stores/business-type';
const { locale } = useI18n({ useScope: 'global' }); const { locale } = useI18n({ useScope: 'global' });
const rawOption = ref(); const rawOption = ref();
const businessTypeDialog = ref<boolean>(false); const bussinessType = defineModel<string>('bussinessType');
const formDataBusinessType = ref<{
name: string;
nameEN: string;
}>({
name: '',
nameEN: '',
});
const useBusinessType = useBusinessTypeStore();
const businessTypeId = defineModel<string>('businessTypeId');
const jobPosition = defineModel<string>('jobPosition'); const jobPosition = defineModel<string>('jobPosition');
const jobDescription = defineModel<string>('jobDescription'); const jobDescription = defineModel<string>('jobDescription');
const payDate = defineModel<string>('payDate'); const payDate = defineModel<string>('payDate');
@ -44,8 +28,6 @@ const typeBusinessENOption = ref([]);
const jobPositionOption = ref([]); const jobPositionOption = ref([]);
const jobPositionENOption = ref([]); const jobPositionENOption = ref([]);
const keySelect = ref<number>(0);
defineProps<{ defineProps<{
title?: string; title?: string;
dense?: boolean; dense?: boolean;
@ -55,33 +37,35 @@ defineProps<{
showTitle?: boolean; showTitle?: boolean;
}>(); }>();
function resetFormBusinessType() {
businessTypeDialog.value = false;
formDataBusinessType.value = { name: '', nameEN: '' };
}
async function submitBusinessType() {
const res = await useBusinessType.create(formDataBusinessType.value);
if (res) {
businessTypeId.value = res.id;
resetFormBusinessType();
keySelect.value++;
}
}
onMounted(async () => { onMounted(async () => {
const resultOption = await fetch('/option/option.json'); const resultOption = await fetch('/option/option.json');
rawOption.value = await resultOption.json(); rawOption.value = await resultOption.json();
typeBusinessENOption.value = rawOption.value.eng.businessType;
jobPositionENOption.value = rawOption.value.eng.position; jobPositionENOption.value = rawOption.value.eng.position;
if (locale.value === 'eng') { if (locale.value === 'eng') {
typeBusinessOption.value = rawOption.value.eng.businessType;
jobPositionOption.value = rawOption.value.eng.position; jobPositionOption.value = rawOption.value.eng.position;
} }
if (locale.value === 'tha') { if (locale.value === 'tha') {
typeBusinessOption.value = rawOption.value.tha.businessType;
jobPositionOption.value = rawOption.value.tha.position; jobPositionOption.value = rawOption.value.tha.position;
} }
}); });
watch([typeBusinessOption, typeBusinessENOption], () => {
typeBusinessFilter = selectFilterOptionRefMod(
typeBusinessOption,
typeBusinessOptions,
'label',
);
typeBusinessENFilter = selectFilterOptionRefMod(
typeBusinessENOption,
typeBusinessENOptions,
'label',
);
});
watch( watch(
() => rate.value, () => rate.value,
(newValue) => { (newValue) => {
@ -151,26 +135,69 @@ let jobPositionENFilter = selectFilterOptionRefMod(
<span>{{ $t('customerBranch.tab.business') }}</span> <span>{{ $t('customerBranch.tab.business') }}</span>
</div> </div>
<SelectBusinessType <q-select
:key="keySelect" outlined
clearable
use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space
:hide-dropdown-icon="readonly"
input-debounce="0"
option-value="value"
option-label="label"
v-model="bussinessType"
class="col-md-6 col-12" class="col-md-6 col-12"
v-model:value="businessTypeId" :dense="dense"
:readonly :readonly="readonly"
creatable :label="$t('customer.form.businessType')"
lang="tha" :options="typeBusinessOptions"
:for="`${prefixId}-select-business-type`"
@filter="typeBusinessFilter"
:rules="[(val: string) => !!val || $t('form.error.required')]" :rules="[(val: string) => !!val || $t('form.error.required')]"
@create="() => (businessTypeDialog = true)" >
/> <template v-slot:no-option>
<SelectBusinessType <q-item>
:key="keySelect" <q-item-section class="text-grey">
{{ $t('general.noData') }}
</q-item-section>
</q-item>
</template>
</q-select>
<q-select
:for="`${prefixId}-input-bussiness-type-en`"
:id="`${prefixId}-input-bussiness-type-en`"
:label="`${$t('customer.form.businessType')} (EN)`"
outlined
clearable
use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space
:hide-dropdown-icon="readonly"
input-debounce="0"
option-value="value"
option-label="label"
v-model="bussinessType"
class="col-md-6 col-12" class="col-md-6 col-12"
v-model:value="businessTypeId" :dense="dense"
:readonly :readonly="readonly"
creatable :options="typeBusinessENOptions"
lang="eng" @filter="typeBusinessENFilter"
:rules="[(val: string) => !!val || $t('form.error.required')]" :rules="[(val: string) => !!val || $t('form.error.required')]"
@create="() => (businessTypeDialog = true)" >
/> <template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
{{ $t('general.noData') }}
</q-item-section>
</q-item>
</template>
</q-select>
<q-select <q-select
outlined outlined
@ -321,12 +348,4 @@ let jobPositionENFilter = selectFilterOptionRefMod(
" "
/> />
</div> </div>
<BusinessTypeDialog
ref="refBusinessTypeDialog"
@close="resetFormBusinessType()"
@submit="submitBusinessType()"
v-model="businessTypeDialog"
v-model:data="formDataBusinessType"
/>
</template> </template>

View file

@ -9,6 +9,8 @@ const contactName = defineModel<string>('contactName');
const email = defineModel<string>('email'); const email = defineModel<string>('email');
const contactTel = defineModel<string>('contactTel'); const contactTel = defineModel<string>('contactTel');
const officeTel = defineModel<string>('officeTel'); const officeTel = defineModel<string>('officeTel');
const agent = defineModel<string>('agent');
const agentUserId = defineModel<string>('agentUserId'); const agentUserId = defineModel<string>('agentUserId');
</script> </script>
@ -107,6 +109,7 @@ const agentUserId = defineModel<string>('agentUserId');
/> />
</template> </template>
</q-input> </q-input>
<SelectAgent <SelectAgent
:label="$t('customer.form.agent')" :label="$t('customer.form.agent')"
v-model:value="agentUserId" v-model:value="agentUserId"

View file

@ -69,19 +69,19 @@ export const uploadFileListCustomer: {
}[] = [ }[] = [
{ {
label: 'customer.typeFile.citizenId', label: 'customer.typeFile.citizenId',
group: 'citizen',
value: 'citizen', value: 'citizen',
group: 'citizen',
}, },
{ {
label: 'customer.typeFile.registrationBook', label: 'customer.typeFile.registrationBook',
group: 'houseRegistration',
value: 'attachment', value: 'attachment',
group: 'houseRegistration',
}, },
{ {
label: 'customer.typeFile.houseMap', label: 'customer.typeFile.houseMap',
group: 'vatRegistration',
value: 'attachment', value: 'attachment',
group: 'vatRegistration',
}, },
{ {
@ -92,7 +92,7 @@ export const uploadFileListCustomer: {
{ {
label: 'customer.typeFile.dbdCertificate', label: 'customer.typeFile.dbdCertificate',
group: 'dbdCertificate', group: 'powerOfAttorney',
value: 'attachment', value: 'attachment',
}, },
@ -144,43 +144,43 @@ export const uploadFileListEmployee: {
}, },
{ {
label: 'customerEmployee.fileType.tm6', label: 'customerEmployee.fileType.tm6',
group: 'tm6',
value: 'attachment', value: 'attachment',
group: 'tm6',
}, },
{ {
label: 'customerEmployee.fileType.workPermit', label: 'customerEmployee.fileType.workPermit',
group: 'workPermit',
value: 'attachment', value: 'attachment',
group: 'workPermit',
}, },
{ {
label: 'customerEmployee.fileType.noticeJobEmployment', label: 'customerEmployee.fileType.noticeJobEmployment',
group: 'noticeJobEmployment',
value: 'attachment', value: 'attachment',
group: 'noticeJobEmployment',
}, },
{ {
label: 'customerEmployee.fileType.noticeJobEntry', label: 'customerEmployee.fileType.noticeJobEntry',
group: 'noticeJobEntry',
value: 'attachment', value: 'attachment',
group: 'noticeJobEntry',
}, },
{ {
label: 'customerEmployee.fileType.historyJob', label: 'customerEmployee.fileType.historyJob',
group: 'historyJob',
value: 'attachment', value: 'attachment',
group: 'historyJob',
}, },
{ {
label: 'customerEmployee.fileType.acceptJob', label: 'customerEmployee.fileType.acceptJob',
group: 'acceptJob',
value: 'attachment', value: 'attachment',
group: 'acceptJob',
}, },
{ {
label: 'customerEmployee.fileType.receipt', label: 'customerEmployee.fileType.receipt',
group: 'receipt',
value: 'attachment', value: 'attachment',
group: 'receipt',
}, },
{ {
label: 'customerEmployee.fileType.other', label: 'customerEmployee.fileType.other',
group: 'other',
value: 'attachment', value: 'attachment',
group: 'other',
}, },
]; ];

View file

@ -22,10 +22,6 @@ import { useRoute } from 'vue-router';
export const useCustomerForm = defineStore('form-customer', () => { export const useCustomerForm = defineStore('form-customer', () => {
const customerStore = useCustomerStore(); const customerStore = useCustomerStore();
const onCreateImageList = ref<{
selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[];
}>({ selectedImage: '', list: [] });
const { t } = useI18n(); const { t } = useI18n();
const flowStore = useFlowStore(); const flowStore = useFlowStore();
@ -34,13 +30,11 @@ export const useCustomerForm = defineStore('form-customer', () => {
const registerAbleBranchOption = ref<{ id: string; name: string }[]>(); const registerAbleBranchOption = ref<{ id: string; name: string }[]>();
const currentBranchRootId = ref<string>('');
const tabFieldRequired = ref<{ const tabFieldRequired = ref<{
[key: string]: (keyof CustomerBranchCreate)[]; [key: string]: (keyof CustomerBranchCreate)[];
}>({ }>({
main: [], main: [],
business: ['businessTypeId', 'jobPosition'], business: ['businessType', 'jobPosition'],
address: [ address: [
'address', 'address',
'addressEN', 'addressEN',
@ -88,7 +82,6 @@ export const useCustomerForm = defineStore('form-customer', () => {
formDataOcr: Record<string, any>; formDataOcr: Record<string, any>;
isImageEdit: boolean; isImageEdit: boolean;
currentCustomerId?: string; currentCustomerId?: string;
imageList: { list: string[]; selectedImage: string };
}>({ }>({
dialogType: 'info', dialogType: 'info',
dialogOpen: false, dialogOpen: false,
@ -105,7 +98,6 @@ export const useCustomerForm = defineStore('form-customer', () => {
treeFile: [], treeFile: [],
formDataOcr: {}, formDataOcr: {},
isImageEdit: false, isImageEdit: false,
imageList: { list: [], selectedImage: '' },
}); });
watch( watch(
@ -168,7 +160,6 @@ export const useCustomerForm = defineStore('form-customer', () => {
state.value.editCustomerCode = data.code; state.value.editCustomerCode = data.code;
state.value.customerImageUrl = `${baseUrl}/customer/${id}/image/${data.selectedImage}`; state.value.customerImageUrl = `${baseUrl}/customer/${id}/image/${data.selectedImage}`;
state.value.defaultCustomerImageUrl = `${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.registeredBranchId = data.registeredBranchId;
resetFormData.status = data.status; resetFormData.status = data.status;
@ -190,7 +181,7 @@ export const useCustomerForm = defineStore('form-customer', () => {
payDate: v.payDate, payDate: v.payDate,
jobDescription: v.jobDescription, jobDescription: v.jobDescription,
jobPosition: v.jobPosition, jobPosition: v.jobPosition,
businessTypeId: v.businessTypeId, businessType: v.businessType,
employmentOffice: v.employmentOffice, employmentOffice: v.employmentOffice,
employmentOfficeEN: v.employmentOfficeEN, employmentOfficeEN: v.employmentOfficeEN,
telephoneNo: v.telephoneNo, telephoneNo: v.telephoneNo,
@ -227,6 +218,7 @@ export const useCustomerForm = defineStore('form-customer', () => {
contactTel: v.contactTel, contactTel: v.contactTel,
officeTel: v.officeTel, officeTel: v.officeTel,
agentUserId: v.agentUserId || undefined, agentUserId: v.agentUserId || undefined,
customerName: v.customerName,
authorizedName: v.authorizedName, authorizedName: v.authorizedName,
authorizedNameEN: v.authorizedNameEN, authorizedNameEN: v.authorizedNameEN,
@ -264,6 +256,7 @@ export const useCustomerForm = defineStore('form-customer', () => {
async function addCurrentCustomerBranch() { async function addCurrentCustomerBranch() {
if (currentFormData.value.customerBranch?.some((v) => !v.id)) return; if (currentFormData.value.customerBranch?.some((v) => !v.id)) return;
currentFormData.value.customerBranch?.push({ currentFormData.value.customerBranch?.push({
id: '',
customerId: '', customerId: '',
branchCode: branchCode:
currentFormData.value.customerBranch.length !== 0 currentFormData.value.customerBranch.length !== 0
@ -297,8 +290,8 @@ export const useCustomerForm = defineStore('form-customer', () => {
birthDate: birthDate:
currentFormData.value.customerBranch?.at(0)?.birthDate || undefined, currentFormData.value.customerBranch?.at(0)?.birthDate || undefined,
businessTypeId: businessType:
currentFormData.value.customerBranch?.at(0)?.businessTypeId || '', currentFormData.value.customerBranch?.at(0)?.businessType || '',
jobPosition: jobPosition:
currentFormData.value.customerBranch?.at(0)?.jobPosition || '', currentFormData.value.customerBranch?.at(0)?.jobPosition || '',
jobDescription: jobDescription:
@ -335,6 +328,8 @@ export const useCustomerForm = defineStore('form-customer', () => {
currentFormData.value.customerBranch?.at(0)?.agentUserId || undefined, currentFormData.value.customerBranch?.at(0)?.agentUserId || undefined,
status: 'CREATED', status: 'CREATED',
customerName:
currentFormData.value.customerBranch?.at(0)?.customerName || '',
registerName: registerName:
currentFormData.value.customerBranch?.at(0)?.registerName || '', currentFormData.value.customerBranch?.at(0)?.registerName || '',
registerNameEN: registerNameEN:
@ -503,14 +498,11 @@ export const useCustomerForm = defineStore('form-customer', () => {
} }
return { return {
onCreateImageList,
tabFieldRequired, tabFieldRequired,
registerAbleBranchOption, registerAbleBranchOption,
state, state,
resetFormData, resetFormData,
currentFormData, currentFormData,
currentBranchRootId,
isFormDataDifferent, isFormDataDifferent,
resetForm, resetForm,
assignFormData, assignFormData,
@ -549,7 +541,7 @@ export const useCustomerBranchForm = defineStore('form-customer-branch', () => {
gender: '', gender: '',
birthDate: undefined, birthDate: undefined,
businessTypeId: '', businessType: '',
jobPosition: '', jobPosition: '',
jobDescription: '', jobDescription: '',
payDate: '', payDate: '',
@ -579,6 +571,7 @@ export const useCustomerBranchForm = defineStore('form-customer-branch', () => {
agentUserId: undefined, agentUserId: undefined,
status: 'CREATED', status: 'CREATED',
customerName: '',
registerName: '', registerName: '',
registerNameEN: '', registerNameEN: '',
registerDate: undefined, registerDate: undefined,
@ -630,7 +623,7 @@ export const useCustomerBranchForm = defineStore('form-customer-branch', () => {
payDateEN: _data.payDateEN, payDateEN: _data.payDateEN,
jobDescription: _data.jobDescription, jobDescription: _data.jobDescription,
jobPosition: _data.jobPosition, jobPosition: _data.jobPosition,
businessTypeId: _data.businessTypeId, businessType: _data.businessType,
employmentOffice: _data.employmentOffice, employmentOffice: _data.employmentOffice,
employmentOfficeEN: _data.employmentOfficeEN, employmentOfficeEN: _data.employmentOfficeEN,
telephoneNo: _data.telephoneNo, telephoneNo: _data.telephoneNo,
@ -664,6 +657,7 @@ export const useCustomerBranchForm = defineStore('form-customer-branch', () => {
officeTel: _data.officeTel, officeTel: _data.officeTel,
agentUserId: _data.agentUserId || undefined, agentUserId: _data.agentUserId || undefined,
codeCustomer: _data.codeCustomer, codeCustomer: _data.codeCustomer,
customerName: _data.customerName,
homeCode: _data.homeCode, homeCode: _data.homeCode,
authorizedName: _data.authorizedName, authorizedName: _data.authorizedName,
authorizedNameEN: _data.authorizedNameEN, authorizedNameEN: _data.authorizedNameEN,
@ -790,7 +784,6 @@ export const useEmployeeForm = defineStore('form-employee', () => {
} }
| undefined; | undefined;
ocr: boolean; ocr: boolean;
imageList: { list: string[]; selectedImage: string };
}>({ }>({
currentBranchId: '', currentBranchId: '',
isImageEdit: false, isImageEdit: false,
@ -815,10 +808,9 @@ export const useEmployeeForm = defineStore('form-employee', () => {
infoEmployeePersonCard: [], infoEmployeePersonCard: [],
formDataEmployeeOwner: undefined, formDataEmployeeOwner: undefined,
ocr: false, ocr: false,
imageList: { list: [], selectedImage: '' },
}); });
const defaultFormData: EmployeeCreate & { image?: File } = { const defaultFormData: EmployeeCreate = {
id: '', id: '',
code: '', code: '',
customerBranchId: '', customerBranchId: '',
@ -900,7 +892,6 @@ export const useEmployeeForm = defineStore('form-employee', () => {
expireDate: new Date(), expireDate: new Date(),
remark: undefined, remark: undefined,
workerType: '', workerType: '',
reportDate: null,
number: '', number: '',
}, },
], ],
@ -946,7 +937,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
}; };
let resetEmployeeData = structuredClone(defaultFormData); let resetEmployeeData = structuredClone(defaultFormData);
const currentFromDataEmployee = ref<EmployeeCreate & { image?: File }>( const currentFromDataEmployee = ref<EmployeeCreate>(
structuredClone(defaultFormData), structuredClone(defaultFormData),
); );
@ -968,14 +959,12 @@ export const useEmployeeForm = defineStore('form-employee', () => {
state.value.currentIndexVisa = -1; state.value.currentIndexVisa = -1;
state.value.currentIndexCheckup = -1; state.value.currentIndexCheckup = -1;
state.value.currentIndexWorkHistory = -1; state.value.currentIndexWorkHistory = -1;
state.value.imageList = { list: [], selectedImage: '' }; state.value.currentTab = 'personalInfo';
// state.value.currentTab = 'personalInfo';
if (clean) { if (clean) {
state.value.formDataEmployeeOwner = undefined; state.value.formDataEmployeeOwner = undefined;
resetEmployeeData = structuredClone(defaultFormData); resetEmployeeData = structuredClone(defaultFormData);
state.value.statusSavePersonal = false; state.value.statusSavePersonal = false;
state.value.profileUrl = ''; state.value.profileUrl = '';
state.value.currentBranchId = '';
} else { } else {
resetEmployeeData.selectedImage = resetEmployeeData.selectedImage =
currentFromDataEmployee.value.selectedImage; currentFromDataEmployee.value.selectedImage;
@ -996,16 +985,12 @@ export const useEmployeeForm = defineStore('form-employee', () => {
state.value.currentIndexPassport state.value.currentIndexPassport
].id === undefined ].id === undefined
) { ) {
const { id, employeeId, updatedAt, createdAt, file, ...payload } =
currentFromDataEmployee.value.employeePassport?.[
state.value.currentIndexPassport
];
const res = await employeeStore.postMeta({ const res = await employeeStore.postMeta({
parentId: currentFromDataEmployee.value.id || '', parentId: currentFromDataEmployee.value.id || '',
group: 'passport', group: 'passport',
meta: payload, meta: currentFromDataEmployee.value.employeePassport?.[
file: file, state.value.currentIndexPassport
],
}); });
if (res) { if (res) {
@ -1019,7 +1004,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
state.value.currentIndexPassport state.value.currentIndexPassport
].id !== undefined ].id !== undefined
) { ) {
const { id, employeeId, updatedAt, createdAt, file, ...payload } = const { id, employeeId, updatedAt, createdAt, ...payload } =
currentFromDataEmployee.value.employeePassport?.[ currentFromDataEmployee.value.employeePassport?.[
state.value.currentIndexPassport state.value.currentIndexPassport
]; ];
@ -1032,7 +1017,6 @@ export const useEmployeeForm = defineStore('form-employee', () => {
state.value.currentIndexPassport state.value.currentIndexPassport
].id || '', ].id || '',
meta: payload, meta: payload,
file: file || undefined,
}); });
} }
@ -1260,7 +1244,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
await assignFormDataEmployee(currentFromDataEmployee.value.id); await assignFormDataEmployee(currentFromDataEmployee.value.id);
} }
async function submitPersonal(imgList?: { async function submitPersonal(imgList: {
selectedImage: string; selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[]; list: { url: string; imgFile: File | null; name: string }[];
}) { }) {
@ -1281,7 +1265,6 @@ export const useEmployeeForm = defineStore('form-employee', () => {
currentFromDataEmployee.value.lastNameEN.trim(); currentFromDataEmployee.value.lastNameEN.trim();
if (state.value.dialogType === 'create') { if (state.value.dialogType === 'create') {
delete currentFromDataEmployee.value.image;
const res = await employeeStore.create( const res = await employeeStore.create(
{ {
...currentFromDataEmployee.value, ...currentFromDataEmployee.value,
@ -1310,7 +1293,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
state.value.currentEmployee?.status === 'CREATED' state.value.currentEmployee?.status === 'CREATED'
? 'ACTIVE' ? 'ACTIVE'
: state.value.currentEmployee?.status, : state.value.currentEmployee?.status,
customerBranchId: state.value.currentBranchId || '', customerBranchId: state.value.formDataEmployeeOwner?.id || '',
employeeWork: [], employeeWork: [],
employeeCheckup: [], employeeCheckup: [],
employeeOtherInfo: undefined, employeeOtherInfo: undefined,
@ -1398,10 +1381,12 @@ export const useEmployeeForm = defineStore('form-employee', () => {
statusSave: true, statusSave: true,
})), })),
), ),
employeeOtherInfo: structuredClone({ employeeOtherInfo: structuredClone(
...(payload.employeeOtherInfo ?? {}), {
statusSave: true, ...payload.employeeOtherInfo,
}), statusSave: !!payload.employeeOtherInfo?.id ? true : false,
} || {},
),
employeeWork: structuredClone( employeeWork: structuredClone(
payload.employeeWork?.length === 0 payload.employeeWork?.length === 0
? state.value.dialogModal ? state.value.dialogModal
@ -1550,7 +1535,6 @@ export const useEmployeeForm = defineStore('form-employee', () => {
expireDate: new Date(), expireDate: new Date(),
remark: undefined, remark: undefined,
workerType: '', workerType: '',
reportDate: null,
number: '', number: '',
}); });
@ -1675,7 +1659,6 @@ export const useEmployeeForm = defineStore('form-employee', () => {
state, state,
currentFromDataEmployee, currentFromDataEmployee,
resetEmployeeData, resetEmployeeData,
addPassport, addPassport,
addVisa, addVisa,
addCheckup, addCheckup,

View file

@ -43,7 +43,6 @@ withDefaults(
defineProps<{ defineProps<{
readonly?: boolean; readonly?: boolean;
isEdit?: boolean; isEdit?: boolean;
hideAction?: boolean;
}>(), }>(),
{ readonly: false, isEdit: false }, { readonly: false, isEdit: false },
); );
@ -61,7 +60,6 @@ async function addStep() {
flowData.value.step.push({ flowData.value.step.push({
responsibleInstitution: [], responsibleInstitution: [],
responsiblePersonId: [], responsiblePersonId: [],
responsibleGroup: [],
value: [], value: [],
detail: '', detail: '',
name: '', name: '',
@ -168,7 +166,6 @@ function triggerPropertiesDialog(step: WorkFlowPayloadStep) {
id="flow-form-dialog" id="flow-form-dialog"
> >
<FormFlow <FormFlow
v-model:user-in-table="userInTable"
v-model:flow-data="flowData" v-model:flow-data="flowData"
v-model:register-branch-id="registerBranchId" v-model:register-branch-id="registerBranchId"
@trigger-properties="triggerPropertiesDialog" @trigger-properties="triggerPropertiesDialog"
@ -208,7 +205,7 @@ function triggerPropertiesDialog(step: WorkFlowPayloadStep) {
style="position: absolute; z-index: 999; top: 0; right: 0" style="position: absolute; z-index: 999; top: 0; right: 0"
> >
<div <div
v-if="flowData.status !== 'INACTIVE' && !hideAction" v-if="flowData.status !== 'INACTIVE'"
class="surface-1 row rounded" class="surface-1 row rounded"
> >
<UndoButton <UndoButton
@ -288,7 +285,6 @@ function triggerPropertiesDialog(step: WorkFlowPayloadStep) {
> >
<template v-slot:btn-form-flow-step-drawer> <template v-slot:btn-form-flow-step-drawer>
<q-btn <q-btn
v-if="!hideAction"
dense dense
flat flat
icon="mdi-plus" icon="mdi-plus"
@ -317,7 +313,6 @@ function triggerPropertiesDialog(step: WorkFlowPayloadStep) {
<FormFlow <FormFlow
:readonly :readonly
onDrawer onDrawer
:hide-action="hideAction"
v-model:user-in-table="userInTable" v-model:user-in-table="userInTable"
v-model:flow-data="flowData" v-model:flow-data="flowData"
v-model:register-branch-id="registerBranchId" v-model:register-branch-id="registerBranchId"

View file

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, reactive, ref, watch } from 'vue'; import { onMounted, reactive, ref, watch } from 'vue';
import { QTableProps } from 'quasar'; import { QSelect, QTableProps } from 'quasar';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@ -11,7 +11,7 @@ import {
} from 'src/stores/workflow-template/types'; } from 'src/stores/workflow-template/types';
import { useWorkflowTemplate } from 'src/stores/workflow-template'; import { useWorkflowTemplate } from 'src/stores/workflow-template';
import { useNavigator } from 'src/stores/navigator'; import { useNavigator } from 'src/stores/navigator';
import { dialog, canAccess } from 'src/stores/utils'; import { dialog } from 'src/stores/utils';
import FloatingActionButton from 'components/FloatingActionButton.vue'; import FloatingActionButton from 'components/FloatingActionButton.vue';
import StatCardComponent from 'src/components/StatCardComponent.vue'; import StatCardComponent from 'src/components/StatCardComponent.vue';
@ -22,7 +22,6 @@ import NoData from 'src/components/NoData.vue';
import KebabAction from 'src/components/shared/KebabAction.vue'; import KebabAction from 'src/components/shared/KebabAction.vue';
import PaginationPageSize from 'src/components/PaginationPageSize.vue'; import PaginationPageSize from 'src/components/PaginationPageSize.vue';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const { t } = useI18n(); const { t } = useI18n();
const workflowStore = useWorkflowTemplate(); const workflowStore = useWorkflowTemplate();
@ -46,7 +45,6 @@ const pageState = reactive({
addModal: false, addModal: false,
viewDrawer: false, viewDrawer: false,
isDrawerEdit: true, isDrawerEdit: true,
searchDate: [],
}); });
const fieldSelected = ref<('order' | 'name' | 'step')[]>([ const fieldSelected = ref<('order' | 'name' | 'step')[]>([
@ -70,6 +68,7 @@ const fieldSelectedOption = ref<{ label: string; value: string }[]>([
}, },
]); ]);
const refFilter = ref<InstanceType<typeof QSelect>>();
const currWorkflowData = ref<WorkflowTemplate>(); const currWorkflowData = ref<WorkflowTemplate>();
const formDataWorkflow = ref<WorkflowTemplatePayload>({ const formDataWorkflow = ref<WorkflowTemplatePayload>({
status: 'CREATED', status: 'CREATED',
@ -103,7 +102,6 @@ const columns = [
function triggerDialog(type: 'add' | 'edit' | 'view') { function triggerDialog(type: 'add' | 'edit' | 'view') {
if (type === 'add') { if (type === 'add') {
registeredBranchId.value = ''; registeredBranchId.value = '';
userInTable.value = [];
formDataWorkflow.value = { formDataWorkflow.value = {
status: 'CREATED', status: 'CREATED',
name: '', name: '',
@ -208,7 +206,7 @@ async function submit() {
...formDataWorkflow.value, ...formDataWorkflow.value,
}); });
} else { } else {
await workflowStore.createWorkflowTemplate({ await workflowStore.creatWorkflowTemplate({
registeredBranchId: registeredBranchId.value, registeredBranchId: registeredBranchId.value,
...formDataWorkflow.value, ...formDataWorkflow.value,
}); });
@ -224,11 +222,7 @@ function assignFormData(workflowData: WorkflowTemplate) {
status: workflowData.status, status: workflowData.status,
name: workflowData.name, name: workflowData.name,
step: workflowData.step.map((s, i) => { step: workflowData.step.map((s, i) => {
userInTable.value[i] = { userInTable.value[i] = { name: s.name, responsiblePerson: [] };
name: s.name,
responsiblePerson: [],
responsibleGroup: [],
};
s.responsiblePerson.forEach((p) => { s.responsiblePerson.forEach((p) => {
userInTable.value[i].responsiblePerson.push({ userInTable.value[i].responsiblePerson.push({
id: p.user.id, id: p.user.id,
@ -242,16 +236,12 @@ function assignFormData(workflowData: WorkflowTemplate) {
code: p.user.code, code: p.user.code,
}); });
}); });
s.responsibleGroup.forEach((g) => {
userInTable.value[i].responsibleGroup.push(g);
});
return { return {
id: s.id, id: s.id,
name: s.name, name: s.name,
detail: s.detail, detail: s.detail,
messengerByArea: s.messengerByArea || false, messengerByArea: s.messengerByArea || false,
value: s.value.length > 0 ? JSON.parse(JSON.stringify(s.value)) : [], value: s.value.length > 0 ? JSON.parse(JSON.stringify(s.value)) : [],
responsibleGroup: s.responsibleGroup.map((g) => g),
responsiblePersonId: s.responsiblePerson.map((p) => p.userId), responsiblePersonId: s.responsiblePerson.map((p) => p.userId),
responsibleInstitution: JSON.parse( responsibleInstitution: JSON.parse(
JSON.stringify(s.responsibleInstitution), JSON.stringify(s.responsibleInstitution),
@ -292,8 +282,6 @@ async function fetchWorkflowList(mobileFetch?: boolean) {
: statusFilter.value === 'statusACTIVE' : statusFilter.value === 'statusACTIVE'
? 'ACTIVE' ? 'ACTIVE'
: 'INACTIVE', : 'INACTIVE',
startDate: pageState.searchDate[0],
endDate: pageState.searchDate[1],
}); });
if (res) { if (res) {
workflowData.value = workflowData.value =
@ -323,18 +311,14 @@ watch(
fetchWorkflowList(); fetchWorkflowList();
}, },
); );
watch( watch([() => pageState.inputSearch, workflowPageSize], () => {
[() => pageState.inputSearch, workflowPageSize, () => pageState.searchDate],
() => {
workflowData.value = []; workflowData.value = [];
workflowPage.value = 1; workflowPage.value = 1;
fetchWorkflowList(); fetchWorkflowList();
}, });
);
</script> </script>
<template> <template>
<FloatingActionButton <FloatingActionButton
v-if="canAccess('workflow', 'edit')"
style="z-index: 999" style="z-index: 999"
hide-icon hide-icon
@click="triggerDialog('add')" @click="triggerDialog('add')"
@ -408,44 +392,26 @@ watch(
<template #prepend> <template #prepend>
<q-icon name="mdi-magnify" /> <q-icon name="mdi-magnify" />
</template> </template>
<template v-slot:append> <template v-if="$q.screen.lt.md" v-slot:append>
<q-separator vertical inset class="q-mr-xs" /> <span class="row">
<AdvanceSearch <q-separator vertical />
v-model="pageState.searchDate" <q-btn
:active="$q.screen.lt.md && statusFilter !== 'all'" icon="mdi-filter-variant"
> unelevated
<div class="q-ml-sm"
v-if="$q.screen.lt.md" padding="4px"
class="q-mt-sm text-weight-medium" size="sm"
> rounded
{{ $t('general.status') }} @click="refFilter?.showPopup"
</div>
<q-select
v-if="$q.screen.lt.md"
v-model="statusFilter"
outlined
dense
option-value="value"
option-label="label"
map-options
emit-value
:for="'field-select-status'"
:options="[
{ label: $t('general.all'), value: 'all' },
{ label: $t('general.active'), value: 'statusACTIVE' },
{
label: $t('general.inactive'),
value: 'statusINACTIVE',
},
]"
/> />
</AdvanceSearch> </span>
</template> </template>
</q-input> </q-input>
<div class="row col-md-5" style="white-space: nowrap"> <div class="row col-md-5" style="white-space: nowrap">
<q-select <q-select
v-if="$q.screen.gt.sm" v-show="$q.screen.gt.sm"
ref="refFilter"
v-model="statusFilter" v-model="statusFilter"
outlined outlined
dense dense
@ -543,12 +509,12 @@ watch(
class="col surface-2 flex items-center justify-center" class="col surface-2 flex items-center justify-center"
> >
<NoData <NoData
v-if="pageState.total !== 0 || pageState.searchDate.length > 0" v-if="pageState.total !== 0"
:not-found="!!pageState.inputSearch" :not-found="!!pageState.inputSearch"
/> />
<CreateButton <CreateButton
v-if="pageState.total === 0 && pageState.searchDate.length === 0" v-if="pageState.total === 0"
@click="triggerDialog('add')" @click="triggerDialog('add')"
label="general.add" label="general.add"
:i18n-args="{ text: $t('flow.title') }" :i18n-args="{ text: $t('flow.title') }"
@ -683,7 +649,6 @@ watch(
" "
/> />
<KebabAction <KebabAction
v-if="canAccess('workflow', 'edit')"
:id-name="props.row.name" :id-name="props.row.name"
:status="props.row.status" :status="props.row.status"
@view=" @view="
@ -765,8 +730,7 @@ watch(
" "
/> />
<KebabAction <KebabAction
v-if="canAccess('workflow', 'edit')" :id-name="props.row.id"
:id-name="props.row.name"
:status="props.row.status" :status="props.row.status"
@view=" @view="
() => { () => {
@ -849,7 +813,6 @@ watch(
@drawer-undo="undo" @drawer-undo="undo"
@close="resetForm" @close="resetForm"
@submit="submit" @submit="submit"
:hide-action="!canAccess('workflow', 'edit')"
:readonly="!pageState.isDrawerEdit" :readonly="!pageState.isDrawerEdit"
:isEdit="pageState.isDrawerEdit" :isEdit="pageState.isDrawerEdit"
v-model="pageState.addModal" v-model="pageState.addModal"

View file

@ -3,7 +3,7 @@ import { nextTick, ref, watch, reactive } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useQuasar, type QTableProps } from 'quasar'; import { QSelect, useQuasar, type QTableProps } from 'quasar';
import DialogProperties from 'src/components/dialog/DialogProperties.vue'; import DialogProperties from 'src/components/dialog/DialogProperties.vue';
import ProductCardComponent from 'components/04_product-service/ProductCardComponent.vue'; import ProductCardComponent from 'components/04_product-service/ProductCardComponent.vue';
@ -33,7 +33,6 @@ import {
SaveButton, SaveButton,
UndoButton, UndoButton,
ToggleButton, ToggleButton,
ImportButton,
} from 'components/button'; } from 'components/button';
import TableProduct from 'src/components/04_product-service/TableProduct.vue'; import TableProduct from 'src/components/04_product-service/TableProduct.vue';
import PaginationPageSize from 'src/components/PaginationPageSize.vue'; import PaginationPageSize from 'src/components/PaginationPageSize.vue';
@ -41,7 +40,7 @@ import PaginationPageSize from 'src/components/PaginationPageSize.vue';
import useFlowStore from 'stores/flow'; import useFlowStore from 'stores/flow';
import { dateFormat } from 'src/utils/datetime'; import { dateFormat } from 'src/utils/datetime';
import { formatNumberDecimal, isRoleInclude, canAccess } from 'stores/utils'; import { formatNumberDecimal, isRoleInclude } from 'stores/utils';
const { getWorkflowTemplate } = useWorkflowTemplate(); const { getWorkflowTemplate } = useWorkflowTemplate();
import { Status } from 'stores/types'; import { Status } from 'stores/types';
@ -60,7 +59,6 @@ import {
ServiceById, ServiceById,
WorkItems, WorkItems,
Attributes, Attributes,
WorkCreate,
} from 'stores/product-service/types'; } from 'stores/product-service/types';
import { computed } from 'vue'; import { computed } from 'vue';
import { import {
@ -69,8 +67,6 @@ import {
} from 'src/stores/workflow-template/types'; } from 'src/stores/workflow-template/types';
import { useWorkflowTemplate } from 'src/stores/workflow-template'; import { useWorkflowTemplate } from 'src/stores/workflow-template';
import { deepEquals } from 'src/utils/arr'; import { deepEquals } from 'src/utils/arr';
import { toRaw } from 'vue';
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const flowStore = useFlowStore(); const flowStore = useFlowStore();
const navigatorStore = useNavigator(); const navigatorStore = useNavigator();
@ -100,15 +96,10 @@ const {
createWork, createWork,
editWork, editWork,
deleteWork, deleteWork,
importProduct,
productExport,
} = productServiceStore; } = productServiceStore;
const { workNameItems } = storeToRefs(productServiceStore); const { workNameItems } = storeToRefs(productServiceStore);
const allStat = ref<{ mode: string; count: number }[]>([]); const allStat = ref<{ mode: string; count: number }[]>([]);
const stat = ref< const stat = ref<
{ {
icon: string; icon: string;
@ -145,29 +136,33 @@ const { t } = useI18n();
const baseUrl = ref<string>(import.meta.env.VITE_API_BASE_URL); const baseUrl = ref<string>(import.meta.env.VITE_API_BASE_URL);
const priceDisplay = computed(() => ({ const priceDisplay = computed(() => ({
// price: !isRoleInclude(['sale_agent']), price: !isRoleInclude(['sale_agent']),
price: true,
agentPrice: isRoleInclude([ agentPrice: isRoleInclude([
'system',
'head_of_admin',
'admin', 'admin',
'executive', 'head_of_admin',
'accountant',
'head_of_sale', 'head_of_sale',
'system',
'owner',
'accountant',
'sale_agent',
]), ]),
serviceCharge: isRoleInclude([ serviceCharge: isRoleInclude([
'system',
'head_of_admin',
'admin', 'admin',
'executive', 'head_of_admin',
'system',
'owner',
'accountant', 'accountant',
]), ]),
})); }));
const actionDisplay = computed(() => canAccess('product', 'edit')); const actionDisplay = computed(() =>
isRoleInclude(['admin', 'head_of_admin', 'system', 'owner', 'accountant']),
);
const splitterModel = computed(() => const splitterModel = computed(() =>
$q.screen.lt.md ? (productMode.value !== 'group' ? 0 : 100) : 25, $q.screen.lt.md ? (productMode.value !== 'group' ? 0 : 100) : 25,
); );
const refFilterGroup = ref<InstanceType<typeof QSelect>>();
const refFilterProductService = ref<InstanceType<typeof QSelect>>();
const holdDialog = ref(false); const holdDialog = ref(false);
const imageDialog = ref(false); const imageDialog = ref(false);
const currentNode = ref<ProductGroup & { type: string }>(); const currentNode = ref<ProductGroup & { type: string }>();
@ -525,7 +520,6 @@ const currentStatusGroupType = ref<Status>('CREATED');
const currentIdGroupType = ref(''); const currentIdGroupType = ref('');
const currentStatus = ref<Status | 'All'>('All'); const currentStatus = ref<Status | 'All'>('All');
const searchDate = ref<string[]>([]);
// img // img
const isImageEdit = ref<boolean>(false); const isImageEdit = ref<boolean>(false);
@ -621,8 +615,6 @@ async function fetchListGroups(mobileFetch?: boolean) {
: currentStatus.value === 'ACTIVE' : currentStatus.value === 'ACTIVE'
? 'ACTIVE' ? 'ACTIVE'
: 'INACTIVE', : 'INACTIVE',
startDate: searchDate.value[0],
endDate: searchDate.value[1],
}); });
if (res) { if (res) {
@ -683,8 +675,6 @@ async function fetchListOfProduct(mobileFetch?: boolean) {
? 'ACTIVE' ? 'ACTIVE'
: undefined, : undefined,
productGroupId: currentIdGroup.value, productGroupId: currentIdGroup.value,
startDate: searchDate.value[0],
endDate: searchDate.value[1],
}); });
if (res) { if (res) {
@ -730,8 +720,6 @@ async function fetchListOfService(mobileFetch?: boolean) {
? 'ACTIVE' ? 'ACTIVE'
: undefined, : undefined,
productGroupId: currentIdGroup.value, productGroupId: currentIdGroup.value,
startDate: searchDate.value[0],
endDate: searchDate.value[1],
}); });
if (res) { if (res) {
@ -1171,7 +1159,6 @@ function clearFormService() {
profileSubmit.value = false; profileSubmit.value = false;
imageProduct.value = undefined; imageProduct.value = undefined;
profileFileImg.value = null; profileFileImg.value = null;
serviceTab.value = 1;
} }
function sameFormService() { function sameFormService() {
@ -1398,7 +1385,6 @@ function submitAddWorkProduct() {
if (!s.hasOwnProperty('productsId')) { if (!s.hasOwnProperty('productsId')) {
s.productsId = []; s.productsId = [];
} }
if (s.productsId.length === 0) return;
s.productsId.push(i.id); s.productsId.push(i.id);
}, },
); );
@ -1451,11 +1437,17 @@ function confirmDeleteWork(id: string, noDialog?: boolean) {
} }
} }
function triggerConfirmCloseWorkName() { function triggerConfirmCloseWork() {
dialogWarningClose(t, { dialogWarningClose(t, {
message: t('dialog.message.warningClose'), message: t('dialog.message.warningClose'),
action: () => { action: () => {
manageWorkNameDialog.value = false; manageWorkNameDialog.value = false;
if (workNameItems.value[workNameItems.value.length - 1].name === '') {
confirmDeleteWork(
workNameItems.value[workNameItems.value.length - 1].id,
true,
);
}
}, },
cancel: () => {}, cancel: () => {},
}); });
@ -1598,7 +1590,6 @@ async function enterNext(type: 'service' | 'product') {
inputSearchProductAndService.value = ''; inputSearchProductAndService.value = '';
currentStatus.value = 'All'; currentStatus.value = 'All';
filterStat.value = []; filterStat.value = [];
searchDate.value = [];
if ( if (
expandedTree.value.length > 1 && expandedTree.value.length > 1 &&
@ -1754,7 +1745,7 @@ watch(currentStatus, async () => {
flowStore.rotate(); flowStore.rotate();
}); });
watch([inputSearch, () => searchDate.value], async () => { watch(inputSearch, async () => {
if (productMode.value === 'group') { if (productMode.value === 'group') {
productGroup.value = []; productGroup.value = [];
currentPageGroup.value = 1; currentPageGroup.value = 1;
@ -1763,7 +1754,7 @@ watch([inputSearch, () => searchDate.value], async () => {
} }
}); });
watch([inputSearchProductAndService, () => searchDate.value], async () => { watch(inputSearchProductAndService, async () => {
product.value = []; product.value = [];
service.value = []; service.value = [];
currentPageServiceAndProduct.value = 1; currentPageServiceAndProduct.value = 1;
@ -1840,84 +1831,6 @@ function handleSubmitSameWorkflow() {
); );
} }
async function copy(id: string) {
{
const res = await fetchListServiceById(id);
if (res) {
formService.value = {
code: res.code.slice(0, -3),
name: res.name,
detail: res.detail,
attributes: res.attributes,
work: res.work.map((v) => ({
id: v.id,
name: v.name,
attributes: v.attributes,
product: v.productOnWork.map((productOnWorkItem) => ({
id: productOnWorkItem.product.id,
installmentNo: productOnWorkItem.installmentNo,
stepCount: productOnWorkItem.stepCount,
})),
})),
status: res.status,
productGroupId: res.productGroupId,
selectedImage: res.selectedImage,
installments: res.installments,
};
workItems.value = res.work.map((item) => {
return {
id: item.id,
name: item.name,
attributes: item.attributes,
product: item.productOnWork.map((productOnWorkItem) => {
return {
...productOnWorkItem.product,
nameEn: productOnWorkItem.product.name,
installmentNo: productOnWorkItem.installmentNo,
};
}),
};
});
}
}
dialogService.value = true;
}
function addWorkName(data: { name: string; order: number }) {
workNameItems.value.push({ id: '', name: data.name, isEdit: true });
}
async function submitWorkName(
workId: string,
data: Partial<WorkCreate & { status: string }>,
) {
if (workNameItems.value.length === 0) return;
if (!workId) await createWork({ ...data, order: 1 });
else await editWork(workId, data);
}
async function triggerExport() {
productExport({
pageSize: 100_000,
productGroupId: currentIdGroup.value,
query: !!inputSearchProductAndService.value
? inputSearchProductAndService.value
: undefined,
status:
currentStatus.value === 'INACTIVE'
? 'INACTIVE'
: currentStatus.value === 'ACTIVE'
? 'ACTIVE'
: undefined,
startDate: searchDate.value[0] ? new Date(searchDate.value[0]) : undefined,
endDate: searchDate.value[1] ? new Date(searchDate.value[1]) : undefined,
});
}
watch( watch(
() => formService.value.attributes.workflowId, () => formService.value.attributes.workflowId,
async (a, b) => { async (a, b) => {
@ -2035,34 +1948,19 @@ watch(
<template v-slot:prepend> <template v-slot:prepend>
<q-icon name="mdi-magnify" /> <q-icon name="mdi-magnify" />
</template> </template>
<template v-slot:append> <template v-if="$q.screen.lt.md" v-slot:append>
<q-separator vertical inset class="q-mr-xs" /> <span class="row">
<AdvanceSearch <q-separator vertical />
v-model="searchDate" <q-btn
:active="$q.screen.lt.md && currentStatus !== 'All'" icon="mdi-filter-variant"
> unelevated
<div class="q-mt-sm text-weight-medium"> class="q-ml-sm"
{{ $t('general.status') }} padding="4px"
</div> size="sm"
<q-select rounded
v-model="currentStatus" @click="refFilterGroup?.showPopup"
for="select-status"
outlined
dense
option-value="value"
option-label="label"
map-options
emit-value
:options="[
{ label: $t('general.all'), value: 'All' },
{ label: $t('general.active'), value: 'ACTIVE' },
{
label: $t('general.inactive'),
value: 'INACTIVE',
},
]"
/> />
</AdvanceSearch> </span>
</template> </template>
</q-input> </q-input>
</div> </div>
@ -2217,43 +2115,26 @@ watch(
<template v-slot:prepend> <template v-slot:prepend>
<q-icon name="mdi-magnify" /> <q-icon name="mdi-magnify" />
</template> </template>
<template v-slot:append> <template v-if="$q.screen.lt.md" v-slot:append>
<q-separator vertical inset class="q-mr-xs" /> <span class="row">
<AdvanceSearch <q-separator vertical />
v-model="searchDate" <q-btn
:active="$q.screen.lt.md && currentStatus !== 'All'" icon="mdi-filter-variant"
> unelevated
<div class="q-ml-sm"
v-if="$q.screen.lt.md" padding="4px"
class="q-mt-sm text-weight-medium" size="sm"
> rounded
{{ $t('general.status') }} @click="refFilterGroup?.showPopup"
</div>
<q-select
v-if="$q.screen.lt.md"
v-model="currentStatus"
for="select-status"
outlined
dense
option-value="value"
option-label="label"
map-options
emit-value
:options="[
{ label: $t('general.all'), value: 'All' },
{ label: $t('general.active'), value: 'ACTIVE' },
{
label: $t('general.inactive'),
value: 'INACTIVE',
},
]"
/> />
</AdvanceSearch> </span>
</template> </template>
</q-input> </q-input>
<div class="row col-md-6" style="white-space: nowrap"> <div class="row col-md-6" style="white-space: nowrap">
<q-select <q-select
v-show="$q.screen.gt.sm" v-show="$q.screen.gt.sm"
ref="refFilterGroup"
v-model="currentStatus" v-model="currentStatus"
for="select-status" for="select-status"
outlined outlined
@ -2274,6 +2155,7 @@ watch(
}, },
]" ]"
></q-select> ></q-select>
<q-select <q-select
v-if="modeView === false" v-if="modeView === false"
id="select-field" id="select-field"
@ -2296,6 +2178,7 @@ watch(
multiple multiple
dense dense
/> />
<q-btn-toggle <q-btn-toggle
v-model="modeView" v-model="modeView"
id="btn-mode" id="btn-mode"
@ -2485,10 +2368,7 @@ watch(
) )
" "
> >
{{ {{ props.row.detail || '-' }}
props.row.detail.replace(/<\/?[^>]+(>|$)/g, '') ||
'-'
}}
</q-td> </q-td>
<q-td <q-td
@ -2538,7 +2418,6 @@ watch(
/> />
<KebabAction <KebabAction
v-if="actionDisplay"
:disable-delete="props.row.status !== 'CREATED'" :disable-delete="props.row.status !== 'CREATED'"
:status="props.row.status" :status="props.row.status"
:id-name="props.row.name" :id-name="props.row.name"
@ -2681,15 +2560,7 @@ watch(
{{ $t('general.recordPerPage') }} {{ $t('general.recordPerPage') }}
</div> </div>
<div> <div>
<PaginationPageSize <PaginationPageSize v-model="pageSizeGroup" />
v-model="pageSizeGroup"
:fetch-data="
async () => {
await fetchListGroups();
flowStore.rotate();
}
"
/>
</div> </div>
</div> </div>
</div> </div>
@ -2747,72 +2618,26 @@ watch(
<template v-slot:prepend> <template v-slot:prepend>
<q-icon name="mdi-magnify" /> <q-icon name="mdi-magnify" />
</template> </template>
<template v-slot:append> <template v-if="$q.screen.lt.md" v-slot:append>
<q-separator vertical inset class="q-mr-xs" /> <span class="row">
<AdvanceSearch <q-separator vertical />
v-model="searchDate" <q-btn
:active="$q.screen.lt.md && currentStatus !== 'All'" icon="mdi-filter-variant"
> unelevated
<div class="q-ml-sm"
v-if="$q.screen.lt.md" padding="4px"
class="q-mt-sm text-weight-medium" size="sm"
> rounded
{{ $t('general.status') }} @click="refFilterProductService?.showPopup"
</div>
<q-select
v-if="$q.screen.lt.md"
:for="'field-select-status'"
v-model="currentStatus"
outlined
dense
option-value="value"
option-label="label"
map-options
emit-value
:options="[
{ label: $t('general.all'), value: 'All' },
{ label: $t('general.active'), value: 'ACTIVE' },
{
label: $t('general.inactive'),
value: 'INACTIVE',
},
]"
@update:model-value="fetchStatus()"
/> />
</AdvanceSearch> </span>
</template> </template>
</q-input> </q-input>
<div
class="flex q-mr-auto q-pl-sm"
v-if="productAndServiceTab === 'product'"
>
<input ref="fileImport" type="file" hidden />
<ImportButton
type="file"
import-file
icon-only
@file-selected="
(file) => {
importProduct(
currentIdGroup,
file,
async () => await fetchListOfProduct(),
);
}
"
/>
<SaveButton
icon-only
:icon="'material-symbols:download'"
color="var(--info-bg)"
@click.stop="triggerExport()"
/>
</div>
<div class="row col-md-6" style="white-space: nowrap"> <div class="row col-md-6" style="white-space: nowrap">
<q-select <q-select
v-show="$q.screen.gt.sm" v-show="$q.screen.gt.sm"
ref="refFilterProductService"
:for="'field-select-status'" :for="'field-select-status'"
v-model="currentStatus" v-model="currentStatus"
outlined outlined
@ -2834,6 +2659,7 @@ watch(
]" ]"
@update:model-value="fetchStatus()" @update:model-value="fetchStatus()"
></q-select> ></q-select>
<q-select <q-select
v-if="modeView === false" v-if="modeView === false"
:hide-dropdown-icon="$q.screen.lt.sm" :hide-dropdown-icon="$q.screen.lt.sm"
@ -2868,6 +2694,7 @@ watch(
multiple multiple
dense dense
/> />
<q-btn-toggle <q-btn-toggle
v-model="modeView" v-model="modeView"
id="btn-mode" id="btn-mode"
@ -3300,15 +3127,8 @@ watch(
" "
/> />
<KebabAction <KebabAction
v-if="actionDisplay"
:use-copy="productAndServiceTab === 'service'"
:status="props.row.status" :status="props.row.status"
:id-name="props.row.name" :id-name="props.row.name"
@copy="
() => {
copy(props.row.id);
}
"
@view=" @view="
async () => { async () => {
if (props.row.type === 'product') { if (props.row.type === 'product') {
@ -3455,15 +3275,7 @@ watch(
{{ $t('general.recordPerPage') }} {{ $t('general.recordPerPage') }}
</div> </div>
<div> <div>
<PaginationPageSize <PaginationPageSize v-model="pageSizeServiceAndProduct" />
v-model="pageSizeServiceAndProduct"
:fetch-data="
async () => {
await alternativeFetch();
flowStore.rotate();
}
"
/>
</div> </div>
</div> </div>
</div> </div>
@ -3595,7 +3407,7 @@ watch(
</div> </div>
<div <div
class="col-12 col-md-10" class="col-12 col-md-10"
id="group-create" id="customer-form-content"
:class="{ :class="{
'q-py-md q-pr-md ': $q.screen.gt.sm, 'q-py-md q-pr-md ': $q.screen.gt.sm,
'q-py-md q-px-lg': !$q.screen.gt.sm, 'q-py-md q-px-lg': !$q.screen.gt.sm,
@ -4114,7 +3926,7 @@ watch(
</div> </div>
<div <div
class="col-12 col-md-10" class="col-12 col-md-10"
id="product-create" id="customer-form-content"
:class="{ :class="{
'q-py-md q-pr-md ': $q.screen.gt.sm, 'q-py-md q-pr-md ': $q.screen.gt.sm,
'q-pa-sm': !$q.screen.gt.sm, 'q-pa-sm': !$q.screen.gt.sm,
@ -4340,7 +4152,7 @@ watch(
'q-py-md q-pr-md ': $q.screen.gt.sm, 'q-py-md q-pr-md ': $q.screen.gt.sm,
'q-pa-sm': !$q.screen.gt.sm, 'q-pa-sm': !$q.screen.gt.sm,
}" }"
id="product-info" id="customer-form-content"
style="height: 100%; max-height: 100%; overflow-y: auto" style="height: 100%; max-height: 100%; overflow-y: auto"
> >
<BasicInfoProduct <BasicInfoProduct
@ -4385,7 +4197,6 @@ watch(
<!-- add service --> <!-- add service -->
<DialogForm <DialogForm
v-if="dialogService"
hide-footer hide-footer
no-address no-address
no-app-box no-app-box
@ -4522,11 +4333,6 @@ watch(
sub: true, sub: true,
})) }))
" "
:active="{
background: 'hsla(var(--blue-6-hsl) / .2)',
foreground: 'var(--blue-6)',
}"
scroll-element="#service-create"
/> />
</div> </div>
<span <span
@ -4549,7 +4355,7 @@ watch(
</div> </div>
<div <div
class="col-12 col-md-10" class="col-12 col-md-10"
id="service-create" id="customer-form-content"
:class="{ :class="{
'q-py-md q-pr-md ': $q.screen.gt.sm, 'q-py-md q-pr-md ': $q.screen.gt.sm,
'q-pa-sm': !$q.screen.gt.sm, 'q-pa-sm': !$q.screen.gt.sm,
@ -4616,7 +4422,6 @@ watch(
@click="serviceTreeView = false" @click="serviceTreeView = false"
/> />
</div> </div>
<SaveButton id="btn-info-basic-save" icon-only type="submit" /> <SaveButton id="btn-info-basic-save" icon-only type="submit" />
</div> </div>
@ -4730,7 +4535,7 @@ watch(
? workNameRef.isWorkNameEdit() ? workNameRef.isWorkNameEdit()
: false; : false;
if (isWorkNameEdit) { if (isWorkNameEdit) {
triggerConfirmCloseWorkName(); triggerConfirmCloseWork();
return true; return true;
} }
return false; return false;
@ -4742,16 +4547,15 @@ watch(
ref="workNameRef" ref="workNameRef"
v-model:name-list="workNameItems" v-model:name-list="workNameItems"
@delete="confirmDeleteWork" @delete="confirmDeleteWork"
@edit="submitWorkName" @edit="editWork"
@add="addWorkName" @add="createWork"
/> />
</div> </div>
</DialogForm> </DialogForm>
<!-- edit service edit package--> <!-- edit service -->
<!-- :edit="!(formDataProductService.status === 'INACTIVE')" --> <!-- :edit="!(formDataProductService.status === 'INACTIVE')" -->
<DialogForm <DialogForm
v-if="dialogServiceEdit"
hide-footer hide-footer
no-address no-address
height="95vh" height="95vh"
@ -5004,7 +4808,6 @@ watch(
background: 'hsla(var(--blue-6-hsl) / .2)', background: 'hsla(var(--blue-6-hsl) / .2)',
foreground: 'var(--blue-6)', foreground: 'var(--blue-6)',
}" }"
scroll-element="#service-info"
/> />
</div> </div>
<span <span
@ -5027,7 +4830,7 @@ watch(
</div> </div>
<div <div
class="col-12 col-md-10" class="col-12 col-md-10"
id="service-info" id="customer-form-content"
:class="{ :class="{
'q-py-md q-pr-md ': $q.screen.gt.sm, 'q-py-md q-pr-md ': $q.screen.gt.sm,
'q-pa-sm': !$q.screen.gt.sm, 'q-pa-sm': !$q.screen.gt.sm,

View file

@ -2,7 +2,7 @@
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { onMounted, reactive, ref } from 'vue'; import { onMounted, reactive, ref } from 'vue';
import { QTableProps } from 'quasar'; import { QSelect, QTableProps } from 'quasar';
import { useNavigator } from 'src/stores/navigator'; import { useNavigator } from 'src/stores/navigator';
import { useProperty } from 'src/stores/property'; import { useProperty } from 'src/stores/property';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
@ -14,10 +14,9 @@ import { FloatingActionButton, PaginationComponent } from 'src/components';
import PaginationPageSize from 'src/components/PaginationPageSize.vue'; import PaginationPageSize from 'src/components/PaginationPageSize.vue';
import PropertyDialog from './PropertyDialog.vue'; import PropertyDialog from './PropertyDialog.vue';
import { Property } from 'src/stores/property/types'; import { Property } from 'src/stores/property/types';
import { dialog, toCamelCase, canAccess } from 'src/stores/utils'; import { dialog, toCamelCase } from 'src/stores/utils';
import CreateButton from 'src/components/AddButton.vue'; import CreateButton from 'src/components/AddButton.vue';
import useOptionStore from 'stores/options'; import useOptionStore from 'stores/options';
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const { t, locale } = useI18n(); const { t, locale } = useI18n();
const $q = useQuasar(); const $q = useQuasar();
@ -39,6 +38,7 @@ const formProperty = ref<Property>({
type: {}, type: {},
}); });
const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all'); const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all');
const refFilter = ref<InstanceType<typeof QSelect>>();
const fieldSelected = ref<('order' | 'name' | 'type')[]>([ const fieldSelected = ref<('order' | 'name' | 'type')[]>([
'order', 'order',
'name', 'name',
@ -118,7 +118,6 @@ const pageState = reactive({
addModal: false, addModal: false,
viewDrawer: false, viewDrawer: false,
isDrawerEdit: true, isDrawerEdit: true,
searchDate: [],
}); });
async function fetchPropertyList(mobileFetch?: boolean) { async function fetchPropertyList(mobileFetch?: boolean) {
@ -135,8 +134,6 @@ async function fetchPropertyList(mobileFetch?: boolean) {
: statusFilter.value === 'statusACTIVE' : statusFilter.value === 'statusACTIVE'
? 'ACTIVE' ? 'ACTIVE'
: 'INACTIVE', : 'INACTIVE',
startDate: pageState.searchDate[0],
endDate: pageState.searchDate[1],
}); });
if (res) { if (res) {
propertyData.value = propertyData.value =
@ -320,18 +317,14 @@ watch(
fetchPropertyList(); fetchPropertyList();
}, },
); );
watch( watch([() => pageState.inputSearch, propertyPageSize], () => {
[() => pageState.inputSearch, propertyPageSize, () => pageState.searchDate],
() => {
propertyData.value = []; propertyData.value = [];
propertyPage.value = 1; propertyPage.value = 1;
fetchPropertyList(); fetchPropertyList();
}, });
);
</script> </script>
<template> <template>
<FloatingActionButton <FloatingActionButton
v-if="canAccess('workflow', 'edit')"
style="z-index: 999" style="z-index: 999"
hide-icon hide-icon
@click="triggerDialog('add')" @click="triggerDialog('add')"
@ -405,44 +398,26 @@ watch(
<template #prepend> <template #prepend>
<q-icon name="mdi-magnify" /> <q-icon name="mdi-magnify" />
</template> </template>
<template v-slot:append> <template v-if="$q.screen.lt.md" v-slot:append>
<q-separator vertical inset class="q-mr-xs" /> <span class="row">
<AdvanceSearch <q-separator vertical />
v-model="pageState.searchDate" <q-btn
:active="$q.screen.lt.md && statusFilter !== 'all'" icon="mdi-filter-variant"
> unelevated
<div class="q-ml-sm"
v-if="$q.screen.lt.md" padding="4px"
class="q-mt-sm text-weight-medium" size="sm"
> rounded
{{ $t('general.status') }} @click="refFilter?.showPopup"
</div>
<q-select
v-if="$q.screen.lt.md"
v-model="statusFilter"
outlined
dense
option-value="value"
option-label="label"
map-options
emit-value
:for="'field-select-status'"
:options="[
{ label: $t('general.all'), value: 'all' },
{ label: $t('general.active'), value: 'statusACTIVE' },
{
label: $t('general.inactive'),
value: 'statusINACTIVE',
},
]"
/> />
</AdvanceSearch> </span>
</template> </template>
</q-input> </q-input>
<div class="row col-md-5" style="white-space: nowrap"> <div class="row col-md-5" style="white-space: nowrap">
<q-select <q-select
v-if="$q.screen.gt.sm" v-show="$q.screen.gt.sm"
ref="refFilter"
v-model="statusFilter" v-model="statusFilter"
outlined outlined
dense dense
@ -537,19 +512,11 @@ watch(
class="col surface-2 flex items-center justify-center" class="col surface-2 flex items-center justify-center"
> >
<NoData <NoData
v-if=" v-if="pageState.total !== 0"
pageState.total !== 0 ||
pageState.searchDate.length > 0 ||
!canAccess('workflow', 'edit')
"
:not-found="!!pageState.inputSearch" :not-found="!!pageState.inputSearch"
/> />
<CreateButton <CreateButton
v-if=" v-if="pageState.total === 0"
pageState.total === 0 &&
pageState.searchDate.length === 0 &&
canAccess('workflow', 'edit')
"
@click="triggerDialog('add')" @click="triggerDialog('add')"
label="general.add" label="general.add"
:i18n-args="{ text: $t('flow.title') }" :i18n-args="{ text: $t('flow.title') }"
@ -707,7 +674,6 @@ watch(
" "
/> />
<KebabAction <KebabAction
v-if="canAccess('workflow', 'edit')"
:id-name="props.row.name" :id-name="props.row.name"
:status="props.row.status" :status="props.row.status"
@view=" @view="
@ -825,7 +791,6 @@ watch(
" "
/> />
<KebabAction <KebabAction
v-if="canAccess('workflow', 'edit')"
:id-name="props.row.id" :id-name="props.row.id"
:status="props.row.status" :status="props.row.status"
@view=" @view="
@ -917,7 +882,6 @@ watch(
@drawer-undo="() => undo()" @drawer-undo="() => undo()"
@close="() => resetForm()" @close="() => resetForm()"
@submit="() => submit()" @submit="() => submit()"
:hide-action="!canAccess('workflow', 'edit')"
:readonly="!pageState.isDrawerEdit" :readonly="!pageState.isDrawerEdit"
:isEdit="pageState.isDrawerEdit" :isEdit="pageState.isDrawerEdit"
v-model="pageState.addModal" v-model="pageState.addModal"

View file

@ -30,7 +30,6 @@ withDefaults(
defineProps<{ defineProps<{
readonly?: boolean; readonly?: boolean;
isEdit?: boolean; isEdit?: boolean;
hideAction?: boolean;
}>(), }>(),
{ readonly: false, isEdit: false }, { readonly: false, isEdit: false },
); );
@ -152,7 +151,7 @@ defineEmits<{
style="position: absolute; z-index: 999; top: 0; right: 0" style="position: absolute; z-index: 999; top: 0; right: 0"
> >
<div <div
v-if="propertyData.status !== 'INACTIVE' && !hideAction" v-if="propertyData.status !== 'INACTIVE'"
class="surface-1 row rounded" class="surface-1 row rounded"
> >
<UndoButton <UndoButton
@ -237,7 +236,6 @@ defineEmits<{
<FormProperty <FormProperty
onDrawer onDrawer
:readonly="!isEdit" :readonly="!isEdit"
:disable-toggle="hideAction"
v-model:name="formProperty.name" v-model:name="formProperty.name"
v-model:name-en="formProperty.nameEN" v-model:name-en="formProperty.nameEN"
v-model:type="formProperty.type" v-model:type="formProperty.type"

View file

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, reactive, ref, watch, computed } from 'vue'; import { onMounted, reactive, ref, watch, computed } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@ -7,15 +7,14 @@ import { useI18n } from 'vue-i18n';
// NOTE: Import stores // NOTE: Import stores
import useCustomerStore from 'stores/customer'; import useCustomerStore from 'stores/customer';
import { useQuotationStore } from 'src/stores/quotations'; import { useQuotationStore } from 'src/stores/quotations';
import { dialog, isRoleInclude, notify, setPrefixName } from 'stores/utils'; import { dialog, isRoleInclude, notify } from 'stores/utils';
import { useNavigator } from 'src/stores/navigator'; import { useNavigator } from 'src/stores/navigator';
import useFlowStore from 'src/stores/flow'; import useFlowStore from 'src/stores/flow';
import useMyBranch from 'stores/my-branch'; import useMyBranch from 'stores/my-branch';
import { useQuotationForm } from './form'; import { useQuotationForm } from './form';
import { hslaColors } from './constants'; import { hslaColors } from './constants';
import { pageTabs, columnQuotation } from './constants'; import { pageTabs, columnQuotation } from './constants';
import { toCamelCase, canAccess } from 'stores/utils'; import { toCamelCase } from 'stores/utils';
import { getUserId } from 'src/services/keycloak';
// NOTE Import Types // NOTE Import Types
import { CustomerBranchCreate, CustomerType } from 'stores/customer/types'; import { CustomerBranchCreate, CustomerType } from 'stores/customer/types';
@ -50,7 +49,6 @@ import { Quotation } from 'src/stores/quotations/types';
import TableQuotation from 'src/components/05_quotation/TableQuotation.vue'; import TableQuotation from 'src/components/05_quotation/TableQuotation.vue';
import PaginationPageSize from 'src/components/PaginationPageSize.vue'; import PaginationPageSize from 'src/components/PaginationPageSize.vue';
import { DialogContainer, DialogHeader } from 'src/components/dialog'; import { DialogContainer, DialogHeader } from 'src/components/dialog';
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
const { t, locale } = useI18n(); const { t, locale } = useI18n();
const $q = useQuasar(); const $q = useQuasar();
@ -60,6 +58,7 @@ const flowStore = useFlowStore();
const userBranch = useMyBranch(); const userBranch = useMyBranch();
const navigatorStore = useNavigator(); const navigatorStore = useNavigator();
const customerStore = useCustomerStore(); const customerStore = useCustomerStore();
const { const {
fetchListOfOptionBranch, fetchListOfOptionBranch,
customerFormUndo, customerFormUndo,
@ -75,7 +74,6 @@ const {
const { const {
state: customerFormState, state: customerFormState,
currentFormData: customerFormData, currentFormData: customerFormData,
currentBranchRootId,
registerAbleBranchOption, registerAbleBranchOption,
tabFieldRequired, tabFieldRequired,
} = storeToRefs(customerFormStore); } = storeToRefs(customerFormStore);
@ -89,8 +87,6 @@ const fieldSelectedOption = computed(() => {
value: v.name, value: v.name,
})); }));
}); });
const keyAddDialog = ref<number>(0);
const special = ref(false); const special = ref(false);
const branchId = ref(''); const branchId = ref('');
const agentPrice = ref<boolean>(false); const agentPrice = ref<boolean>(false);
@ -107,14 +103,12 @@ const pageState = reactive({
fieldSelected: [''], fieldSelected: [''],
gridView: false, gridView: false,
total: 0, total: 0,
sellerId: '',
currentTab: 'Issued', currentTab: 'Issued',
addModal: false, addModal: false,
quotationModal: false, quotationModal: false,
employeeModal: false, employeeModal: false,
receiptModal: false, receiptModal: false,
searchDate: [],
}); });
pageState.fieldSelected = [...columnQuotation.map((v) => v.name)]; pageState.fieldSelected = [...columnQuotation.map((v) => v.name)];
@ -275,10 +269,6 @@ const customerNameInfo = computed(() => {
return name || '-'; return name || '-';
}); });
function handleWindowFocus() {
fetchQuotationList();
}
onMounted(async () => { onMounted(async () => {
pageState.gridView = $q.screen.lt.md ? true : false; pageState.gridView = $q.screen.lt.md ? true : false;
navigatorStore.current.title = 'quotation.title'; navigatorStore.current.title = 'quotation.title';
@ -305,7 +295,6 @@ onMounted(async () => {
pageSize: quotationPageSize.value, pageSize: quotationPageSize.value,
status: 'Issued', status: 'Issued',
urgentFirst: true, urgentFirst: true,
sellerId: pageState.sellerId || undefined,
}); });
if (ret) { if (ret) {
@ -316,12 +305,6 @@ onMounted(async () => {
} }
flowStore.rotate(); flowStore.rotate();
window.addEventListener('focus', handleWindowFocus);
});
onUnmounted(() => {
window.removeEventListener('focus', handleWindowFocus);
}); });
async function fetchQuotationList(mobileFetch?: boolean) { async function fetchQuotationList(mobileFetch?: boolean) {
@ -344,9 +327,6 @@ async function fetchQuotationList(mobileFetch?: boolean) {
: 'Issued', : 'Issued',
query: pageState.inputSearch, query: pageState.inputSearch,
urgentFirst: true, urgentFirst: true,
startDate: pageState.searchDate[0],
endDate: pageState.searchDate[1],
sellerId: pageState.sellerId || undefined,
}); });
if (ret) { if (ret) {
@ -370,12 +350,7 @@ async function fetchQuotationList(mobileFetch?: boolean) {
} }
watch( watch(
[ [() => pageState.currentTab, () => pageState.inputSearch, quotationPageSize],
() => pageState.currentTab,
() => pageState.inputSearch,
() => pageState.searchDate,
quotationPageSize,
],
() => { () => {
quotationPage.value = 1; quotationPage.value = 1;
quotationData.value = []; quotationData.value = [];
@ -422,11 +397,6 @@ async function storeDataLocal(id: string) {
window.open(url, '_blank'); window.open(url, '_blank');
} }
async function filterBySellerId() {
pageState.sellerId = pageState.sellerId ? '' : getUserId();
await fetchQuotationList();
}
</script> </script>
<template> <template>
@ -434,7 +404,6 @@ async function filterBySellerId() {
hide-icon hide-icon
style="z-index: 999" style="z-index: 999"
@click.stop="triggerAddQuotationDialog" @click.stop="triggerAddQuotationDialog"
v-if="canAccess('quotation', 'create')"
/> />
<div class="column full-height no-wrap"> <div class="column full-height no-wrap">
@ -548,21 +517,6 @@ async function filterBySellerId() {
<template #prepend> <template #prepend>
<q-icon name="mdi-magnify" /> <q-icon name="mdi-magnify" />
</template> </template>
<template v-slot:append>
<q-separator vertical inset class="q-mr-xs" />
<AdvanceSearch v-model="pageState.searchDate">
<template #prepend>
<div class="text-weight-medium q-mb-sm">
<q-checkbox
size="xs"
:model-value="!!pageState.sellerId"
@click="filterBySellerId"
/>
{{ $t('quotation.ownOnly') }}
</div>
</template>
</AdvanceSearch>
</template>
</q-input> </q-input>
<div class="row col-md-5 justify-end" style="white-space: nowrap"> <div class="row col-md-5 justify-end" style="white-space: nowrap">
@ -680,20 +634,12 @@ async function filterBySellerId() {
class="col surface-2 flex items-center justify-center" class="col surface-2 flex items-center justify-center"
> >
<NoData <NoData
v-if=" v-if="pageState.inputSearch || pageState.currentTab !== 'Issued'"
pageState.inputSearch ||
!canAccess('quotation', 'create') ||
pageState.currentTab !== 'Issued'
"
:not-found="!!pageState.inputSearch" :not-found="!!pageState.inputSearch"
/> />
<CreateButton <CreateButton
v-if=" v-if="!pageState.inputSearch && pageState.currentTab === 'Issued'"
!pageState.inputSearch &&
pageState.currentTab === 'Issued' &&
canAccess('quotation', 'create')
"
@click="triggerAddQuotationDialog" @click="triggerAddQuotationDialog"
label="general.add" label="general.add"
:i18n-args="{ text: $t('quotation.title') }" :i18n-args="{ text: $t('quotation.title') }"
@ -723,8 +669,6 @@ async function filterBySellerId() {
:visible-columns="pageState.fieldSelected" :visible-columns="pageState.fieldSelected"
:grid="pageState.gridView" :grid="pageState.gridView"
:hide-edit="pageState.currentTab !== 'Issued'" :hide-edit="pageState.currentTab !== 'Issued'"
:hide-action="!canAccess('quotation', 'edit')"
:hide-delete="!canAccess('quotation', 'delete')"
:hide-btn-preview="pageState.currentTab === 'PaymentSuccess'" :hide-btn-preview="pageState.currentTab === 'PaymentSuccess'"
@preview="(id: any) => storeDataLocal(id)" @preview="(id: any) => storeDataLocal(id)"
@view=" @view="
@ -750,8 +694,7 @@ async function filterBySellerId() {
<div class="col-md-4 col-sm-6 col-12 column"> <div class="col-md-4 col-sm-6 col-12 column">
<QuotationCard <QuotationCard
class="col" class="col"
:hide-action="!canAccess('quotation', 'edit')" hide-kebab-delete
:hide-kebab-delete="!canAccess('quotation', 'delete')"
:hide-kebab-edit="!(pageState.currentTab === 'Issued')" :hide-kebab-edit="!(pageState.currentTab === 'Issued')"
:hide-preview="pageState.currentTab === 'PaymentSuccess'" :hide-preview="pageState.currentTab === 'PaymentSuccess'"
:urgent="item.row.urgent" :urgent="item.row.urgent"
@ -777,13 +720,8 @@ async function filterBySellerId() {
:worker-count="item.row._count.worker" :worker-count="item.row._count.worker"
:worker-max="item.row.workerMax || item.row._count.worker" :worker-max="item.row.workerMax || item.row._count.worker"
:customer-name=" :customer-name="
item.row.customerBranch.customer.customerType === 'CORP' item.row.customerBranch.registerName ||
? $i18n.locale === 'tha' `${item.row.customerBranch.firstName || '-'} ${item.row.customerBranch.lastName || ''}`
? item.row.customerBranch.registerName
: item.row.customerBranch.registerNameEN
: $i18n.locale === 'tha'
? `${item.row.customerBranch.firstName || '-'} ${item.row.customerBranch.lastName || ''}`
: `${item.row.customerBranch.firstNameEN || '-'} ${item.row.customerBranch.lastNameEN || ''}`
" "
:reporter=" :reporter="
$i18n.locale === 'eng' $i18n.locale === 'eng'
@ -882,7 +820,6 @@ async function filterBySellerId() {
<!-- NOTE: START - Quotation Form, Add Quotation --> <!-- NOTE: START - Quotation Form, Add Quotation -->
<DialogForm <DialogForm
:key="keyAddDialog"
:title="$t('general.add', { text: $t('quotation.title') })" :title="$t('general.add', { text: $t('quotation.title') })"
v-model:modal="pageState.addModal" v-model:modal="pageState.addModal"
:submit-label="$t('general.add', { text: $t('quotation.title') })" :submit-label="$t('general.add', { text: $t('quotation.title') })"
@ -892,14 +829,13 @@ async function filterBySellerId() {
:submit=" :submit="
() => { () => {
quotationFormState.mode = 'create'; quotationFormState.mode = 'create';
quotationFormData.customerBranchId = currentBranchRootId;
triggerQuotationDialog({ statusDialog: 'create' }); triggerQuotationDialog({ statusDialog: 'create' });
} }
" "
:close=" :close="
() => { () => {
branchId = ''; branchId = '';
currentBranchRootId = ''; quotationFormData.customerBranchId = '';
} }
" "
:beforeClose=" :beforeClose="
@ -949,7 +885,7 @@ async function filterBySellerId() {
v-model:agent-price="agentPrice" v-model:agent-price="agentPrice"
v-model:branch-id="branchId" v-model:branch-id="branchId"
v-model:special="special" v-model:special="special"
v-model:customer-branch-id="currentBranchRootId" v-model:customer-branch-id="quotationFormData.customerBranchId"
@add-customer="triggerSelectTypeCustomerd()" @add-customer="triggerSelectTypeCustomerd()"
/> />
</div> </div>
@ -1018,7 +954,6 @@ async function filterBySellerId() {
() => { () => {
customerFormState.dialogModal = false; customerFormState.dialogModal = false;
onCreateImageList = { selectedImage: '', list: [] }; onCreateImageList = { selectedImage: '', list: [] };
keyAddDialog++;
} }
" "
> >
@ -1073,16 +1008,7 @@ async function filterBySellerId() {
" "
:title=" :title="
customerFormData.customerType === 'PERS' customerFormData.customerType === 'PERS'
? setPrefixName( ? `${customerFormData.customerBranch[0]?.firstName} ${customerFormData.customerBranch[0]?.lastName}`
{
namePrefix: customerFormData.customerBranch[0]?.namePrefix,
firstName: customerFormData.customerBranch[0]?.firstName,
lastName: customerFormData.customerBranch[0]?.lastName,
firstNameEN: customerFormData.customerBranch[0]?.firstNameEN,
lastNameEN: customerFormData.customerBranch[0]?.lastNameEN,
},
{ locale },
)
: customerFormData.customerBranch[0]?.registerName : customerFormData.customerBranch[0]?.registerName
" "
:caption=" :caption="
@ -1191,8 +1117,8 @@ async function filterBySellerId() {
id="form-basic-info-customer" id="form-basic-info-customer"
:onCreate="customerFormState.dialogType === 'create'" :onCreate="customerFormState.dialogType === 'create'"
@edit=" @edit="
((customerFormState.dialogType = 'edit'), (customerFormState.dialogType = 'edit'),
(customerFormState.readonly = false)) (customerFormState.readonly = false)
" "
@cancel="() => customerFormUndo(false)" @cancel="() => customerFormUndo(false)"
@delete=" @delete="
@ -1210,7 +1136,7 @@ async function filterBySellerId() {
customerFormData.customerBranch[0].legalPersonNo customerFormData.customerBranch[0].legalPersonNo
" "
v-model:business-type=" v-model:business-type="
customerFormData.customerBranch[0].businessTypeId customerFormData.customerBranch[0].businessType
" "
v-model:job-position=" v-model:job-position="
customerFormData.customerBranch[0].jobPosition customerFormData.customerBranch[0].jobPosition
@ -1291,6 +1217,7 @@ async function filterBySellerId() {
res = await customerStore.createBranch({ res = await customerStore.createBranch({
...customerFormData.customerBranch[idx], ...customerFormData.customerBranch[idx],
customerId: customerFormState.editCustomerId || '', customerId: customerFormState.editCustomerId || '',
id: undefined,
}); });
} }
if (res) { if (res) {

Some files were not shown because too many files have changed in this diff Show more