Merge branch 'develop'
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 6s

This commit is contained in:
Methapon2001 2025-04-08 16:00:10 +07:00
commit bc5097a0a8
29 changed files with 2748 additions and 1199 deletions

View file

@ -1,5 +1,28 @@
{ {
"eng": { "eng": {
"visaType": [
{
"label": "Non-LA",
"value": "nla"
},
{
"label": "Non-B",
"value": "nb"
},
{
"label": "TV.60",
"value": "tv60"
},
{
"label": "Non-TR",
"value": "ntr"
},
{
"label": "TV.30",
"value": "tv30"
}
],
"workerStatus": [ "workerStatus": [
{ {
"label": "Normal", "label": "Normal",
@ -154,7 +177,8 @@
{ "label": "VS2", "value": "VS2" }, { "label": "VS2", "value": "VS2" },
{ "label": "WO", "value": "WO" }, { "label": "WO", "value": "WO" },
{ "label": "WP390", "value": "WP390" }, { "label": "WP390", "value": "WP390" },
{ "label": "WP44", "value": "WP44" } { "label": "WP44", "value": "WP44" },
{ "label": "CUST", "value": "CUST" }
], ],
"prefix": [ "prefix": [
@ -183,29 +207,44 @@
} }
], ],
"training": [ "border": [
{ {
"label": "Myanmar Labor Training Center - Mae Sot, Tak Province", "label": "Mae Sot, Tak Province",
"value": "trainingTak" "value": "trainingTak"
}, },
{ {
"label": "Myanmar Labor Training Center - Kawthoung, Ranong Province", "label": "Koh Song, Ranong province",
"value": "trainingRanong" "value": "trainingRanong"
}, },
{ {
"label": "Laos Labor Training Center - Nong Khai, Nong Khai Province", "label": "Nong Khai, Nong Khai Province",
"value": "trainingNongKhai" "value": "trainingNongKhai"
}, },
{ {
"label": "Cambodian Labor Training Center - Aranyaprathet, Sa Kaeo Province", "label": "Aranyaprathet, Sa Kaeo Province",
"value": "trainingSaKaeo" "value": "trainingSaKaeo"
}, },
{ {
"label": "Cambodian Labor Training Center - Ban Laem, Chanthaburi Province", "label": "Ban Laem, Chanthaburi Province",
"value": "trainingChanthaburi" "value": "trainingChanthaburi"
} }
], ],
"training": [
{
"label": "The first center accepts work. and end of employment Tak Province",
"value": "trainingTak"
},
{
"label": "The first center accepts work. and end of employment Nong Khai Province",
"value": "trainingNongKhai"
},
{
"label": "The first center accepts work. and end of employment Sa Kaeo Province",
"value": "trainingSaKaeo"
}
],
"nationality": [ "nationality": [
{ {
"label": "Thai", "label": "Thai",
@ -1050,6 +1089,29 @@
}, },
"tha": { "tha": {
"visaType": [
{
"label": "Non-LA",
"value": "nla"
},
{
"label": "Non-B",
"value": "nb"
},
{
"label": "ผผ.60",
"value": "tv60"
},
{
"label": "Non-TR",
"value": "ntr"
},
{
"label": "ผผ.30",
"value": "tv30"
}
],
"workerStatus": [ "workerStatus": [
{ {
"label": "ปกติ", "label": "ปกติ",
@ -1204,7 +1266,8 @@
{ "label": "VS2", "value": "VS2" }, { "label": "VS2", "value": "VS2" },
{ "label": "WO", "value": "WO" }, { "label": "WO", "value": "WO" },
{ "label": "WP390", "value": "WP390" }, { "label": "WP390", "value": "WP390" },
{ "label": "WP44", "value": "WP44" } { "label": "WP44", "value": "WP44" },
{ "label": "CUST", "value": "CUST" }
], ],
"prefix": [ "prefix": [
@ -1233,29 +1296,44 @@
} }
], ],
"training": [ "border": [
{ {
"label": "สถานที่อบรมแรงงานเมียนมา-แม่สอด จ.ตาก", "label": "แม่สอด จ.ตาก",
"value": "trainingTak" "value": "trainingTak"
}, },
{ {
"label": "สถานที่อบรมแรงงานเมียนมา-เกาะสอง จ.ระนอง", "label": "เกาะสอง จ.ระนอง",
"value": "trainingRanong" "value": "trainingRanong"
}, },
{ {
"label": "สถานที่อบรมแรงงานลาว-หนองคาย จ.หนองคาย", "label": "หนองคาย จ.หนองคาย",
"value": "trainingNongKhai" "value": "trainingNongKhai"
}, },
{ {
"label": "สถานที่อบรมแรงงานกัมพูชา-อรัญประเทศ จ.สระแก้ว", "label": "อรัญประเทศ จ.สระแก้ว",
"value": "trainingSaKaeo" "value": "trainingSaKaeo"
}, },
{ {
"label": "สถานที่อบรมแรงงานกัมพูชา-บ้านแหลม จ.จันทบุรี", "label": "บ้านแหลม จ.จันทบุรี",
"value": "trainingChanthaburi" "value": "trainingChanthaburi"
} }
], ],
"training": [
{
"label": "ศูนย์แรกรับเข้าทำงาน และสิ้นสุดการจ้าง จังหวัดตาก",
"value": "trainingTak"
},
{
"label": "ศูนย์แรกรับเข้าทำงาน และสิ้นสุดการจ้าง จังหวัดหนองคาย",
"value": "trainingNongKhai"
},
{
"label": "ศูนย์แรกรับเข้าทำงาน และสิ้นสุดการจ้าง จังหวัดสระแก้ว",
"value": "trainingSaKaeo"
}
],
"nationality": [ "nationality": [
{ {
"label": "ไทย", "label": "ไทย",

View file

@ -2,6 +2,7 @@ import { defineBoot } from '#q-app/wrappers';
import { createI18n } from 'vue-i18n'; import { createI18n } from 'vue-i18n';
import messages from 'src/i18n'; import messages from 'src/i18n';
import { Lang } from 'src/utils/ui';
export type MessageLanguages = keyof typeof messages; export type MessageLanguages = keyof typeof messages;
// Type-define 'eng' as the master schema for the resource // Type-define 'eng' as the master schema for the resource
@ -26,7 +27,7 @@ export const i18n = createI18n<
MessageLanguages, MessageLanguages,
false false
>({ >({
locale: 'eng', locale: 'tha',
legacy: false, legacy: false,
messages, messages,
}); });

View file

@ -36,6 +36,7 @@ defineProps<{
outlined?: boolean; outlined?: boolean;
readonly?: boolean; readonly?: boolean;
view?: boolean; view?: boolean;
single?: boolean;
}>(); }>();
defineEmits<{ defineEmits<{
@ -121,7 +122,7 @@ watch(
/> />
{{ $t(`${title}`) }} {{ $t(`${title}`) }}
<AddButton <AddButton
v-if="!readonly" v-if="!readonly && !single"
id="btn-add-bank" id="btn-add-bank"
icon-only icon-only
class="q-ml-sm" class="q-ml-sm"
@ -141,7 +142,10 @@ watch(
style="padding-block: 0.01px" style="padding-block: 0.01px"
spaced="lg" spaced="lg"
/> />
<span class="col-12 app-text-muted-2 flex justify-between items-center"> <span
v-if="!single"
class="col-12 app-text-muted-2 flex justify-between items-center"
>
{{ `${$t('branch.form.bankAccountNo')} ${i + 1}` }} {{ `${$t('branch.form.bankAccountNo')} ${i + 1}` }}
<div class="row items-center"> <div class="row items-center">
<div style="height: 30.8px" /> <div style="height: 30.8px" />
@ -172,6 +176,7 @@ watch(
</span> </span>
<div <div
v-if="!single"
class="bordered q-mr-sm rounded col 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 }"
> >

View file

@ -1,13 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import useUserStore from 'stores/user'; import useUserStore from 'stores/user';
import useOptionStore from 'stores/options'; import useOptionStore from 'stores/options';
import { UserAttachmentDelete } from 'stores/user/types'; import { UserAttachmentDelete, AgencyStatus } from 'stores/user/types';
import { dialog, selectFilterOptionRefMod } from 'stores/utils'; import { dialog } from 'stores/utils';
import { onMounted, ref, watch } from 'vue'; import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { Icon } from '@iconify/vue'; import { Icon } from '@iconify/vue';
import { QSelect } from 'quasar';
import DatePicker from '../shared/DatePicker.vue'; import DatePicker from '../shared/DatePicker.vue';
import SelectInput from 'src/components/shared/SelectInput.vue';
import SelectOffice from 'components/shared/select-muliple/SelectOffice.vue'; import SelectOffice from 'components/shared/select-muliple/SelectOffice.vue';
@ -33,13 +33,15 @@ 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 checkpointEN = defineModel<string | null | undefined>('checkPointEn');
const agencyFile = defineModel<File[]>('agencyFile'); const agencyFile = defineModel<File[]>('agencyFile');
const agencyFileList = const agencyFileList =
defineModel<{ name: string; url: string }[]>('agencyFileList'); defineModel<{ name: string; url: string }[]>('agencyFileList');
const remark = defineModel<string | null | undefined>('remark');
const agencyStatus = defineModel<string | null | undefined>('agencyStatus');
const attachmentRef = ref(); const attachmentRef = ref();
const checkpointENOption = ref([]);
defineProps<{ defineProps<{
dense?: boolean; dense?: boolean;
@ -76,59 +78,11 @@ function deleteFile(name: string) {
}); });
} }
const nationalityOptions = ref<Record<string, unknown>[]>([]); onMounted(async () => {
let nationalityFilter: ( const resultOption = await fetch('/option/option.json');
value: string, const rawOption = await resultOption.json();
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void, checkpointENOption.value = rawOption.eng.border;
) => void;
const trainingPlaceOptions = ref<Record<string, unknown>[]>([]);
let trainingPlaceFilter: (
value: string,
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
) => void;
const responsibleAreaOptions = ref<Record<string, unknown>[]>([]);
let responsibleAreaFilter: (
value: string,
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void,
) => void;
onMounted(() => {
if (optionStore.globalOption?.nationality) {
nationalityFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption.nationality),
nationalityOptions,
'label',
);
trainingPlaceFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption.training),
trainingPlaceOptions,
'label',
);
responsibleAreaFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption.area),
responsibleAreaOptions,
'label',
);
}
}); });
watch(
() => optionStore.globalOption,
() => {
nationalityFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption.nationality),
nationalityOptions,
'label',
);
trainingPlaceFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption.training),
trainingPlaceOptions,
'label',
);
},
);
</script> </script>
<template> <template>
<div class="row col-12"> <div class="row col-12">
@ -218,136 +172,108 @@ watch(
class="row col-12 q-col-gutter-sm" class="row col-12 q-col-gutter-sm"
style="margin-left: 0px; padding-left: 0px" style="margin-left: 0px; padding-left: 0px"
> >
<q-select <SelectInput
outlined :model-value="readonly ? sourceNationality || '-' : sourceNationality"
clearable
use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space
input-debounce="0"
option-value="value"
option-label="label"
class="col-md-3 col-6"
id="input-source-nationality" id="input-source-nationality"
for="input-source-nationality" for="input-source-nationality"
:dense="dense" :option="optionStore.globalOption.nationality"
:readonly="readonly" class="col-md-3 col-6"
:hide-dropdown-icon="readonly" :readonly
clearable
:label="$t('personnel.form.sourceNationality')" :label="$t('personnel.form.sourceNationality')"
:options="nationalityOptions"
@filter="nationalityFilter"
:model-value="readonly ? sourceNationality || '-' : sourceNationality"
@update:model-value=" @update:model-value="
(v) => (typeof v === 'string' ? (sourceNationality = v) : '') (v) => (typeof v === 'string' ? (sourceNationality = v) : '')
" "
@clear="sourceNationality = ''" />
> <SelectInput
<template v-slot:no-option> :model-value="readonly ? importNationality || '-' : importNationality"
<q-item>
<q-item-section class="text-grey">
{{ $t('general.noData') }}
</q-item-section>
</q-item>
</template>
</q-select>
<q-select
outlined
clearable
use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space
input-debounce="0"
option-value="value"
option-label="label"
class="col-md-3 col-6"
id="input-import-nationality" id="input-import-nationality"
for="input-import-nationality" for="input-import-nationality"
:dense="dense" :option="optionStore.globalOption.nationality"
:readonly="readonly" class="col-md-3 col-6"
:hide-dropdown-icon="readonly" :readonly
clearable
:label="$t('personnel.form.importNationality')" :label="$t('personnel.form.importNationality')"
:options="nationalityOptions"
@filter="nationalityFilter"
:model-value="readonly ? importNationality || '-' : importNationality"
@update:model-value=" @update:model-value="
(v) => (typeof v === 'string' ? (importNationality = v) : '') (v) => (typeof v === 'string' ? (importNationality = v) : '')
" "
@clear="importNationality = ''" />
> <SelectInput
<template v-slot:no-option> :model-value="readonly ? trainingPlace || '-' : trainingPlace"
<q-item>
<q-item-section class="text-grey">
{{ $t('general.noData') }}
</q-item-section>
</q-item>
</template>
</q-select>
<q-select
outlined
clearable
use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space
input-debounce="0"
option-label="label"
option-value="label"
class="col-md-6 col-12"
id="select-trainig-place" id="select-trainig-place"
for="select-trainig-place" for="select-trainig-place"
:dense="dense" :option="optionStore.globalOption.training"
:readonly="readonly" class="col-md-6 col-12"
:hide-dropdown-icon="readonly" :readonly
:label="$t('personnel.form.trainingPlace')" :label="$t('personnel.form.trainingPlace')"
:options="trainingPlaceOptions" clearable
@filter="trainingPlaceFilter"
:model-value="readonly ? trainingPlace || '-' : trainingPlace"
@update:model-value=" @update:model-value="
(v) => (typeof v === 'string' ? (trainingPlace = v) : '') (v) => (typeof v === 'string' ? (trainingPlace = v) : '')
" "
@clear="trainingPlace = ''" />
> <SelectInput
<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
for="input-checkpoint"
:dense="dense"
outlined
:readonly="readonly"
:label="$t('personnel.form.checkpoint')"
class="col-6"
:model-value="readonly ? checkpoint || '-' : checkpoint" :model-value="readonly ? checkpoint || '-' : checkpoint"
id="select-checkpoint"
for="select-checkpoint"
:option="optionStore.globalOption.border"
class="col-6"
:readonly
:label="$t('personnel.form.checkpoint')"
clearable
@update:model-value=" @update:model-value="
(v) => (typeof v === 'string' ? (checkpoint = v) : '') (v) => (typeof v === 'string' ? (checkpoint = v) : '')
" "
@clear="checkpoint = ''" />
<SelectInput
:model-value="readonly ? checkpoint || '-' : checkpoint"
id="select-checkpoint-en"
for="select-checkpoint-en"
:option="checkpointENOption"
class="col-6"
:readonly
:label="$t('personnel.form.checkpointEN')"
clearable
@update:model-value="
(v) => (typeof v === 'string' ? (checkpoint = v) : '')
"
/>
<SelectInput
:model-value="readonly ? agencyStatus || '-' : agencyStatus"
id="select-checkpoint-en"
for="select-checkpoint-en"
:option="[
{ label: $t('personnel.form.normal'), value: AgencyStatus.Normal },
{
label: $t('personnel.form.canceled'),
value: AgencyStatus.Canceled,
},
{
label: $t('personnel.form.blacklist'),
value: AgencyStatus.Blacklist,
},
]"
class="col-md-6 col-12"
:readonly
:label="$t('personnel.form.agencyStatus')"
clearable
@update:model-value="
(v) => (typeof v === 'string' ? (agencyStatus = v) : '')
"
/> />
<q-input <q-input
for="input-checkpoint-en" for="input-discount-condition"
:dense="dense" :dense="dense"
outlined outlined
:readonly="readonly" :readonly
:label="$t('personnel.form.checkpointEN')" :label="$t('general.remark')"
class="col-6" class="col-12"
:model-value="readonly ? checkpointEN || '-' : checkpointEN" type="textarea"
:model-value="readonly ? remark || '-' : remark"
@update:model-value=" @update:model-value="
(v) => (typeof v === 'string' ? (checkpointEN = v) : '') (v) => (typeof v === 'string' ? (remark = v) : '')
" "
@clear="checkpointEN = ''" @clear="remark = ''"
/> />
<q-file <q-file
ref="attachmentRef" ref="attachmentRef"
@ -358,7 +284,7 @@ watch(
multiple multiple
append append
:label="$t('personnel.form.attachment')" :label="$t('personnel.form.attachment')"
class="col-12" class="col"
v-model="agencyFile" v-model="agencyFile"
> >
<template v-slot:prepend> <template v-slot:prepend>

View file

@ -41,6 +41,12 @@ let prefixNameFilter: (
update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => void, update: (callbackFn: () => void, afterFn?: (ref: QSelect) => void) => 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>[]>([]); const genderOptions = ref<Record<string, unknown>[]>([]);
let genderFilter: ( let genderFilter: (
value: string, value: string,
@ -55,7 +61,9 @@ let nationalityFilter: (
function matPreFixName() { function matPreFixName() {
if (gender.value === 'male') prefixName.value = 'mr'; if (gender.value === 'male') prefixName.value = 'mr';
if (gender.value === 'female') prefixName.value = 'mrs'; if (gender.value === 'female' && prefixName.value === 'mr') {
prefixName.value = 'mrs';
}
} }
onMounted(() => { onMounted(() => {
@ -64,6 +72,13 @@ onMounted(() => {
prefixNameOptions, prefixNameOptions,
'label', 'label',
); );
prefixNameFilterEn = selectFilterOptionRefMod(
ref(optionStore.rawOption?.eng.prefix),
prefixNameOptionsEn,
'label',
);
genderFilter = selectFilterOptionRefMod( genderFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.gender), ref(optionStore.globalOption?.gender),
genderOptions, genderOptions,
@ -94,6 +109,12 @@ watch(
nationalityOptions, nationalityOptions,
'label', 'label',
); );
prefixNameFilterEn = selectFilterOptionRefMod(
ref(optionStore.rawOption?.eng.prefix),
prefixNameOptionsEn,
'label',
);
}, },
); );
@ -175,6 +196,7 @@ 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>
@ -194,7 +216,11 @@ watch(
class="col" class="col"
:label="$t('personnel.form.firstName')" :label="$t('personnel.form.firstName')"
v-model="firstName" v-model="firstName"
:rules="[(val: string) => !!val || $t('form.error.required')]" :rules="
employee
? []
: [(val: string) => !!val || $t('form.error.required')]
"
/> />
<q-input <q-input
@ -229,25 +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)"
> >
<q-input <q-select
:for="`${prefixId}-input-first-name`"
:dense="dense"
outlined outlined
use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space hide-bottom-space
:readonly="readonly" input-debounce="0"
:disable="!readonly" option-label="label"
option-value="value"
hide-dropdown-icon
autocomplete="off"
class="col-md-1 col-6" class="col-md-1 col-6"
label="Title" :dense="dense"
:model-value=" :readonly="readonly"
readonly :options="prefixNameOptionsEn"
? capitalize(prefixName || '') || '-' :for="`${prefixId}-select-prefix-name-en`"
: capitalize(prefixName || '') label="Prefix"
" @filter="prefixNameFilter"
:model-value="readonly ? prefixName || '-' : prefixName"
@update:model-value=" @update:model-value="
(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>
<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`"
@ -287,10 +329,22 @@ watch(
class="col" class="col"
label="Surname" label="Surname"
v-model="lastNameEN" v-model="lastNameEN"
:rules="[ :rules="
(val: string) => employee
!val || /^[A-Za-z\s]+$/.test(val) || $t('form.error.letterOnly'), ? [
]" (val: string) => !!val || $t('form.error.required'),
(val: string) =>
!val ||
/^[A-Za-z\s]+$/.test(val) ||
$t('form.error.letterOnly'),
]
: [
(val: string) =>
!val ||
/^[A-Za-z\s]+$/.test(val) ||
$t('form.error.letterOnly'),
]
"
/> />
</div> </div>
@ -392,11 +446,15 @@ watch(
:readonly="readonly" :readonly="readonly"
:label="$t('form.birthDate')" :label="$t('form.birthDate')"
:disabled-dates="disabledAfterToday" :disabled-dates="disabledAfterToday"
:rules="[ :rules="
(val: string) => employee
!!val || ? []
$t('form.error.selectField', { field: $t('form.birthDate') }), : [
]" (val: string) =>
!!val ||
$t('form.error.selectField', { field: $t('form.birthDate') }),
]
"
/> />
<q-input <q-input
@ -478,7 +536,6 @@ watch(
:hide-dropdown-icon="readonly" :hide-dropdown-icon="readonly"
:for="`${prefixId}-select-gender`" :for="`${prefixId}-select-gender`"
:label="$t('form.gender')" :label="$t('form.gender')"
:rules="[(val: string) => !!val || $t('form.error.required')]"
@filter="genderFilter" @filter="genderFilter"
> >
<template v-slot:no-option> <template v-slot:no-option>
@ -511,7 +568,6 @@ watch(
:hide-dropdown-icon="readonly" :hide-dropdown-icon="readonly"
:for="`${prefixId}-select-nationality`" :for="`${prefixId}-select-nationality`"
:label="$t('general.nationality')" :label="$t('general.nationality')"
:rules="[(val: string) => !!val || $t('form.error.required')]"
@filter="nationalityFilter" @filter="nationalityFilter"
> >
<template v-slot:no-option> <template v-slot:no-option>

View file

@ -106,7 +106,7 @@ const employeeOther = defineModel<EmployeeOtherCreate>('employeeOther');
:readonly="readonly || employeeOther.statusSave" :readonly="readonly || employeeOther.statusSave"
hide-bottom-space hide-bottom-space
class="col-md-3 col-6" class="col-md-3 col-6"
:label="$t('form.firstName')" :label="$t('general.nativeLanguage', { msg: $t('form.firstName') })"
:model-value="employeeOther.fatherFirstName" :model-value="employeeOther.fatherFirstName"
@update:model-value=" @update:model-value="
(v) => (v) =>
@ -122,7 +122,7 @@ const employeeOther = defineModel<EmployeeOtherCreate>('employeeOther');
:readonly="readonly || employeeOther.statusSave" :readonly="readonly || employeeOther.statusSave"
hide-bottom-space hide-bottom-space
class="col-md-3 col-6" class="col-md-3 col-6"
:label="$t('form.lastName')" :label="$t('general.nativeLanguage', { msg: $t('form.lastName') })"
:model-value="employeeOther.fatherLastName" :model-value="employeeOther.fatherLastName"
@update:model-value=" @update:model-value="
(v) => (v) =>
@ -177,7 +177,7 @@ const employeeOther = defineModel<EmployeeOtherCreate>('employeeOther');
:readonly="readonly || employeeOther.statusSave" :readonly="readonly || employeeOther.statusSave"
hide-bottom-space hide-bottom-space
class="col-md-3 col-6" class="col-md-3 col-6"
:label="$t('form.firstName')" :label="$t('general.nativeLanguage', { msg: $t('form.firstName') })"
:model-value="employeeOther.motherFirstName" :model-value="employeeOther.motherFirstName"
@update:model-value=" @update:model-value="
(v) => (v) =>
@ -193,7 +193,7 @@ const employeeOther = defineModel<EmployeeOtherCreate>('employeeOther');
:readonly="readonly || employeeOther.statusSave" :readonly="readonly || employeeOther.statusSave"
hide-bottom-space hide-bottom-space
class="col-md-3 col-6" class="col-md-3 col-6"
:label="$t('form.lastName')" :label="$t('general.nativeLanguage', { msg: $t('form.lastName') })"
:model-value="employeeOther.motherLastName" :model-value="employeeOther.motherLastName"
@update:model-value=" @update:model-value="
(v) => (v) =>

View file

@ -103,7 +103,7 @@ onMounted(() => {
'label', 'label',
); );
passportIssuingCountryFilter = selectFilterOptionRefMod( passportIssuingCountryFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption.nationality), ref(optionStore.rawOption?.eng.nationality),
passportIssuingCountryOptions, passportIssuingCountryOptions,
'label', 'label',
); );
@ -121,13 +121,13 @@ onMounted(() => {
); );
genderFilter = selectFilterOptionRefMod( genderFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.gender), ref(optionStore.rawOption?.eng.gender),
genderOptions, genderOptions,
'label', 'label',
); );
nationalityFilter = selectFilterOptionRefMod( nationalityFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.nationality), ref(optionStore.rawOption?.eng.nationality),
nationalityOptions, nationalityOptions,
'label', 'label',
); );
@ -152,7 +152,7 @@ watch(
); );
passportIssuingCountryFilter = selectFilterOptionRefMod( passportIssuingCountryFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption.nationality), ref(optionStore.rawOption?.eng.nationality),
passportIssuingCountryOptions, passportIssuingCountryOptions,
'label', 'label',
); );
@ -164,13 +164,13 @@ watch(
); );
genderFilter = selectFilterOptionRefMod( genderFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.gender), ref(optionStore.rawOption?.eng.gender),
genderOptions, genderOptions,
'label', 'label',
); );
nationalityFilter = selectFilterOptionRefMod( nationalityFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.nationality), ref(optionStore.rawOption?.eng.nationality),
nationalityOptions, nationalityOptions,
'label', 'label',
); );

View file

@ -92,7 +92,7 @@ let workerTypeFilter: (
onMounted(() => { onMounted(() => {
visaTypeFilter = selectFilterOptionRefMod( visaTypeFilter = selectFilterOptionRefMod(
ref(optionStore.globalOption?.nationality), ref(optionStore.globalOption?.visaType),
visaTypeOptions, visaTypeOptions,
'label', 'label',
); );

View file

@ -3,6 +3,7 @@ import { QSelect } from 'quasar';
import { CustomerBranch } from 'stores/customer/types'; import { CustomerBranch } from 'stores/customer/types';
import { selectFilterOptionRefMod } from 'stores/utils'; import { selectFilterOptionRefMod } from 'stores/utils';
import { onMounted, ref, watch } from 'vue'; import { onMounted, ref, watch } from 'vue';
import SelectCustomer from 'components/shared/select/SelectCustomer.vue';
import { import {
EditButton, EditButton,
DeleteButton, DeleteButton,
@ -11,6 +12,7 @@ import {
} from 'components/button'; } from 'components/button';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import useOptionStore from 'stores/options'; import useOptionStore from 'stores/options';
import { formatAddress } from 'src/utils/address';
const { locale } = useI18n(); const { locale } = useI18n();
@ -21,6 +23,13 @@ const optionsBranch = defineModel<{ id: string; name: string }[]>(
); );
// employee // employee
const customerBranchId = defineModel<string>('customerBranchId');
const currentCustomerBranch = defineModel<CustomerBranch>(
'currentCustomerBranch',
);
const customerBranch = defineModel<{ const customerBranch = defineModel<{
id: string; id: string;
address: string; address: string;
@ -107,180 +116,14 @@ defineEmits<{
</div> </div>
<div class="col-12 row" style="gap: var(--size-2)"> <div class="col-12 row" style="gap: var(--size-2)">
<q-select <SelectCustomer
:id="`${prefixId}-select-employer-branch`" v-model:value="customerBranchId"
:for="`${prefixId}-select-employer-branch`" v-model:value-option="currentCustomerBranch"
:use-input="!customerBranch"
autocomplete="off"
input-debounce="0"
:hide-dropdown-icon="readonly"
:dense="dense"
outlined
:readonly="readonly"
hide-bottom-space
class="col-12"
:label="$t('customer.form.branchCode')" :label="$t('customer.form.branchCode')"
v-model="customerBranch" class="col-12 field-two"
:option-value=" required
(v) => ({ :readonly
id: v.id, />
address: v.address,
addressEN: v.addressEN,
provinceId: v.provinceId,
districtId: v.districtId,
subDistrictId: v.subDistrictId,
zipCode: v.zipCode,
})
"
emit-value
map-options
:options="employeeOwnerOption"
@filter="(val, update) => $emit('filterOwnerBranch', val, update)"
:rules="[
(val: string) =>
!!val ||
$t('form.error.selectField', {
field: $t('customerEmployee.branch'),
}),
]"
>
<template v-slot:option="scope">
<q-item
v-if="scope.opt"
v-bind="scope.itemProps"
class="row items-start col-12 no-padding"
>
<div class="q-ma-sm">
<i class="isax isax-frame5" style="color: var(--brand-1)" />
</div>
<div class="q-mt-sm">
<div>
<span v-if="scope.opt.customer.customerType">
<span style="font-weight: 600">
{{
scope.opt.customer.customerType === 'CORP'
? $t('customer.form.registerName')
: $t('customer.form.ownerName')
}}:
</span>
{{
scope.opt.customer.customerType === 'CORP'
? $i18n.locale === 'eng'
? scope.opt.registerNameEN
: scope.opt.registerName
: $i18n.locale === 'eng'
? `${optionStore.mapOption(scope.opt.namePrefix)} ${scope.opt.firstNameEN} ${scope.opt.lastNameEN}` ||
'-'
: `${optionStore.mapOption(scope.opt.namePrefix)} ${scope.opt.firstName} ${scope.opt.lastName}` ||
'-'
}}
({{ scope.opt.code }})
</span>
</div>
<div class="text-caption app-text-muted-2 q-mb-xs">
<span v-if="scope.opt.customer" class="col column">
{{
$t(
`branch.form.title.${scope.opt.code.endsWith('-00') ? 'branchHQLabel' : 'branchLabel'}`,
)
}}
{{
!scope.opt.code.endsWith('-00')
? +scope.opt.code.split('-')[1]
: ''
}}
</span>
<span v-if="scope.opt.province" class="col">
{{ $t('general.address') }}
{{
$i18n.locale === 'eng'
? `${scope.opt.addressEN || ''}, ${scope.opt.mooEN && `${$t('form.moo')} ${scope.opt.mooEN},`} ${scope.opt.soiEN && `${$t('form.soi')} ${scope.opt.soiEN},`} ${scope.opt.streetEN && `${scope.opt.streetEN} Rd,`} ${scope.opt.subDistrict.nameEN || ''}, ${scope.opt.district.nameEN || ''}, ${scope.opt.province.nameEN || ''}`
: `${scope.opt.address || ''}, ${scope.opt.moo && `${$t('form.moo')} ${scope.opt.moo},`} ${scope.opt.soi && `${$t('form.soi')} ${scope.opt.soi},`} ${scope.opt.street && `${$t('form.road')} ${scope.opt.street},`} ${scope.opt.subDistrict.name || ''}, ${scope.opt.district.name || ''}, ${scope.opt.province.name || ''}`
}}
{{ scope.opt.subDistrict?.zipCode || '' }}
</span>
</div>
</div>
</q-item>
<q-separator class="q-mx-sm" />
</template>
<template v-slot:selected-item="scope">
<div
v-if="scope.opt"
class="row items-center no-wrap"
style="width: 1px"
>
<div class="q-mr-sm">
<span style="font-weight: 600">
{{
scope.opt.customer.customerType === 'CORP'
? $t('customer.form.registerName')
: $t('customer.form.ownerName')
}}:
</span>
{{
scope.opt.customer.customerType === 'CORP'
? $i18n.locale === 'eng'
? scope.opt.registerNameEN
: scope.opt.registerName
: $i18n.locale === 'eng'
? `${optionStore.mapOption(scope.opt.namePrefix)} ${scope.opt.firstNameEN} ${scope.opt.lastNameEN}` ||
'-'
: `${optionStore.mapOption(scope.opt.namePrefix)} ${scope.opt.firstName} ${scope.opt.lastName}` ||
'-'
}}
({{ scope.opt.code }})
</div>
<div
class="text-caption app-text-muted-2"
v-if="scope.opt.customer && scope.opt.province"
>
{{
$t(
`branch.form.title.${scope.opt.code.endsWith('-00') ? 'branchHQLabel' : 'branchLabel'}`,
)
}}
{{
!scope.opt.code.endsWith('-00')
? +scope.opt.code.split('-')[1]
: ''
}}
{{ $t('general.address') }}
{{
$i18n.locale === 'eng'
? `${scope.opt.addressEN || ''}, ${scope.opt.mooEN && `${$t('form.moo')} ${scope.opt.mooEN},`} ${scope.opt.soiEN && `${$t('form.soi')} ${scope.opt.soiEN},`} ${scope.opt.streetEN && `${scope.opt.streetEN} Rd,`} ${scope.opt.subDistrict.nameEN || ''}, ${scope.opt.district.nameEN || ''}, ${scope.opt.province.nameEN || ''}`
: `${scope.opt.address || ''}, ${scope.opt.moo && `${$t('form.moo')} ${scope.opt.moo},`} ${scope.opt.soi && `${$t('form.soi')} ${scope.opt.soi},`} ${scope.opt.street && `${$t('form.road')} ${scope.opt.street},`} ${scope.opt.subDistrict.name || ''}, ${scope.opt.district.name || ''}, ${scope.opt.province.name || ''}`
}}
{{ scope.opt.subDistrict?.zipCode || '' }}
<q-tooltip v-if="scope.opt.customer && scope.opt.province">
{{ $t('customerBranch.form.title') }}:
{{ $t('general.address') }}
{{
$i18n.locale === 'eng'
? `${scope.opt.addressEN || ''}, ${scope.opt.mooEN && `${$t('form.moo')} ${scope.opt.mooEN},`} ${scope.opt.soiEN && `${$t('form.soi')} ${scope.opt.soiEN},`} ${scope.opt.streetEN && `${scope.opt.streetEN} Rd,`} ${scope.opt.subDistrict.nameEN || ''}, ${scope.opt.district.nameEN || ''}, ${scope.opt.province.nameEN || ''}`
: `${scope.opt.address || ''}, ${scope.opt.moo && `${$t('form.moo')} ${scope.opt.moo},`} ${scope.opt.soi && `${$t('form.soi')} ${scope.opt.soi},`} ${scope.opt.street && `${$t('form.road')} ${scope.opt.street},`} ${scope.opt.subDistrict.name || ''}, ${scope.opt.district.name || ''}, ${scope.opt.province.name || ''}`
}}
{{ scope.opt.subDistrict?.zipCode || '' }}
</q-tooltip>
</div>
</div>
</template>
<template v-slot:append>
<q-icon
v-if="!readonly && customerBranch"
name="mdi-close-circle"
@click.stop="customerBranch = undefined"
class="cursor-pointer clear-btn"
/>
</template>
</q-select>
<q-input <q-input
:for="`${prefixId}-input-code`" :for="`${prefixId}-input-code`"

View file

@ -14,6 +14,9 @@ defineProps<{
const group = defineModel('group', { default: '' }); const group = defineModel('group', { default: '' });
const name = defineModel('name', { default: '' }); const name = defineModel('name', { default: '' });
const nameEn = defineModel('nameEn', { default: '' }); const nameEn = defineModel('nameEn', { default: '' });
const contactName = defineModel('contactName', { default: '' });
const email = defineModel('email', { default: '' });
const contactTel = defineModel('contactTel', { default: '' });
type Options = { label: string; value: string }; type Options = { label: string; value: string };
</script> </script>
@ -35,7 +38,7 @@ type Options = { label: string; value: string };
<div class="col-12 row q-col-gutter-sm"> <div class="col-12 row q-col-gutter-sm">
<SelectInput <SelectInput
:class="{ col: $q.screen.lt.md }" class="col"
:disable="!readonly && onDrawer" :disable="!readonly && onDrawer"
:readonly="readonly" :readonly="readonly"
for="input-agencies-code" for="input-agencies-code"
@ -62,7 +65,7 @@ type Options = { label: string; value: string };
outlined outlined
:readonly="readonly" :readonly="readonly"
hide-bottom-space hide-bottom-space
class="col-md col-12" class="col-md-4 col-12"
:label="$t('agencies.name')" :label="$t('agencies.name')"
v-model="name" v-model="name"
:rules="[(val: string) => !!val || $t('form.error.required')]" :rules="[(val: string) => !!val || $t('form.error.required')]"
@ -73,10 +76,78 @@ type Options = { label: string; value: string };
outlined outlined
:readonly="readonly" :readonly="readonly"
hide-bottom-space hide-bottom-space
class="col-md col-12" class="col-md-4 col-12"
:label="'Agencies Name'" :label="'Agencies Name'"
v-model="nameEn" v-model="nameEn"
/> />
<q-input
for="input-agencies-contact-name"
dense
outlined
:readonly="readonly"
hide-bottom-space
class="col-md-4 col-12"
:label="$t('agencies.contactName')"
:model-value="readonly ? contactName || '-' : contactName"
@update:model-value="
(v) => (typeof v === 'string' ? (contactName = v) : '')
"
/>
<q-input
for="input-agencies-email"
dense
outlined
hide-bottom-space
:readonly="readonly"
:label="$t('form.email')"
:rules="
readonly
? undefined
: [
(v: string) =>
!v ||
/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g.test(v) ||
$t('form.error.invalid'),
]
"
class="col-md-4 col-12"
:model-value="readonly ? email || '-' : email"
@update:model-value="(v) => (typeof v === 'string' ? (email = v) : '')"
@clear="email = ''"
>
<template #prepend>
<q-icon
size="xs"
name="mdi-email-outline"
class="cursor-pointer"
color="primary"
/>
</template>
</q-input>
<q-input
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('form.telephone')"
:model-value="readonly ? contactTel || '-' : contactTel"
@update:model-value="
(v) => (typeof v === 'string' ? (contactTel = v) : '')
"
>
<template #prepend>
<q-icon
size="xs"
name="mdi-phone-outline"
class="cursor-pointer"
color="primary"
/>
</template>
</q-input>
</div> </div>
</div> </div>
</template> </template>

View file

@ -93,13 +93,6 @@ function clearUpload() {
imgUrl.value = ''; imgUrl.value = '';
} }
watch(
() => tab.value,
async () => {
await initializeSignaturePad();
},
);
onMounted(async () => { onMounted(async () => {
await initializeSignaturePad(); await initializeSignaturePad();
}); });

View file

@ -158,7 +158,7 @@ onMounted(async () => {
</template> </template>
<template #option="{ opt, scope }"> <template #option="{ opt, scope }">
<q-item v-bind="scope.itemProps"> <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>

View file

@ -54,7 +54,9 @@ const columns = [
field: (v: Employee) => field: (v: Employee) =>
locale.value === Lang.English locale.value === Lang.English
? `${v.firstNameEN} ${v.lastNameEN}` ? `${v.firstNameEN} ${v.lastNameEN}`
: `${v.firstName} ${v.lastName}`, : v.firstName
? `${v.firstName} ${v.lastName}`
: `${v.firstNameEN} ${v.lastNameEN}`,
}, },
{ {
name: 'birthDate', name: 'birthDate',

View file

@ -153,6 +153,7 @@ export default {
tableOfContent: 'Table of Contents', tableOfContent: 'Table of Contents',
draw: 'Draw', draw: 'Draw',
newUpload: 'New Upload', newUpload: 'New Upload',
nativeLanguage: '{msg} Native Language',
}, },
menu: { menu: {
@ -451,7 +452,7 @@ export default {
responsibleArea: 'Responsibel Area', responsibleArea: 'Responsibel Area',
discount: 'Discount Condition', discount: 'Discount Condition',
sourceNationality: 'Source Nationality', sourceNationality: 'Source Nationality',
importNationality: 'import Nationality', importNationality: 'Import Nationality',
trainingPlace: 'Training Place', trainingPlace: 'Training Place',
checkpoint: 'Checkpoint', checkpoint: 'Checkpoint',
checkpointEN: 'Checkpoint (EN)', checkpointEN: 'Checkpoint (EN)',
@ -459,6 +460,10 @@ export default {
citizenId: 'Citizen ID', citizenId: 'Citizen ID',
citizenIssue: 'Citizen Issue', citizenIssue: 'Citizen Issue',
citizenExpire: 'Citizen Expire', citizenExpire: 'Citizen Expire',
agencyStatus: 'Agency Status',
normal: 'Normal',
canceled: 'Canceled',
blacklist: 'Black list',
}, },
}, },
customer: { customer: {
@ -912,6 +917,9 @@ export default {
code: 'Agencies Code', code: 'Agencies Code',
group: 'Agencies Group', group: 'Agencies Group',
name: 'Agencies Name', name: 'Agencies Name',
contactName: 'Contact Name',
contactTel: 'Contact Telephone',
bankInfo: 'Bank Information',
}, },
requestList: { requestList: {
@ -1183,6 +1191,7 @@ export default {
'Product with the same name already exists. If you want to create with this name please select another code.', 'Product with the same name already exists. If you want to create with this name please select another code.',
userExists: 'User already exits.', userExists: 'User already exits.',
sameNameExists: 'Same name exists.', sameNameExists: 'Same name exists.',
samePropertyNameExists: 'Same property name exists.',
validateError: 'Validate Error', validateError: 'Validate Error',
codeMisMatch: 'Code Mismatch', codeMisMatch: 'Code Mismatch',

View file

@ -153,6 +153,7 @@ export default {
tableOfContent: 'สารบัญ', tableOfContent: 'สารบัญ',
draw: 'วาด', draw: 'วาด',
newUpload: 'อัปโหลดใหม่', newUpload: 'อัปโหลดใหม่',
nativeLanguage: '{msg} ภาษาต้นทาง',
}, },
menu: { menu: {
@ -455,6 +456,10 @@ export default {
citizenId: 'เลขที่บัตรประชาชน', citizenId: 'เลขที่บัตรประชาชน',
citizenIssue: 'วันที่ออกบัตร', citizenIssue: 'วันที่ออกบัตร',
citizenExpire: 'วันที่หมดอายุ', citizenExpire: 'วันที่หมดอายุ',
agencyStatus: 'สถานะการเป็นเอเจนซี่',
normal: 'ปกติ',
canceled: 'ยกเลิก',
blacklist: 'แบล็คลิสต์',
}, },
}, },
customer: { customer: {
@ -780,7 +785,7 @@ export default {
branch: 'สาขาที่ออกใบเสนอราคา', branch: 'สาขาที่ออกใบเสนอราคา',
branchVirtual: 'จุดรับบริการที่ออกใบเสนอราคา', branchVirtual: 'จุดรับบริการที่ออกใบเสนอราคา',
customer: 'ลูกค้า', customer: 'ลูกค้า',
newCustomer: 'แรงงานใหม่', newCustomer: 'ลูกค้าใหม่',
employeeList: 'รายชื่อแรงงาน', employeeList: 'รายชื่อแรงงาน',
employee: 'แรงงาน', employee: 'แรงงาน',
employeeName: 'ชื่อ-นามสกุล แรงงาน', employeeName: 'ชื่อ-นามสกุล แรงงาน',
@ -903,6 +908,9 @@ export default {
code: 'รหัสหน่วยงาน', code: 'รหัสหน่วยงาน',
group: 'กลุ่มหน่วยงาน', group: 'กลุ่มหน่วยงาน',
name: 'ชื่อหน่วยงาน', name: 'ชื่อหน่วยงาน',
contactName: 'ชื่อผู้ติดต่อ',
contactTel: 'เบอร์โทรผู้ติดต่อ',
bankInfo: 'ข้อมูลธนาคาร',
}, },
requestList: { requestList: {
@ -1163,6 +1171,7 @@ export default {
'สินค้าที่มีชื่อเดียวกันมีในระบบแล้ว หากคุณต้องการสร้างด้วยชื่อนี้โปรดเลือกรหัสอื่น', 'สินค้าที่มีชื่อเดียวกันมีในระบบแล้ว หากคุณต้องการสร้างด้วยชื่อนี้โปรดเลือกรหัสอื่น',
userExists: 'ชื่อผู้ใช้นี้มีอยู่ในระบบอยู่แล้ว', userExists: 'ชื่อผู้ใช้นี้มีอยู่ในระบบอยู่แล้ว',
sameNameExists: 'ชื่อนี้ถูกใช้ไปแล้ว', sameNameExists: 'ชื่อนี้ถูกใช้ไปแล้ว',
samePropertyNameExists: 'คุณสมบัตินี้มีอยู่ในระบบอยู่แล้ว',
validateError: 'เกิดข้อผิดพลาดจากการตรวจสอบ', validateError: 'เกิดข้อผิดพลาดจากการตรวจสอบ',
codeMisMatch: 'รหัสไม่ตรงกัน', codeMisMatch: 'รหัสไม่ตรงกัน',

View file

@ -151,6 +151,8 @@ const defaultFormData = {
citizenExpire: null, citizenExpire: null,
citizenIssue: null, citizenIssue: null,
citizenId: '', citizenId: '',
remark: '',
agencyStatus: null,
}; };
const formData = ref<UserCreate>({ const formData = ref<UserCreate>({
@ -199,6 +201,8 @@ const formData = ref<UserCreate>({
citizenExpire: null, citizenExpire: null,
citizenIssue: null, citizenIssue: null,
citizenId: '', citizenId: '',
remark: '',
agencyStatus: null,
}); });
const fieldSelectedOption = ref<{ label: string; value: string }[]>([ const fieldSelectedOption = ref<{ label: string; value: string }[]>([
@ -419,6 +423,7 @@ async function onSubmit(excludeDialog?: boolean) {
: ''; : '';
const formDataEdit = { const formDataEdit = {
...formData.value, ...formData.value,
checkpointEN: formData.value.checkpoint,
status: !statusToggle.value ? 'INACTIVE' : 'ACTIVE', status: !statusToggle.value ? 'INACTIVE' : 'ACTIVE',
} as const; } as const;
@ -450,6 +455,7 @@ async function onSubmit(excludeDialog?: boolean) {
: hqId.value : hqId.value
? hqId.value ? hqId.value
: ''; : '';
formData.value.checkpointEN = formData.value.checkpoint;
const result = await userStore.create( const result = await userStore.create(
formData.value, formData.value,
onCreateImageList.value, onCreateImageList.value,
@ -603,6 +609,8 @@ async function assignFormData(idEdit: string) {
(foundUser.citizenIssue && new Date(foundUser.citizenIssue)) || null, (foundUser.citizenIssue && new Date(foundUser.citizenIssue)) || null,
citizenExpire: citizenExpire:
(foundUser.citizenExpire && new Date(foundUser.citizenExpire)) || null, (foundUser.citizenExpire && new Date(foundUser.citizenExpire)) || null,
remark: foundUser.remark || '',
agencyStatus: foundUser.agencyStatus || '',
}; };
formData.value.status === 'ACTIVE' || 'CREATED' formData.value.status === 'ACTIVE' || 'CREATED'
@ -1781,10 +1789,11 @@ 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:checkpoint-en="formData.checkpointEN"
v-model:agency-file="agencyFile" v-model:agency-file="agencyFile"
v-model:agency-file-list="agencyFileList" 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:agency-status="formData.agencyStatus"
/> />
</div> </div>
</div> </div>
@ -1854,7 +1863,6 @@ watch(
<div <div
class="col" class="col"
id="personnel-form"
:class="{ :class="{
'q-px-lg q-pb-lg': $q.screen.gt.sm, 'q-px-lg q-pb-lg': $q.screen.gt.sm,
'q-px-md q-pb-sm': !$q.screen.gt.sm, 'q-px-md q-pb-sm': !$q.screen.gt.sm,
@ -1898,7 +1906,7 @@ watch(
? [ ? [
{ {
name: $t('personnel.form.workInformation'), name: $t('personnel.form.workInformation'),
anchor: 'dialog-info-work', anchor: 'dialog-form-work',
}, },
] ]
: [], : [],
@ -1914,6 +1922,7 @@ watch(
</div> </div>
</div> </div>
<div <div
id="personnel-form"
class="col-md-10 col-12 full-height scroll" class="col-md-10 col-12 full-height scroll"
:class="{ :class="{
'q-py-md q-pr-md ': $q.screen.gt.sm, 'q-py-md q-pr-md ': $q.screen.gt.sm,
@ -1992,7 +2001,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:checkpoint-en="formData.checkpointEN" v-model:agency-status="formData.agencyStatus"
v-model:remark="formData.remark"
v-model:agency-file="agencyFile" v-model:agency-file="agencyFile"
/> />
</div> </div>

View file

@ -102,27 +102,33 @@ const optionStore = useOptionStore();
const ocrStore = useOcrStore(); const ocrStore = useOcrStore();
const refFilter = ref<InstanceType<typeof QSelect>>(); const refFilter = ref<InstanceType<typeof QSelect>>();
const statusEmployeeCreate = ref<boolean>(false);
const mrz = ref<Awaited<ReturnType<typeof parseResultMRZ>>>(); const mrz = ref<Awaited<ReturnType<typeof parseResultMRZ>>>();
const tabFieldRequired = ref<{ [key: string]: (keyof CustomerBranchCreate)[] }>(
{
main: [],
business: ['businessType', 'jobPosition'],
address: [
'address',
'addressEN',
'provinceId',
'districtId',
'subDistrictId',
],
contact: [],
},
);
const { state: customerFormState, currentFormData: customerFormData } = const {
storeToRefs(customerFormStore); fetchListOfOptionBranch,
const { state: employeeFormState, currentFromDataEmployee } = customerFormUndo,
storeToRefs(employeeFormStore); customerConfirmUnsave,
deleteCustomerById,
validateTabField,
deleteCustomerBranchById,
} = customerFormStore;
const { employeeFormUndo, employeeConfirmUnsave, deleteEmployeeById } =
employeeFormStore;
const {
state: customerFormState,
currentFormData: customerFormData,
registerAbleBranchOption,
tabFieldRequired,
} = storeToRefs(customerFormStore);
const {
state: employeeFormState,
currentFromDataEmployee,
onCreateImageList,
statusEmployeeCreate,
refreshImageState,
} = storeToRefs(employeeFormStore);
async function init() { async function init() {
navigatorStore.current.title = 'menu.customer'; navigatorStore.current.title = 'menu.customer';
@ -209,13 +215,7 @@ const dialogCustomerImageUpload = ref<InstanceType<typeof ImageUploadDialog>>();
const dialogEmployeeImageUpload = ref<InstanceType<typeof ImageUploadDialog>>(); const dialogEmployeeImageUpload = ref<InstanceType<typeof ImageUploadDialog>>();
// image // image
const refreshImageState = ref(false);
const imageList = ref<{ selectedImage: string; list: string[] }>(); const imageList = ref<{ selectedImage: string; list: string[] }>();
const onCreateImageList = ref<{
selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[];
}>({ selectedImage: '', list: [] });
watch(() => route.name, init); watch(() => route.name, init);
watch( watch(
[currentTab, currentStatus, inputSearch, customerTypeSelected, pageSize], [currentTab, currentStatus, inputSearch, customerTypeSelected, pageSize],
@ -276,8 +276,6 @@ const fieldSelected = ref<string[]>(
].filter((v, index, self) => self.indexOf(v) === index), ].filter((v, index, self) => self.indexOf(v) === index),
); );
const registerAbleBranchOption = ref<{ id: string; name: string }[]>();
const branch = ref<CustomerBranch[]>(); const branch = ref<CustomerBranch[]>();
const customerStats = [ const customerStats = [
@ -294,81 +292,6 @@ const fieldCustomer = [
const employeeHistoryDialog = ref(false); const employeeHistoryDialog = ref(false);
const employeeHistory = ref<EmployeeHistory[]>(); const employeeHistory = ref<EmployeeHistory[]>();
function validateTabField<T = CustomerBranchCreate>(
value: T,
fieldRequired: { [key: string]: (keyof T)[] },
) {
const list: string[] = [];
for (const tab in fieldRequired) {
for (const field of fieldRequired[tab]) {
if (!value[field] && !list.includes(tab)) list.push(tab);
}
}
return list;
}
function deleteCustomerById(id: string) {
dialog({
color: 'negative',
icon: 'mdi-alert',
title: t('dialog.title.confirmDelete'),
actionText: t('general.delete'),
persistent: true,
message: t('dialog.message.confirmDelete'),
action: async () => {
await customerStore.deleteById(id);
await fetchListCustomer(true, $q.screen.xs);
customerFormState.value.dialogModal = false;
flowStore.rotate();
},
cancel: () => {},
});
}
async function deleteCustomerBranchById(id: string) {
return await new Promise((resolve) => {
dialog({
color: 'negative',
icon: 'mdi-alert',
title: t('dialog.title.confirmDelete'),
actionText: t('general.delete'),
persistent: true,
message: t('dialog.message.confirmDelete'),
action: async () => {
await customerStore.deleteBranchById(id);
flowStore.rotate();
resolve(true);
},
cancel: () => {
resolve(false);
},
});
});
}
async function fetchListOfOptionBranch() {
if (registerAbleBranchOption.value) return;
const uid = getUserId();
const role = getRole();
if (!uid) return; // should not possible as the system require login to be able to access resource.
if (role?.includes('system')) {
const result = await userBranchStore.fetchListOptionBranch();
if (result && result.total > 0)
registerAbleBranchOption.value = result.result;
} else {
const result = await userBranchStore.fetchListMyBranch(uid);
if (result && result.total > 0)
registerAbleBranchOption.value = result.result;
}
// TODO: Assign (first) branch of the user as register branch of the data
}
async function fetchListCustomer(fetchStats = false, mobileFetch?: boolean) { async function fetchListCustomer(fetchStats = false, mobileFetch?: boolean) {
const total = statsCustomerType.value.PERS + statsCustomerType.value.CORP; const total = statsCustomerType.value.PERS + statsCustomerType.value.CORP;
@ -503,64 +426,6 @@ async function toggleStatusCustomer(id: string, status: boolean) {
await fetchListCustomer(false, $q.screen.xs); await fetchListCustomer(false, $q.screen.xs);
flowStore.rotate(); flowStore.rotate();
} }
async function deleteEmployeeById(opts: {
id?: string;
type?: 'passport' | 'visa' | 'healthCheck' | 'work';
index?: number;
}) {
dialog({
color: 'negative',
icon: 'mdi-alert',
title: t('dialog.title.confirmDelete'),
actionText: t('general.delete'),
persistent: true,
message: t('dialog.message.confirmDelete'),
action: async () => {
if (opts.type === 'passport' && opts.index !== undefined) {
await employeeFormStore.deletePassport(opts.index);
}
if (opts.type === 'visa' && opts.index !== undefined) {
await employeeFormStore.deleteVisa(opts.index);
}
if (opts.type === 'healthCheck' && opts.index !== undefined) {
await employeeFormStore.deleteHealthCheck(opts.index);
}
if (opts.type === 'work' && opts.index !== undefined) {
await employeeFormStore.deleteWorkHistory(opts.index);
} else {
if (!!opts.id) {
const result = await employeeStore.deleteById(opts.id);
if (result) {
employeeFormState.value.drawerModal = false;
employeeFormState.value.dialogModal = false;
}
}
}
if (route.name !== 'CustomerBranchManagement') {
await fetchListEmployee(
currentTab.value === 'employer'
? {
page: 1,
pageSize: 999,
customerId: customerFormState.value.currentCustomerId,
}
: { fetchStats: true, mobileFetch: $q.screen.xs },
);
flowStore.rotate();
}
},
cancel: () => {},
});
}
async function openHistory(id: string) { async function openHistory(id: string) {
const res = await employeeStore.getEditHistory(id); const res = await employeeStore.getEditHistory(id);
employeeHistory.value = res.reverse(); employeeHistory.value = res.reverse();
@ -594,64 +459,6 @@ async function editEmployeeFormPersonal(id: string) {
employeeFormState.value.drawerModal = true; employeeFormState.value.drawerModal = true;
} }
function employeeConfirmUnsave(close = true) {
dialog({
color: 'warning',
icon: 'mdi-alert',
title: t('form.warning.title'),
actionText: t('general.ok'),
persistent: true,
message: t('form.warning.unsave'),
action: () => {
employeeFormStore.resetFormDataEmployee();
onCreateImageList.value = { selectedImage: '', list: [] };
employeeFormState.value.editReadonly = true;
employeeFormState.value.dialogModal = !close;
employeeFormState.value.drawerModal = !close;
},
cancel: () => {},
});
}
function employeeFormUndo(close = true) {
if (employeeFormStore.isFormDataDifferent()) {
return employeeConfirmUnsave(close);
}
employeeFormStore.resetFormDataEmployee();
employeeFormState.value.editReadonly = true;
}
function customerConfirmUnsave(close = true) {
dialog({
color: 'warning',
icon: 'mdi-alert',
title: t('form.warning.title'),
actionText: t('general.ok'),
persistent: true,
message: t('form.warning.unsave'),
action: () => {
customerFormStore.resetForm();
customerFormState.value.readonly = true;
if (!customerFormState.value.drawerModal) {
customerFormState.value.dialogModal = !close;
} else {
customerFormState.value.drawerModal = !close;
}
},
cancel: () => {},
});
}
function customerFormUndo(close = true) {
if (customerFormStore.isFormDataDifferent()) {
return customerConfirmUnsave(close);
}
customerFormStore.resetForm();
customerFormState.value.readonly = true;
}
async function createCustomerForm(customerType: 'CORP' | 'PERS') { async function createCustomerForm(customerType: 'CORP' | 'PERS') {
customerFormState.value.dialogModal = true; customerFormState.value.dialogModal = true;
customerFormState.value.dialogType = 'create'; customerFormState.value.dialogType = 'create';
@ -686,7 +493,7 @@ async function fetchImageList(
// TODO: When in employee form, if select address same as customer then auto fill // TODO: When in employee form, if select address same as customer then auto fill
watch( watch(
() => employeeFormState.value.formDataEmployeeOwner, () => employeeFormState.value.currentCustomerBranch,
(e) => { (e) => {
if (!e) return; if (!e) return;
if (employeeFormState.value.formDataEmployeeSameAddr) { if (employeeFormState.value.formDataEmployeeSameAddr) {
@ -703,21 +510,21 @@ watch(
watch( watch(
() => employeeFormState.value.formDataEmployeeSameAddr, () => employeeFormState.value.formDataEmployeeSameAddr,
(isSame) => { (isSame) => {
if (!employeeFormState.value.formDataEmployeeOwner) return; if (!employeeFormState.value.currentCustomerBranch) return;
if (isSame) { if (isSame) {
currentFromDataEmployee.value.address = currentFromDataEmployee.value.address =
employeeFormState.value.formDataEmployeeOwner.address; employeeFormState.value.currentCustomerBranch.address;
currentFromDataEmployee.value.addressEN = currentFromDataEmployee.value.addressEN =
employeeFormState.value.formDataEmployeeOwner.addressEN; employeeFormState.value.currentCustomerBranch.addressEN;
currentFromDataEmployee.value.provinceId = currentFromDataEmployee.value.provinceId =
employeeFormState.value.formDataEmployeeOwner.provinceId; employeeFormState.value.currentCustomerBranch.provinceId;
currentFromDataEmployee.value.districtId = currentFromDataEmployee.value.districtId =
employeeFormState.value.formDataEmployeeOwner.districtId; employeeFormState.value.currentCustomerBranch.districtId;
currentFromDataEmployee.value.subDistrictId = currentFromDataEmployee.value.subDistrictId =
employeeFormState.value.formDataEmployeeOwner.subDistrictId; employeeFormState.value.currentCustomerBranch.subDistrictId;
} }
currentFromDataEmployee.value.customerBranchId = currentFromDataEmployee.value.customerBranchId =
employeeFormState.value.formDataEmployeeOwner.id; employeeFormState.value.currentCustomerBranch.id;
}, },
); );
@ -1540,7 +1347,13 @@ const emptyCreateDialog = ref(false);
customerFormState.branchIndex = 0; customerFormState.branchIndex = 0;
} }
" "
@delete="deleteCustomerById(props.row.id)" @delete="
deleteCustomerById(
props.row.id,
async () =>
await fetchListCustomer(true, $q.screen.xs),
)
"
@change-status=" @change-status="
async () => { async () => {
triggerChangeStatus( triggerChangeStatus(
@ -1588,7 +1401,23 @@ const emptyCreateDialog = ref(false);
" "
@delete=" @delete="
(item: any) => { (item: any) => {
deleteEmployeeById({ id: item.id }); 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=" @toggle-status="
@ -1763,7 +1592,16 @@ const emptyCreateDialog = ref(false);
customerFormState.branchIndex = 0; customerFormState.branchIndex = 0;
} }
" "
@delete="deleteCustomerById(props.row.id)" @delete="
deleteCustomerById(
props.row.id,
async () =>
await fetchListCustomer(
true,
$q.screen.xs,
),
)
"
@change-status=" @change-status="
triggerChangeStatus( triggerChangeStatus(
props.row.id, props.row.id,
@ -1898,7 +1736,23 @@ const emptyCreateDialog = ref(false);
@edit="(item: any) => editEmployeeFormPersonal(item.id)" @edit="(item: any) => editEmployeeFormPersonal(item.id)"
@delete=" @delete="
(item: any) => { (item: any) => {
deleteEmployeeById({ id: item.id }); 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=" @toggle-status="
@ -2036,7 +1890,7 @@ const emptyCreateDialog = ref(false);
async (currentBranch) => { async (currentBranch) => {
createEmployeeForm(); createEmployeeForm();
await nextTick(); await nextTick();
employeeFormState.formDataEmployeeOwner = { ...currentBranch }; employeeFormState.currentBranchId = currentBranch.id;
} }
" "
v-model:branch="branch" v-model:branch="branch"
@ -2265,7 +2119,10 @@ const emptyCreateDialog = ref(false);
@cancel="() => customerFormUndo(false)" @cancel="() => customerFormUndo(false)"
@delete=" @delete="
customerFormState.editCustomerId && customerFormState.editCustomerId &&
deleteCustomerById(customerFormState.editCustomerId) deleteCustomerById(
customerFormState.editCustomerId,
async () => await fetchListCustomer(true, $q.screen.xs),
)
" "
:customer-type="customerFormData.customerType" :customer-type="customerFormData.customerType"
v-model:registered-branch-id="customerFormData.registeredBranchId" v-model:registered-branch-id="customerFormData.registeredBranchId"
@ -2435,7 +2292,11 @@ const emptyCreateDialog = ref(false);
if (!customerFormState.editCustomerId) return; if (!customerFormState.editCustomerId) return;
if (idx === 0) { if (idx === 0) {
deleteCustomerById(customerFormState.editCustomerId); deleteCustomerById(
customerFormState.editCustomerId,
async () =>
await fetchListCustomer(true, $q.screen.xs),
);
return; return;
} }
if (!!customerFormData.customerBranch?.[idx].id) { if (!!customerFormData.customerBranch?.[idx].id) {
@ -2870,12 +2731,30 @@ const emptyCreateDialog = ref(false);
id="btn-info-basic-delete" id="btn-info-basic-delete"
icon-only icon-only
@click=" @click="
() => deleteEmployeeById({ id: currentFromDataEmployee.id }) () =>
deleteEmployeeById({
id: currentFromDataEmployee.id,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId:
customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
})
" "
type="button" type="button"
/> />
</div> </div>
</div> </div>
<BasicInformation <BasicInformation
no-action no-action
id="form-information" id="form-information"
@ -2888,11 +2767,13 @@ const emptyCreateDialog = ref(false);
title="form.field.basicInformation" title="form.field.basicInformation"
:readonly="!employeeFormState.isEmployeeEdit" :readonly="!employeeFormState.isEmployeeEdit"
:employee-owner-option="employeeStore.ownerOption || []" :employee-owner-option="employeeStore.ownerOption || []"
v-model:customer-branch="employeeFormState.formDataEmployeeOwner" v-model:customer-branch-id="employeeFormState.currentBranchId"
v-model:current-customer-branch="
employeeFormState.currentCustomerBranch
"
v-model:employee-id="employeeFormState.currentEmployeeCode" v-model:employee-id="employeeFormState.currentEmployeeCode"
v-model:nrc-no="currentFromDataEmployee.nrcNo" v-model:nrc-no="currentFromDataEmployee.nrcNo"
v-model:code="currentFromDataEmployee.code" v-model:code="currentFromDataEmployee.code"
@filter-owner-branch="employeeFormStore.employeeFilterOwnerBranch"
class="q-mb-xl" class="q-mb-xl"
/> />
<FormPerson <FormPerson
@ -2918,6 +2799,7 @@ const emptyCreateDialog = ref(false);
class="q-mb-xl" class="q-mb-xl"
/> />
<AddressForm <AddressForm
disabledRule
id="form-personal-address" id="form-personal-address"
prefix-id="form-employee" prefix-id="form-employee"
:readonly="!employeeFormState.isEmployeeEdit" :readonly="!employeeFormState.isEmployeeEdit"
@ -2940,7 +2822,7 @@ const emptyCreateDialog = ref(false);
class="q-mb-xl" class="q-mb-xl"
/> />
<div class="row q-mb-md" id="drawer-info-file-upload"> <div class="row q-mb-md" id="form-info-file-upload">
<div class="col-12 q-pb-sm text-weight-bold text-body1"> <div class="col-12 q-pb-sm text-weight-bold text-body1">
<q-icon <q-icon
flat flat
@ -3448,7 +3330,24 @@ const emptyCreateDialog = ref(false);
@click.stop=" @click.stop="
() => { () => {
employeeFormState.currentIndexPassport = index; employeeFormState.currentIndexPassport = index;
deleteEmployeeById({ type: 'passport', index }); deleteEmployeeById({
type: 'passport',
index,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId:
customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
});
} }
" "
type="button" type="button"
@ -3599,7 +3498,24 @@ const emptyCreateDialog = ref(false);
@click.stop=" @click.stop="
() => { () => {
employeeFormState.currentIndexVisa = index; employeeFormState.currentIndexVisa = index;
deleteEmployeeById({ type: 'visa', index }); deleteEmployeeById({
type: 'visa',
index,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId:
customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
});
} }
" "
type="button" type="button"
@ -3671,7 +3587,23 @@ const emptyCreateDialog = ref(false);
@delete=" @delete="
(index) => { (index) => {
employeeFormState.currentIndexCheckup = index; employeeFormState.currentIndexCheckup = index;
deleteEmployeeById({ type: 'healthCheck', index }); deleteEmployeeById({
type: 'healthCheck',
index,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId: customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
});
} }
" "
@save=" @save="
@ -3757,7 +3689,23 @@ const emptyCreateDialog = ref(false);
@delete=" @delete="
(index) => { (index) => {
employeeFormState.currentIndexWorkHistory = index; employeeFormState.currentIndexWorkHistory = index;
deleteEmployeeById({ type: 'work', index }); deleteEmployeeById({
type: 'work',
index,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId: customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
});
} }
" "
@save=" @save="
@ -4224,7 +4172,10 @@ const emptyCreateDialog = ref(false);
@cancel="() => customerFormUndo(false)" @cancel="() => customerFormUndo(false)"
@delete=" @delete="
customerFormState.editCustomerId && customerFormState.editCustomerId &&
deleteCustomerById(customerFormState.editCustomerId) deleteCustomerById(
customerFormState.editCustomerId,
async () => await fetchListCustomer(true, $q.screen.xs),
)
" "
:customer-type="customerFormData.customerType" :customer-type="customerFormData.customerType"
v-model:registered-branch-id="customerFormData.registeredBranchId" v-model:registered-branch-id="customerFormData.registeredBranchId"
@ -4821,7 +4772,23 @@ const emptyCreateDialog = ref(false);
icon-only icon-only
@click=" @click="
() => () =>
deleteEmployeeById({ id: currentFromDataEmployee.id }) deleteEmployeeById({
id: currentFromDataEmployee.id,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId:
customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
})
" "
type="button" type="button"
/> />
@ -4835,16 +4802,13 @@ const emptyCreateDialog = ref(false);
outlined outlined
title="form.field.basicInformation" title="form.field.basicInformation"
:readonly="!employeeFormState.isEmployeeEdit" :readonly="!employeeFormState.isEmployeeEdit"
:employee-owner-option="employeeStore.ownerOption" v-model:customer-branch-id="employeeFormState.currentBranchId"
v-model:customer-branch=" v-model:current-customer-branch="
employeeFormState.formDataEmployeeOwner employeeFormState.currentCustomerBranch
" "
v-model:employee-id="employeeFormState.currentEmployeeCode" v-model:employee-id="employeeFormState.currentEmployeeCode"
v-model:nrc-no="currentFromDataEmployee.nrcNo" v-model:nrc-no="currentFromDataEmployee.nrcNo"
v-model:code="currentFromDataEmployee.code" v-model:code="currentFromDataEmployee.code"
@filter-owner-branch="
employeeFormStore.employeeFilterOwnerBranch
"
class="q-mb-xl" class="q-mb-xl"
/> />
<FormPerson <FormPerson
@ -4871,6 +4835,7 @@ const emptyCreateDialog = ref(false);
<AddressForm <AddressForm
id="drawer-form-personal-address" id="drawer-form-personal-address"
employee employee
disabledRule
v-model:address="currentFromDataEmployee.address" v-model:address="currentFromDataEmployee.address"
v-model:address-en="currentFromDataEmployee.addressEN" v-model:address-en="currentFromDataEmployee.addressEN"
v-model:moo="currentFromDataEmployee.moo" v-model:moo="currentFromDataEmployee.moo"
@ -5450,6 +5415,20 @@ const emptyCreateDialog = ref(false);
deleteEmployeeById({ deleteEmployeeById({
type: 'passport', type: 'passport',
index, index,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId:
customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
}); });
} }
" "
@ -5621,7 +5600,24 @@ const emptyCreateDialog = ref(false);
icon-only icon-only
@click.stop=" @click.stop="
() => { () => {
deleteEmployeeById({ type: 'visa', index }); deleteEmployeeById({
type: 'visa',
index,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId:
customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
});
} }
" "
type="button" type="button"
@ -5742,7 +5738,24 @@ const emptyCreateDialog = ref(false);
@delete=" @delete="
(index) => { (index) => {
employeeFormState.currentIndexCheckup = index; employeeFormState.currentIndexCheckup = index;
deleteEmployeeById({ type: 'healthCheck', index }); deleteEmployeeById({
type: 'healthCheck',
index,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId:
customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
});
} }
" "
/> />
@ -5812,7 +5825,24 @@ const emptyCreateDialog = ref(false);
@delete=" @delete="
(index) => { (index) => {
employeeFormState.currentIndexWorkHistory = index; employeeFormState.currentIndexWorkHistory = index;
deleteEmployeeById({ type: 'work', index }); deleteEmployeeById({
type: 'work',
index,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId:
customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
});
} }
" "
@save=" @save="

View file

@ -1,22 +1,50 @@
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { useI18n } from 'vue-i18n';
import { import {
CustomerBranch,
CustomerBranchCreate, CustomerBranchCreate,
CustomerCreate, CustomerCreate,
CustomerType, CustomerType,
} from 'stores/customer/types'; } from 'stores/customer/types';
import { Employee, EmployeeCreate } from 'stores/employee/types'; import { Employee, EmployeeCreate } from 'stores/employee/types';
import { dialog } from 'stores/utils';
import useMyBranch from 'stores/my-branch'; import useMyBranch from 'stores/my-branch';
import useCustomerStore from 'stores/customer'; import useCustomerStore from 'stores/customer';
import useEmployeeStore from 'stores/employee'; import useEmployeeStore from 'stores/employee';
import useFlowStore from 'stores/flow'; import useFlowStore from 'stores/flow';
import useMyBranchStore from 'stores/my-branch';
import { baseUrl } from 'src/stores/utils'; import { baseUrl } from 'src/stores/utils';
import { getRole, getUserId } from 'src/services/keycloak';
import { useRoute } from 'vue-router';
export const useCustomerForm = defineStore('form-customer', () => { export const useCustomerForm = defineStore('form-customer', () => {
const customerStore = useCustomerStore(); const customerStore = useCustomerStore();
const { t } = useI18n();
const flowStore = useFlowStore();
const userBranchStore = useMyBranchStore();
const registerAbleBranchOption = ref<{ id: string; name: string }[]>();
const tabFieldRequired = ref<{
[key: string]: (keyof CustomerBranchCreate)[];
}>({
main: [],
business: ['businessType', 'jobPosition'],
address: [
'address',
'addressEN',
'provinceId',
'districtId',
'subDistrictId',
],
contact: [],
});
const defaultFormData: CustomerCreate = { const defaultFormData: CustomerCreate = {
// code: '', // code: '',
// namePrefix: '', // namePrefix: '',
@ -360,7 +388,118 @@ export const useCustomerForm = defineStore('form-customer', () => {
} }
} }
async function fetchListOfOptionBranch() {
if (registerAbleBranchOption.value) return;
const uid = getUserId();
const role = getRole();
if (!uid) return; // should not possible as the system require login to be able to access resource.
if (role?.includes('system')) {
const result = await userBranchStore.fetchListOptionBranch();
if (result && result.total > 0)
registerAbleBranchOption.value = result.result;
} else {
const result = await userBranchStore.fetchListMyBranch(uid);
if (result && result.total > 0)
registerAbleBranchOption.value = result.result;
}
// TODO: Assign (first) branch of the user as register branch of the data
}
function customerFormUndo(close = true) {
if (isFormDataDifferent()) {
return customerConfirmUnsave(close);
}
resetForm();
state.value.readonly = true;
}
function customerConfirmUnsave(close = true) {
dialog({
color: 'warning',
icon: 'mdi-alert',
title: t('form.warning.title'),
actionText: t('general.ok'),
persistent: true,
message: t('form.warning.unsave'),
action: () => {
resetForm();
state.value.readonly = true;
if (!state.value.drawerModal) {
state.value.dialogModal = !close;
} else {
state.value.drawerModal = !close;
}
},
cancel: () => {},
});
}
function deleteCustomerById(
id: string,
fetch?: (...args: unknown[]) => unknown,
) {
dialog({
color: 'negative',
icon: 'mdi-alert',
title: t('dialog.title.confirmDelete'),
actionText: t('general.delete'),
persistent: true,
message: t('dialog.message.confirmDelete'),
action: async () => {
await customerStore.deleteById(id);
await fetch();
state.value.dialogModal = false;
flowStore.rotate();
},
cancel: () => {},
});
}
function validateTabField<T = CustomerBranchCreate>(
value: T,
fieldRequired: { [key: string]: (keyof T)[] },
) {
const list: string[] = [];
for (const tab in fieldRequired) {
for (const field of fieldRequired[tab]) {
if (!value[field] && !list.includes(tab)) list.push(tab);
}
}
return list;
}
async function deleteCustomerBranchById(id: string) {
return await new Promise((resolve) => {
dialog({
color: 'negative',
icon: 'mdi-alert',
title: t('dialog.title.confirmDelete'),
actionText: t('general.delete'),
persistent: true,
message: t('dialog.message.confirmDelete'),
action: async () => {
await customerStore.deleteBranchById(id);
flowStore.rotate();
resolve(true);
},
cancel: () => {
resolve(false);
},
});
});
}
return { return {
tabFieldRequired,
registerAbleBranchOption,
state, state,
resetFormData, resetFormData,
currentFormData, currentFormData,
@ -370,13 +509,18 @@ export const useCustomerForm = defineStore('form-customer', () => {
submitFormCustomer, submitFormCustomer,
addCurrentCustomerBranch, addCurrentCustomerBranch,
deleteAttachment, deleteAttachment,
fetchListOfOptionBranch,
customerFormUndo,
customerConfirmUnsave,
deleteCustomerById,
validateTabField,
deleteCustomerBranchById,
}; };
}); });
export const useCustomerBranchForm = defineStore('form-customer-branch', () => { export const useCustomerBranchForm = defineStore('form-customer-branch', () => {
const customerStore = useCustomerStore(); const customerStore = useCustomerStore();
const customerFormStore = useCustomerForm(); const customerFormStore = useCustomerForm();
const defaultFormData: CustomerBranchCreate & { const defaultFormData: CustomerBranchCreate & {
id?: string; id?: string;
codeCustomer?: string; codeCustomer?: string;
@ -581,11 +725,23 @@ export const useCustomerBranchForm = defineStore('form-customer-branch', () => {
}); });
export const useEmployeeForm = defineStore('form-employee', () => { export const useEmployeeForm = defineStore('form-employee', () => {
const { t } = useI18n();
const customerStore = useCustomerStore(); const customerStore = useCustomerStore();
const employeeStore = useEmployeeStore(); const employeeStore = useEmployeeStore();
const flowStore = useFlowStore(); const flowStore = useFlowStore();
const branchStore = useMyBranch(); const branchStore = useMyBranch();
const route = useRoute();
const refreshImageState = ref(false);
const onCreateImageList = ref<{
selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[];
}>({ selectedImage: '', list: [] });
const statusEmployeeCreate = ref<boolean>(false);
const state = ref<{ const state = ref<{
dialogType: 'info' | 'create' | 'edit'; dialogType: 'info' | 'create' | 'edit';
imageDialog: boolean; imageDialog: boolean;
@ -594,6 +750,8 @@ export const useEmployeeForm = defineStore('form-employee', () => {
drawerModal: boolean; drawerModal: boolean;
isImageEdit: boolean; isImageEdit: boolean;
currentBranchId: string;
currentCustomerBranch?: CustomerBranch;
currentEmployeeCode: string; currentEmployeeCode: string;
currentEmployee: Employee | null; currentEmployee: Employee | null;
currentIndexPassport: number; currentIndexPassport: number;
@ -627,6 +785,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
| undefined; | undefined;
ocr: boolean; ocr: boolean;
}>({ }>({
currentBranchId: '',
isImageEdit: false, isImageEdit: false,
currentIndexPassport: -1, currentIndexPassport: -1,
currentIndexVisa: -1, currentIndexVisa: -1,
@ -1089,6 +1248,8 @@ export const useEmployeeForm = defineStore('form-employee', () => {
selectedImage: string; selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[]; list: { url: string; imgFile: File | null; name: string }[];
}) { }) {
let employeeId: string | undefined = undefined;
currentFromDataEmployee.value.firstName = currentFromDataEmployee.value.firstName =
currentFromDataEmployee.value.firstName.trim(); currentFromDataEmployee.value.firstName.trim();
currentFromDataEmployee.value.middleName = currentFromDataEmployee.value.middleName =
@ -1107,7 +1268,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
const res = await employeeStore.create( const res = await employeeStore.create(
{ {
...currentFromDataEmployee.value, ...currentFromDataEmployee.value,
customerBranchId: state.value.formDataEmployeeOwner?.id || '', customerBranchId: state.value.currentBranchId || '',
employeeWork: [], employeeWork: [],
employeeCheckup: [], employeeCheckup: [],
@ -1117,6 +1278,7 @@ export const useEmployeeForm = defineStore('form-employee', () => {
); );
if (res) { if (res) {
employeeId = res.id;
await assignFormDataEmployee(res.id); await assignFormDataEmployee(res.id);
currentFromDataEmployee.value.id = res.id; currentFromDataEmployee.value.id = res.id;
state.value.statusSavePersonal = true; state.value.statusSavePersonal = true;
@ -1138,10 +1300,12 @@ export const useEmployeeForm = defineStore('form-employee', () => {
}, },
); );
if (res) { if (res) {
employeeId = res.id;
await assignFormDataEmployee(res.id); await assignFormDataEmployee(res.id);
state.value.statusSavePersonal = true; state.value.statusSavePersonal = true;
} }
} }
return employeeId;
} }
async function assignFormDataEmployee(id?: string) { async function assignFormDataEmployee(id?: string) {
@ -1183,7 +1347,19 @@ export const useEmployeeForm = defineStore('form-employee', () => {
employeePassport: structuredClone( employeePassport: structuredClone(
payload.employeePassport?.length === 0 payload.employeePassport?.length === 0
? state.value.dialogModal ? state.value.dialogModal
? defaultFormData.employeePassport ? defaultFormData.employeePassport.map((v) => ({
...v,
namePrefix: payload.namePrefix,
firstName: payload.firstName,
firstNameEN: payload.firstNameEN,
middleName: payload.middleName,
middleNameEN: payload.middleNameEN,
lastName: payload.lastName,
lastNameEN: payload.lastNameEN,
gender: payload.gender,
nationality: payload.nationality,
birthDate: payload.dateOfBirth,
}))
: [] : []
: payload.employeePassport, : payload.employeePassport,
), ),
@ -1270,6 +1446,8 @@ export const useEmployeeForm = defineStore('form-employee', () => {
state.value.currentIndexVisa = -1; state.value.currentIndexVisa = -1;
} }
state.value.currentBranchId = payload.customerBranchId;
const foundBranch = await customerStore.fetchListCustomerBranchById( const foundBranch = await customerStore.fetchListCustomerBranchById(
payload.customerBranchId, payload.customerBranchId,
); );
@ -1325,17 +1503,17 @@ export const useEmployeeForm = defineStore('form-employee', () => {
issueDate: new Date(), issueDate: new Date(),
type: '', type: '',
expireDate: new Date(), expireDate: new Date(),
birthDate: new Date(), birthDate: currentFromDataEmployee.value.dateOfBirth,
workerStatus: '', workerStatus: '',
nationality: '', nationality: currentFromDataEmployee.value.nationality,
gender: '', gender: currentFromDataEmployee.value.gender,
lastNameEN: '', lastNameEN: currentFromDataEmployee.value.lastNameEN,
lastName: '', lastName: currentFromDataEmployee.value.lastName,
middleNameEN: '', middleNameEN: currentFromDataEmployee.value.middleNameEN,
middleName: '', middleName: currentFromDataEmployee.value.middleName,
firstNameEN: '', firstNameEN: currentFromDataEmployee.value.firstNameEN,
firstName: '', firstName: currentFromDataEmployee.value.firstName,
namePrefix: '', namePrefix: currentFromDataEmployee.value.namePrefix,
number: '', number: '',
}); });
@ -1396,7 +1574,88 @@ export const useEmployeeForm = defineStore('form-employee', () => {
(currentFromDataEmployee.value.employeeWork?.length || 0) - 1; (currentFromDataEmployee.value.employeeWork?.length || 0) - 1;
} }
function employeeFormUndo(close = true) {
if (isFormDataDifferent()) {
return employeeConfirmUnsave(close);
}
resetFormDataEmployee();
state.value.editReadonly = true;
}
function employeeConfirmUnsave(close = true) {
dialog({
color: 'warning',
icon: 'mdi-alert',
title: t('form.warning.title'),
actionText: t('general.ok'),
persistent: true,
message: t('form.warning.unsave'),
action: () => {
resetFormDataEmployee();
onCreateImageList.value = { selectedImage: '', list: [] };
state.value.editReadonly = true;
state.value.dialogModal = !close;
state.value.drawerModal = !close;
},
cancel: () => {},
});
}
async function deleteEmployeeById(opts: {
id?: string;
type?: 'passport' | 'visa' | 'healthCheck' | 'work';
index?: number;
fetch?: (...args: unknown[]) => unknown;
removeArray?: (...args: unknown[]) => unknown;
}) {
dialog({
color: 'negative',
icon: 'mdi-alert',
title: t('dialog.title.confirmDelete'),
actionText: t('general.delete'),
persistent: true,
message: t('dialog.message.confirmDelete'),
action: async () => {
if (opts.type === 'passport' && opts.index !== undefined) {
await deletePassport(opts.index);
}
if (opts.type === 'visa' && opts.index !== undefined) {
await deleteVisa(opts.index);
}
if (opts.type === 'healthCheck' && opts.index !== undefined) {
await deleteHealthCheck(opts.index);
}
if (opts.type === 'work' && opts.index !== undefined) {
await deleteWorkHistory(opts.index);
} else {
if (!!opts.id) {
const result = await employeeStore.deleteById(opts.id);
if (result) {
state.value.drawerModal = false;
state.value.dialogModal = false;
}
}
}
if (route.name !== 'CustomerBranchManagement') {
await opts.fetch?.();
flowStore.rotate();
}
opts.removeArray?.();
},
cancel: () => {},
});
}
return { return {
refreshImageState,
statusEmployeeCreate,
onCreateImageList,
state, state,
currentFromDataEmployee, currentFromDataEmployee,
resetEmployeeData, resetEmployeeData,
@ -1423,5 +1682,9 @@ export const useEmployeeForm = defineStore('form-employee', () => {
employeeFilterOwnerBranch, employeeFilterOwnerBranch,
isFormDataDifferent, isFormDataDifferent,
employeeFormUndo,
employeeConfirmUnsave,
deleteEmployeeById,
}; };
}); });

View file

@ -14,19 +14,22 @@ 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 } 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';
const { t } = useI18n(); const { t, locale } = useI18n();
const $q = useQuasar(); const $q = useQuasar();
const navigatorStore = useNavigator(); const navigatorStore = useNavigator();
const propertyStore = useProperty(); const propertyStore = useProperty();
const optionStore = useOptionStore();
const { const {
data: propertyData, data: propertyData,
page: propertyPage, page: propertyPage,
pageSize: propertyPageSize, pageSize: propertyPageSize,
pageMax: propertyPageMax, pageMax: propertyPageMax,
} = storeToRefs(propertyStore); } = storeToRefs(propertyStore);
const { globalOption } = storeToRefs(optionStore);
const currPropertyData = ref<Property>(); const currPropertyData = ref<Property>();
const formProperty = ref<Property>({ const formProperty = ref<Property>({
@ -146,6 +149,7 @@ async function fetchPropertyList(mobileFetch?: boolean) {
function triggerDialog(type: 'add' | 'edit' | 'view') { function triggerDialog(type: 'add' | 'edit' | 'view') {
if (type === 'add') { if (type === 'add') {
resetForm();
pageState.addModal = true; pageState.addModal = true;
pageState.isDrawerEdit = true; pageState.isDrawerEdit = true;
} }
@ -271,6 +275,31 @@ async function submit() {
}); });
} }
await fetchPropertyList($q.screen.xs); await fetchPropertyList($q.screen.xs);
// assign new property to global option
const propList = propertyData.value.map((v) => {
return {
label: locale.value === 'eng' ? v.nameEN : v.name,
value: toCamelCase(v.nameEN),
type: v.type.type,
};
});
const existingValues = new Set(
globalOption.value.propertiesField.map(
(item: { value: string }) => item.value,
),
);
const newProps = propList.filter((prop) => !existingValues.has(prop.value));
if (newProps) {
globalOption.value.propertiesField.splice(
globalOption.value.propertiesField.length - 4,
0,
...newProps,
);
}
currPropertyData.value;
resetForm(); resetForm();
} }

View file

@ -2,10 +2,12 @@
import { onMounted, 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';
// NOTE: Import stores // NOTE: Import stores
import useCustomerStore from 'stores/customer';
import { useQuotationStore } from 'src/stores/quotations'; import { useQuotationStore } from 'src/stores/quotations';
import { isRoleInclude } from 'stores/utils'; import { dialog, isRoleInclude, notify } from 'stores/utils';
import { useNavigator } from 'src/stores/navigator'; import { 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';
@ -38,26 +40,43 @@ import { AddressForm } from 'components/form';
import { import {
EmployerFormBusiness, EmployerFormBusiness,
EmployerFormAbout, EmployerFormAbout,
EmployerFormBasicInfo,
EmployerFormBranch,
} from 'src/pages/03_customer-management/components'; } from 'src/pages/03_customer-management/components';
import { useCustomerForm } from 'src/pages/03_customer-management/form'; import { useCustomerForm } from 'src/pages/03_customer-management/form';
import { Quotation } from 'src/stores/quotations/types'; 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';
const { t, locale } = useI18n();
const $q = useQuasar(); const $q = useQuasar();
const quotationFormStore = useQuotationForm(); const quotationFormStore = useQuotationForm();
const customerFormStore = useCustomerForm(); const customerFormStore = useCustomerForm();
const flowStore = useFlowStore(); const flowStore = useFlowStore();
const userBranch = useMyBranch(); const userBranch = useMyBranch();
const navigatorStore = useNavigator(); const navigatorStore = useNavigator();
const customerStore = useCustomerStore();
const {
fetchListOfOptionBranch,
customerFormUndo,
deleteCustomerById,
validateTabField,
deleteCustomerBranchById,
} = customerFormStore;
const { const {
currentFormData: quotationFormData, currentFormData: quotationFormData,
currentFormState: quotationFormState, currentFormState: quotationFormState,
} = storeToRefs(quotationFormStore); } = storeToRefs(quotationFormStore);
const { state: customerFormState, currentFormData: customerFormData } = const {
storeToRefs(customerFormStore); state: customerFormState,
currentFormData: customerFormData,
registerAbleBranchOption,
tabFieldRequired,
} = storeToRefs(customerFormStore);
const { currentMyBranch } = storeToRefs(userBranch); const { currentMyBranch } = storeToRefs(userBranch);
const fieldSelectedOption = computed(() => { const fieldSelectedOption = computed(() => {
@ -170,8 +189,7 @@ async function submitCustomer() {
customerFormData.value.registeredBranchId = isRoleInclude(['system']) customerFormData.value.registeredBranchId = isRoleInclude(['system'])
? branchId.value ? branchId.value
: currentMyBranch.value.id; : currentMyBranch.value.id;
await customerFormStore.submitFormCustomer(); await customerFormStore.addCurrentCustomerBranch();
customerFormState.value.dialogModal = false; customerFormState.value.dialogModal = false;
// customerFormState.value.dialogType = 'info'; // customerFormState.value.dialogType = 'info';
} }
@ -241,6 +259,16 @@ const {
stats: quotationStats, stats: quotationStats,
} = storeToRefs(quotationStore); } = storeToRefs(quotationStore);
const customerNameInfo = computed(() => {
if (customerFormData.value.customerBranch === undefined) return;
const name =
locale.value === 'eng'
? `${customerFormData.value.customerBranch[0]?.firstNameEN} ${customerFormData.value.customerBranch[0]?.lastNameEN}`
: `${customerFormData.value.customerBranch[0]?.firstName} ${customerFormData.value.customerBranch[0]?.lastName}`;
return name || '-';
});
onMounted(async () => { 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';
@ -911,30 +939,49 @@ async function storeDataLocal(id: string) {
<!-- NOTE: START - Customer / Employer Form --> <!-- NOTE: START - Customer / Employer Form -->
<DialogForm <DialogContainer
hide-footer :model-value="customerFormState.dialogModal"
ref="formDialogRef" :on-open="
v-model:modal="customerFormState.dialogModal" async () => {
:title=" customerFormStore.resetForm(customerFormState.dialogType === 'create');
customerFormState.dialogType === 'create' onCreateImageList = { selectedImage: '', list: [] };
? $t(`general.add`, { customerFormState.customerImageUrl = '';
text: `${$t('customer.employer')} `, await fetchListOfOptionBranch();
}) await customerFormStore.addCurrentCustomerBranch();
: `${$t('customer.employer')} `
"
:submit="
() => {
submitCustomer();
} }
" "
:close=" :on-close="
() => { () => {
customerFormState.dialogModal = false; customerFormState.dialogModal = false;
customerFormStore.resetForm(true); onCreateImageList = { selectedImage: '', list: [] };
setDefaultCustomer();
} }
" "
> >
<template #header>
<DialogHeader
:title="
customerFormState.dialogType === 'create'
? $t(`general.add`, {
text: `${$t('customer.employer')} `,
})
: `${$t('customer.employer')} `
"
>
<template #title-after>
<span
:style="`color: hsla(var(--${customerFormData.customerType === 'PERS' ? 'teal-10-hsl' : 'violet-11-hsl'})/1)`"
>
:
{{
customerFormData.customerType === 'CORP'
? $t('customer.employerLegalEntity')
: $t('customer.employerNaturalPerson')
}}
</span>
</template>
</DialogHeader>
</template>
<div <div
:class="{ :class="{
'q-mx-lg q-my-md': $q.screen.gt.sm, 'q-mx-lg q-my-md': $q.screen.gt.sm,
@ -942,10 +989,10 @@ async function storeDataLocal(id: string) {
}" }"
> >
<ProfileBanner <ProfileBanner
prefix="dialog"
v-if="customerFormData.customerBranch !== undefined" v-if="customerFormData.customerBranch !== undefined"
active active
hide-fade hide-fade
prefix="dialog"
:fallback-cover="`/images/customer-${customerFormData.customerType}-banner-bg.jpg`" :fallback-cover="`/images/customer-${customerFormData.customerType}-banner-bg.jpg`"
:img=" :img="
customerFormState.customerImageUrl || customerFormState.customerImageUrl ||
@ -961,13 +1008,13 @@ async function storeDataLocal(id: string) {
" "
:title=" :title="
customerFormData.customerType === 'PERS' customerFormData.customerType === 'PERS'
? `${customerFormData.customerBranch[0]?.firstName || ''} ${customerFormData.customerBranch[0]?.lastName || ''}` ? `${customerFormData.customerBranch[0]?.firstName} ${customerFormData.customerBranch[0]?.lastName}`
: customerFormData.customerBranch[0]?.registerName || '' : customerFormData.customerBranch[0]?.registerName
" "
:caption=" :caption="
customerFormData.customerType === 'PERS' customerFormData.customerType === 'PERS'
? `${customerFormData.customerBranch[0]?.firstNameEN || ''} ${customerFormData.customerBranch[0]?.lastNameEN || ''}` ? `${customerFormData.customerBranch[0]?.firstNameEN} ${customerFormData.customerBranch[0]?.lastNameEN}`
: customerFormData.customerBranch[0]?.registerNameEN || '' : customerFormData.customerBranch[0]?.registerNameEN
" "
@view=" @view="
() => { () => {
@ -981,143 +1028,295 @@ async function storeDataLocal(id: string) {
/> />
</div> </div>
<div <div
class="col" style="flex: 1; width: 100%; overflow-y: auto"
id="customer-form"
:class="{ :class="{
'q-px-lg q-pb-lg': $q.screen.gt.sm, 'q-px-lg q-pb-lg': $q.screen.gt.sm,
'q-px-md q-pb-sm': !$q.screen.gt.sm, 'q-px-md q-pb-sm': !$q.screen.gt.sm,
}" }"
> >
<div <div class="col surface-1 full-height rounded bordered scroll row">
style="overflow-y: auto"
class="row full-width full-height surface-1 rounded bordered relative-position"
>
<div <div
:class="{ class="col"
'q-py-md q-px-lg': $q.screen.gt.sm, style="height: 100%; max-height: 100; overflow-y: auto"
'q-py-sm q-px-lg': !$q.screen.gt.sm, v-if="$q.screen.gt.sm"
}"
style="position: absolute; z-index: 99999; top: 0; right: 0"
> >
<div <div class="q-py-md q-pl-md q-pr-sm">
v-if="customerFormData.status !== 'INACTIVE'" <SideMenu
class="surface-1 row rounded" :menu="[
> {
<SaveButton name: $t('form.field.basicInformation'),
v-if=" anchor: 'form-basic-info-customer',
customerFormState.dialogType === 'edit' || },
customerFormState.dialogType === 'create' {
" name: $t('customer.form.group.branch'),
id="btn-info-basic-save" anchor: 'form-branch-customer-branch',
icon-only useBtn: true,
type="submit" },
/> ...(customerFormData.customerBranch?.map((v, i) => ({
name:
i === 0
? $t('customer.form.headQuarters.title')
: $t('customer.form.branch.title', {
name: i,
}),
anchor: `form-branch-customer-no-${i}`,
sub: true,
})) || []),
]"
background="transparent"
:active="{
background: 'hsla(var(--blue-6-hsl) / .2)',
foreground: 'var(--blue-6)',
}"
scroll-element="#customer-form-content"
>
<template v-slot:btn-form-branch-customer-branch>
<q-btn
dense
flat
icon="mdi-plus"
size="sm"
rounded
padding="0px 0px"
style="color: var(--stone-9)"
@click.stop="submitCustomer()"
v-if="
customerFormState.branchIndex === -1 &&
!!customerFormState.editCustomerId &&
customerFormState.dialogType !== 'create'
"
:disabled="!customerFormState.readonly"
/>
</template>
</SideMenu>
</div> </div>
</div> </div>
<div <div
class="col full-height rounded scroll row q-py-md q-pl-md q-pr-sm" class="col-12 col-md-10"
v-if="$q.screen.gt.sm"
>
<SideMenu
:menu="[
{
name: $t('form.customerInformation'),
anchor: 'form-information',
},
{
name: $t('customerBranch.tab.business'),
anchor: 'form-business',
},
{
name: $t('form.address'),
anchor: 'form-address',
},
]"
background="transparent"
:active="{
background: 'hsla(var(--blue-6-hsl) / .2)',
foreground: 'var(--blue-6)',
}"
scroll-element="#branch-form"
/>
</div>
<div
class="col-12 col-md-10 full-height q-col-gutter-sm"
: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-pa-sm': !$q.screen.gt.sm,
}" }"
id="branch-form" id="customer-form-content"
style="overflow-y: auto" style="height: 100%; max-height: 100%; overflow-y: auto"
> >
<EmployerFormAbout <EmployerFormBasicInfo
id="form-information" prefixId="form"
show-title v-if="
:index="'0'" customerFormData.customerBranch !== undefined &&
:customerType="customerFormData.customerType" customerFormData.customerBranch.length > 0
:readonly="customerFormState.dialogType === 'info'"
v-model:citizen-id="formDataCustomerBranch.citizenId"
v-model:prefix-name="formDataCustomerBranch.namePrefix"
v-model:first-name="formDataCustomerBranch.firstName"
v-model:last-name="formDataCustomerBranch.lastName"
v-model:first-name-en="formDataCustomerBranch.firstNameEN"
v-model:last-name-en="formDataCustomerBranch.lastNameEN"
v-model:gender="formDataCustomerBranch.gender"
v-model:birth-date="formDataCustomerBranch.birthDate"
v-model:customer-name="formDataCustomerBranch.customerName"
v-model:legal-person-no="formDataCustomerBranch.legalPersonNo"
v-model:register-name="formDataCustomerBranch.registerName"
v-model:register-name-en="formDataCustomerBranch.registerNameEN"
v-model:register-date="formDataCustomerBranch.registerDate"
v-model:authorized-capital="
formDataCustomerBranch.authorizedCapital
" "
v-model:telephone-no="formDataCustomerBranch.telephoneNo" class="q-mb-xl"
:readonly="
(customerFormState.dialogType === 'edit' &&
customerFormState.readonly === true) ||
customerFormState.dialogType === 'info'
"
:action-disabled="customerFormState.branchIndex !== -1"
id="form-basic-info-customer"
:onCreate="customerFormState.dialogType === 'create'"
@edit="
(customerFormState.dialogType = 'edit'),
(customerFormState.readonly = false)
"
@cancel="() => customerFormUndo(false)"
@delete="
customerFormState.editCustomerId &&
deleteCustomerById(customerFormState.editCustomerId)
"
:customer-type="customerFormData.customerType"
v-model:registered-branch-id="customerFormData.registeredBranchId"
v-model:customer-name="customerNameInfo"
v-model:register-name="
customerFormData.customerBranch[0].registerName
"
v-model:citizen-id="customerFormData.customerBranch[0].citizenId"
v-model:legal-person-no="
customerFormData.customerBranch[0].legalPersonNo
"
v-model:business-type="
customerFormData.customerBranch[0].businessType
"
v-model:job-position="
customerFormData.customerBranch[0].jobPosition
"
v-model:telephone-no="
customerFormData.customerBranch[0].telephoneNo
"
v-model:branch-options="registerAbleBranchOption"
/> />
<div class="row q-col-gutter-sm" id="form-branch-customer-branch">
<div class="col-12 text-weight-bold text-body1 row items-center">
<q-icon
flat
size="xs"
class="q-pa-sm rounded q-mr-xs"
color="info"
name="mdi-briefcase-outline"
style="background-color: var(--surface-3)"
/>
<span>{{ $t('customer.form.group.branch') }}</span>
</div>
<EmployerFormBusiness <template
id="form-business" v-for="(_, idx) in customerFormData.customerBranch"
show-title :key="idx"
prefix-id="dialog" >
dense <!-- v-if="customerFormData.customerBranch" -->
outlined <q-form
:readonly="customerFormState.dialogType === 'info'" class="full-width"
v-model:bussiness-type="formDataCustomerBranch.businessType" greedy
v-model:job-position="formDataCustomerBranch.jobPosition" @submit.prevent="
v-model:job-description="formDataCustomerBranch.jobDescription" async () => {
v-model:pay-date="formDataCustomerBranch.payDate" if (!customerFormData.customerBranch) return;
v-model:pay-date-en="formDataCustomerBranch.payDateEN"
v-model:wage-rate="formDataCustomerBranch.wageRate" if (customerFormData.customerType === 'PERS') {
v-model:wage-rate-text="formDataCustomerBranch.wageRateText" tabFieldRequired.main = [
/> 'citizenId',
<AddressForm 'namePrefix',
id="form-address" 'firstName',
prefix-id="employer" 'firstNameEN',
dense 'lastName',
outlined 'lastNameEN',
use-employment 'gender',
:title="$t('form.address')" 'birthDate',
:addressTitle="$t('form.address')" ];
:addressTitleEN="$t('form.address', { suffix: '(EN)' })" }
:readonly="customerFormState.dialogType === 'info'" if (customerFormData.customerType === 'CORP') {
v-model:bussiness-type="formDataCustomerBranch.businessType" tabFieldRequired.main = ['legalPersonNo', 'registerName'];
v-model:address="formDataCustomerBranch.address" }
v-model:address-en="formDataCustomerBranch.addressEN" let tapIsUndefined = validateTabField(
v-model:street="formDataCustomerBranch.street" customerFormData.customerBranch?.[idx],
v-model:street-en="formDataCustomerBranch.streetEN" tabFieldRequired,
v-model:moo="formDataCustomerBranch.moo" );
v-model:moo-en="formDataCustomerBranch.mooEN"
v-model:soi="formDataCustomerBranch.soi" if (tapIsUndefined.length > 0) {
v-model:soi-en="formDataCustomerBranch.soiEN" return dialog({
v-model:province-id="formDataCustomerBranch.provinceId" color: 'warning',
v-model:district-id="formDataCustomerBranch.districtId" icon: 'mdi-alert',
v-model:sub-district-id="formDataCustomerBranch.subDistrictId" title: t('dialog.title.incompleteDataEntry'),
v-model:home-code="formDataCustomerBranch.homeCode" actionText: t('general.ok'),
/> persistent: true,
message: t('dialog.message.incompleteDataEntry', {
tap: `${tapIsUndefined.map((v: string) => t(`customerBranch.tab.${v}`)).join(', ')}`,
}),
action: async () => {
return;
},
});
}
if (!customerFormData.customerBranch[idx].id) {
let res: any;
if (idx === 0) {
res =
await customerFormStore.submitFormCustomer(
onCreateImageList,
);
} else {
res = await customerStore.createBranch({
...customerFormData.customerBranch[idx],
customerId: customerFormState.editCustomerId || '',
id: undefined,
});
}
if (res) {
customerFormState.readonly = true;
notify('create', $t('general.success'));
}
} else {
if (!customerFormState.editCustomerId) return;
await customerStore.editBranchById(
customerFormData.customerBranch[idx].id || '',
{
...customerFormData.customerBranch[idx],
id: undefined,
},
);
}
const uploadResult =
customerFormData.customerBranch[idx].file?.map(
async (v) => {
if (!v.file) return;
const ext = v.file.name.split('.').at(-1);
let filename = v.group + '-' + new Date().getTime();
if (ext) filename += `.${ext}`;
const res = await customerStore.putAttachment({
branchId:
customerFormData.customerBranch?.[idx].id || '',
file: v.file,
filename,
});
if (res) {
await customerFormStore.assignFormData(
customerFormState.editCustomerId,
);
}
},
) || [];
for (const r of uploadResult) await r;
await customerFormStore.assignFormData(
customerFormState.editCustomerId,
);
customerFormStore.resetForm();
}
"
>
<!-- v-if="!!customerFormState.editCustomerId" -->
<EmployerFormBranch
:index="idx"
prefixId="form"
v-if="customerFormData.customerBranch"
v-model:customer="customerFormData"
v-model:customer-branch="customerFormData.customerBranch[idx]"
:onCreate="customerFormState.dialogType === 'create'"
:customer-type="customerFormData.customerType"
:readonly="customerFormState.branchIndex !== idx"
:action-disabled="
!customerFormState.readonly ||
(customerFormState.branchIndex !== -1 &&
customerFormState.branchIndex !== idx)
"
@edit="() => (customerFormState.branchIndex = idx)"
@cancel="() => customerFormUndo(false)"
@delete="
async () => {
if (!customerFormState.editCustomerId) return;
if (idx === 0) {
deleteCustomerById(customerFormState.editCustomerId);
return;
}
if (!!customerFormData.customerBranch?.[idx].id) {
const action = await deleteCustomerBranchById(
customerFormData.customerBranch[idx].id || '',
);
if (action) {
await customerFormStore.assignFormData(
customerFormState.editCustomerId,
);
}
customerFormStore.resetForm();
}
}
"
@save="() => {}"
/>
</q-form>
</template>
</div>
</div> </div>
</div> </div>
</div> </div>
</DialogForm> </DialogContainer>
</template> </template>
<style scoped></style> <style scoped></style>

View file

@ -164,7 +164,9 @@ const selectedWorkerItem = computed(() => {
employeeName: employeeName:
locale.value === Lang.English locale.value === Lang.English
? `${e.firstNameEN} ${e.lastNameEN}` ? `${e.firstNameEN} ${e.lastNameEN}`
: `${e.firstName} ${e.lastName}`, : e.firstName
? `${e.firstName} ${e.lastName}`
: `${e.firstNameEN} ${e.lastNameEN}`,
birthDate: dateFormatJS({ date: e.dateOfBirth }), birthDate: dateFormatJS({ date: e.dateOfBirth }),
gender: e.gender, gender: e.gender,
age: calculateAge(e.dateOfBirth), age: calculateAge(e.dateOfBirth),
@ -590,8 +592,9 @@ async function convertDataToFormSubmit() {
} }
}), }),
...newWorkerList.value.map((v) => { ...newWorkerList.value.map((v) => {
const { attachment, ...payload } = v; {
return payload; return v.id;
}
}), }),
]), ]),
); );

File diff suppressed because it is too large Load diff

View file

@ -221,6 +221,7 @@ export const useQuotationForm = defineStore('form-quotation', () => {
newWorkerList.value.push({ newWorkerList.value.push({
//passportNo: obj.data.passportNo, //passportNo: obj.data.passportNo,
//documentExpireDate: obj.data.documentExpireDate, //documentExpireDate: obj.data.documentExpireDate,
id: obj.data.id,
lastNameEN: obj.data.lastNameEN, lastNameEN: obj.data.lastNameEN,
lastName: obj.data.lastName, lastName: obj.data.lastName,
middleNameEN: obj.data.middleNameEN, middleNameEN: obj.data.middleNameEN,

View file

@ -10,6 +10,7 @@ import DrawerInfo from 'src/components/DrawerInfo.vue';
import DialogForm from 'src/components/DialogForm.vue'; import DialogForm from 'src/components/DialogForm.vue';
import ProfileBanner from 'src/components/ProfileBanner.vue'; import ProfileBanner from 'src/components/ProfileBanner.vue';
import SideMenu from 'src/components/SideMenu.vue'; import SideMenu from 'src/components/SideMenu.vue';
import FormBank from 'src/components/01_branch-management/FormBank.vue';
import FormBasicInfoAgencies from 'src/components/07_agencies-management/FormBasicInfoAgencies.vue'; import FormBasicInfoAgencies from 'src/components/07_agencies-management/FormBasicInfoAgencies.vue';
import { import {
UndoButton, UndoButton,
@ -19,6 +20,7 @@ import {
} from 'src/components/button'; } from 'src/components/button';
import AddressForm from 'src/components/form/AddressForm.vue'; import AddressForm from 'src/components/form/AddressForm.vue';
import ImageUploadDialog from 'src/components/ImageUploadDialog.vue'; import ImageUploadDialog from 'src/components/ImageUploadDialog.vue';
import { BankBook } from 'src/stores/branch/types';
const institutionStore = useInstitution(); const institutionStore = useInstitution();
@ -73,6 +75,9 @@ const data = defineModel<InstitutionPayload>('data', {
group: '', group: '',
name: '', name: '',
nameEN: '', nameEN: '',
contactName: '',
contactEmail: '',
contactTel: '',
code: '', code: '',
addressEN: '', addressEN: '',
address: '', address: '',
@ -88,6 +93,19 @@ const data = defineModel<InstitutionPayload>('data', {
selectedImage: '', selectedImage: '',
}, },
}); });
const formBankBook = defineModel<BankBook[]>('formBankBook', {
default: [
{
bankName: '',
accountNumber: '',
bankBranch: '',
accountName: '',
accountType: '',
currentlyUse: true,
bankUrl: '',
},
],
});
function viewImage() { function viewImage() {
imageState.imageDialog = true; imageState.imageDialog = true;
@ -237,11 +255,15 @@ watch(
:menu="[ :menu="[
{ {
name: $t('form.field.basicInformation'), name: $t('form.field.basicInformation'),
anchor: 'agencies-basic-info', anchor: 'agencies-form-basic-info',
}, },
{ {
name: $t('general.address'), name: $t('general.address'),
anchor: 'agencies-address-info', anchor: 'agencies-form-address-info',
},
{
name: $t('agencies.bankInfo'),
anchor: 'agencies-form-bank-info',
}, },
]" ]"
background="transparent" background="transparent"
@ -275,14 +297,17 @@ watch(
</div> </div>
</div> </div>
<FormBasicInfoAgencies <FormBasicInfoAgencies
id="agencies-basic-info" id="agencies-form-basic-info"
class="q-mb-xl" class="q-mb-xl"
v-model:group="data.group" v-model:group="data.group"
v-model:name="data.name" v-model:name="data.name"
v-model:name-en="data.nameEN" v-model:name-en="data.nameEN"
v-model:contact-name="data.contactName"
v-model:email="data.contactEmail"
v-model:contact-tel="data.contactTel"
/> />
<AddressForm <AddressForm
id="agencies-address-info" id="agencies-form-address-info"
dense dense
:prefix-id="''" :prefix-id="''"
v-model:address="data.address" v-model:address="data.address"
@ -297,6 +322,14 @@ watch(
v-model:district-id="data.districtId" v-model:district-id="data.districtId"
v-model:sub-district-id="data.subDistrictId" v-model:sub-district-id="data.subDistrictId"
/> />
<FormBank
id="agencies-form-bank-info"
title="agencies.bankInfo"
class="q-pt-xl"
dense
single
v-model:bank-book-list="formBankBook"
/>
</div> </div>
</div> </div>
</DialogForm> </DialogForm>
@ -427,13 +460,17 @@ watch(
name: $t('general.address'), name: $t('general.address'),
anchor: 'agencies-address-info', anchor: 'agencies-address-info',
}, },
{
name: $t('agencies.bankInfo'),
anchor: 'agencies-bank-info',
},
]" ]"
background="transparent" background="transparent"
:active="{ :active="{
background: 'hsla(var(--blue-6-hsl) / .2)', background: 'hsla(var(--blue-6-hsl) / .2)',
foreground: 'var(--blue-6)', foreground: 'var(--blue-6)',
}" }"
scroll-element="#agencies-form-content" scroll-element="#agencies-view-content"
/> />
</div> </div>
</div> </div>
@ -444,7 +481,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="user-form-content" id="agencies-view-content"
style="height: 100%; max-height: 100; overflow-y: auto" style="height: 100%; max-height: 100; overflow-y: auto"
> >
<FormBasicInfoAgencies <FormBasicInfoAgencies
@ -455,6 +492,9 @@ watch(
v-model:group="data.group" v-model:group="data.group"
v-model:name="data.name" v-model:name="data.name"
v-model:name-en="data.nameEN" v-model:name-en="data.nameEN"
v-model:contact-name="data.contactName"
v-model:email="data.contactEmail"
v-model:contact-tel="data.contactTel"
/> />
<AddressForm <AddressForm
id="agencies-address-info" id="agencies-address-info"
@ -473,6 +513,15 @@ watch(
v-model:district-id="data.districtId" v-model:district-id="data.districtId"
v-model:sub-district-id="data.subDistrictId" v-model:sub-district-id="data.subDistrictId"
/> />
<FormBank
id="agencies-bank-info"
title="agencies.bankInfo"
class="q-pt-xl"
dense
single
:readonly
v-model:bank-book-list="formBankBook"
/>
</div> </div>
</div> </div>
</div> </div>

View file

@ -85,6 +85,9 @@ const blankFormData: InstitutionPayload = {
code: '', code: '',
name: '', name: '',
nameEN: '', nameEN: '',
contactName: '',
contactEmail: '',
contactTel: '',
addressEN: '', addressEN: '',
address: '', address: '',
soi: '', soi: '',
@ -98,6 +101,16 @@ const blankFormData: InstitutionPayload = {
provinceId: '', provinceId: '',
selectedImage: '', selectedImage: '',
status: 'CREATED', status: 'CREATED',
bank: [
{
bankName: '',
accountNumber: '',
bankBranch: '',
accountName: '',
accountType: '',
currentlyUse: true,
},
],
}; };
const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all'); const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all');
@ -160,6 +173,19 @@ function assignFormData(data: Institution) {
provinceId: data.provinceId, provinceId: data.provinceId,
selectedImage: data.selectedImage, selectedImage: data.selectedImage,
status: data.status, status: data.status,
contactEmail: data.contactEmail,
contactName: data.contactName,
contactTel: data.contactTel,
bank: [
{
bankName: data.bank[0]?.bankName,
accountNumber: data.bank[0]?.accountNumber,
bankBranch: data.bank[0]?.bankBranch,
accountName: data.bank[0]?.accountName,
accountType: data.bank[0]?.accountType,
currentlyUse: data.bank[0]?.currentlyUse,
},
],
}; };
} }
@ -169,6 +195,9 @@ async function submit(opt?: { selectedImage: string }) {
code: formData.value.code, code: formData.value.code,
name: formData.value.name, name: formData.value.name,
nameEN: formData.value.nameEN, nameEN: formData.value.nameEN,
contactName: formData.value.contactName,
contactEmail: formData.value.contactEmail,
contactTel: formData.value.contactTel,
addressEN: formData.value.addressEN, addressEN: formData.value.addressEN,
address: formData.value.address, address: formData.value.address,
soi: formData.value.soi, soi: formData.value.soi,
@ -181,6 +210,14 @@ async function submit(opt?: { selectedImage: string }) {
districtId: formData.value.districtId, districtId: formData.value.districtId,
provinceId: formData.value.provinceId, provinceId: formData.value.provinceId,
status: formData.value.status, status: formData.value.status,
bank: formData.value.bank.map((v) => ({
bankName: v.bankName,
accountNumber: v.accountNumber,
bankBranch: v.bankBranch,
accountName: v.accountName,
accountType: v.accountType,
currentlyUse: v.currentlyUse,
})),
}; };
if ( if (
(pageState.isDrawerEdit && currAgenciesData.value?.id) || (pageState.isDrawerEdit && currAgenciesData.value?.id) ||
@ -922,6 +959,7 @@ watch(
v-model="pageState.addModal" v-model="pageState.addModal"
v-model:drawer-model="pageState.viewDrawer" v-model:drawer-model="pageState.viewDrawer"
v-model:data="formData" v-model:data="formData"
v-model:form-bank-book="formData.bank"
v-model:on-create-image-list="onCreateImageList" v-model:on-create-image-list="onCreateImageList"
/> />
</template> </template>

View file

@ -1,4 +1,5 @@
import { District, Province, SubDistrict } from '../address'; import { District, Province, SubDistrict } from '../address';
import { BankBook } from '../branch/types';
import { Status } from '../types'; import { Status } from '../types';
export type Institution = { export type Institution = {
@ -25,6 +26,11 @@ export type Institution = {
districtId: string; districtId: string;
provinceId: string; provinceId: string;
status: Status; status: Status;
contactName?: string | null;
contactEmail?: string | null;
contactTel?: string | null;
bank: BankBook[];
}; };
export type InstitutionPayload = { export type InstitutionPayload = {
@ -34,6 +40,11 @@ export type InstitutionPayload = {
group?: string; group?: string;
selectedImage?: string | null; selectedImage?: string | null;
contactName?: string | null;
contactEmail?: string | null;
contactTel?: string | null;
bank: BankBook[];
addressEN: string; addressEN: string;
address: string; address: string;
soi?: string | null; soi?: string | null;

View file

@ -75,6 +75,7 @@ const useOptionStore = defineStore('optionStore', () => {
} }
return { return {
rawOption,
globalOption, globalOption,
mapOption, mapOption,
}; };

View file

@ -57,6 +57,9 @@ export type User = {
citizenIssue?: Date | null; citizenIssue?: Date | null;
citizenId: string; citizenId: string;
branch: Branch[]; branch: Branch[];
remark?: string;
agencyStatus?: AgencyStatus;
}; };
export type UserCreate = { export type UserCreate = {
@ -105,8 +108,17 @@ export type UserCreate = {
citizenExpire?: Date | null; citizenExpire?: Date | null;
citizenIssue?: Date | null; citizenIssue?: Date | null;
citizenId: string; citizenId: string;
remark?: string;
agencyStatus?: AgencyStatus | string;
}; };
export enum AgencyStatus {
Normal = 'Normal',
Canceled = 'Canceled',
Blacklist = 'Blacklist',
}
export type UserAttachment = { export type UserAttachment = {
name: string; name: string;
url: string; url: string;

View file

@ -19,34 +19,34 @@ export function formatAddress(opt: {
let addressParts: string[]; let addressParts: string[];
if (opt.en) { if (opt.en) {
// en // en
addressParts = [`${opt.addressEN},`]; addressParts = [`${opt.addressEN}`];
if (opt.mooEN) addressParts.push(`Moo ${opt.mooEN},`); if (opt.mooEN) addressParts.push(`Moo ${opt.mooEN}`);
if (opt.soiEN) addressParts.push(`Soi ${opt.soiEN},`); if (opt.soiEN) addressParts.push(`Soi ${opt.soiEN}`);
if (opt.streetEN) addressParts.push(`${opt.streetEN} Rd.`); if (opt.streetEN) addressParts.push(`${opt.streetEN} Rd.`);
if (opt.subDistrict) { if (opt.subDistrict) {
addressParts.push(`${opt.subDistrict.nameEN} sub-district,`); addressParts.push(`${opt.subDistrict.nameEN} sub-district`);
} }
if (opt.district) addressParts.push(`${opt.district.nameEN} district,`); if (opt.district) addressParts.push(`${opt.district.nameEN} distric`);
if (opt.province) addressParts.push(`${opt.province.nameEN},`); if (opt.province) addressParts.push(`${opt.province.nameEN}`);
} else { } else {
// th // th
addressParts = [`${opt.address},`]; addressParts = [`${opt.address}`];
if (opt.moo) addressParts.push(`หมู่ ${opt.moo},`); if (opt.moo) addressParts.push(`หมู่ ${opt.moo}`);
if (opt.soi) addressParts.push(`ซอย ${opt.soi},`); if (opt.soi) addressParts.push(`ซอย ${opt.soi}`);
if (opt.street) addressParts.push(`ถนน${opt.street},`); if (opt.street) addressParts.push(`ถนน${opt.street}`);
if (opt.subDistrict) { if (opt.subDistrict) {
addressParts.push( addressParts.push(
`${opt.province?.id === '10' ? 'แขวง' : 'ตำบล'}${opt.subDistrict.name},`, `${opt.province?.id === '10' ? 'แขวง' : 'ตำบล'}${opt.subDistrict.name}`,
); );
} }
if (opt.district) { if (opt.district) {
addressParts.push( addressParts.push(
`${opt.province?.id === '10' ? 'เขต' : 'อำเภอ'}${opt.district.name},`, `${opt.province?.id === '10' ? 'เขต' : 'อำเภอ'}${opt.district.name}`,
); );
} }
if (opt.province) addressParts.push(`จังหวัด${opt.province.name},`); if (opt.province) addressParts.push(`จังหวัด${opt.province.name}`);
// addressParts.push( // addressParts.push(
// `${opt.province.id === '10' ? t('address.subArea') : t('address.subDistrict')}${opt.subDistrict.name},`, // `${opt.province.id === '10' ? t('address.subArea') : t('address.subDistrict')}${opt.subDistrict.name},`,
// ); // );