feat: complete basic profile update logic
This commit is contained in:
@@ -7,13 +7,62 @@
|
||||
<h2 class="text-2xl font-medium text-slate-900 dark:text-white">
|
||||
Profile Settings
|
||||
</h2>
|
||||
<user-basic-profile username="John Doe" size="72px" />
|
||||
<user-basic-profile
|
||||
:src="avatarUrl"
|
||||
:display-name="displayName"
|
||||
size="72px"
|
||||
@change="handleImageUpload"
|
||||
@update="updateUser"
|
||||
/>
|
||||
|
||||
<form @submit.prevent="updateUser('profile')">
|
||||
<label :class="{ error: $v.name.$error }">
|
||||
{{ $t('PROFILE_SETTINGS.FORM.NAME.LABEL') }}
|
||||
<input
|
||||
v-model="name"
|
||||
type="text"
|
||||
:placeholder="$t('PROFILE_SETTINGS.FORM.NAME.PLACEHOLDER')"
|
||||
@input="$v.name.$touch"
|
||||
/>
|
||||
<span v-if="$v.name.$error" class="message">
|
||||
{{ $t('PROFILE_SETTINGS.FORM.NAME.ERROR') }}
|
||||
</span>
|
||||
</label>
|
||||
<label :class="{ error: $v.displayName.$error }">
|
||||
{{ $t('PROFILE_SETTINGS.FORM.DISPLAY_NAME.LABEL') }}
|
||||
<input
|
||||
v-model="displayName"
|
||||
type="text"
|
||||
:placeholder="
|
||||
$t('PROFILE_SETTINGS.FORM.DISPLAY_NAME.PLACEHOLDER')
|
||||
"
|
||||
@input="$v.displayName.$touch"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
v-if="!globalConfig.disableUserProfileUpdate"
|
||||
:class="{ error: $v.email.$error }"
|
||||
>
|
||||
{{ $t('PROFILE_SETTINGS.FORM.EMAIL.LABEL') }}
|
||||
<input
|
||||
v-model.trim="email"
|
||||
type="email"
|
||||
:placeholder="$t('PROFILE_SETTINGS.FORM.EMAIL.PLACEHOLDER')"
|
||||
@input="$v.email.$touch"
|
||||
/>
|
||||
<span v-if="$v.email.$error" class="message">
|
||||
{{ $t('PROFILE_SETTINGS.FORM.EMAIL.ERROR') }}
|
||||
</span>
|
||||
</label>
|
||||
<woot-button type="submit" :is-loading="isProfileUpdating">
|
||||
{{ $t('PROFILE_SETTINGS.BTN_TEXT') }}
|
||||
</woot-button>
|
||||
</form>
|
||||
|
||||
<personal-wrapper
|
||||
header="Personal message signature"
|
||||
description="Create a unique message signature to appear at the end of every message you send from any inbox. You can also include an inline image, which is supported in live-chat, email, and API inboxes."
|
||||
:show-action-button="true"
|
||||
button-text="Save message signature"
|
||||
:show-action-button="false"
|
||||
>
|
||||
<template #settingsItem>
|
||||
<message-signature />
|
||||
@@ -74,7 +123,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
<script>
|
||||
import UserBasicProfile from './UserBasicProfile.vue';
|
||||
import MessageSignature from './MessageSignature.vue';
|
||||
import PersonalWrapper from './PersonalWrapper.vue';
|
||||
@@ -83,21 +132,148 @@ import ChangePassword from './ChangePassword.vue';
|
||||
import NotificationPreferences from './NotificationPreferences.vue';
|
||||
import AudioNotifications from './AudioNotifications.vue';
|
||||
import AccessToken from './AccessToken.vue';
|
||||
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
||||
import uiSettingsMixin, {
|
||||
isEditorHotKeyEnabled,
|
||||
} from 'dashboard/mixins/uiSettings';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import { required, minLength, email } from 'vuelidate/lib/validators';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { clearCookiesOnLogout } from '../../../../store/utils/api';
|
||||
import { hasValidAvatarUrl } from 'dashboard/helper/URLHelper';
|
||||
|
||||
const keyOptions = [
|
||||
{
|
||||
key: 'enter',
|
||||
src: '/assets/images/dashboard/profile/hot-key-enter.svg',
|
||||
heading: 'Enter (↵)',
|
||||
content:
|
||||
'Send messages by pressing Enter key instead of clicking send button.',
|
||||
export default {
|
||||
components: {
|
||||
UserBasicProfile,
|
||||
MessageSignature,
|
||||
PersonalWrapper,
|
||||
HotKeyCard,
|
||||
ChangePassword,
|
||||
NotificationPreferences,
|
||||
AudioNotifications,
|
||||
AccessToken,
|
||||
},
|
||||
{
|
||||
key: 'cmd_enter',
|
||||
src: '/assets/images/dashboard/profile/hot-key-ctrl-enter.svg',
|
||||
heading: 'CMD / Ctrl + Enter (⌘ + ↵)',
|
||||
content:
|
||||
'Send messages by pressing Enter key instead of clicking send button.',
|
||||
mixins: [alertMixin, globalConfigMixin, uiSettingsMixin],
|
||||
data() {
|
||||
return {
|
||||
avatarFile: '',
|
||||
avatarUrl: '',
|
||||
name: '',
|
||||
displayName: '',
|
||||
email: '',
|
||||
isProfileUpdating: false,
|
||||
errorMessage: '',
|
||||
keyOptions: [
|
||||
{
|
||||
key: 'enter',
|
||||
src: '/assets/images/dashboard/profile/hot-key-enter.svg',
|
||||
heading: 'Enter (↵)',
|
||||
content:
|
||||
'Send messages by pressing Enter key instead of clicking send button.',
|
||||
},
|
||||
{
|
||||
key: 'cmd_enter',
|
||||
src: '/assets/images/dashboard/profile/hot-key-ctrl-enter.svg',
|
||||
heading: 'CMD / Ctrl + Enter (⌘ + ↵)',
|
||||
content:
|
||||
'Send messages by pressing Enter key instead of clicking send button.',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
];
|
||||
validations: {
|
||||
name: {
|
||||
required,
|
||||
minLength: minLength(1),
|
||||
},
|
||||
displayName: {},
|
||||
email: {
|
||||
required,
|
||||
email,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
currentUser: 'getCurrentUser',
|
||||
currentUserId: 'getCurrentUserID',
|
||||
globalConfig: 'globalConfig/get',
|
||||
}),
|
||||
showDeleteButton() {
|
||||
return hasValidAvatarUrl(this.avatarUrl);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentUserId(newCurrentUserId, prevCurrentUserId) {
|
||||
if (prevCurrentUserId !== newCurrentUserId) {
|
||||
this.initializeUser();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.currentUserId) {
|
||||
this.initializeUser();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initializeUser() {
|
||||
this.name = this.currentUser.name;
|
||||
this.email = this.currentUser.email;
|
||||
this.avatarUrl = this.currentUser.avatar_url;
|
||||
this.displayName = this.currentUser.display_name;
|
||||
},
|
||||
isEditorHotKeyEnabled,
|
||||
async updateUser() {
|
||||
this.$v.$touch();
|
||||
if (this.$v.$invalid) {
|
||||
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.ERROR'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.isProfileUpdating = true;
|
||||
const hasEmailChanged = this.currentUser.email !== this.email;
|
||||
try {
|
||||
await this.$store.dispatch('updateProfile', {
|
||||
name: this.name,
|
||||
email: this.email,
|
||||
avatar: this.avatarFile,
|
||||
displayName: this.displayName,
|
||||
});
|
||||
this.isProfileUpdating = false;
|
||||
if (hasEmailChanged) {
|
||||
clearCookiesOnLogout();
|
||||
this.errorMessage = this.$t('PROFILE_SETTINGS.AFTER_EMAIL_CHANGED');
|
||||
}
|
||||
this.errorMessage = this.$t('PROFILE_SETTINGS.UPDATE_SUCCESS');
|
||||
} catch (error) {
|
||||
this.errorMessage = this.$t('RESET_PASSWORD.API.ERROR_MESSAGE');
|
||||
if (error?.response?.data?.error) {
|
||||
this.errorMessage = error.response.data.error;
|
||||
}
|
||||
} finally {
|
||||
this.isProfileUpdating = false;
|
||||
this.showAlert(this.errorMessage);
|
||||
}
|
||||
},
|
||||
handleImageUpload({ file, url }) {
|
||||
this.avatarFile = file;
|
||||
this.avatarUrl = url;
|
||||
},
|
||||
async deleteAvatar() {
|
||||
try {
|
||||
await this.$store.dispatch('deleteAvatar');
|
||||
this.avatarUrl = '';
|
||||
this.avatarFile = '';
|
||||
this.showAlert(this.$t('PROFILE_SETTINGS.AVATAR_DELETE_SUCCESS'));
|
||||
} catch (error) {
|
||||
this.showAlert(this.$t('PROFILE_SETTINGS.AVATAR_DELETE_FAILED'));
|
||||
}
|
||||
},
|
||||
toggleEditorMessageKey(key) {
|
||||
this.updateUISettings({ editor_message_key: key });
|
||||
this.showAlert(
|
||||
this.$t('PROFILE_SETTINGS.FORM.SEND_MESSAGE.UPDATE_SUCCESS')
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
<template>
|
||||
<woot-message-editor
|
||||
id="message-signature-input"
|
||||
v-model="messageSignature"
|
||||
class="message-editor h-[10rem]"
|
||||
:is-format-mode="true"
|
||||
:placeholder="$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE.PLACEHOLDER')"
|
||||
:enabled-menu-options="customEditorMenuList"
|
||||
:enable-suggestions="false"
|
||||
:show-image-resize-toolbar="true"
|
||||
/>
|
||||
<div class="flex flex-col gap-6">
|
||||
<woot-message-editor
|
||||
id="message-signature-input"
|
||||
v-model="messageSignature"
|
||||
class="message-editor h-[10rem]"
|
||||
:is-format-mode="true"
|
||||
:placeholder="$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE.PLACEHOLDER')"
|
||||
:enabled-menu-options="customEditorMenuList"
|
||||
:enable-suggestions="false"
|
||||
:show-image-resize-toolbar="true"
|
||||
/>
|
||||
<woot-button
|
||||
class="w-fit"
|
||||
:is-loading="isUpdating"
|
||||
type="button"
|
||||
@click.prevent="updateSignature"
|
||||
>
|
||||
{{ $t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.BTN_TEXT') }}
|
||||
</woot-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -5,17 +5,25 @@
|
||||
Profile picture
|
||||
</span>
|
||||
<div
|
||||
class="relative rounded-xl h-[72px] w-[72px]"
|
||||
class="relative rounded-xl h-[72px] w-[72px] cursor-pointer"
|
||||
:title="title"
|
||||
@mouse-over="showUpload"
|
||||
@click="openFileInput"
|
||||
>
|
||||
<img
|
||||
class="rounded-xl h-[72px] w-[72px]"
|
||||
src="https://i.pravatar.cc/300"
|
||||
:src="src"
|
||||
draggable="false"
|
||||
@load="onImgLoad"
|
||||
@error="onImgError"
|
||||
/>
|
||||
<input
|
||||
ref="file"
|
||||
type="file"
|
||||
accept="image/png, image/jpeg, image/jpg, image/gif, image/webp"
|
||||
style="display: none"
|
||||
@change="handleImageUpload"
|
||||
/>
|
||||
<div
|
||||
class="absolute z-10 flex items-center justify-center w-6 h-6 p-1 border border-white rounded-full select-none -top-2 -right-2 dark:border-white bg-slate-200 dark:bg-slate-700"
|
||||
>
|
||||
@@ -36,50 +44,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-6">
|
||||
<form-input
|
||||
name="full_name"
|
||||
class="flex-1"
|
||||
:class="{ error: false }"
|
||||
:label="$t('REGISTER.FULL_NAME.LABEL')"
|
||||
placeholder="Enter your name"
|
||||
:has-error="false"
|
||||
:error-message="$t('REGISTER.FULL_NAME.ERROR')"
|
||||
/>
|
||||
<form-input
|
||||
name="display_name"
|
||||
class="flex-1"
|
||||
:class="{ error: false }"
|
||||
label="Display name"
|
||||
placeholder="Enter your display name"
|
||||
:has-error="false"
|
||||
:error-message="$t('REGISTER.FULL_NAME.ERROR')"
|
||||
/>
|
||||
<form-input
|
||||
name="email"
|
||||
class="flex-1"
|
||||
:class="{ error: false }"
|
||||
label="Email"
|
||||
placeholder="Enter your email"
|
||||
:has-error="false"
|
||||
:error-message="$t('REGISTER.FULL_NAME.ERROR')"
|
||||
/>
|
||||
<woot-button
|
||||
color-scheme="primary"
|
||||
class="rounded-xl w-fit"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
Update profile
|
||||
</woot-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import FormInput from 'v3/components/Form/Input.vue';
|
||||
export default {
|
||||
components: {
|
||||
FormInput,
|
||||
},
|
||||
props: {
|
||||
src: {
|
||||
type: String,
|
||||
@@ -125,6 +93,17 @@ export default {
|
||||
showUpload() {
|
||||
this.showUploadIcon = true;
|
||||
},
|
||||
openFileInput() {
|
||||
// Trigger click event on the hidden file input
|
||||
this.$refs.file.click();
|
||||
},
|
||||
handleImageUpload(event) {
|
||||
const [file] = event.target.files;
|
||||
this.$emit('change', {
|
||||
file,
|
||||
url: file ? URL.createObjectURL(file) : null,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user