feat: edit profile on drawer

This commit is contained in:
puriphatt 2024-06-27 08:32:31 +00:00
parent 23725ad730
commit b24f785311
3 changed files with 156 additions and 188 deletions

View file

@ -21,9 +21,11 @@ defineProps<{
noDetail?: boolean; noDetail?: boolean;
noBg?: boolean; noBg?: boolean;
detailColumnCount?: number; detailColumnCount?: number;
canEditProfile?: boolean;
}>(); }>();
defineEmits<{ defineEmits<{
(e: 'editProfile'): void;
(e: 'deleteCard', id: string): void; (e: 'deleteCard', id: string): void;
( (
e: 'updateCard', e: 'updateCard',
@ -160,6 +162,12 @@ defineEmits<{
bordered bordered
class="avatar" class="avatar"
style="border: 2px solid var(--border-color)" style="border: 2px solid var(--border-color)"
:class="{ 'edit-profile': canEditProfile }"
@click="
() => {
if (canEditProfile) $emit('editProfile');
}
"
> >
<q-img <q-img
:src="v.img" :src="v.img"
@ -360,4 +368,13 @@ defineEmits<{
background-color: var(--surface-2); background-color: var(--surface-2);
text-wrap: nowrap; text-wrap: nowrap;
} }
.edit-profile {
cursor: pointer;
transition: opacity 0.2s ease;
&:hover {
opacity: 80%;
}
}
</style> </style>

View file

@ -1,9 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, watch } from 'vue'; import { ref, onMounted, watch, computed } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { dialog } from 'src/stores/utils';
import { useI18n } from 'vue-i18n';
import useFlowStore from 'src/stores/flow';
import useUserStore from 'stores/user'; import useUserStore from 'stores/user';
import useBranchStore from 'src/stores/branch'; import useBranchStore from 'src/stores/branch';
import { dialog } from 'src/stores/utils';
import { import {
User, User,
@ -26,9 +28,7 @@ import FormPerson from 'src/components/02_personnel-management/FormPerson.vue';
import FormByType from 'src/components/02_personnel-management/FormByType.vue'; import FormByType from 'src/components/02_personnel-management/FormByType.vue';
import DrawerInfo from 'src/components/DrawerInfo.vue'; import DrawerInfo from 'src/components/DrawerInfo.vue';
import InfoForm from 'src/components/02_personnel-management/InfoForm.vue'; import InfoForm from 'src/components/02_personnel-management/InfoForm.vue';
import { computed } from 'vue'; import ProfileUpload from 'src/components/ProfileUpload.vue';
import { useI18n } from 'vue-i18n';
import useFlowStore from 'src/stores/flow';
const { locale } = useI18n(); const { locale } = useI18n();
@ -144,6 +144,8 @@ reader.addEventListener('load', () => {
if (typeof reader.result === 'string') { if (typeof reader.result === 'string') {
urlProfile.value = reader.result; urlProfile.value = reader.result;
} }
if (infoDrawerEdit.value) infoPersonCard.value[0].img = urlProfile.value;
}); });
watch(profileFile, () => { watch(profileFile, () => {
@ -229,7 +231,8 @@ function onClose() {
brId.value = ''; brId.value = '';
userId.value = ''; userId.value = '';
userCode.value = ''; userCode.value = '';
urlProfile.value = ''; urlProfile.value = undefined;
profileFile.value = undefined;
agencyFile.value = []; agencyFile.value = [];
modal.value = false; modal.value = false;
isEdit.value = false; isEdit.value = false;
@ -249,121 +252,101 @@ async function onSubmit() {
} }
if (isEdit.value && userId.value) { if (isEdit.value && userId.value) {
dialog({ if (!userId.value) return;
color: 'primary',
icon: 'mdi-pencil-outline',
title: 'ยืนยันการแก้ไขข้อมูล',
actionText: 'ตกลง',
persistent: true,
message: 'คุณต้องการแก้ไขข้อมูล ใช่หรือไม่',
action: async () => {
if (!userId.value) return;
const formDataEdit = { const formDataEdit = {
...formData.value, ...formData.value,
status: !statusToggle.value ? 'INACTIVE' : 'ACTIVE', status: !statusToggle.value ? 'INACTIVE' : 'ACTIVE',
} as const; } as const;
await userStore.editById(userId.value, formDataEdit); await userStore.editById(userId.value, formDataEdit);
if ( if (
hqId.value && hqId.value &&
currentUser.value?.branch[0] && currentUser.value?.branch[0] &&
hqId.value !== currentUser.value.branch[0].id && hqId.value !== currentUser.value.branch[0].id &&
brId.value !== currentUser.value.branch[0].id brId.value !== currentUser.value.branch[0].id
) { ) {
await branchStore.removeUser( await branchStore.removeUser(
currentUser.value.branch[0].id, currentUser.value.branch[0].id,
userId.value, userId.value,
); );
await branchStore.addUser( await branchStore.addUser(
!!brId.value ? brId.value : hqId.value, !!brId.value ? brId.value : hqId.value,
userId.value, userId.value,
); );
} }
if (!currentUser.value?.branch[0]) { if (!currentUser.value?.branch[0]) {
if (brId.value || hqId.value) { if (brId.value || hqId.value) {
await branchStore.addUser( await branchStore.addUser(
(!!brId.value ? brId.value : hqId.value)!, (!!brId.value ? brId.value : hqId.value)!,
userId.value, userId.value,
); );
} }
} }
if (userId.value && formDataEdit.userType === 'AGENCY') { if (userId.value && 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(userId.value, payload);
} }
} }
userStore.fetchList({ userStore.fetchList({
includeBranch: true, includeBranch: true,
query: !!inputSearch.value ? inputSearch.value : undefined, query: !!inputSearch.value ? inputSearch.value : undefined,
userType: selectorLabel.value ?? undefined, userType: selectorLabel.value ?? undefined,
});
typeStats.value = await userStore.typeStats();
const res = await branchStore.userStats(formData.value.userType);
if (res) {
userStats.value = res;
}
onClose();
},
}); });
typeStats.value = await userStore.typeStats();
const res = await branchStore.userStats(formData.value.userType);
if (res) {
userStats.value = res;
}
onClose();
} else { } else {
dialog({ if (!hqId.value) return;
color: 'primary',
icon: 'mdi-account',
title: 'ยืนยันการเพิ่มบุคลากร',
actionText: 'ตกลง',
persistent: true,
message: 'คุณต้องการเพิ่มบุคลากร ใช่หรือไม่',
action: async () => {
if (!hqId.value) return;
statusToggle.value statusToggle.value
? (formData.value.status = 'CREATED') ? (formData.value.status = 'CREATED')
: (formData.value.status = 'INACTIVE'); : (formData.value.status = 'INACTIVE');
const result = await userStore.create(formData.value); const result = await userStore.create(formData.value);
if (result) { if (result) {
await branchStore.addUser( await branchStore.addUser(
!!brId.value ? brId.value : hqId.value, !!brId.value ? brId.value : hqId.value,
result.id, result.id,
); );
} }
if (result && formData.value.userType === 'AGENCY') { if (result && formData.value.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(result.id, payload); await userStore.addAttachment(result.id, payload);
} }
} }
selectorLabel.value = formData.value.userType; selectorLabel.value = formData.value.userType;
userStore.fetchList({ userStore.fetchList({
includeBranch: true, includeBranch: true,
query: !!inputSearch.value ? inputSearch.value : undefined, query: !!inputSearch.value ? inputSearch.value : undefined,
userType: selectorLabel.value ?? undefined, userType: selectorLabel.value ?? undefined,
});
typeStats.value = await userStore.typeStats();
const res = await branchStore.userStats(formData.value.userType);
if (res) {
userStats.value = res;
}
onClose();
},
}); });
typeStats.value = await userStore.typeStats();
const res = await branchStore.userStats(formData.value.userType);
if (res) {
userStats.value = res;
}
onClose();
} }
flowStore.rotate(); flowStore.rotate();
} }
@ -474,9 +457,13 @@ async function assignFormData(idEdit: string) {
userCode.value = foundUser.code; userCode.value = foundUser.code;
urlProfile.value = foundUser.profileImageUrl; urlProfile.value = foundUser.profileImageUrl;
isEdit.value = true; isEdit.value = true;
profileSubmit.value = true; profileSubmit.value = true;
hqId.value && (await userStore.fetchBrOption(hqId.value)); hqId.value && (await userStore.fetchBrOption(hqId.value));
if (infoPersonCard.value) {
infoPersonCard.value[0].img = foundUser.profileImageUrl;
}
if (formData.value.districtId) { if (formData.value.districtId) {
await adrressStore.fetchSubDistrictByProvinceId( await adrressStore.fetchSubDistrictByProvinceId(
formData.value.districtId, formData.value.districtId,
@ -728,7 +715,11 @@ watch(
v-if="currentUser" v-if="currentUser"
:category="$t('personnelTitle')" :category="$t('personnelTitle')"
bg-on bg-on
:badgeClass="formData.gender === 'male' ? 'app-bg-male' : 'app-bg-female'" :badgeClass="
formData.gender === 'male'
? 'app-bg-male text-white'
: 'app-bg-female text-white'
"
:badgeLabel="userCode" :badgeLabel="userCode"
:isEdit="infoDrawerEdit" :isEdit="infoDrawerEdit"
:title=" :title="
@ -757,12 +748,18 @@ watch(
<div class="q-ma-md"> <div class="q-ma-md">
<AppBox class="surface-1" style="padding: 0"> <AppBox class="surface-1" style="padding: 0">
<PersonCard <PersonCard
:can-edit-profile="infoDrawerEdit"
no-hover no-hover
no-action no-action
no-detail no-detail
no-bg no-bg
:list="infoPersonCard" :list="infoPersonCard"
:gridColumns="1" :gridColumns="1"
@edit-profile="
() => {
inputFile.click();
}
"
/> />
</AppBox> </AppBox>
</div> </div>
@ -843,86 +840,12 @@ watch(
:close="() => onClose()" :close="() => onClose()"
> >
<template #prepend> <template #prepend>
<div class="text-center"> <ProfileUpload
<div class="upload-img-preview"> v-model:url-profile="urlProfile"
<q-img v-model:status-toggle="statusToggle"
v-if="urlProfile" v-model:profile-submit="profileSubmit"
:src="urlProfile" @input-file="inputFile.click()"
style="object-fit: cover; width: 100%; height: 100%" />
/>
<q-icon
v-else
name="mdi-account full-height"
size="3vw"
style="color: var(--border-color)"
/>
</div>
<div
v-if="urlProfile && !profileSubmit"
class="col-12 row q-mt-md q-col-gutter-x-md"
>
<div class="col-6">
<q-btn
id="btn-profile-cancel"
dense
unelevated
outlined
padding="8px"
class="cancel-img-btn full-width"
:label="$t('cancel')"
@click="urlProfile = ''"
/>
</div>
<div class="col-6">
<q-btn
id="btn-profile-save"
dense
unelevated
outlined
padding="8px"
class="submit-img-btn full-width"
:label="$t('save')"
@click="profileSubmit = true"
/>
</div>
</div>
<q-btn
id="btn-profile-edit"
v-else-if="urlProfile && profileSubmit"
dense
unelevated
outlined
padding="8px"
icon="mdi-pencil-outline"
class="edit-img-btn q-mt-md full-width"
:label="$t('formDialogBtnEditProfile')"
@click.prevent="inputFile.click(), (profileSubmit = false)"
/>
<q-btn
id="btn-profile-upload"
v-else
dense
unelevated
outlined
padding="8px"
class="upload-img-btn q-mt-md full-width"
:label="$t('formDialogBtnUploadProfile')"
:class="{ dark: $q.dark.isActive }"
@click="inputFile.click()"
/>
<q-separator class="col-12 q-my-md" />
<div class="text-left q-pt-md">
<q-toggle
dense
size="md"
v-model="statusToggle"
padding="none"
class="q-pr-md"
/>
<span>{{ $t('formDialogTitleUserStatus') }}</span>
</div>
</div>
</template> </template>
<template #information> <template #information>

View file

@ -269,6 +269,12 @@ const inputFile = (() => {
const reader = new FileReader(); const reader = new FileReader();
reader.addEventListener('load', () => { reader.addEventListener('load', () => {
if (typeof reader.result === 'string') profileUrl.value = reader.result; if (typeof reader.result === 'string') profileUrl.value = reader.result;
if (infoDrawerEdit.value && currentCustomer.value)
currentCustomer.value.imageUrl = profileUrl.value as string;
if (infoDrawerEmployeeEdit.value)
infoEmployeePersonCard.value[0].img = profileUrl.value;
}); });
element.addEventListener('change', () => { element.addEventListener('change', () => {
@ -977,6 +983,7 @@ async function assignFormDataEmployee(id: string) {
currentEmployeeCode.value = foundEmployee.code; currentEmployeeCode.value = foundEmployee.code;
currentEmployee.value = foundEmployee; currentEmployee.value = foundEmployee;
profileUrl.value = foundEmployee.profileImageUrl; profileUrl.value = foundEmployee.profileImageUrl;
foundEmployee.profileImageUrl foundEmployee.profileImageUrl
@ -1041,6 +1048,10 @@ async function assignFormDataEmployee(id: string) {
} else { } else {
formDataEmployeeSameAddr.value = false; formDataEmployeeSameAddr.value = false;
} }
if (infoEmployeePersonCard.value) {
infoEmployeePersonCard.value[0].img = foundEmployee.profileImageUrl;
}
} }
flowStore.rotate(); flowStore.rotate();
} }
@ -2302,6 +2313,9 @@ watch(selectorLabel, async () => {
" "
v-model:drawer-open="infoDrawer" v-model:drawer-open="infoDrawer"
:badgeLabel="currentCustomer?.code" :badgeLabel="currentCustomer?.code"
:badgeClass="
currentCustomer?.customerType === 'CORP' ? 'app-bg-corp' : 'app-bg-pers'
"
:undo="() => undo()" :undo="() => undo()"
:isEdit="infoDrawerEdit" :isEdit="infoDrawerEdit"
:close="() => onClose()" :close="() => onClose()"
@ -2317,8 +2331,9 @@ watch(selectorLabel, async () => {
<InfoForm> <InfoForm>
<template #person-card> <template #person-card>
<div class="q-ma-md"> <div class="q-ma-md">
<AppBox class="surface-1" style="padding: 0"> <AppBox class="surface-1 bordered-b" style="padding: 0">
<UsersDetailCardComponent <UsersDetailCardComponent
:can-edit-profile="infoDrawerEdit"
:hideButton="true" :hideButton="true"
v-if="!!currentCustomer" v-if="!!currentCustomer"
no-bg no-bg
@ -2339,6 +2354,11 @@ watch(selectorLabel, async () => {
status: currentCustomer.status, status: currentCustomer.status,
code: 'HQ006', code: 'HQ006',
}" }"
@edit-profile="
() => {
inputFile.click();
}
"
/> />
</AppBox> </AppBox>
</div> </div>
@ -2497,7 +2517,9 @@ watch(selectorLabel, async () => {
:title="$t('formTitleBranch', { msg: currentBranch.name })" :title="$t('formTitleBranch', { msg: currentBranch.name })"
v-model:drawer-open="infoDrawerBranch" v-model:drawer-open="infoDrawerBranch"
:badgeLabel="currentBranch.code" :badgeLabel="currentBranch.code"
badgeClass="app-bg-pers" :badgeClass="
currentCustomer?.customerType === 'CORP' ? 'app-bg-corp' : 'app-bg-pers'
"
:undo=" :undo="
() => { () => {
cloneData(); cloneData();
@ -2752,12 +2774,18 @@ watch(selectorLabel, async () => {
<div class="q-ma-md"> <div class="q-ma-md">
<AppBox class="surface-1" style="padding: 0"> <AppBox class="surface-1" style="padding: 0">
<PersonCard <PersonCard
:can-edit-profile="infoDrawerEmployeeEdit"
no-hover no-hover
no-action no-action
no-detail no-detail
no-bg no-bg
:list="infoEmployeePersonCard" :list="infoEmployeePersonCard"
:gridColumns="1" :gridColumns="1"
@edit-profile="
() => {
inputFile.click();
}
"
/> />
</AppBox> </AppBox>
</div> </div>