You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

579 lines
21 KiB

<template>
<div class="xl:tw-max-w-[1246px] xl:tw-mx-auto">
<div class="xl:tw-flex xl:tw-justify-between xl:tw-items-start">
<userSidebar :userData="userData" :firstName="firstName" :lastName="lastName" class="tw-hidden xl:tw-block">
</userSidebar>
<div class="tw-bg-white xl:tw-p-[30px] xl:tw-rounded-[20px] xl:tw-min-w-[900px] xl:tw-max-w-[900px]">
<div class="tw-text-[20px] tw-mb-[30px] tw-font-bold tw-text-base-primary md:t24">
<two-dots class="tw-mr-[30px]"></two-dots>{{ $t("userProfile.personalInfo") }}
</div>
<!-- alert -->
<div v-if="successUpdate" class="tw-flex tw-w-full tw-right-auto tw-left-auto">
<div type="success"
class="tw-absolute tw-top-0 tw-right-0 tw-left-0 tw-mx-auto tw-flex tw-flex-row tw-w-fit tw-items-center tw-bg-success-background tw-rounded-[12px] tw-px-[12px] tw-py-[10px]">
<img src="~/assets/svg/alert_success.svg" class="tw-w-[16px] tw-mr-[10px] md:tw-w-[20px]" alt="" />
<div class="tw-hidden md:tw-block tw-text-success-default tw-font-bold tw-text-[16px] md:tw-mr-[16px]">
Awesome!
</div>
<div class="tw-text-[12px] tw-text-success-default tw-mr-[9px] md:tw-text-[16px] md:tw-mr-[16px]">
Successfully updated.
</div>
<img src="~/assets/svg/alert_close.svg" @click="successUpdate = !successUpdate" />
</div>
</div>
<div v-if="fileTooBig" class="tw-flex tw-w-full tw-right-auto tw-left-auto">
<div type="error"
class="tw-absolute tw-top-0 tw-right-0 tw-left-0 tw-mx-auto tw-flex tw-flex-row tw-w-fit tw-items-center tw-bg-error-background tw-rounded-[16px] tw-px-[12px] tw-py-[10px] tw-text-[12px]">
<img src="~/assets/svg/alert_notice.svg" class="tw-w-[16px] tw-mr-[10px] md:tw-w-[20px]" alt="" />
<div class="tw-hidden md:tw-block tw-text-error-default tw-mr-[25px] tw-text-[16px]">
Oops!
</div>
<div class="tw-text-error-default tw-mr-[8px] md:tw-text-[16px] md:tw-mr-[16px]">
Please upload an image under 2MB.
</div>
<img src="~/assets/svg/alert_close_red.svg" @click="fileTooBig = !fileTooBig" alt="" />
</div>
</div>
<!-- user picture -->
<div class="tw-flex tw-items-center tw-mb-[30px]">
<div>
<img v-if="userData.picture" :src="userData.picture"
class="tw-border tw-border-solid tw-border-neutral-300 tw-h-[64px] tw-w-[64px] tw-rounded-[50px] tw-mr-[15px] md:tw-h-[90px] md:tw-w-[90px] md:tw-mr-[30px]"
alt="" />
<div v-else
class="tw-flex tw-justify-center tw-items-center tw-bg-primary-1 tw-text-white tw-text-[32px] tw-h-[64px] tw-w-[64px] tw-rounded-[50px] tw-mr-[15px] md:tw-h-[90px] md:tw-w-[90px] md:tw-mr-[30px]">
{{ lastName.split("")[0] || "" }}
</div>
</div>
<div>
<button
class="tw-border tw-border-solid tw-border-primary-1 tw-text-[14px] tw-text-primary-1 tw-px-[16px] tw-py-[9px] tw-rounded-[12px] md:tw-text-[16px]"
@click="handleUploadEvent">
{{ $t("userProfile.uploadPicture") }}
</button>
<input type="file" class="d-none" ref="uploader" @change="userPictureUpload" />
</div>
</div>
<!-- form -->
<form
class="tw-grid tw-grid-cols-1 tw-gap-y-[20px] tw-mb-[58px] md:tw-grid-cols-2 md:tw-gap-x-[60px] md:tw-mb-[50px]">
<!-- tilte -->
<div class="element md:tw-col-span-2 md:tw-max-w-[394px]">
<div class="tw-text-[14px] tw-text-base-primary tw-mb-[10px]">
{{ $t("userProfile.title") }}
</div>
<elementSelectWithIndex :select="{
id: 'title',
label: 'Title',
required: false,
}" :yearList="genderOptions" :default="userData.title" :validation="validation.title"
@change="userData.title = $event"></elementSelectWithIndex>
</div>
<!-- firstName -->
<div class="element">
<elementInput :input="{
id: 'firstName',
label: 'First Name',
required: true,
type: 'text',
}" :default="userData.first_name" :validation="validation.first_name"
@change="userData.first_name = $event"></elementInput>
</div>
<!-- lastName -->
<div class="element">
<elementInput :input="{
id: 'lastName',
label: 'Last Name',
required: true,
type: 'text',
}" :default="userData.last_name" :validation="validation.last_name" @change="userData.last_name = $event">
</elementInput>
</div>
<!-- email -->
<div class="element">
<elementInput :input="{
id: 'email',
label: 'userProfile.personalMail',
required: true,
type: 'email',
}" :default="userData.email" :validation="validation.email" @change="userData.email = $event">
</elementInput>
</div>
<!-- Phone -->
<div>
<div class="tw-text-[14px] tw-text-base-primary tw-mb-[10px]">
{{ $t("userProfile.phone") }}
</div>
<div>
<vue-country-code class="d-none" v-model="countryCode" enabledCountryCode :enabledFlags="false"
@onSelect="showCode"></vue-country-code>
<vue-phone-number-input v-model="userData.phone" color="#EF5A5A" valid-color="#EE9546" :border-radius="5"
no-flags no-example @update="getPhoneData" :translations="translateOption">
</vue-phone-number-input>
</div>
</div>
<!-- Birthday -->
<div class="md:tw-col-span-2 xl:tw-col-span-1">
<div class="tw-text-[14px] tw-text-base-primary tw-mb-[10px]">
{{ $t("userProfile.birthday")
}}<span class="tw-text-error-default">*</span>
</div>
<div class="tw-grid tw-grid-cols-3 tw-gap-[20px]">
<div class="element">
<elementSelectWithIndex :select="{
id: 'yearSelect',
required: true,
}" :yearList="yearOptions" :default="yearSelect" :validation="validation.yearSelect"
@change="yearSelect = $event"></elementSelectWithIndex>
</div>
<div class="element">
<elementSelectWithIndex :select="{
id: 'monthSelect',
required: false,
}" :yearList="monthOptions" :default="monthSelect" :validation="validation.monthSelect"
@change="monthSelect = $event"></elementSelectWithIndex>
</div>
<div class="element">
<elementSelectWithIndex :select="{
id: 'daySelect',
required: false,
}" :yearList="dayOptions" :default="daySelect" :validation="validation.daySelect"
@change="daySelect = $event"></elementSelectWithIndex>
</div>
</div>
</div>
<!-- Country -->
<div class="element md:tw-col-span-2 xl:tw-col-span-1">
<elementSelect :select="{
id: 'Country',
label: 'userProfile.countryAndRegion',
required: true,
}" :selectList="countryOptions" :default="userData.country" :validation="validation.country"
@change="userData.country = $event"></elementSelect>
</div>
<!-- language -->
<!-- <div class="md:tw-col-span-2 md:tw-mt-[20px]">
<div class="md:tw-flex md:tw-flex-row">
<div
class="tw-text-[14px] tw-text-base-primary tw-mb-[20px] md:tw-mr-[40px] md:tw-mb-0"
>
{{ $t("userProfile.preferLanguage") }}
</div>
<div class="tw-flex tw-flex-row">
<div class="element tw-mr-[40px]">
<elementCheckBox
:input="{
id: 'languageSelect.en',
label: 'English',
}"
:default="languageSelect.en"
@update="userData.prefer_country = $event"
></elementCheckBox>
</div>
<div class="element">
<elementCheckBox
:input="{
id: 'languageSelect.zhtw',
label: 'Chinese',
}"
:dafault="languageSelect.zhtw"
@update="userData.prefer_country = $event"
></elementCheckBox>
</div>
</div>
</div>
</div> -->
<!-- language -->
<v-spacer class="tw-items-center md:tw-flex md:tw-justify-start md:tw-mt-[20px] md:tw-flex-nowrap">
<div class="tw-inline-block tw-mb-[20px] md:tw-whitespace-nowrap md:tw-mb-0">
{{ $t("userProfile.preferLanguage") }}
</div>
<v-spacer class="tw-flex md:tw-w-fit md:tw-ml-[30px]">
<v-checkbox v-model="languageSelect.en" class="mt-0" hide-details :label="$t('English')"></v-checkbox>
<v-checkbox v-model="languageSelect.zhtw" class="ms-10 mt-0" hide-details :label="$t('Chinese')">
</v-checkbox>
</v-spacer>
</v-spacer>
</form>
<!-- button -->
<div>
<button
class="tw-text-[18px] tw-bg-primary-1 tw-text-white tw-py-[12px] tw-w-full tw-rounded-[16px] tw-mb-[10px] md:tw-w-fit md:tw-px-[16px] md:tw-mr-[20px]"
@click="patchUserData()">
{{ $t("userProfile.saveButton") }}
</button>
<button
class="tw-text-[18px] tw-bg-white tw-text-primary-1 tw-py-[12px] tw-w-full tw-rounded-[16px] md:tw-w-fit md:tw-px-[16px] xl:tw-hidden">
{{ $t("Cancel") }}
</button>
</div>
</div>
</div>
<CropImageDialog :isCropImageDialogActive="isCropImageDialogActive" :cropImagePreview="cropImagePreview"
@close-crop-dialog="closeCropDialog" @upload-image-success="handleImageUpdate"></CropImageDialog>
</div>
</template>
<script>
import SavedExhibitions from "../../components/user/savedExhibitions.vue";
import SavedExhibitionsDialog from "../../components/user/savedExhibitionsDialog.vue";
import Setting from "../../components/user/Setting.vue";
import SettingDialog from "../../components/user/settingDialog.vue";
import CropImageDialog from "../../components/user/cropImageDialog.vue";
import CompanyInfo from "../../components/user/companyInfo.vue";
import CompanyInfoDialog from "../../components/user/companyInfoDialog.vue";
import ContactInfo from "../../components/user/contactInfo.vue";
import vClickOutside from "v-click-outside";
import BookingList from "../../components/user/bookingList.vue";
import userSidebar from "@/components/user/userSidebar.vue";
import TwoDots from "@/components/TwoDots";
import elementInput from "@/components/newComponent/form/ElementInput";
import elementSelectWithIndex from "@/components/newComponent/form/ElementSelectWithIndex.vue";
import elementSelect from "@/components/newComponent/form/ElementSelect";
import elementCheckBox from "@/components/newComponent/form/ElementCheckBox";
import is from "is_js";
export default {
name: "profile",
layout: "profile",
directives: {
clickOutside: vClickOutside.directive,
},
components: {
SavedExhibitions,
SavedExhibitionsDialog,
Setting,
SettingDialog,
CropImageDialog,
CompanyInfo,
CompanyInfoDialog,
ContactInfo,
BookingList,
userSidebar,
TwoDots,
elementInput,
elementSelectWithIndex,
elementSelect,
elementCheckBox,
is,
},
data() {
return {
genderOptions: ["Mr.", "Ms."],
// isEditInfoDialogActive: false,
successUpdate: false,
fileTooBig: false,
firstName: "",
lastName: "",
countryCode: "",
userData: {},
userCompanyId: [],
yearOptions: [],
monthOptions: [],
dayOptions: [],
countryOptions: [],
yearSelect: "",
monthSelect: "",
daySelect: "",
languageSelect: {
en: "",
zhtw: "",
},
isCropImageDialogActive: false,
cropImagePreview: "",
translateOption: {
countrySelectorLabel: this.$t("country code"),
phoneNumberLabel: this.$t("phone number"),
},
rules: {
require: (value) => !!value || this.$t("Required."),
email: (v) =>
/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(
v
) || this.$t("Invalid email"),
checkPassword: (v) =>
(/(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])/.test(v) &&
v.length >= 8 &&
v.length <= 20) ||
this.$t(
"Passwords must be 8-20 characters with at least 1 number, 1 lower case letter and 1 upper case letter"
),
},
// personalInfoValid: false,
phoneValid: false,
isNavActive: true,
// width: 0,
validation: {
title: true,
first_name: true,
last_name: true,
email: true,
yearSelect: true,
monthSelect: true,
daySelect: true,
country: true,
},
errors: null,
english: false,
chinese: false,
};
},
created() {
// this.fetchCountry();
this.$store.dispatch("updatePicture");
// this.fetchUserData();
},
mounted() {
this.yearOptions = Array.from(new Array(103), (val, index) =>
(index + 1920).toString()
);
this.monthOptions = Array.from(new Array(13), (val, index) => {
if (index < 10 && index > 0) {
return "0" + index.toString();
}
if (index >= 10) {
return index.toString();
}
});
this.dayOptions = Array.from(new Array(32), (val, index) => {
if (index < 10 && index > 0) {
return "0" + index.toString();
}
if (index >= 10) {
return index.toString();
}
});
this.$nextTick(() => {
window.addEventListener("resize", this.onResize);
});
},
methods: {
// acitveEditInfoDialog() {
// if (this.width < 1366) {
// this.isEditInfoDialogActive = !this.isEditInfoDialogActive;
// } else {
// this.tabs = "editPersonalInfo";
// }
// },
patchUserData() {
// if (this.width < 1366) {
// this.isEditInfoDialogActive = !this.isEditInfoDialogActive;
// }
this.validators();
if (this.validators()) {
this.userData.prefer_country = JSON.stringify(this.languageSelect);
if (this.$vuetify.breakpoint.name !== "xs") {
this.userData.birth_date =
this.yearSelect + "-" + this.monthSelect + "-" + this.daySelect;
if (this.userData.birth_date.length < 10) {
this.userData.birth_date = null;
}
}
const patchData = JSON.parse(JSON.stringify(this.userData));
delete patchData.LoginLog;
delete patchData.UserCompany;
delete patchData.UserSocialRelation;
delete patchData.UserExhibition;
this.$axios
.put(
`/member/users/${this.$route.params.id}?jwt=${this.$auth.$storage.getUniversal("jwt").token || ""
}`,
patchData
)
.then((res) => {
if (res.status == 200) {
this.$notify({
type: "success",
text: "更新成功",
});
}
this.fetchUserData();
this.$auth.$storage.setUniversal("userPicture", patchData.picture);
this.$auth.$storage.setUniversal(
"userLastName",
patchData.last_name
);
this.$store.dispatch("updatePicture");
})
.catch((err) => {
console.log(err);
});
}
},
fetchUserData() {
this.$axios
.get(
`/member/users/${this.$route.params.id}?jwt=${this.$auth.$storage.getUniversal("jwt").token
}`
)
.then((res) => {
this.userData = res.data;
this.userCompanyId = res.data.UserCompany;
this.firstName = res.data.first_name;
this.lastName = res.data.last_name;
this.userData.phone
? (this.phoneValid = true)
: (this.phoneValid = false);
!this.userData.prefer_country &&
typeof this.userData.prefer_country === "object"
? this.userData.prefer_country
: (this.languageSelect = JSON.parse(this.userData.prefer_country));
if (
!this.userData.birth_date &&
typeof this.userData.birth_date === "object"
) {
this.yearSelect = "";
this.monthSelect = "";
this.daySelect = "";
} else {
const date = this.userData.birth_date.split("-");
this.yearSelect = date[0];
this.monthSelect = date[1];
this.daySelect = date[2];
}
})
.catch((err) => {
console.log(err);
});
},
handleUploadEvent() {
window.addEventListener("focus", () => { }, { once: true });
this.$refs.uploader.click();
},
userPictureUpload(e) {
const payload = new FormData();
payload.append("file", e.target.files[0]);
const fileSize = (e.target.files[0].size / 1024 / 1024).toFixed(1);
if (fileSize >= 2) {
this.fileTooBig = !this.fileTooBig;
} else {
this.$axios
.post("/users/images", payload)
.then((response) => {
this.cropImagePreview = response.data.image.url;
})
.catch((error) => console.log(error));
this.isCropImageDialogActive = !this.isCropImageDialogActive;
}
},
getPhoneData(phoneData) {
this.countryCode = phoneData.countryCode;
this.phoneValid = phoneData.isValid;
},
showCode(object) {
this.userData.country_code = object.dialCode;
},
fetchCountry() {
this.$axios
.get(`/users/countries?lang=${this.$i18n.locale.replace("-", "")}`)
.then((res) => {
this.countryOptions = res.data.result;
})
.catch((err) => {
console.log(err);
});
},
closeCropDialog() {
this.isCropImageDialogActive = !this.isCropImageDialogActive;
},
handleImageUpdate(pictureURL) {
this.userData.picture = pictureURL;
this.patchUserData();
this.closeCropDialog();
},
validators() {
if (is.empty(this.userData.first_name)) {
this.validation.first_name = false;
} else {
this.validation.first_name = true;
}
if (is.empty(this.userData.last_name)) {
this.validation.last_name = false;
} else {
this.validation.last_name = true;
}
if (is.empty(this.userData.email) || is.not.email(this.userData.email)) {
this.validation.email = false;
} else {
this.validation.email = true;
}
if (is.empty(this.yearSelect)) {
this.validation.yearSelect = false;
} else {
this.validation.yearSelect = true;
}
if (is.empty(this.monthSelect)) {
this.validation.monthSelect = false;
} else {
this.validation.monthSelect = true;
}
if (is.empty(this.daySelect)) {
this.validation.daySelect = false;
} else {
this.validation.daySelect = true;
}
if (is.null(this.userData.country) || this.userData.country == 0) {
this.validation.country = false;
} else {
this.validation.country = true;
}
this.errors = Object.entries(this.validation).filter(
(e) => e[1] == false
);
if (this.errors.length > 0) {
return false;
} else {
return true;
}
},
// save() {
// this.validators();
// if (this.validators()) {
// const patchData = JSON.parse(JSON.stringify(this.userCompanyInfo));
// this.$axios
// .post(
// `/member/company?jwt=${
// this.$auth.$storage.getUniversal("jwt").token
// }`,
// patchData
// )
// .then((result) => {
// if (result.status == 200) {
// this.$emit("refetch-user");
// this.reset();
// this.$modal.hide("addCompany");
// }
// })
// .catch((err) => {
// console.log(err);
// });
// }
// },
},
};
</script>
<style scoped lang="scss" scoped>
.success-alert {
position: absolute;
left: 13%;
z-index: 10;
top: -3%;
}
.success-alert-dialog {
position: absolute;
left: 13%;
z-index: 10;
top: 3%;
}
</style>