feat: customer employee update & drawer info

This commit is contained in:
puriphatt 2024-06-12 08:40:07 +00:00
parent a61b3e550e
commit 46a7f9ca96
7 changed files with 473 additions and 71 deletions

View file

@ -8,6 +8,7 @@ defineProps<{
addressTitle?: string;
addressTitleEN?: string;
noAddress?: boolean;
noPaddingTab?: boolean;
}>();
const address = defineModel('address', { default: '' });
@ -16,53 +17,105 @@ const provinceId = defineModel<string | null | undefined>('provinceId');
const districtId = defineModel<string | null | undefined>('districtId');
const subDistrictId = defineModel<string | null | undefined>('subDistrictId');
const zipCode = defineModel<string>('zipCode', { default: '' });
const employeeTab = defineModel<string>('employeeTab');
const tabsList = defineModel<{ name: string; label: string }[]>('tabsList');
</script>
<template>
<div class="row items-stretch" style="position: absolute; inset: 0">
<div class="col-3" v-if="$slots['person-card']">
<slot name="person-card"></slot>
</div>
<div class="column absolute-top" style="inset: 0">
<div
:class="{
'col-9': $slots['person-card'],
'col-12': !$slots['person-card'],
}"
style="position: relative"
v-if="tabsList && tabsList.length > 0"
class="row surface-2 q-px-md q-pt-md full-width"
style="border-bottom: 1px solid var(--brand-1)"
>
<AppBox
bordered
:class="{
'q-my-md q-mr-md': $slots['person-card'],
'q-ma-md': !$slots['person-card'],
}"
style="position: absolute; overflow-y: auto; inset: 0"
<q-tabs
dense
class="app-text-muted cancel-overflow"
v-model="employeeTab"
active-class="active-tab"
indicator-color="transparent"
>
<div class="row q-col-gutter-y-md">
<slot name="information"></slot>
<slot name="person"></slot>
<slot name="address" v-if="!noAddress">
<FormAddress
dense
outlined
separator
:readonly="readonly"
v-model:address="address"
v-model:addressEN="addressEN"
v-model:provinceId="provinceId"
v-model:districtId="districtId"
v-model:subDistrictId="subDistrictId"
v-model:zipCode="zipCode"
:title="titleFormAddress"
:addressTitle="addressTitle || ''"
:addressTitleEN="addressTitleEN || ''"
v-if="!$slots.address"
/>
</slot>
<slot name="qr-code"></slot>
<slot name="location"></slot>
<slot name="by-type"></slot>
</div>
</AppBox>
<q-tab
v-for="tab in tabsList"
v-bind:key="tab.name"
class="content-tab text-capitalize"
:name="tab.name"
:label="$t(tab.label)"
style="z-index: 999"
/>
</q-tabs>
</div>
<div class="row full-width" style="flex: 1">
<div class="col-3" v-if="$slots['person-card']">
<slot name="person-card"></slot>
</div>
<div
:class="{
'col-9': $slots['person-card'],
'col-12': !$slots['person-card'],
}"
class="relative-position"
>
<AppBox
bordered
:noPadding="noPaddingTab"
:class="{
'q-my-md q-mr-md': $slots['person-card'],
'q-ma-md': !$slots['person-card'],
}"
style="position: absolute; overflow-y: auto; inset: 0"
>
<div class="row q-col-gutter-y-md">
<slot name="information"></slot>
<slot name="person"></slot>
<slot name="address" v-if="!noAddress">
<FormAddress
dense
outlined
separator
:readonly="readonly"
v-model:address="address"
v-model:addressEN="addressEN"
v-model:provinceId="provinceId"
v-model:districtId="districtId"
v-model:subDistrictId="subDistrictId"
v-model:zipCode="zipCode"
:title="titleFormAddress"
:addressTitle="addressTitle || ''"
:addressTitleEN="addressTitleEN || ''"
v-if="!$slots.address"
/>
</slot>
<slot name="qr-code"></slot>
<slot name="location"></slot>
<slot name="by-type"></slot>
</div>
</AppBox>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.active-tab {
color: var(--brand-1);
background-color: var(--surface-3);
border-top: 1px solid var(--brand-1);
border-left: 1px solid var(--brand-1);
border-right: 1px solid var(--brand-1);
border-top-left-radius: var(--radius-2);
border-top-right-radius: var(--radius-2);
margin-bottom: -1.5px;
border-bottom: 3px solid var(--surface-3);
}
.content-tab {
border-top-left-radius: var(--radius-2);
border-top-right-radius: var(--radius-2);
position: relative;
}
:deep(.q-tabs__content) {
overflow: visible;
}
</style>

