feat(02): citizen id, issue, expire

This commit is contained in:
puriphatt 2024-09-12 15:41:54 +07:00
parent 6a6bf68cd9
commit aece743ccf
6 changed files with 387 additions and 264 deletions

View file

@ -20,6 +20,9 @@ const birthDate = defineModel<Date | string | null>('birthDate');
const nationality = defineModel<string>('nationality'); const nationality = defineModel<string>('nationality');
const midName = defineModel<string | null>('midName'); const midName = defineModel<string | null>('midName');
const midNameEN = defineModel<string | null>('midNameEN'); const midNameEN = defineModel<string | null>('midNameEN');
const citizenId = defineModel<string>('citizenId');
const citizenIssue = defineModel<Date | null>('citizenIssue');
const citizenExpire = defineModel<Date | null>('citizenExpire');
const props = defineProps<{ const props = defineProps<{
dense?: boolean; dense?: boolean;
@ -112,6 +115,26 @@ watch(
</div> </div>
<div class="col-12 row q-col-gutter-sm"> <div class="col-12 row q-col-gutter-sm">
<q-input
v-if="!employee"
outlined
class="col-5"
hide-bottom-space
v-model="citizenId"
mask="#############"
:readonly="readonly"
:dense="dense"
:label="$t('personnel.form.citizenId')"
:rules="[
(val) => (val && val.length > 0) || $t('form.error.required'),
(val) =>
(val && val.length === 13 && /[0-9]+/.test(val)) ||
$t('form.error.invalidCustomeMessage', {
msg: $t('form.error.requireLength', { msg: 13 }),
}),
]"
for="input-citizen-id"
/>
<div class="col-12 row" style="display: flex; gap: var(--size-2)"> <div class="col-12 row" style="display: flex; gap: var(--size-2)">
<q-select <q-select
outlined outlined
@ -196,7 +219,7 @@ watch(
:readonly="readonly" :readonly="readonly"
:disable="!readonly" :disable="!readonly"
class="col-md-1 col-6" class="col-md-1 col-6"
:label="$t('personnel.form.prefixName')" label="Title"
:model-value=" :model-value="
readonly readonly
? capitalize(prefixName || '') || '-' ? capitalize(prefixName || '') || '-'
@ -215,7 +238,7 @@ watch(
:readonly="readonly" :readonly="readonly"
hide-bottom-space hide-bottom-space
class="col" class="col"
:label="$t('personnel.form.firstNameEN')" label="Name"
v-model="firstNameEN" v-model="firstNameEN"
:rules="[ :rules="[
(val: string) => !!val || $t('form.error.required'), (val: string) => !!val || $t('form.error.required'),
@ -230,7 +253,7 @@ watch(
:readonly="readonly" :readonly="readonly"
hide-bottom-space hide-bottom-space
class="col-md-3 col-6" class="col-md-3 col-6"
:label="$t('personnel.form.middleNameEN')" label="Mid Name"
:model-value="readonly ? midNameEN || '-' : midNameEN" :model-value="readonly ? midNameEN || '-' : midNameEN"
@update:model-value=" @update:model-value="
(v) => (typeof v === 'string' ? (midNameEN = v) : '') (v) => (typeof v === 'string' ? (midNameEN = v) : '')
@ -244,7 +267,7 @@ watch(
:readonly="readonly" :readonly="readonly"
hide-bottom-space hide-bottom-space
class="col" class="col"
:label="$t('personnel.form.lastNameEN')" label="Surname"
v-model="lastNameEN" v-model="lastNameEN"
:rules="[ :rules="[
(val: string) => !!val || $t('form.error.required'), (val: string) => !!val || $t('form.error.required'),
@ -268,12 +291,22 @@ watch(
(v) => (typeof v === 'string' ? (telephoneNo = v) : '') (v) => (typeof v === 'string' ? (telephoneNo = v) : '')
" "
@clear="telephoneNo = ''" @clear="telephoneNo = ''"
/> >
<template #prepend>
<q-icon
size="xs"
name="mdi-phone-outline"
class="cursor-pointer"
color="primary"
/>
</template>
</q-input>
<q-input <q-input
v-if="!employee" v-if="!employee"
:for="`${prefixId}-input-email`" :for="`${prefixId}-input-email`"
:dense="dense" :dense="dense"
outlined outlined
hide-bottom-space
:readonly="readonly" :readonly="readonly"
:label="$t('form.email')" :label="$t('form.email')"
:rules=" :rules="
@ -290,7 +323,16 @@ watch(
:model-value="readonly ? email || '-' : email" :model-value="readonly ? email || '-' : email"
@update:model-value="(v) => (typeof v === 'string' ? (email = v) : '')" @update:model-value="(v) => (typeof v === 'string' ? (email = v) : '')"
@clear="email = ''" @clear="email = ''"
/> >
<template #prepend>
<q-icon
size="xs"
name="mdi-email-outline"
class="cursor-pointer"
color="primary"
/>
</template>
</q-input>
<q-select <q-select
v-if="!employee" v-if="!employee"
@ -355,6 +397,33 @@ watch(
" "
/> />
<DatePicker
v-if="!employee"
v-model="citizenIssue"
class="col-md-2 col-6"
:id="`${prefixId}-input-citizen-issue`"
:readonly="readonly"
:label="$t('personnel.form.citizenIssue')"
:disabled-dates="disabledAfterToday"
:rules="[
(val: string) =>
!!val ||
$t('form.error.selectField', {
field: $t('personnel.form.citizenIssue'),
}),
]"
/>
<DatePicker
v-if="!employee"
v-model="citizenExpire"
class="col-md-2 col-6"
:id="`${prefixId}-input-citizen-expire`"
:readonly="readonly"
:label="$t('personnel.form.citizenExpire')"
:disabled-dates="disabledAfterToday"
/>
<q-select <q-select
v-if="employee" v-if="employee"
outlined outlined

View file

@ -498,7 +498,7 @@ watch(districtId, fetchSubDistrict);
:label="$t('form.fullAddress')" :label="$t('form.fullAddress')"
readonly readonly
:disable="!readonly && !sameWithEmployer" :disable="!readonly && !sameWithEmployer"
:for="`${prefixId}-${indexId !== undefined ? `input-street-${indexId}` : 'input-street'}`" :for="`${prefixId}-${indexId !== undefined ? `input-full-address-${indexId}` : 'input-full-address'}`"
/> />
</div> </div>
@ -727,7 +727,7 @@ watch(districtId, fetchSubDistrict);
label="Full Address" label="Full Address"
readonly readonly
:disable="!readonly && !sameWithEmployer" :disable="!readonly && !sameWithEmployer"
:for="`${prefixId}-${indexId !== undefined ? `input-street-${indexId}` : 'input-street'}`" :for="`${prefixId}-${indexId !== undefined ? `input-full-address-en-${indexId}` : 'input-full-address-en'}`"
/> />
</div> </div>
</div> </div>

View file

@ -270,6 +270,9 @@ export default {
checkpoint: 'Checkpoint', checkpoint: 'Checkpoint',
checkpointEN: 'Checkpoint (EN)', checkpointEN: 'Checkpoint (EN)',
attachment: 'Attachment Document', attachment: 'Attachment Document',
citizenId: 'Citizen ID',
citizenIssue: 'Citizen Issue',
citizenExpire: 'Citizen Expire',
}, },
}, },
customer: { customer: {

View file

@ -269,6 +269,9 @@ export default {
checkpoint: 'ด่าน', checkpoint: 'ด่าน',
checkpointEN: 'ด่าน ภาษาอังกฤษ', checkpointEN: 'ด่าน ภาษาอังกฤษ',
attachment: 'เอกสารประจำตัว', attachment: 'เอกสารประจำตัว',
citizenId: 'เลขที่บัตรประชาชน',
citizenIssue: 'วันที่ออกบัตร',
citizenExpire: 'วันที่หมดอายุ',
}, },
}, },
customer: { customer: {

View file

@ -1,14 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, watch, computed } from 'vue'; import { ref, onMounted, watch, computed } from 'vue';
import useUtilsStore, { dialog, baseUrl } from 'stores/utils';
import { calculateAge } from 'src/utils/datetime';
import { useQuasar, type QTableProps } from 'quasar';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import useFlowStore from 'stores/flow';
import useUserStore from 'stores/user'; import useUserStore from 'stores/user';
import useFlowStore from 'stores/flow';
import useBranchStore from 'stores/branch'; import useBranchStore from 'stores/branch';
import useOptionStore from 'stores/options';
import useAddressStore from 'stores/address';
import useMyBranch from 'src/stores/my-branch';
import { calculateAge } from 'src/utils/datetime';
import { useQuasar, type QTableProps } from 'quasar';
import useUtilsStore, { dialog, baseUrl } from 'stores/utils';
import { isRoleInclude, resetScrollBar } from 'src/stores/utils'; import { isRoleInclude, resetScrollBar } from 'src/stores/utils';
import { BranchUserStats } from 'stores/branch/types';
import { import {
User, User,
@ -24,42 +29,99 @@ import {
UndoButton, UndoButton,
} from 'components/button'; } from 'components/button';
import { BranchUserStats } from 'stores/branch/types'; import NoData from 'components/NoData.vue';
import useAddressStore from 'stores/address'; import { AddressForm } from 'components/form';
import ButtonAddComponent from 'components/ButtonAddCompoent.vue'; import SideMenu from 'components/SideMenu.vue';
import PersonCard from 'components/shared/PersonCard.vue';
import StatCardComponent from 'components/StatCardComponent.vue';
import AddButton from 'components/AddButton.vue'; import AddButton from 'components/AddButton.vue';
import DialogForm from 'components/DialogForm.vue';
import DrawerInfo from 'components/DrawerInfo.vue';
import ProfileBanner from 'components/ProfileBanner.vue';
import PersonCard from 'components/shared/PersonCard.vue';
import KebabAction from 'components/shared/KebabAction.vue';
import TooltipComponent from 'components/TooltipComponent.vue'; import TooltipComponent from 'components/TooltipComponent.vue';
import FormInformation from 'components/02_personnel-management/FormInformation.vue'; import StatCardComponent from 'components/StatCardComponent.vue';
import ImageUploadDialog from 'components/ImageUploadDialog.vue';
import ButtonAddComponent from 'components/ButtonAddCompoent.vue';
import PaginationComponent from 'components/PaginationComponent.vue';
import InfoForm from 'components/02_personnel-management/InfoForm.vue';
import FormPerson from 'components/02_personnel-management/FormPerson.vue'; import FormPerson from 'components/02_personnel-management/FormPerson.vue';
import FormByType from 'components/02_personnel-management/FormByType.vue'; import FormByType from 'components/02_personnel-management/FormByType.vue';
import DrawerInfo from 'components/DrawerInfo.vue'; import FormInformation from 'components/02_personnel-management/FormInformation.vue';
import InfoForm from 'components/02_personnel-management/InfoForm.vue';
import NoData from 'components/NoData.vue';
import PaginationComponent from 'components/PaginationComponent.vue';
import useOptionStore from 'stores/options';
import ProfileBanner from 'components/ProfileBanner.vue';
import SideMenu from 'components/SideMenu.vue';
import ImageUploadDialog from 'components/ImageUploadDialog.vue';
import { AddressForm } from 'components/form';
import DialogForm from 'components/DialogForm.vue';
import KebabAction from 'src/components/shared/KebabAction.vue';
import useMyBranch from 'src/stores/my-branch';
const { locale, t } = useI18n(); const { locale, t } = useI18n();
const $q = useQuasar(); const $q = useQuasar();
const optionStore = useOptionStore();
const utilsStore = useUtilsStore();
const flowStore = useFlowStore();
const userStore = useUserStore(); const userStore = useUserStore();
const flowStore = useFlowStore();
const utilsStore = useUtilsStore();
const optionStore = useOptionStore();
const branchStore = useBranchStore(); const branchStore = useBranchStore();
const adrressStore = useAddressStore(); const adrressStore = useAddressStore();
const useMyBranchStore = useMyBranch(); const useMyBranchStore = useMyBranch();
const { data: userData } = storeToRefs(userStore); const { data: userData } = storeToRefs(userStore);
const { myBranch } = storeToRefs(useMyBranchStore); const { myBranch } = storeToRefs(useMyBranchStore);
// state & control
const modal = ref(false);
const isEdit = ref(false);
const hideStat = ref(false);
const modeView = ref(false);
const infoDrawer = ref(false);
const isImageEdit = ref(false);
const imageDialog = ref(false);
const infoDrawerEdit = ref(false);
const refreshImageState = ref(false);
const inputSearch = ref('');
const currentTab = ref<string>('ALL');
const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all');
const currentPage = ref(1);
const pageSize = ref(30);
const currentMaxPage = computed(() =>
userData.value ? Math.ceil(userData.value?.total / pageSize.value) : 1,
);
// form & data
const hqId = ref<string>();
const brId = ref<string>();
const currentUser = ref<User>();
const userCode = ref<string>();
const statusToggle = ref(true);
const agencyFile = ref<File[]>([]);
const agencyFileList = ref<{ name: string; url: string }[]>([]);
const typeStats = ref<UserTypeStats>();
const userStats = ref<BranchUserStats[]>();
const urlProfile = ref<string>();
const profileFileImg = ref<File | null>(null);
const imageList = ref<{ selectedImage: string; list: string[] }>();
const onCreateImageList = ref<{
selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[];
}>({ selectedImage: '', list: [] });
const formMenuIcon = ref<{ icon: string; color: string; bgColor: string }[]>([
{
icon: 'mdi-account-outline',
color: 'hsl(var(--info-bg))',
bgColor: 'var(--surface-1)',
},
{
icon: 'mdi-map-marker-radius-outline',
color: 'hsl(var(--info-bg))',
bgColor: 'var(--surface-1)',
},
{
icon: 'mdi-briefcase-outline',
color: 'hsl(var(--info-bg))',
bgColor: 'var(--surface-1)',
},
]);
const defaultFormData = { const defaultFormData = {
provinceId: null, provinceId: null,
districtId: null, districtId: null,
@ -70,6 +132,12 @@ const defaultFormData = {
gender: '', gender: '',
addressEN: '', addressEN: '',
address: '', address: '',
mooEN: '',
moo: '',
soiEN: '',
soi: '',
streetEN: '',
street: '',
trainingPlace: null, trainingPlace: null,
importNationality: null, importNationality: null,
sourceNationality: null, sourceNationality: null,
@ -95,9 +163,58 @@ const defaultFormData = {
namePrefix: null, namePrefix: null,
middleNameEN: '', middleNameEN: '',
middleName: '', middleName: '',
citizenExpire: null,
citizenIssue: null,
citizenId: '',
}; };
const modeView = ref(false); const formData = ref<UserCreate>({
selectedImage: '',
branchId: '',
provinceId: null,
districtId: null,
subDistrictId: null,
telephoneNo: '',
email: '',
zipCode: '',
gender: '',
addressEN: '',
address: '',
mooEN: '',
moo: '',
soiEN: '',
soi: '',
streetEN: '',
street: '',
trainingPlace: null,
importNationality: null,
sourceNationality: null,
licenseExpireDate: null,
licenseIssueDate: null,
licenseNo: null,
discountCondition: null,
retireDate: null,
startDate: null,
registrationNo: null,
lastNameEN: '',
lastName: '',
middleNameEN: '',
middleName: '',
firstNameEN: '',
firstName: '',
namePrefix: null,
userRole: '',
userType: '',
birthDate: null,
responsibleArea: null,
checkpoint: null,
checkpointEN: null,
username: '',
status: 'CREATED',
citizenExpire: null,
citizenIssue: null,
citizenId: '',
});
const fieldSelectedOption = ref<{ label: string; value: string }[]>([ const fieldSelectedOption = ref<{ label: string; value: string }[]>([
{ {
@ -131,25 +248,6 @@ const fieldSelectedOption = ref<{ label: string; value: string }[]>([
}, },
]); ]);
const formMenuIcon = ref<{ icon: string; color: string; bgColor: string }[]>([
{
icon: 'mdi-account-outline',
color: 'hsl(var(--info-bg))',
bgColor: 'var(--surface-1)',
},
{
icon: 'mdi-map-marker-radius-outline',
color: 'hsl(var(--info-bg))',
bgColor: 'var(--surface-1)',
},
{
icon: 'mdi-briefcase-outline',
color: 'hsl(var(--info-bg))',
bgColor: 'var(--surface-1)',
},
]);
const fieldSelected = ref< const fieldSelected = ref<
( (
| 'orderNumber' | 'orderNumber'
@ -170,86 +268,6 @@ const fieldSelected = ref<
'userRole', 'userRole',
]); ]);
const refImageUpload = ref<InstanceType<typeof ImageUploadDialog>>();
const hideStat = ref(false);
const userCode = ref<string>();
const currentUser = ref<User>();
const infoDrawerEdit = ref(false);
const infoPersonId = ref<string>('');
const infoDrawer = ref(false);
const profileSubmit = ref(false);
const urlProfile = ref<string>();
const isEdit = ref(false);
const modal = ref(false);
const statusToggle = ref(true);
const statusFilter = ref<'all' | 'statusACTIVE' | 'statusINACTIVE'>('all');
const inputSearch = ref('');
const userId = ref<string>();
const selectorLabel = ref<string>('ALL');
const hqId = ref<string>();
const brId = ref<string>();
const formDialogRef = ref();
const userStats = ref<BranchUserStats[]>();
const typeStats = ref<UserTypeStats>();
const agencyFile = ref<File[]>([]);
const agencyFileList = ref<{ name: string; url: string }[]>([]);
const formData = ref<UserCreate>({
selectedImage: '',
branchId: '',
provinceId: null,
districtId: null,
subDistrictId: null,
telephoneNo: '',
email: '',
zipCode: '',
gender: '',
addressEN: '',
address: '',
trainingPlace: null,
importNationality: null,
sourceNationality: null,
licenseExpireDate: null,
licenseIssueDate: null,
licenseNo: null,
discountCondition: null,
retireDate: null,
startDate: null,
registrationNo: null,
lastNameEN: '',
lastName: '',
middleNameEN: '',
middleName: '',
firstNameEN: '',
firstName: '',
namePrefix: null,
userRole: '',
userType: '',
birthDate: null,
responsibleArea: null,
checkpoint: null,
checkpointEN: null,
username: '',
status: 'CREATED',
});
const isImageEdit = ref<boolean>(false);
const imageUrl = ref<string>('');
const profileFileImg = ref<File | null>(null);
const imageDialog = ref(false);
const refreshImageState = ref(false);
const imageList = ref<{ selectedImage: string; list: string[] }>();
const onCreateImageList = ref<{
selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[];
}>({ selectedImage: '', list: [] });
// const inputFile = document.createElement('input');
// inputFile.type = 'file';
// inputFile.accept = 'image/*';
// const reader = new FileReader();
const columns = [ const columns = [
{ {
name: 'orderNumber', name: 'orderNumber',
@ -297,9 +315,53 @@ const columns = [
}, },
] satisfies QTableProps['columns']; ] satisfies QTableProps['columns'];
// watch(profileFileImg, () => { function undo() {
// if (profileFileImg.value) reader.readAsDataURL(profileFileImg.value); if (!currentUser.value) return;
// }); isImageEdit.value = false;
infoDrawerEdit.value = false;
assignFormData(currentUser.value.id);
}
function mapRole(value: string) {
const option = userStore.userOption.roleOpts.find(
(option) => option.value === value,
);
return option ? option.label : '-';
}
function mapUserType(label: string) {
const userTypeMap: { [key: string]: string } = {
USER: 'USER',
MESSENGER: 'MESSENGER',
DELEGATE: 'DELEGATE',
AGENCY: 'AGENCY',
};
formData.value.userType = userTypeMap[label];
}
function onClose(excludeDialog?: boolean) {
if (excludeDialog) {
infoDrawer.value = excludeDialog || false;
return;
}
hqId.value = '';
brId.value = '';
userCode.value = '';
urlProfile.value = '';
profileFileImg.value = null;
infoDrawerEdit.value = false;
agencyFile.value = [];
modal.value = false;
isEdit.value = false;
statusToggle.value = true;
isImageEdit.value = false;
currentUser.value = undefined;
Object.assign(formData.value, defaultFormData);
mapUserType(currentTab.value);
imageList.value = { selectedImage: '', list: [] };
onCreateImageList.value = { selectedImage: '', list: [] };
flowStore.rotate();
}
async function openDialog( async function openDialog(
action?: 'FORM' | 'INFO', action?: 'FORM' | 'INFO',
@ -366,41 +428,9 @@ async function openDialog(
userStore.userOption.brOpts = []; userStore.userOption.brOpts = [];
} }
function undo() {
if (!infoPersonId.value) return;
isImageEdit.value = false;
infoDrawerEdit.value = false;
assignFormData(infoPersonId.value);
}
function onClose(excludeDialog?: boolean) {
if (excludeDialog) {
infoDrawer.value = excludeDialog || false;
return;
}
hqId.value = '';
brId.value = '';
userId.value = '';
userCode.value = '';
urlProfile.value = '';
profileFileImg.value = null;
infoDrawerEdit.value = false;
agencyFile.value = [];
modal.value = false;
isEdit.value = false;
profileSubmit.value = false;
statusToggle.value = true;
isImageEdit.value = false;
currentUser.value = undefined;
Object.assign(formData.value, defaultFormData);
mapUserType(selectorLabel.value);
imageList.value = { selectedImage: '', list: [] };
onCreateImageList.value = { selectedImage: '', list: [] };
flowStore.rotate();
}
async function onSubmit(excludeDialog?: boolean) { async function onSubmit(excludeDialog?: boolean) {
if (isEdit.value && userId.value) { if (isEdit.value && currentUser.value) {
if (!userId.value) return; if (!currentUser.value.id) return;
formData.value.branchId = brId.value formData.value.branchId = brId.value
? brId.value ? brId.value
@ -412,16 +442,16 @@ async function onSubmit(excludeDialog?: boolean) {
status: !statusToggle.value ? 'INACTIVE' : 'ACTIVE', status: !statusToggle.value ? 'INACTIVE' : 'ACTIVE',
} as const; } as const;
await userStore.editById(userId.value, formDataEdit); await userStore.editById(currentUser.value.id, formDataEdit);
if (userId.value && formDataEdit.userType === 'AGENCY') { if (currentUser.value.id && formDataEdit.userType === 'AGENCY') {
if (!agencyFile.value) return; if (!agencyFile.value) return;
const payload: UserAttachmentCreate = { const payload: UserAttachmentCreate = {
file: agencyFile.value, file: agencyFile.value,
}; };
if (payload?.file) { if (payload?.file) {
await userStore.addAttachment(userId.value, payload); await userStore.addAttachment(currentUser.value.id, payload);
} }
} }
@ -463,7 +493,7 @@ async function onSubmit(excludeDialog?: boolean) {
} }
} }
selectorLabel.value = formData.value.userType; currentTab.value = formData.value.userType;
await fetchUserList(); await fetchUserList();
@ -496,16 +526,6 @@ async function onDelete(id: string) {
}); });
} }
function mapUserType(label: string) {
const userTypeMap: { [key: string]: string } = {
USER: 'USER',
MESSENGER: 'MESSENGER',
DELEGATE: 'DELEGATE',
AGENCY: 'AGENCY',
};
formData.value.userType = userTypeMap[label];
}
async function toggleStatus(id: string) { async function toggleStatus(id: string) {
const record = userData.value?.result.find((v) => v.id === id); const record = userData.value?.result.find((v) => v.id === id);
@ -549,9 +569,6 @@ async function assignFormData(idEdit: string) {
if (foundUser) { if (foundUser) {
currentUser.value = foundUser; currentUser.value = foundUser;
if (currentUser.value) {
infoPersonId.value = currentUser.value.id;
}
formData.value = { formData.value = {
branchId: foundUser.branch[0]?.id, branchId: foundUser.branch[0]?.id,
provinceId: foundUser.provinceId, provinceId: foundUser.provinceId,
@ -563,6 +580,12 @@ async function assignFormData(idEdit: string) {
gender: foundUser.gender, gender: foundUser.gender,
addressEN: foundUser.addressEN, addressEN: foundUser.addressEN,
address: foundUser.address, address: foundUser.address,
moo: foundUser.moo,
mooEN: foundUser.mooEN,
soi: foundUser.soi,
soiEN: foundUser.soiEN,
street: foundUser.street,
streetEN: foundUser.streetEN,
trainingPlace: foundUser.trainingPlace, trainingPlace: foundUser.trainingPlace,
importNationality: foundUser.importNationality, importNationality: foundUser.importNationality,
sourceNationality: foundUser.sourceNationality, sourceNationality: foundUser.sourceNationality,
@ -595,12 +618,16 @@ async function assignFormData(idEdit: string) {
(foundUser.retireDate && new Date(foundUser.retireDate)) || null, (foundUser.retireDate && new Date(foundUser.retireDate)) || null,
startDate: (foundUser.startDate && new Date(foundUser.startDate)) || null, startDate: (foundUser.startDate && new Date(foundUser.startDate)) || null,
birthDate: (foundUser.birthDate && new Date(foundUser.birthDate)) || null, birthDate: (foundUser.birthDate && new Date(foundUser.birthDate)) || null,
citizenId: foundUser.citizenId,
citizenIssue:
(foundUser.citizenIssue && new Date(foundUser.citizenIssue)) || null,
citizenExpire:
(foundUser.citizenExpire && new Date(foundUser.citizenExpire)) || null,
}; };
formData.value.status === 'ACTIVE' || 'CREATED' formData.value.status === 'ACTIVE' || 'CREATED'
? (statusToggle.value = true) ? (statusToggle.value = true)
: (statusToggle.value = false); : (statusToggle.value = false);
userId.value = foundUser.id;
if (foundUser.branch) { if (foundUser.branch) {
if (foundUser.branch?.[0].isHeadOffice) { if (foundUser.branch?.[0].isHeadOffice) {
@ -615,7 +642,6 @@ async function assignFormData(idEdit: string) {
urlProfile.value = `${baseUrl}/user/${foundUser.id}/profile-image/${foundUser.selectedImage}`; urlProfile.value = `${baseUrl}/user/${foundUser.id}/profile-image/${foundUser.selectedImage}`;
isEdit.value = true; isEdit.value = true;
profileSubmit.value = true;
hqId.value && (await userStore.fetchBrOption(hqId.value)); hqId.value && (await userStore.fetchBrOption(hqId.value));
await fetchImageList(foundUser.id, foundUser.selectedImage); await fetchImageList(foundUser.id, foundUser.selectedImage);
@ -628,6 +654,31 @@ async function assignFormData(idEdit: string) {
} }
} }
async function fetchImageList(id: string, selectedName: string) {
const res = await userStore.fetchImageListById(id);
imageList.value = {
selectedImage: selectedName,
list: res.map((n: string) => `user/${id}/profile-image/${n}`),
};
return res;
}
async function fetchUserList() {
await userStore.fetchList({
includeBranch: true,
pageSize: pageSize.value,
page: currentPage.value,
query: !!inputSearch.value ? inputSearch.value : undefined,
userType: currentTab.value === 'ALL' ? undefined : currentTab.value,
status:
statusFilter.value === 'all'
? undefined
: statusFilter.value === 'statusACTIVE'
? 'ACTIVE'
: 'INACTIVE',
});
}
onMounted(async () => { onMounted(async () => {
utilsStore.currentTitle.title = 'personnel.title'; utilsStore.currentTitle.title = 'personnel.title';
utilsStore.currentTitle.path = [{ text: 'personnel.caption', i18n: true }]; utilsStore.currentTitle.path = [{ text: 'personnel.caption', i18n: true }];
@ -649,24 +700,8 @@ onMounted(async () => {
flowStore.rotate(); flowStore.rotate();
}); });
function mapRole(value: string) {
const option = userStore.userOption.roleOpts.find(
(option) => option.value === value,
);
return option ? option.label : '-';
}
async function fetchImageList(id: string, selectedName: string) {
const res = await userStore.fetchImageListById(id);
imageList.value = {
selectedImage: selectedName,
list: res.map((n: string) => `user/${id}/profile-image/${n}`),
};
return res;
}
watch( watch(
() => selectorLabel.value, () => currentTab.value,
async (label) => { async (label) => {
mapUserType(label); mapUserType(label);
await fetchUserList(); await fetchUserList();
@ -695,28 +730,6 @@ watch(
}, },
); );
const currentPage = ref(1);
const pageSize = ref(30);
const currentMaxPage = computed(() =>
userData.value ? Math.ceil(userData.value?.total / pageSize.value) : 1,
);
async function fetchUserList() {
await userStore.fetchList({
includeBranch: true,
pageSize: pageSize.value,
page: currentPage.value,
query: !!inputSearch.value ? inputSearch.value : undefined,
userType: selectorLabel.value === 'ALL' ? undefined : selectorLabel.value,
status:
statusFilter.value === 'all'
? undefined
: statusFilter.value === 'statusACTIVE'
? 'ACTIVE'
: 'INACTIVE',
});
}
watch( watch(
() => profileFileImg.value, () => profileFileImg.value,
() => { () => {
@ -762,9 +775,9 @@ watch(
" "
> >
{{ {{
selectorLabel === 'ALL' currentTab === 'ALL'
? Object.values(typeStats).reduce((acc, val) => acc + val, 0) ? Object.values(typeStats).reduce((acc, val) => acc + val, 0)
: typeStats[selectorLabel] : typeStats[currentTab]
}} }}
</q-badge> </q-badge>
<q-btn <q-btn
@ -788,7 +801,7 @@ watch(
v-if="typeStats && userData?.result" v-if="typeStats && userData?.result"
labelI18n labelI18n
:branch=" :branch="
selectorLabel === 'ALL' currentTab === 'ALL'
? Object.entries(typeStats).map(([key, val]) => ({ ? Object.entries(typeStats).map(([key, val]) => ({
count: val, count: val,
label: `personnel.${key}`, label: `personnel.${key}`,
@ -805,15 +818,15 @@ watch(
})) }))
: [ : [
{ {
label: `personnel.${selectorLabel}`, label: `personnel.${currentTab}`,
count: typeStats[selectorLabel], count: typeStats[currentTab],
icon: 'mdi-account-outline', icon: 'mdi-account-outline',
color: color:
selectorLabel === 'USER' currentTab === 'USER'
? 'cyan' ? 'cyan'
: selectorLabel === 'MESSENGER' : currentTab === 'MESSENGER'
? 'yellow' ? 'yellow'
: selectorLabel === 'DELEGATE' : currentTab === 'DELEGATE'
? 'red' ? 'red'
: 'magenta', : 'magenta',
}, },
@ -952,7 +965,7 @@ watch(
inline-label inline-label
mobile-arrows mobile-arrows
dense dense
v-model="selectorLabel" v-model="currentTab"
align="left" align="left"
class="full-width" class="full-width"
active-color="info" active-color="info"
@ -963,7 +976,7 @@ watch(
async () => { async () => {
currentPage = 1; currentPage = 1;
inputSearch = ''; inputSearch = '';
selectorLabel = 'ALL'; currentTab = 'ALL';
statusFilter = 'all'; statusFilter = 'all';
flowStore.rotate(); flowStore.rotate();
} }
@ -971,9 +984,7 @@ watch(
> >
<div <div
class="row text-capitalize" class="row text-capitalize"
:class=" :class="currentTab === 'ALL' ? 'text-bold' : 'app-text-muted'"
selectorLabel === 'ALL' ? 'text-bold' : 'app-text-muted'
"
> >
{{ $t('general.all') }} {{ $t('general.all') }}
</div> </div>
@ -982,7 +993,7 @@ watch(
name="USER" name="USER"
@click=" @click="
async () => { async () => {
selectorLabel = 'USER'; currentTab = 'USER';
statusFilter = 'all'; statusFilter = 'all';
currentPage = 1; currentPage = 1;
inputSearch = ''; inputSearch = '';
@ -992,9 +1003,7 @@ watch(
> >
<div <div
class="row text-capitalize" class="row text-capitalize"
:class=" :class="currentTab === 'USER' ? 'text-bold' : 'app-text-muted'"
selectorLabel === 'USER' ? 'text-bold' : 'app-text-muted'
"
> >
{{ $t('personnel.USER') }} {{ $t('personnel.USER') }}
</div> </div>
@ -1003,7 +1012,7 @@ watch(
name="MESSENGER" name="MESSENGER"
@click=" @click="
async () => { async () => {
selectorLabel = 'MESSENGER'; currentTab = 'MESSENGER';
statusFilter = 'all'; statusFilter = 'all';
currentPage = 1; currentPage = 1;
inputSearch = ''; inputSearch = '';
@ -1014,7 +1023,7 @@ watch(
<div <div
class="row text-capitalize" class="row text-capitalize"
:class=" :class="
selectorLabel === 'MESSENGER' ? 'text-bold' : 'app-text-muted' currentTab === 'MESSENGER' ? 'text-bold' : 'app-text-muted'
" "
> >
{{ $t('personnel.MESSENGER') }} {{ $t('personnel.MESSENGER') }}
@ -1024,7 +1033,7 @@ watch(
name="DELEGATE" name="DELEGATE"
@click=" @click="
async () => { async () => {
selectorLabel = 'DELEGATE'; currentTab = 'DELEGATE';
statusFilter = 'all'; statusFilter = 'all';
currentPage = 1; currentPage = 1;
inputSearch = ''; inputSearch = '';
@ -1035,7 +1044,7 @@ watch(
<div <div
class="row text-capitalize" class="row text-capitalize"
:class=" :class="
selectorLabel === 'DELEGATE' ? 'text-bold' : 'app-text-muted' currentTab === 'DELEGATE' ? 'text-bold' : 'app-text-muted'
" "
> >
{{ $t('personnel.DELEGATE') }} {{ $t('personnel.DELEGATE') }}
@ -1045,7 +1054,7 @@ watch(
name="AGENCY" name="AGENCY"
@click=" @click="
async () => { async () => {
selectorLabel = 'AGENCY'; currentTab = 'AGENCY';
statusFilter = 'all'; statusFilter = 'all';
currentPage = 1; currentPage = 1;
inputSearch = ''; inputSearch = '';
@ -1056,7 +1065,7 @@ watch(
<div <div
class="row text-capitalize" class="row text-capitalize"
:class=" :class="
selectorLabel === 'AGENCY' ? 'text-bold' : 'app-text-muted' currentTab === 'AGENCY' ? 'text-bold' : 'app-text-muted'
" "
> >
{{ $t('personnel.AGENCY') }} {{ $t('personnel.AGENCY') }}
@ -1186,7 +1195,7 @@ watch(
<div class="column"> <div class="column">
<div class="col ellipsis" style="max-width: 20vw"> <div class="col ellipsis" style="max-width: 20vw">
{{ {{
$i18n.locale === 'eng' locale === 'eng'
? `${props.row.firstNameEN} ${props.row.lastNameEN}`.trim() ? `${props.row.firstNameEN} ${props.row.lastNameEN}`.trim()
: `${props.row.firstName} ${props.row.lastName}`.trim() : `${props.row.firstName} ${props.row.lastName}`.trim()
}} }}
@ -1196,7 +1205,7 @@ watch(
:delay="300" :delay="300"
> >
{{ {{
$i18n.locale === 'eng' locale === 'eng'
? `${props.row.firstNameEN} ${props.row.lastNameEN}`.trim() ? `${props.row.firstNameEN} ${props.row.lastNameEN}`.trim()
: `${props.row.firstName} ${props.row.lastName}`.trim() : `${props.row.firstName} ${props.row.lastName}`.trim()
}} }}
@ -1313,7 +1322,7 @@ watch(
:data="{ :data="{
code: props.row.code, code: props.row.code,
name: name:
$i18n.locale === 'eng' locale === 'eng'
? `${props.row.firstNameEN} ${props.row.lastNameEN}`.trim() ? `${props.row.firstNameEN} ${props.row.lastNameEN}`.trim()
: `${props.row.firstName} ${props.row.lastName}`.trim(), : `${props.row.firstName} ${props.row.lastName}`.trim(),
img: props.row.profileImageUrl img: props.row.profileImageUrl
@ -1501,7 +1510,7 @@ watch(
hide-action hide-action
:isEdit="infoDrawerEdit" :isEdit="infoDrawerEdit"
:title=" :title="
$i18n.locale === 'eng' locale === 'eng'
? `${currentUser.firstNameEN} ${currentUser.lastNameEN}` ? `${currentUser.firstNameEN} ${currentUser.lastNameEN}`
: `${currentUser.firstName} ${currentUser.lastName}` : `${currentUser.firstName} ${currentUser.lastName}`
" "
@ -1530,7 +1539,7 @@ watch(
hideFade hideFade
:menu="formMenuIcon" :menu="formMenuIcon"
:toggleTitle="$t('status.title')" :toggleTitle="$t('status.title')"
:title="`${$i18n.locale === 'eng' ? `${formData.firstNameEN} ${formData.lastNameEN}` : `${formData.firstName} ${formData.lastName}`}`" :title="`${locale === 'eng' ? `${formData.firstNameEN} ${formData.lastNameEN}` : `${formData.firstName} ${formData.lastName}`}`"
:caption="userCode" :caption="userCode"
:img=" :img="
`${baseUrl}/user/${currentUser.id}/profile-image/${formData.selectedImage}`.concat( `${baseUrl}/user/${currentUser.id}/profile-image/${formData.selectedImage}`.concat(
@ -1556,7 +1565,8 @@ watch(
@edit="imageDialog = isImageEdit = true" @edit="imageDialog = isImageEdit = true"
@update:toggle-status=" @update:toggle-status="
async (v) => { async (v) => {
await triggerChangeStatus(infoPersonId, v); if (!currentUser) return;
await triggerChangeStatus(currentUser.id, v);
} }
" "
/> />
@ -1609,7 +1619,12 @@ watch(
v-if="!infoDrawerEdit" v-if="!infoDrawerEdit"
id="btn-info-basic-delete" id="btn-info-basic-delete"
icon-only icon-only
@click="() => onDelete(infoPersonId)" @click="
() => {
if (!currentUser) return;
onDelete(currentUser.id);
}
"
type="button" type="button"
/> />
</div> </div>
@ -1692,6 +1707,9 @@ watch(
v-model:email="formData.email" v-model:email="formData.email"
v-model:gender="formData.gender" v-model:gender="formData.gender"
v-model:birth-date="formData.birthDate" v-model:birth-date="formData.birthDate"
v-model:citizen-id="formData.citizenId"
v-model:citizen-issue="formData.citizenIssue"
v-model:citizen-expire="formData.citizenExpire"
:title="'personnel.form.personalInformation'" :title="'personnel.form.personalInformation'"
prefix-id="drawer-info-personnel" prefix-id="drawer-info-personnel"
dense dense
@ -1705,6 +1723,12 @@ watch(
id="info-address" id="info-address"
v-model:address="formData.address" v-model:address="formData.address"
v-model:addressEN="formData.addressEN" v-model:addressEN="formData.addressEN"
v-model:moo="formData.moo"
v-model:mooEN="formData.mooEN"
v-model:soi="formData.soi"
v-model:soiEN="formData.soiEN"
v-model:street="formData.street"
v-model:streetEN="formData.streetEN"
v-model:provinceId="formData.provinceId" v-model:provinceId="formData.provinceId"
v-model:districtId="formData.districtId" v-model:districtId="formData.districtId"
v-model:subDistrictId="formData.subDistrictId" v-model:subDistrictId="formData.subDistrictId"
@ -1734,7 +1758,7 @@ watch(
v-model:checkpointEN="formData.checkpointEN" v-model:checkpointEN="formData.checkpointEN"
v-model:agencyFile="agencyFile" v-model:agencyFile="agencyFile"
v-model:agencyFileList="agencyFileList" v-model:agencyFileList="agencyFileList"
v-model:userId="userId" v-model:userId="currentUser.id"
/> />
</div> </div>
</div> </div>
@ -1746,7 +1770,6 @@ watch(
<DialogForm <DialogForm
hideFooter hideFooter
removeDialog removeDialog
ref="formDialogRef"
:badgeClass="formData.gender === 'male' ? 'app-bg-male' : 'app-bg-female'" :badgeClass="formData.gender === 'male' ? 'app-bg-male' : 'app-bg-female'"
:badgeLabel="userCode" :badgeLabel="userCode"
:title="$t('personnel.addTitle')" :title="$t('personnel.addTitle')"
@ -1774,7 +1797,7 @@ watch(
}[formData.gender] }[formData.gender]
" "
:toggleTitle="$t('status.title')" :toggleTitle="$t('status.title')"
:title="`${$i18n.locale === 'eng' ? `${formData.firstNameEN} ${formData.lastNameEN}` : `${formData.firstName} ${formData.lastName}`}`" :title="`${locale === 'eng' ? `${formData.firstNameEN} ${formData.lastNameEN}` : `${formData.firstName} ${formData.lastName}`}`"
:fallbackImg=" :fallbackImg="
{ {
male: '/no-img-man.png', male: '/no-img-man.png',
@ -1880,12 +1903,21 @@ watch(
v-model:email="formData.email" v-model:email="formData.email"
v-model:gender="formData.gender" v-model:gender="formData.gender"
v-model:birth-date="formData.birthDate" v-model:birth-date="formData.birthDate"
v-model:citizen-id="formData.citizenId"
v-model:citizen-issue="formData.citizenIssue"
v-model:citizen-expire="formData.citizenExpire"
class="q-mb-xl" class="q-mb-xl"
/> />
<AddressForm <AddressForm
id="dialog-form-address" id="dialog-form-address"
v-model:address="formData.address" v-model:address="formData.address"
v-model:addressEN="formData.addressEN" v-model:addressEN="formData.addressEN"
v-model:moo="formData.moo"
v-model:mooEN="formData.mooEN"
v-model:soi="formData.soi"
v-model:soiEN="formData.soiEN"
v-model:street="formData.street"
v-model:streetEN="formData.streetEN"
v-model:provinceId="formData.provinceId" v-model:provinceId="formData.provinceId"
v-model:districtId="formData.districtId" v-model:districtId="formData.districtId"
v-model:subDistrictId="formData.subDistrictId" v-model:subDistrictId="formData.subDistrictId"
@ -1916,9 +1948,7 @@ watch(
</div> </div>
</DialogForm> </DialogForm>
<!-- :default-url="`${baseUrl}/user/${currentUser?.id}/image`" -->
<ImageUploadDialog <ImageUploadDialog
ref="refImageUpload"
v-model:dialogState="imageDialog" v-model:dialogState="imageDialog"
v-model:file="profileFileImg" v-model:file="profileFileImg"
v-model:image-url="urlProfile" v-model:image-url="urlProfile"
@ -1969,7 +1999,7 @@ watch(
<span v-if="!modal" class="justify-center flex text-bold"> <span v-if="!modal" class="justify-center flex text-bold">
{{ $t('general.image') }} {{ $t('general.image') }}
{{ {{
$i18n.locale === 'eng' locale === 'eng'
? `${formData.firstNameEN} ${formData.lastNameEN}` ? `${formData.firstNameEN} ${formData.lastNameEN}`
: `${formData.firstName} ${formData.lastName}` : `${formData.firstName} ${formData.lastName}`
}} }}

View file

@ -34,6 +34,12 @@ export type User = {
provinceId: string | null; provinceId: string | null;
addressEN: string; addressEN: string;
address: string; address: string;
mooEN: string;
moo: string;
soiEN: string;
soi: string;
streetEN: string;
street: string;
lastNameEN: string; lastNameEN: string;
lastName: string; lastName: string;
middleNameEN?: string | null; middleNameEN?: string | null;
@ -47,6 +53,9 @@ export type User = {
responsibleArea: string; responsibleArea: string;
checkpoint?: string | null; checkpoint?: string | null;
checkpointEN?: string | null; checkpointEN?: string | null;
citizenExpire?: Date | null;
citizenIssue?: Date | null;
citizenId: string;
branch: Branch[]; branch: Branch[];
}; };
@ -62,6 +71,12 @@ export type UserCreate = {
gender: string; gender: string;
addressEN: string; addressEN: string;
address: string; address: string;
mooEN: string;
moo: string;
soiEN: string;
soi: string;
streetEN: string;
street: string;
trainingPlace?: string | null; trainingPlace?: string | null;
importNationality?: string | null; importNationality?: string | null;
sourceNationality?: string | null; sourceNationality?: string | null;
@ -87,6 +102,9 @@ export type UserCreate = {
responsibleArea?: string | null; responsibleArea?: string | null;
checkpoint?: string | null; checkpoint?: string | null;
checkpointEN?: string | null; checkpointEN?: string | null;
citizenExpire?: Date | null;
citizenIssue?: Date | null;
citizenId: string;
}; };
export type UserAttachment = { export type UserAttachment = {