View file

@ -9,7 +9,7 @@ const employerID = defineModel<string>('employerID');
// employee
const customerBranchId = defineModel<string>('customerBranchId');
const employeeID = defineModel<string>('employeeID');
const employeeId = defineModel<string>('employeeId');
const nrcNo = defineModel<string>('nrcNo');
defineProps<{
@ -85,6 +85,7 @@ onMounted(async () => {});
id="select-employer-branch"
use-input
input-debounce="0"
:hide-dropdown-icon="readonly"
:dense="dense"
:outlined="!readonly"
:readonly="readonly"
@ -113,7 +114,7 @@ onMounted(async () => {});
hide-bottom-space
class="col-3"
:label="$t('formDialogEmployeeID')"
v-model="employeeID"
v-model="employeeId"
/>
<q-input
mask="## #### ###### #"

View file

@ -135,7 +135,7 @@ onMounted(async () => {
v-bind:key="index"
:name="`tab${index}`"
class="row col-12 items-start"
style="overflow: auto; height: 50vh"
style="overflow: auto"
>
<div class="col-3 app-text-muted">
{{ $t(`formDialogTitleHealthCheck`) }}

View file

@ -107,7 +107,7 @@ onMounted(async () => {
v-bind:key="index"
:name="`tab${index}`"
class="row col-12 items-start"
style="overflow: auto; height: 50vh"
style="overflow: auto"
>
<div class="col-3 app-text-muted"> {{ $t(`workHistory`) }}</div>
<div class="col-9 row q-col-gutter-md">

View file

@ -145,6 +145,7 @@ function reset() {
style="position: relative"
:class="{ dark: $q.dark.isActive }"
>
<slot></slot>
<slot name="info"></slot>
</div>

View file

@ -726,8 +726,6 @@ watch(
>
<template #info>
<InfoForm
:addressTitle="$t('formDialogTitleAddressPure')"
:addressTitleEN="$t('formDialogTitleAddressPure') + ' ENG'"
:readonly="!infoDrawerEdit"
v-model:address="formData.address"
v-model:addressEN="formData.addressEN"

View file

@ -35,7 +35,13 @@ import {
CustomerBranchCreate,
CustomerType,
} from 'stores/customer/types';
import { EmployeeCreate, Employee } from 'stores/employee/types';
import {
EmployeeCreate,
Employee,
EmployeeWork,
EmployeeCheckup,
EmployeeOther,
} from 'stores/employee/types';
import { onMounted } from 'vue';
import FormPerson from 'src/components/02_personnel-management/FormPerson.vue';
@ -106,9 +112,13 @@ const fieldSelectedCustomer = ref<{ label: string; value: string }>({
value: 'all',
});
const currentEmployee = ref<Employee>();
const isEmployeeEdit = ref(false);
const formDataEmployeeSameAddr = ref(false);
const formDataEmployeeTab = ref('personalInfo');
const employeeOwnerBranchOption = ref();
const infoDrawerEmployee = ref(false);
const infoDrawerEmployeeEdit = ref(false);
const infoEmployeePersonCard = ref();
const formDataEmployee = ref<EmployeeCreate>({
image: null,
customerBranchId: '',
@ -325,12 +335,56 @@ function openDialogCustomerType() {
}
}
function openDialogInputForm(action: 'FORM' | 'INFO' = 'FORM', id?: string) {
async function openDialogInputForm(
action: 'FORM' | 'INFO' = 'FORM',
id?: string,
) {
if (action === 'INFO') {
if (!id) return;
infoDrawer.value = true;
if (selectorLabel.value === 'EMPLOYER') infoDrawer.value = true;
if (selectorLabel.value === 'EMPLOYEE') {
await assignFormDataEmployee(id);
infoDrawerEmployee.value = true;
infoDrawerEmployeeEdit.value = false;
const employee = currentEmployee.value;
infoEmployeePersonCard.value = employee
? [
{
id: employee.id,
img: `${employee.profileImageUrl}`,
name:
locale.value === 'en-US'
? `${employee.firstNameEN} ${employee.lastNameEN}`
: `${employee.firstName} ${employee.lastName}`,
male: employee.gender === 'male',
female: employee.gender === 'female',
badge: employee.code,
disabled: employee.status === 'INACTIVE',
},
]
: [];
const code = currentEmployee.value?.code.split('-')[0];
const result = await fetchListBranch({
includeCustomer: true,
query: code,
pageSize: 30,
});
if (result) {
employeeStore.ownerOption = result.result.map((i) => ({
label: i.code,
value: i.id,
}));
}
}
} else {
dialogInputForm.value = true;
if (selectorLabel.value === 'EMPLOYER') dialogInputForm.value = true;
if (selectorLabel.value === 'EMPLOYEE') {
if (!id) return;
await assignFormDataEmployee(id);
dialogEmployee.value = true;
}
}
}
@ -390,6 +444,12 @@ function clearForm() {
}
function clearFormEmployee() {
profileUrl.value = null;
profileSubmit.value = false;
dialogEmployee.value = false;
isEmployeeEdit.value = false;
infoDrawerEmployee.value = false;
infoDrawerEmployeeEdit.value = false;
formDataEmployee.value = {
image: null,
customerBranchId: '',
@ -520,15 +580,14 @@ async function onSubmit() {
}
if (selectorLabel.value === 'EMPLOYEE') {
console.log(profileFile.value);
await employeeStore.create({
...formDataEmployee.value,
image: profileSubmit.value ? profileFile.value ?? null : null,
});
const resultList = await employeeStore.fetchList();
if (resultList) listEmployee.value = resultList.result;
clearFormEmployee();
dialogEmployee.value = false;
}
}
@ -582,21 +641,37 @@ async function fetchListCustomer() {
}
async function onSubmitEdit(id: string) {
if (!formData.value) return;
if (selectorLabel.value === 'EMPLOYER') {
if (!formData.value) return;
const tempValue: CustomerUpdate = {
status: 'ACTIVE',
customerType: formData.value.customerType,
customerName: formData.value.customerName,
customerNameEN: formData.value.customerNameEN,
customerBranch: formData.value.customerBranch,
taxNo: formData.value.taxNo,
};
const tempValue: CustomerUpdate = {
status: 'ACTIVE',
customerType: formData.value.customerType,
customerName: formData.value.customerName,
customerNameEN: formData.value.customerNameEN,
customerBranch: formData.value.customerBranch,
taxNo: formData.value.taxNo,
};
await editById(id, tempValue);
infoDrawer.value = false;
fetchListCustomer();
clearForm();
await editById(id, tempValue);
infoDrawer.value = false;
fetchListCustomer();
clearForm();
}
if (selectorLabel.value === 'EMPLOYEE') {
if (!formDataEmployee.value) return;
await employeeStore.editById(id, {
...formDataEmployee.value,
image: profileSubmit.value ? profileFile.value ?? null : null,
});
const resultList = await employeeStore.fetchList();
if (resultList) listEmployee.value = resultList.result;
clearFormEmployee();
}
}
async function onDelete(id: string) {
@ -703,6 +778,103 @@ async function assignFormData(data: Customer & { branch: CustomerBranch[] }) {
});
}
async function assignFormDataEmployee(id: string) {
if (!listEmployee.value) return;
const foundEmployee = listEmployee.value.find((x: Employee) => x.id === id);
if (foundEmployee) {
const fetchCheckup = await employeeStore.fetchCheckup(foundEmployee.id);
const fetchWork = await employeeStore.fetchWork(foundEmployee.id);
const fetchOther = await employeeStore.fetchOther(foundEmployee.id);
const employeeCheckup =
fetchCheckup &&
fetchCheckup.map((i: EmployeeCheckup) => ({
coverageExpireDate: i.coverageExpireDate,
coverageStartDate: i.coverageStartDate,
insuranceCompany: i.insuranceCompany,
medicalBenefitScheme: i.medicalBenefitScheme,
remark: i.remark,
hospitalName: i.hospitalName,
provinceId: i.provinceId,
checkupResult: i.checkupResult,
checkupType: i.checkupType,
}));
const employeeWork =
fetchWork &&
fetchWork.map((i: EmployeeWork) => ({
workEndDate: i.workEndDate,
workPermitExpireDate: i.workPermitExpireDate,
workPermitIssuDate: i.workPermitIssuDate,
workPermitNo: i.workPermitNo,
workplace: i.workplace,
jobType: i.jobType,
positionName: i.positionName,
ownerName: i.ownerName,
remark: i.remark,
}));
const employeeOther = fetchOther && {
motherBirthPlace: fetchOther.motherBirthPlace,
motherLastNameEN: fetchOther.motherLastNameEN,
motherFirstNameEN: fetchOther.motherFirstNameEN,
fatherLastNameEN: fetchOther.fatherLastNameEN,
fatherFirstNameEN: fetchOther.fatherFirstNameEN,
motherLastName: fetchOther.motherLastName,
motherFirstName: fetchOther.motherFirstName,
fatherLastName: fetchOther.fatherLastName,
fatherFirstName: fetchOther.fatherFirstName,
fatherBirthPlace: fetchOther.fatherBirthPlace,
citizenId: fetchOther.citizenId,
};
currentEmployee.value = foundEmployee;
profileUrl.value = foundEmployee.profileImageUrl;
profileSubmit.value = true;
isEmployeeEdit.value = true;
formDataEmployee.value = {
image: foundEmployee.profileImageUrl,
customerBranchId: foundEmployee.customerBranchId,
nrcNo: foundEmployee.nrcNo,
dateOfBirth: foundEmployee.dateOfBirth,
gender: foundEmployee.gender,
nationality: foundEmployee.nationality,
firstName: foundEmployee.firstName,
firstNameEN: foundEmployee.firstNameEN,
lastName: foundEmployee.lastName,
lastNameEN: foundEmployee.lastNameEN,
addressEN: foundEmployee.addressEN,
address: foundEmployee.address,
zipCode: foundEmployee.zipCode,
passportType: foundEmployee.passportType,
passportNumber: foundEmployee.passportNumber,
passportIssueDate: foundEmployee.passportIssueDate,
passportExpiryDate: foundEmployee.passportExpiryDate,
passportIssuingCountry: foundEmployee.passportIssuingCountry,
passportIssuingPlace: foundEmployee.passportIssuingPlace,
previousPassportReference: foundEmployee.previousPassportReference,
visaType: foundEmployee.visaType,
visaNumber: foundEmployee.visaNumber,
visaIssueDate: foundEmployee.visaIssueDate,
visaExpiryDate: foundEmployee.visaExpiryDate,
visaIssuingPlace: foundEmployee.visaIssuingPlace,
visaStayUntilDate: foundEmployee.visaStayUntilDate,
tm6Number: foundEmployee.tm6Number,
entryDate: foundEmployee.entryDate,
workerStatus: foundEmployee.workerStatus,
subDistrictId: foundEmployee.subDistrictId,
districtId: foundEmployee.districtId,
provinceId: foundEmployee.provinceId,
employeeWork: employeeWork,
employeeCheckup: employeeCheckup,
employeeOtherInfo: employeeOther,
};
}
}
onMounted(async () => {
const resultStats = await getStatsCustomer();
@ -737,7 +909,6 @@ onMounted(async () => {
const resultOption = await fetch('/option/option.json');
employeeStore.globalOption = await resultOption.json();
console.log(employeeStore.globalOption);
});
watch(locale, () => {
@ -1028,6 +1199,8 @@ watch(fieldSelectedCustomer, async () => {
],
})) || []
"
@update-card="openDialogInputForm"
@enter-card="openDialogInputForm"
@delete-card="onDelete"
/>
</div>
@ -1291,10 +1464,16 @@ watch(fieldSelectedCustomer, async () => {
:title="$t('customerEmployeeAdd')"
:submit="
() => {
onSubmit();
isEmployeeEdit
? currentEmployee && onSubmitEdit(currentEmployee.id)
: onSubmit();
}
"
:close="
() => {
clearFormEmployee();
}
"
:close="() => {}"
>
<template #prepend>
<ProfileUpload
@ -2041,6 +2220,176 @@ watch(fieldSelectedCustomer, async () => {
</InfoForm>
</template>
</DrawerInfo>
<!-- Employee Info -->
<DrawerInfo
:title="
currentEmployee
? $i18n.locale === 'en-US'
? `${currentEmployee.firstNameEN} ${currentEmployee.lastNameEN}`
: `${currentEmployee.firstName} ${currentEmployee.lastName}`
: '-'
"
v-model:drawer-open="infoDrawerEmployee"
:is-edit="infoDrawerEmployeeEdit"
:editData="() => (infoDrawerEmployeeEdit = true)"
:undo="() => (infoDrawerEmployeeEdit = false)"
:close="() => clearFormEmployee()"
:submit="
() => {
currentEmployee && onSubmitEdit(currentEmployee.id);
}
"
>
<InfoForm
v-if="employeeStore.globalOption"
:readonly="!infoDrawerEmployeeEdit"
:noAddress="formDataEmployeeTab !== 'personalInfo'"
:noPaddingTab="
formDataEmployeeTab === 'healthCheck' ||
formDataEmployeeTab === 'workHistory'
"
v-model:tabs-list="employeeTab"
v-model:employee-tab="formDataEmployeeTab"
v-model:address="formDataEmployee.address"
v-model:addressEN="formDataEmployee.addressEN"
v-model:provinceId="formDataEmployee.provinceId"
v-model:districtId="formDataEmployee.districtId"
v-model:subDistrictId="formDataEmployee.subDistrictId"
v-model:zipCode="formDataEmployee.zipCode"
>
<template #person-card>
<div class="q-ma-md">
<AppBox class="surface-1" style="padding: 0">
<PersonCard
no-hover
no-action
no-detail
no-bg
:list="infoEmployeePersonCard"
:gridColumns="1"
/>
</AppBox>
</div>
</template>
<template #information>
<BasicInformation
v-if="formDataEmployeeTab === 'personalInfo' && currentEmployee"
employee
dense
outlined
separator
:readonly="!infoDrawerEmployeeEdit"
:employee-owner-option="employeeStore.ownerOption"
v-model:customer-branch-id="formDataEmployee.customerBranchId"
v-model:employee-id="currentEmployee.code"
v-model:nrc-no="formDataEmployee.nrcNo"
@filter-owner-branch="employeeFilterOwnerBranch"
/>
</template>
<template #person>
<FormPerson
v-if="formDataEmployeeTab === 'personalInfo'"
dense
outlined
employee
separator
:readonly="!infoDrawerEmployeeEdit"
v-model:firstName="formDataEmployee.firstName"
v-model:lastName="formDataEmployee.lastName"
v-model:firstNameEN="formDataEmployee.firstNameEN"
v-model:lastNameEN="formDataEmployee.lastNameEN"
v-model:gender="formDataEmployee.gender"
v-model:birthDate="formDataEmployee.dateOfBirth"
v-model:nationality="formDataEmployee.nationality"
/>
<FormEmployeeHealthCheck
v-if="
formDataEmployeeTab === 'healthCheck' &&
formDataEmployee.employeeCheckup
"
:readonly="!infoDrawerEmployeeEdit"
dense
outlined
v-model:employee-checkup="formDataEmployee.employeeCheckup"
v-model:checkup-type-option="
employeeStore.globalOption.tha.nationality
"
v-model:medical-benefit-option="
employeeStore.globalOption.tha.typeInsurance
"
v-model:insurance-company-option="
employeeStore.globalOption.tha.insurancePlace
"
/>
<FormEmployeeWorkHistory
v-if="formDataEmployeeTab === 'workHistory'"
dense
outlined
:readonly="!infoDrawerEmployeeEdit"
v-model:employee-work="formDataEmployee.employeeWork"
v-model:position-name-option="employeeStore.globalOption.tha.position"
v-model:job-type-option="employeeStore.globalOption.tha.businessType"
v-model:workplace-option="employeeStore.globalOption.tha.area"
/>
<FormEmployeeOther
v-if="
formDataEmployeeTab === 'other' &&
formDataEmployee.employeeOtherInfo
"
:readonly="!infoDrawerEmployeeEdit"
dense
outlined
v-model:employee-other="formDataEmployee.employeeOtherInfo"
/>
</template>
<template #by-type>
<FormEmployeePassport
v-if="formDataEmployeeTab === 'personalInfo'"
dense
outlined
employee
separator
:readonly="!infoDrawerEmployeeEdit"
v-model:passport-type="formDataEmployee.passportType"
v-model:passport-number="formDataEmployee.passportNumber"
v-model:passport-issue-date="formDataEmployee.passportIssueDate"
v-model:passport-expiry-date="formDataEmployee.passportExpiryDate"
v-model:passport-issuing-place="formDataEmployee.passportIssuingPlace"
v-model:passport-issuing-country="
formDataEmployee.passportIssuingCountry
"
v-model:previous-passport-reference="
formDataEmployee.previousPassportReference
"
v-model:passport-type-option="
employeeStore.globalOption.tha.nationality
"
v-model:passport-issuing-country-option="
employeeStore.globalOption.tha.nationality
"
/>
<FormEmployeeVisa
v-if="formDataEmployeeTab === 'personalInfo'"
dense
outlined
employee
:readonly="!infoDrawerEmployeeEdit"
v-model:visa-type="formDataEmployee.visaType"
v-model:visa-number="formDataEmployee.visaNumber"
v-model:visa-issue-date="formDataEmployee.visaIssueDate"
v-model:visa-expiry-date="formDataEmployee.visaExpiryDate"
v-model:visa-issuing-place="formDataEmployee.visaIssuingPlace"
v-model:visa-stay-until-date="formDataEmployee.visaStayUntilDate"
v-model:tm6-number="formDataEmployee.tm6Number"
v-model:entry-date="formDataEmployee.entryDate"
v-model:visa-type-option="employeeStore.globalOption.tha.nationality"
/>
</template>
</InfoForm>
</DrawerInfo>
</template>
<style scoped>