feat: Revamp profile settings screen (#9352)
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: iamsivin <iamsivin@gmail.com>
This commit is contained in:
@@ -1,137 +0,0 @@
|
|||||||
<template>
|
|
||||||
<form @submit.prevent="changePassword()">
|
|
||||||
<div class="flex flex-col w-full gap-4">
|
|
||||||
<woot-input
|
|
||||||
v-model="currentPassword"
|
|
||||||
type="password"
|
|
||||||
:styles="inputStyles"
|
|
||||||
:class="{ error: $v.currentPassword.$error }"
|
|
||||||
:label="$t('PROFILE_SETTINGS.FORM.CURRENT_PASSWORD.LABEL')"
|
|
||||||
:placeholder="$t('PROFILE_SETTINGS.FORM.CURRENT_PASSWORD.PLACEHOLDER')"
|
|
||||||
:error="`${
|
|
||||||
$v.currentPassword.$error
|
|
||||||
? $t('PROFILE_SETTINGS.FORM.CURRENT_PASSWORD.ERROR')
|
|
||||||
: ''
|
|
||||||
}`"
|
|
||||||
@input="$v.currentPassword.$touch"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<woot-input
|
|
||||||
v-model="password"
|
|
||||||
type="password"
|
|
||||||
:styles="inputStyles"
|
|
||||||
:class="{ error: $v.password.$error }"
|
|
||||||
:label="$t('PROFILE_SETTINGS.FORM.PASSWORD.LABEL')"
|
|
||||||
:placeholder="$t('PROFILE_SETTINGS.FORM.PASSWORD.PLACEHOLDER')"
|
|
||||||
:error="`${
|
|
||||||
$v.password.$error ? $t('PROFILE_SETTINGS.FORM.PASSWORD.ERROR') : ''
|
|
||||||
}`"
|
|
||||||
@input="$v.password.$touch"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<woot-input
|
|
||||||
v-model="passwordConfirmation"
|
|
||||||
type="password"
|
|
||||||
:styles="inputStyles"
|
|
||||||
:class="{ error: $v.passwordConfirmation.$error }"
|
|
||||||
:label="$t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.LABEL')"
|
|
||||||
:placeholder="
|
|
||||||
$t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.PLACEHOLDER')
|
|
||||||
"
|
|
||||||
:error="`${
|
|
||||||
$v.passwordConfirmation.$error
|
|
||||||
? $t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.ERROR')
|
|
||||||
: ''
|
|
||||||
}`"
|
|
||||||
@input="$v.passwordConfirmation.$touch"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<form-button
|
|
||||||
type="submit"
|
|
||||||
color-scheme="primary"
|
|
||||||
variant="solid"
|
|
||||||
size="large"
|
|
||||||
:disabled="isButtonDisabled"
|
|
||||||
>
|
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.BTN_TEXT') }}
|
|
||||||
</form-button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { required, minLength } from 'vuelidate/lib/validators';
|
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
|
||||||
import { parseAPIErrorResponse } from 'dashboard/store/utils/api';
|
|
||||||
import FormButton from 'v3/components/Form/Button.vue';
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
FormButton,
|
|
||||||
},
|
|
||||||
mixins: [alertMixin],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
currentPassword: '',
|
|
||||||
password: '',
|
|
||||||
passwordConfirmation: '',
|
|
||||||
isPasswordChanging: false,
|
|
||||||
errorMessage: '',
|
|
||||||
inputStyles: {
|
|
||||||
borderRadius: '12px',
|
|
||||||
padding: '6px 12px',
|
|
||||||
fontSize: '14px',
|
|
||||||
marginBottom: '2px',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
validations: {
|
|
||||||
currentPassword: {
|
|
||||||
required,
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
minLength: minLength(6),
|
|
||||||
},
|
|
||||||
passwordConfirmation: {
|
|
||||||
minLength: minLength(6),
|
|
||||||
isEqPassword(value) {
|
|
||||||
if (value !== this.password) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
isButtonDisabled() {
|
|
||||||
return (
|
|
||||||
!this.currentPassword ||
|
|
||||||
!this.passwordConfirmation ||
|
|
||||||
!this.$v.passwordConfirmation.isEqPassword
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async changePassword() {
|
|
||||||
this.$v.$touch();
|
|
||||||
if (this.$v.$invalid) {
|
|
||||||
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.ERROR'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let alertMessage = this.$t('PROFILE_SETTINGS.PASSWORD_UPDATE_SUCCESS');
|
|
||||||
try {
|
|
||||||
await this.$store.dispatch('updateProfile', {
|
|
||||||
password: this.password,
|
|
||||||
password_confirmation: this.passwordConfirmation,
|
|
||||||
current_password: this.currentPassword,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
alertMessage =
|
|
||||||
parseAPIErrorResponse(error) ||
|
|
||||||
this.$t('RESET_PASSWORD.API.ERROR_MESSAGE');
|
|
||||||
} finally {
|
|
||||||
this.showAlert(alertMessage);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,264 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex items-center w-full overflow-y-auto">
|
|
||||||
<div class="flex flex-col h-full p-5 pt-16 mx-auto my-0 font-inter">
|
|
||||||
<div class="flex flex-col gap-16 pb-8 sm:max-w-[720px]">
|
|
||||||
<div class="flex flex-col gap-6">
|
|
||||||
<h2 class="mt-4 text-2xl font-medium text-ash-900">
|
|
||||||
{{ $t('PROFILE_SETTINGS.TITLE') }}
|
|
||||||
</h2>
|
|
||||||
<user-profile-picture
|
|
||||||
:src="avatarUrl"
|
|
||||||
:name="name"
|
|
||||||
size="72px"
|
|
||||||
@change="updateProfilePicture"
|
|
||||||
@delete="deleteProfilePicture"
|
|
||||||
/>
|
|
||||||
<user-basic-details
|
|
||||||
:name="name"
|
|
||||||
:display-name="displayName"
|
|
||||||
:email="email"
|
|
||||||
:email-enabled="!globalConfig.disableUserProfileUpdate"
|
|
||||||
@update-user="updateProfile"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form-section
|
|
||||||
:title="$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.TITLE')"
|
|
||||||
:description="
|
|
||||||
$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.NOTE')
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<message-signature
|
|
||||||
:message-signature="messageSignature"
|
|
||||||
@update-signature="updateSignature"
|
|
||||||
/>
|
|
||||||
</form-section>
|
|
||||||
<form-section
|
|
||||||
:title="$t('PROFILE_SETTINGS.FORM.SEND_MESSAGE.TITLE')"
|
|
||||||
:description="$t('PROFILE_SETTINGS.FORM.SEND_MESSAGE.NOTE')"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="flex flex-col justify-between w-full gap-5 sm:gap-4 sm:flex-row"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
v-for="hotKey in hotKeys"
|
|
||||||
:key="hotKey.key"
|
|
||||||
class="px-0 reset-base"
|
|
||||||
>
|
|
||||||
<hot-key-card
|
|
||||||
:key="hotKey.title"
|
|
||||||
:title="hotKey.title"
|
|
||||||
:description="hotKey.description"
|
|
||||||
:light-image="hotKey.lightImage"
|
|
||||||
:dark-image="hotKey.darkImage"
|
|
||||||
:active="isEditorHotKeyEnabled(uiSettings, hotKey.key)"
|
|
||||||
@click="toggleHotKey(hotKey.key)"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form-section>
|
|
||||||
<form-section
|
|
||||||
:title="$t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.TITLE')"
|
|
||||||
>
|
|
||||||
<change-password v-if="!globalConfig.disableUserProfileUpdate" />
|
|
||||||
</form-section>
|
|
||||||
<form-section
|
|
||||||
:title="$t('PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.TITLE')"
|
|
||||||
:description="
|
|
||||||
$t('PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.NOTE')
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<audio-notifications />
|
|
||||||
</form-section>
|
|
||||||
<form-section :title="$t('PROFILE_SETTINGS.FORM.NOTIFICATIONS.TITLE')">
|
|
||||||
<notification-preferences />
|
|
||||||
</form-section>
|
|
||||||
<form-section
|
|
||||||
:title="$t('PROFILE_SETTINGS.FORM.ACCESS_TOKEN.TITLE')"
|
|
||||||
:description="
|
|
||||||
useInstallationName(
|
|
||||||
$t('PROFILE_SETTINGS.FORM.ACCESS_TOKEN.NOTE'),
|
|
||||||
globalConfig.installationName
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<access-token
|
|
||||||
:value="currentUser.access_token"
|
|
||||||
@on-copy="onCopyToken"
|
|
||||||
/>
|
|
||||||
</form-section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
|
||||||
import uiSettingsMixin, {
|
|
||||||
isEditorHotKeyEnabled,
|
|
||||||
} from 'dashboard/mixins/uiSettings';
|
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import { clearCookiesOnLogout } from 'dashboard/store/utils/api.js';
|
|
||||||
import { copyTextToClipboard } from 'shared/helpers/clipboard';
|
|
||||||
|
|
||||||
import UserProfilePicture from './UserProfilePicture.vue';
|
|
||||||
import UserBasicDetails from './UserBasicDetails.vue';
|
|
||||||
import MessageSignature from './MessageSignature.vue';
|
|
||||||
import HotKeyCard from './HotKeyCard.vue';
|
|
||||||
import ChangePassword from './ChangePassword.vue';
|
|
||||||
import NotificationPreferences from './NotificationPreferences.vue';
|
|
||||||
import AudioNotifications from './AudioNotifications.vue';
|
|
||||||
import FormSection from 'dashboard/components/FormSection.vue';
|
|
||||||
import AccessToken from './AccessToken.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
MessageSignature,
|
|
||||||
FormSection,
|
|
||||||
UserProfilePicture,
|
|
||||||
UserBasicDetails,
|
|
||||||
HotKeyCard,
|
|
||||||
ChangePassword,
|
|
||||||
NotificationPreferences,
|
|
||||||
AudioNotifications,
|
|
||||||
AccessToken,
|
|
||||||
},
|
|
||||||
mixins: [alertMixin, globalConfigMixin, uiSettingsMixin],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
avatarFile: '',
|
|
||||||
avatarUrl: '',
|
|
||||||
name: '',
|
|
||||||
displayName: '',
|
|
||||||
email: '',
|
|
||||||
messageSignature: '',
|
|
||||||
hotKeys: [
|
|
||||||
{
|
|
||||||
key: 'enter',
|
|
||||||
title: this.$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.ENTER_KEY.HEADING'
|
|
||||||
),
|
|
||||||
description: this.$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.ENTER_KEY.CONTENT'
|
|
||||||
),
|
|
||||||
lightImage: '/assets/images/dashboard/profile/hot-key-enter.svg',
|
|
||||||
darkImage: '/assets/images/dashboard/profile/hot-key-enter-dark.svg',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'cmd_enter',
|
|
||||||
title: this.$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.CMD_ENTER_KEY.HEADING'
|
|
||||||
),
|
|
||||||
description: this.$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.CMD_ENTER_KEY.CONTENT'
|
|
||||||
),
|
|
||||||
lightImage: '/assets/images/dashboard/profile/hot-key-ctrl-enter.svg',
|
|
||||||
darkImage:
|
|
||||||
'/assets/images/dashboard/profile/hot-key-ctrl-enter-dark.svg',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters({
|
|
||||||
currentUser: 'getCurrentUser',
|
|
||||||
currentUserId: 'getCurrentUserID',
|
|
||||||
globalConfig: 'globalConfig/get',
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
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;
|
|
||||||
this.messageSignature = this.currentUser.message_signature;
|
|
||||||
},
|
|
||||||
isEditorHotKeyEnabled,
|
|
||||||
async dispatchUpdate(payload, successMessage, errorMessage) {
|
|
||||||
let alertMessage = '';
|
|
||||||
try {
|
|
||||||
await this.$store.dispatch('updateProfile', payload);
|
|
||||||
alertMessage = successMessage;
|
|
||||||
|
|
||||||
return true; // return the value so that the status can be known
|
|
||||||
} catch (error) {
|
|
||||||
alertMessage = error?.response?.data?.error
|
|
||||||
? error.response.data.error
|
|
||||||
: errorMessage;
|
|
||||||
|
|
||||||
return false; // return the value so that the status can be known
|
|
||||||
} finally {
|
|
||||||
this.showAlert(alertMessage);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async updateProfile(userAttributes) {
|
|
||||||
const { name, email, displayName } = userAttributes;
|
|
||||||
const hasEmailChanged = this.currentUser.email !== email;
|
|
||||||
this.name = name || this.name;
|
|
||||||
this.email = email || this.email;
|
|
||||||
this.displayName = displayName || this.displayName;
|
|
||||||
|
|
||||||
const updatePayload = {
|
|
||||||
name: this.name,
|
|
||||||
email: this.email,
|
|
||||||
displayName: this.displayName,
|
|
||||||
avatar: this.avatarFile,
|
|
||||||
};
|
|
||||||
|
|
||||||
const success = await this.dispatchUpdate(
|
|
||||||
updatePayload,
|
|
||||||
hasEmailChanged
|
|
||||||
? this.$t('PROFILE_SETTINGS.AFTER_EMAIL_CHANGED')
|
|
||||||
: this.$t('PROFILE_SETTINGS.UPDATE_SUCCESS'),
|
|
||||||
this.$t('RESET_PASSWORD.API.ERROR_MESSAGE')
|
|
||||||
);
|
|
||||||
|
|
||||||
if (hasEmailChanged && success) clearCookiesOnLogout();
|
|
||||||
},
|
|
||||||
async updateSignature(signature) {
|
|
||||||
const payload = { message_signature: signature };
|
|
||||||
let successMessage = this.$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.API_SUCCESS'
|
|
||||||
);
|
|
||||||
let errorMessage = this.$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.API_ERROR'
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.dispatchUpdate(payload, successMessage, errorMessage);
|
|
||||||
},
|
|
||||||
updateProfilePicture({ file, url }) {
|
|
||||||
this.avatarFile = file;
|
|
||||||
this.avatarUrl = url;
|
|
||||||
},
|
|
||||||
async deleteProfilePicture() {
|
|
||||||
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'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggleHotKey(key) {
|
|
||||||
this.hotKeys = this.hotKeys.map(hotKey =>
|
|
||||||
hotKey.key === key ? { ...hotKey, active: !hotKey.active } : hotKey
|
|
||||||
);
|
|
||||||
this.updateUISettings({ editor_message_key: key });
|
|
||||||
this.showAlert(
|
|
||||||
this.$t('PROFILE_SETTINGS.FORM.SEND_MESSAGE.UPDATE_SUCCESS')
|
|
||||||
);
|
|
||||||
},
|
|
||||||
async onCopyToken(value) {
|
|
||||||
await copyTextToClipboard(value);
|
|
||||||
this.showAlert(this.$t('COMPONENTS.CODE.COPY_SUCCESSFUL'));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<template>
|
|
||||||
<form class="flex flex-col gap-6" @submit.prevent="updateSignature()">
|
|
||||||
<woot-message-editor
|
|
||||||
id="message-signature-input"
|
|
||||||
v-model="signature"
|
|
||||||
class="message-editor h-[10rem] !px-3"
|
|
||||||
:is-format-mode="true"
|
|
||||||
:placeholder="$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE.PLACEHOLDER')"
|
|
||||||
:enabled-menu-options="customEditorMenuList"
|
|
||||||
:enable-suggestions="false"
|
|
||||||
:show-image-resize-toolbar="true"
|
|
||||||
/>
|
|
||||||
<form-button
|
|
||||||
type="submit"
|
|
||||||
color-scheme="primary"
|
|
||||||
variant="solid"
|
|
||||||
size="large"
|
|
||||||
>
|
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.BTN_TEXT') }}
|
|
||||||
</form-button>
|
|
||||||
</form>
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
import { ref, watch } from 'vue';
|
|
||||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
|
||||||
import { MESSAGE_SIGNATURE_EDITOR_MENU_OPTIONS } from 'dashboard/constants/editor';
|
|
||||||
import FormButton from 'v3/components/Form/Button.vue';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
messageSignature: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const customEditorMenuList = MESSAGE_SIGNATURE_EDITOR_MENU_OPTIONS;
|
|
||||||
const signature = ref(props.messageSignature);
|
|
||||||
const emit = defineEmits(['update-signature']);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.messageSignature,
|
|
||||||
newValue => {
|
|
||||||
signature.value = newValue;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateSignature = () => {
|
|
||||||
emit('update-signature', signature.value);
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { frontendURL } from 'dashboard/helper/URLHelper';
|
|
||||||
|
|
||||||
const Index = () => import('./Index.vue');
|
|
||||||
|
|
||||||
export default {
|
|
||||||
routes: [
|
|
||||||
{
|
|
||||||
path: frontendURL('accounts/:accountId/personal'),
|
|
||||||
name: 'personal_settings',
|
|
||||||
roles: ['administrator', 'agent'],
|
|
||||||
component: Index,
|
|
||||||
props: {
|
|
||||||
headerTitle: 'PROFILE_SETTINGS.TITLE',
|
|
||||||
icon: 'edit',
|
|
||||||
showNewButton: false,
|
|
||||||
showSidemenuIcon: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<input
|
<input
|
||||||
:id="`radio-${option.value}`"
|
:id="`radio-${option.value}`"
|
||||||
v-model="selectedValue"
|
v-model="selectedValue"
|
||||||
class="shadow cursor-pointer grid place-items-center border-2 border-ash-200 appearance-none rounded-full w-4 h-4 checked:bg-primary-600 before:content-[''] before:bg-primary-600 before:border-4 before:rounded-full before:border-ash-25 checked:before:w-[14px] checked:before:h-[14px] checked:border checked:border-primary-600"
|
class="shadow-sm cursor-pointer grid place-items-center border-2 border-ash-200 appearance-none rounded-full w-4 h-4 checked:bg-primary-600 before:content-[''] before:bg-primary-600 before:border-4 before:rounded-full before:border-ash-25 checked:before:w-[14px] checked:before:h-[14px] checked:border checked:border-primary-600"
|
||||||
type="radio"
|
type="radio"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
/>
|
/>
|
||||||
@@ -1,82 +1,73 @@
|
|||||||
<template>
|
<template>
|
||||||
<form @submit.prevent="changePassword()">
|
<form @submit.prevent="changePassword()">
|
||||||
<div
|
<div class="flex flex-col w-full gap-4">
|
||||||
class="profile--settings--row text-black-900 dark:text-slate-300 flex items-center"
|
<woot-input
|
||||||
>
|
v-model="currentPassword"
|
||||||
<div class="w-1/4">
|
type="password"
|
||||||
<h4 class="text-lg text-black-900 dark:text-slate-200">
|
:styles="inputStyles"
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.TITLE') }}
|
:class="{ error: $v.currentPassword.$error }"
|
||||||
</h4>
|
:label="$t('PROFILE_SETTINGS.FORM.CURRENT_PASSWORD.LABEL')"
|
||||||
<p>{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.NOTE') }}</p>
|
:placeholder="$t('PROFILE_SETTINGS.FORM.CURRENT_PASSWORD.PLACEHOLDER')"
|
||||||
</div>
|
:error="`${
|
||||||
<div class="w-[45%] p-4">
|
$v.currentPassword.$error
|
||||||
<woot-input
|
? $t('PROFILE_SETTINGS.FORM.CURRENT_PASSWORD.ERROR')
|
||||||
v-model="currentPassword"
|
: ''
|
||||||
type="password"
|
}`"
|
||||||
:class="{ error: $v.currentPassword.$error }"
|
@input="$v.currentPassword.$touch"
|
||||||
:label="$t('PROFILE_SETTINGS.FORM.CURRENT_PASSWORD.LABEL')"
|
/>
|
||||||
:placeholder="
|
|
||||||
$t('PROFILE_SETTINGS.FORM.CURRENT_PASSWORD.PLACEHOLDER')
|
|
||||||
"
|
|
||||||
:error="
|
|
||||||
$v.currentPassword.$error
|
|
||||||
? $t('PROFILE_SETTINGS.FORM.CURRENT_PASSWORD.ERROR')
|
|
||||||
: ''
|
|
||||||
"
|
|
||||||
@blur="$v.currentPassword.$touch"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<woot-input
|
<woot-input
|
||||||
v-model="password"
|
v-model="password"
|
||||||
type="password"
|
type="password"
|
||||||
:class="{ error: $v.password.$error }"
|
:styles="inputStyles"
|
||||||
:label="$t('PROFILE_SETTINGS.FORM.PASSWORD.LABEL')"
|
:class="{ error: $v.password.$error }"
|
||||||
:placeholder="$t('PROFILE_SETTINGS.FORM.PASSWORD.PLACEHOLDER')"
|
:label="$t('PROFILE_SETTINGS.FORM.PASSWORD.LABEL')"
|
||||||
:error="
|
:placeholder="$t('PROFILE_SETTINGS.FORM.PASSWORD.PLACEHOLDER')"
|
||||||
$v.password.$error ? $t('PROFILE_SETTINGS.FORM.PASSWORD.ERROR') : ''
|
:error="`${
|
||||||
"
|
$v.password.$error ? $t('PROFILE_SETTINGS.FORM.PASSWORD.ERROR') : ''
|
||||||
@blur="$v.password.$touch"
|
}`"
|
||||||
/>
|
@input="$v.password.$touch"
|
||||||
|
/>
|
||||||
|
|
||||||
<woot-input
|
<woot-input
|
||||||
v-model="passwordConfirmation"
|
v-model="passwordConfirmation"
|
||||||
type="password"
|
type="password"
|
||||||
:class="{ error: $v.passwordConfirmation.$error }"
|
:styles="inputStyles"
|
||||||
:label="$t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.LABEL')"
|
:class="{ error: $v.passwordConfirmation.$error }"
|
||||||
:placeholder="
|
:label="$t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.LABEL')"
|
||||||
$t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.PLACEHOLDER')
|
:placeholder="
|
||||||
"
|
$t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.PLACEHOLDER')
|
||||||
:error="
|
"
|
||||||
$v.passwordConfirmation.$error
|
:error="`${
|
||||||
? $t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.ERROR')
|
$v.passwordConfirmation.$error
|
||||||
: ''
|
? $t('PROFILE_SETTINGS.FORM.PASSWORD_CONFIRMATION.ERROR')
|
||||||
"
|
: ''
|
||||||
@blur="$v.passwordConfirmation.$touch"
|
}`"
|
||||||
/>
|
@input="$v.passwordConfirmation.$touch"
|
||||||
|
/>
|
||||||
|
|
||||||
<woot-button
|
<form-button
|
||||||
:is-loading="isPasswordChanging"
|
type="submit"
|
||||||
type="submit"
|
color-scheme="primary"
|
||||||
:disabled="
|
variant="solid"
|
||||||
!currentPassword ||
|
size="large"
|
||||||
!passwordConfirmation ||
|
:disabled="isButtonDisabled"
|
||||||
!$v.passwordConfirmation.isEqPassword
|
>
|
||||||
"
|
{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.BTN_TEXT') }}
|
||||||
>
|
</form-button>
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.BTN_TEXT') }}
|
|
||||||
</woot-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { required, minLength } from 'vuelidate/lib/validators';
|
import { required, minLength } from 'vuelidate/lib/validators';
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
import { parseAPIErrorResponse } from 'dashboard/store/utils/api';
|
import { parseAPIErrorResponse } from 'dashboard/store/utils/api';
|
||||||
|
import FormButton from 'v3/components/Form/Button.vue';
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
FormButton,
|
||||||
|
},
|
||||||
mixins: [alertMixin],
|
mixins: [alertMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -85,6 +76,12 @@ export default {
|
|||||||
passwordConfirmation: '',
|
passwordConfirmation: '',
|
||||||
isPasswordChanging: false,
|
isPasswordChanging: false,
|
||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
|
inputStyles: {
|
||||||
|
borderRadius: '12px',
|
||||||
|
padding: '6px 12px',
|
||||||
|
fontSize: '14px',
|
||||||
|
marginBottom: '2px',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
validations: {
|
validations: {
|
||||||
@@ -105,10 +102,13 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
isButtonDisabled() {
|
||||||
currentUser: 'getCurrentUser',
|
return (
|
||||||
currentUserId: 'getCurrentUserID',
|
!this.currentPassword ||
|
||||||
}),
|
!this.passwordConfirmation ||
|
||||||
|
!this.$v.passwordConfirmation.isEqPassword
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async changePassword() {
|
async changePassword() {
|
||||||
@@ -117,38 +117,21 @@ export default {
|
|||||||
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.ERROR'));
|
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.ERROR'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let alertMessage = this.$t('PROFILE_SETTINGS.PASSWORD_UPDATE_SUCCESS');
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('updateProfile', {
|
await this.$store.dispatch('updateProfile', {
|
||||||
password: this.password,
|
password: this.password,
|
||||||
password_confirmation: this.passwordConfirmation,
|
password_confirmation: this.passwordConfirmation,
|
||||||
current_password: this.currentPassword,
|
current_password: this.currentPassword,
|
||||||
});
|
});
|
||||||
this.errorMessage = this.$t('PROFILE_SETTINGS.PASSWORD_UPDATE_SUCCESS');
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.errorMessage =
|
alertMessage =
|
||||||
parseAPIErrorResponse(error) ||
|
parseAPIErrorResponse(error) ||
|
||||||
this.$t('RESET_PASSWORD.API.ERROR_MESSAGE');
|
this.$t('RESET_PASSWORD.API.ERROR_MESSAGE');
|
||||||
} finally {
|
} finally {
|
||||||
this.isPasswordChanging = false;
|
this.showAlert(alertMessage);
|
||||||
this.showAlert(this.errorMessage);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '~dashboard/assets/scss/mixins.scss';
|
|
||||||
|
|
||||||
.profile--settings--row {
|
|
||||||
@include border-normal-bottom;
|
|
||||||
padding: var(--space-normal);
|
|
||||||
.small-3 {
|
|
||||||
padding: var(--space-normal) var(--space-medium) var(--space-normal) 0;
|
|
||||||
}
|
|
||||||
.small-9 {
|
|
||||||
padding: var(--space-normal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,152 +1,116 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="overflow-auto p-6">
|
<div class="grid py-16 px-5 font-inter mx-auto gap-16 sm:max-w-[720px]">
|
||||||
<form @submit.prevent="updateUser('profile')">
|
<div class="flex flex-col gap-6">
|
||||||
<div
|
<h2 class="text-2xl font-medium text-ash-900">
|
||||||
class="flex flex-row border-b border-slate-50 dark:border-slate-700 items-center flex p-4"
|
{{ $t('PROFILE_SETTINGS.TITLE') }}
|
||||||
>
|
</h2>
|
||||||
<div class="w-1/4 py-4 pr-6 ml-0">
|
<user-profile-picture
|
||||||
<h4 class="text-lg text-black-900 dark:text-slate-200">
|
:src="avatarUrl"
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.PROFILE_SECTION.TITLE') }}
|
:name="name"
|
||||||
</h4>
|
size="72px"
|
||||||
<p>{{ $t('PROFILE_SETTINGS.FORM.PROFILE_SECTION.NOTE') }}</p>
|
@change="updateProfilePicture"
|
||||||
</div>
|
@delete="deleteProfilePicture"
|
||||||
<div class="p-4 w-[45%]">
|
/>
|
||||||
<woot-avatar-uploader
|
<user-basic-details
|
||||||
:label="$t('PROFILE_SETTINGS.FORM.PROFILE_IMAGE.LABEL')"
|
:name="name"
|
||||||
:src="avatarUrl"
|
:display-name="displayName"
|
||||||
@change="handleImageUpload"
|
:email="email"
|
||||||
/>
|
:email-enabled="!globalConfig.disableUserProfileUpdate"
|
||||||
<div v-if="showDeleteButton" class="avatar-delete-btn">
|
@update-user="updateProfile"
|
||||||
<woot-button
|
/>
|
||||||
type="button"
|
</div>
|
||||||
color-scheme="alert"
|
|
||||||
variant="hollow"
|
<form-section
|
||||||
size="small"
|
:title="$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.TITLE')"
|
||||||
@click="deleteAvatar"
|
:description="$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.NOTE')"
|
||||||
>
|
|
||||||
{{ $t('PROFILE_SETTINGS.DELETE_AVATAR') }}
|
|
||||||
</woot-button>
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<message-signature />
|
|
||||||
<div
|
|
||||||
class="border-b border-slate-50 dark:border-slate-700 items-center flex p-4 text-black-900 dark:text-slate-300 row"
|
|
||||||
>
|
>
|
||||||
<div class="w-1/4 py-4 pr-6 ml-0">
|
<message-signature
|
||||||
<h4 class="text-lg text-black-900 dark:text-slate-200">
|
:message-signature="messageSignature"
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.SEND_MESSAGE.TITLE') }}
|
@update-signature="updateSignature"
|
||||||
</h4>
|
/>
|
||||||
<p>
|
</form-section>
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.SEND_MESSAGE.NOTE') }}
|
<form-section
|
||||||
</p>
|
:title="$t('PROFILE_SETTINGS.FORM.SEND_MESSAGE.TITLE')"
|
||||||
</div>
|
:description="$t('PROFILE_SETTINGS.FORM.SEND_MESSAGE.NOTE')"
|
||||||
<div class="p-4 w-[45%] flex gap-4 flex-row">
|
>
|
||||||
|
<div
|
||||||
|
class="flex flex-col justify-between w-full gap-5 sm:gap-4 sm:flex-row"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
v-for="keyOption in keyOptions"
|
v-for="hotKey in hotKeys"
|
||||||
:key="keyOption.key"
|
:key="hotKey.key"
|
||||||
class="cursor-pointer p-0"
|
class="px-0 reset-base"
|
||||||
@click="toggleEditorMessageKey(keyOption.key)"
|
|
||||||
>
|
>
|
||||||
<preview-card
|
<hot-key-card
|
||||||
:heading="keyOption.heading"
|
:key="hotKey.title"
|
||||||
:content="keyOption.content"
|
:title="hotKey.title"
|
||||||
:src="keyOption.src"
|
:description="hotKey.description"
|
||||||
:active="isEditorHotKeyEnabled(uiSettings, keyOption.key)"
|
:light-image="hotKey.lightImage"
|
||||||
|
:dark-image="hotKey.darkImage"
|
||||||
|
:active="isEditorHotKeyEnabled(uiSettings, hotKey.key)"
|
||||||
|
@click="toggleHotKey(hotKey.key)"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form-section>
|
||||||
<change-password v-if="!globalConfig.disableUserProfileUpdate" />
|
<form-section :title="$t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.TITLE')">
|
||||||
<notification-settings />
|
<change-password v-if="!globalConfig.disableUserProfileUpdate" />
|
||||||
<div
|
</form-section>
|
||||||
class="border-b border-slate-50 dark:border-slate-700 items-center flex p-4 text-black-900 dark:text-slate-300 row"
|
<form-section
|
||||||
|
:title="$t('PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.TITLE')"
|
||||||
|
:description="
|
||||||
|
$t('PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.NOTE')
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<div class="w-1/4 py-4 pr-6 ml-0">
|
<audio-notifications />
|
||||||
<h4 class="text-lg text-black-900 dark:text-slate-200">
|
</form-section>
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.ACCESS_TOKEN.TITLE') }}
|
<form-section :title="$t('PROFILE_SETTINGS.FORM.NOTIFICATIONS.TITLE')">
|
||||||
</h4>
|
<notification-preferences />
|
||||||
<p>
|
</form-section>
|
||||||
{{
|
<form-section
|
||||||
useInstallationName(
|
:title="$t('PROFILE_SETTINGS.FORM.ACCESS_TOKEN.TITLE')"
|
||||||
$t('PROFILE_SETTINGS.FORM.ACCESS_TOKEN.NOTE'),
|
:description="
|
||||||
globalConfig.installationName
|
useInstallationName(
|
||||||
)
|
$t('PROFILE_SETTINGS.FORM.ACCESS_TOKEN.NOTE'),
|
||||||
}}
|
globalConfig.installationName
|
||||||
</p>
|
)
|
||||||
</div>
|
"
|
||||||
<div class="p-4 w-[45%]">
|
>
|
||||||
<masked-text :value="currentUser.access_token" />
|
<access-token :value="currentUser.access_token" @on-copy="onCopyToken" />
|
||||||
</div>
|
</form-section>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { required, minLength, email } from 'vuelidate/lib/validators';
|
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import { clearCookiesOnLogout } from '../../../../store/utils/api';
|
|
||||||
import { hasValidAvatarUrl } from 'dashboard/helper/URLHelper';
|
|
||||||
import NotificationSettings from './NotificationSettings.vue';
|
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
|
||||||
import ChangePassword from './ChangePassword.vue';
|
|
||||||
import MessageSignature from './MessageSignature.vue';
|
|
||||||
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
||||||
import uiSettingsMixin, {
|
import uiSettingsMixin, {
|
||||||
isEditorHotKeyEnabled,
|
isEditorHotKeyEnabled,
|
||||||
} from 'dashboard/mixins/uiSettings';
|
} from 'dashboard/mixins/uiSettings';
|
||||||
import MaskedText from 'dashboard/components/MaskedText.vue';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
import PreviewCard from 'dashboard/components/ui/PreviewCard.vue';
|
import { mapGetters } from 'vuex';
|
||||||
|
import { clearCookiesOnLogout } from 'dashboard/store/utils/api.js';
|
||||||
|
import { copyTextToClipboard } from 'shared/helpers/clipboard';
|
||||||
|
|
||||||
|
import UserProfilePicture from './UserProfilePicture.vue';
|
||||||
|
import UserBasicDetails from './UserBasicDetails.vue';
|
||||||
|
import MessageSignature from './MessageSignature.vue';
|
||||||
|
import HotKeyCard from './HotKeyCard.vue';
|
||||||
|
import ChangePassword from './ChangePassword.vue';
|
||||||
|
import NotificationPreferences from './NotificationPreferences.vue';
|
||||||
|
import AudioNotifications from './AudioNotifications.vue';
|
||||||
|
import FormSection from 'dashboard/components/FormSection.vue';
|
||||||
|
import AccessToken from './AccessToken.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
NotificationSettings,
|
|
||||||
ChangePassword,
|
|
||||||
MessageSignature,
|
MessageSignature,
|
||||||
PreviewCard,
|
FormSection,
|
||||||
MaskedText,
|
UserProfilePicture,
|
||||||
|
UserBasicDetails,
|
||||||
|
HotKeyCard,
|
||||||
|
ChangePassword,
|
||||||
|
NotificationPreferences,
|
||||||
|
AudioNotifications,
|
||||||
|
AccessToken,
|
||||||
},
|
},
|
||||||
mixins: [alertMixin, globalConfigMixin, uiSettingsMixin],
|
mixins: [alertMixin, globalConfigMixin, uiSettingsMixin],
|
||||||
data() {
|
data() {
|
||||||
@@ -156,59 +120,40 @@ export default {
|
|||||||
name: '',
|
name: '',
|
||||||
displayName: '',
|
displayName: '',
|
||||||
email: '',
|
email: '',
|
||||||
isProfileUpdating: false,
|
messageSignature: '',
|
||||||
errorMessage: '',
|
hotKeys: [
|
||||||
keyOptions: [
|
|
||||||
{
|
{
|
||||||
key: 'enter',
|
key: 'enter',
|
||||||
src: '/assets/images/dashboard/editor/enter-editor.png',
|
title: this.$t(
|
||||||
heading: this.$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.ENTER_KEY.HEADING'
|
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.ENTER_KEY.HEADING'
|
||||||
),
|
),
|
||||||
content: this.$t(
|
description: this.$t(
|
||||||
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.ENTER_KEY.CONTENT'
|
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.ENTER_KEY.CONTENT'
|
||||||
),
|
),
|
||||||
|
lightImage: '/assets/images/dashboard/profile/hot-key-enter.svg',
|
||||||
|
darkImage: '/assets/images/dashboard/profile/hot-key-enter-dark.svg',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'cmd_enter',
|
key: 'cmd_enter',
|
||||||
src: '/assets/images/dashboard/editor/cmd-editor.png',
|
title: this.$t(
|
||||||
heading: this.$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.CMD_ENTER_KEY.HEADING'
|
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.CMD_ENTER_KEY.HEADING'
|
||||||
),
|
),
|
||||||
content: this.$t(
|
description: this.$t(
|
||||||
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.CMD_ENTER_KEY.CONTENT'
|
'PROFILE_SETTINGS.FORM.SEND_MESSAGE.CARD.CMD_ENTER_KEY.CONTENT'
|
||||||
),
|
),
|
||||||
|
lightImage: '/assets/images/dashboard/profile/hot-key-ctrl-enter.svg',
|
||||||
|
darkImage:
|
||||||
|
'/assets/images/dashboard/profile/hot-key-ctrl-enter-dark.svg',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
validations: {
|
|
||||||
name: {
|
|
||||||
required,
|
|
||||||
minLength: minLength(1),
|
|
||||||
},
|
|
||||||
displayName: {},
|
|
||||||
email: {
|
|
||||||
required,
|
|
||||||
email,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
currentUser: 'getCurrentUser',
|
currentUser: 'getCurrentUser',
|
||||||
currentUserId: 'getCurrentUserID',
|
currentUserId: 'getCurrentUserID',
|
||||||
globalConfig: 'globalConfig/get',
|
globalConfig: 'globalConfig/get',
|
||||||
}),
|
}),
|
||||||
showDeleteButton() {
|
|
||||||
return hasValidAvatarUrl(this.avatarUrl);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
currentUserId(newCurrentUserId, prevCurrentUserId) {
|
|
||||||
if (prevCurrentUserId !== newCurrentUserId) {
|
|
||||||
this.initializeUser();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.currentUserId) {
|
if (this.currentUserId) {
|
||||||
@@ -221,45 +166,66 @@ export default {
|
|||||||
this.email = this.currentUser.email;
|
this.email = this.currentUser.email;
|
||||||
this.avatarUrl = this.currentUser.avatar_url;
|
this.avatarUrl = this.currentUser.avatar_url;
|
||||||
this.displayName = this.currentUser.display_name;
|
this.displayName = this.currentUser.display_name;
|
||||||
|
this.messageSignature = this.currentUser.message_signature;
|
||||||
},
|
},
|
||||||
isEditorHotKeyEnabled,
|
isEditorHotKeyEnabled,
|
||||||
async updateUser() {
|
async dispatchUpdate(payload, successMessage, errorMessage) {
|
||||||
this.$v.$touch();
|
let alertMessage = '';
|
||||||
if (this.$v.$invalid) {
|
|
||||||
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.ERROR'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isProfileUpdating = true;
|
|
||||||
const hasEmailChanged = this.currentUser.email !== this.email;
|
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('updateProfile', {
|
await this.$store.dispatch('updateProfile', payload);
|
||||||
name: this.name,
|
alertMessage = successMessage;
|
||||||
email: this.email,
|
|
||||||
avatar: this.avatarFile,
|
return true; // return the value so that the status can be known
|
||||||
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) {
|
} catch (error) {
|
||||||
this.errorMessage = this.$t('RESET_PASSWORD.API.ERROR_MESSAGE');
|
alertMessage = error?.response?.data?.error
|
||||||
if (error?.response?.data?.error) {
|
? error.response.data.error
|
||||||
this.errorMessage = error.response.data.error;
|
: errorMessage;
|
||||||
}
|
|
||||||
|
return false; // return the value so that the status can be known
|
||||||
} finally {
|
} finally {
|
||||||
this.isProfileUpdating = false;
|
this.showAlert(alertMessage);
|
||||||
this.showAlert(this.errorMessage);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleImageUpload({ file, url }) {
|
async updateProfile(userAttributes) {
|
||||||
|
const { name, email, displayName } = userAttributes;
|
||||||
|
const hasEmailChanged = this.currentUser.email !== email;
|
||||||
|
this.name = name || this.name;
|
||||||
|
this.email = email || this.email;
|
||||||
|
this.displayName = displayName || this.displayName;
|
||||||
|
|
||||||
|
const updatePayload = {
|
||||||
|
name: this.name,
|
||||||
|
email: this.email,
|
||||||
|
displayName: this.displayName,
|
||||||
|
avatar: this.avatarFile,
|
||||||
|
};
|
||||||
|
|
||||||
|
const success = await this.dispatchUpdate(
|
||||||
|
updatePayload,
|
||||||
|
hasEmailChanged
|
||||||
|
? this.$t('PROFILE_SETTINGS.AFTER_EMAIL_CHANGED')
|
||||||
|
: this.$t('PROFILE_SETTINGS.UPDATE_SUCCESS'),
|
||||||
|
this.$t('RESET_PASSWORD.API.ERROR_MESSAGE')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasEmailChanged && success) clearCookiesOnLogout();
|
||||||
|
},
|
||||||
|
async updateSignature(signature) {
|
||||||
|
const payload = { message_signature: signature };
|
||||||
|
let successMessage = this.$t(
|
||||||
|
'PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.API_SUCCESS'
|
||||||
|
);
|
||||||
|
let errorMessage = this.$t(
|
||||||
|
'PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.API_ERROR'
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.dispatchUpdate(payload, successMessage, errorMessage);
|
||||||
|
},
|
||||||
|
updateProfilePicture({ file, url }) {
|
||||||
this.avatarFile = file;
|
this.avatarFile = file;
|
||||||
this.avatarUrl = url;
|
this.avatarUrl = url;
|
||||||
},
|
},
|
||||||
async deleteAvatar() {
|
async deleteProfilePicture() {
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('deleteAvatar');
|
await this.$store.dispatch('deleteAvatar');
|
||||||
this.avatarUrl = '';
|
this.avatarUrl = '';
|
||||||
@@ -269,12 +235,19 @@ export default {
|
|||||||
this.showAlert(this.$t('PROFILE_SETTINGS.AVATAR_DELETE_FAILED'));
|
this.showAlert(this.$t('PROFILE_SETTINGS.AVATAR_DELETE_FAILED'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleEditorMessageKey(key) {
|
toggleHotKey(key) {
|
||||||
|
this.hotKeys = this.hotKeys.map(hotKey =>
|
||||||
|
hotKey.key === key ? { ...hotKey, active: !hotKey.active } : hotKey
|
||||||
|
);
|
||||||
this.updateUISettings({ editor_message_key: key });
|
this.updateUISettings({ editor_message_key: key });
|
||||||
this.showAlert(
|
this.showAlert(
|
||||||
this.$t('PROFILE_SETTINGS.FORM.SEND_MESSAGE.UPDATE_SUCCESS')
|
this.$t('PROFILE_SETTINGS.FORM.SEND_MESSAGE.UPDATE_SUCCESS')
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
async onCopyToken(value) {
|
||||||
|
await copyTextToClipboard(value);
|
||||||
|
this.showAlert(this.$t('COMPONENTS.CODE.COPY_SUCCESSFUL'));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,110 +1,50 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<form class="flex flex-col gap-6" @submit.prevent="updateSignature()">
|
||||||
class="profile--settings--row text-black-900 dark:text-slate-300 flex items-center"
|
<woot-message-editor
|
||||||
>
|
id="message-signature-input"
|
||||||
<div class="w-1/4 py-4 pr-6 ml-0">
|
v-model="signature"
|
||||||
<h4 class="text-lg text-black-900 dark:text-slate-200">
|
class="message-editor h-[10rem] !px-3"
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.TITLE') }}
|
:is-format-mode="true"
|
||||||
</h4>
|
:placeholder="$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE.PLACEHOLDER')"
|
||||||
<p>{{ $t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.NOTE') }}</p>
|
:enabled-menu-options="customEditorMenuList"
|
||||||
</div>
|
:enable-suggestions="false"
|
||||||
<div class="p-4 w-[45%]">
|
:show-image-resize-toolbar="true"
|
||||||
<div>
|
/>
|
||||||
<label for="message-signature-input">{{
|
<form-button
|
||||||
$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE.LABEL')
|
type="submit"
|
||||||
}}</label>
|
color-scheme="primary"
|
||||||
<woot-message-editor
|
variant="solid"
|
||||||
id="message-signature-input"
|
size="large"
|
||||||
v-model="messageSignature"
|
>
|
||||||
class="message-editor h-[10rem]"
|
{{ $t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.BTN_TEXT') }}
|
||||||
:is-format-mode="true"
|
</form-button>
|
||||||
:placeholder="
|
</form>
|
||||||
$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE.PLACEHOLDER')
|
|
||||||
"
|
|
||||||
:enabled-menu-options="customEditorMenuList"
|
|
||||||
:enable-suggestions="false"
|
|
||||||
:show-image-resize-toolbar="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<woot-button
|
|
||||||
:is-loading="isUpdating"
|
|
||||||
type="button"
|
|
||||||
@click.prevent="updateSignature"
|
|
||||||
>
|
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.BTN_TEXT') }}
|
|
||||||
</woot-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
<script setup>
|
||||||
<script>
|
import { ref, watch } from 'vue';
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
|
|
||||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
|
||||||
import { MESSAGE_SIGNATURE_EDITOR_MENU_OPTIONS } from 'dashboard/constants/editor';
|
import { MESSAGE_SIGNATURE_EDITOR_MENU_OPTIONS } from 'dashboard/constants/editor';
|
||||||
|
import FormButton from 'v3/components/Form/Button.vue';
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
messageSignature: {
|
||||||
WootMessageEditor,
|
type: String,
|
||||||
},
|
default: '',
|
||||||
mixins: [alertMixin],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
messageSignature: '',
|
|
||||||
enableMessageSignature: false,
|
|
||||||
isUpdating: false,
|
|
||||||
errorMessage: '',
|
|
||||||
customEditorMenuList: MESSAGE_SIGNATURE_EDITOR_MENU_OPTIONS,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters({
|
|
||||||
currentUser: 'getCurrentUser',
|
|
||||||
currentUserId: 'getCurrentUserID',
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.initValues();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
initValues() {
|
|
||||||
const { message_signature: messageSignature } = this.currentUser;
|
|
||||||
this.messageSignature = messageSignature || '';
|
|
||||||
},
|
|
||||||
async updateSignature() {
|
|
||||||
try {
|
|
||||||
await this.$store.dispatch('updateProfile', {
|
|
||||||
message_signature: this.messageSignature || '',
|
|
||||||
});
|
|
||||||
this.errorMessage = this.$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.API_SUCCESS'
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
this.errorMessage = this.$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.API_ERROR'
|
|
||||||
);
|
|
||||||
if (error?.response?.data?.message) {
|
|
||||||
this.errorMessage = error.response.data.message;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this.isUpdating = false;
|
|
||||||
this.initValues();
|
|
||||||
this.showAlert(this.errorMessage);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const customEditorMenuList = MESSAGE_SIGNATURE_EDITOR_MENU_OPTIONS;
|
||||||
|
const signature = ref(props.messageSignature);
|
||||||
|
const emit = defineEmits(['update-signature']);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.messageSignature,
|
||||||
|
newValue => {
|
||||||
|
signature.value = newValue;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateSignature = () => {
|
||||||
|
emit('update-signature', signature.value);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.message-editor {
|
|
||||||
@apply px-3 mb-4;
|
|
||||||
|
|
||||||
::v-deep {
|
|
||||||
.ProseMirror-menubar {
|
|
||||||
@apply left-2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between w-full gap-2 p-4 border border-solid border-ash-200 rounded-xl dark:bg-ash-25"
|
class="flex items-center justify-between w-full gap-2 p-4 border border-solid border-ash-200 rounded-xl"
|
||||||
>
|
>
|
||||||
<div class="flex flex-row items-center gap-2">
|
<div class="flex flex-row items-center gap-2">
|
||||||
<fluent-icon
|
<fluent-icon
|
||||||
@@ -1,649 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div id="profile-settings-notifications">
|
|
||||||
<div
|
|
||||||
class="profile--settings--row text-black-900 dark:text-slate-300 flex items-center"
|
|
||||||
>
|
|
||||||
<div class="w-1/4">
|
|
||||||
<h4 class="text-lg text-black-900 dark:text-slate-200">
|
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.TITLE') }}
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.NOTE') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="w-[45%] p-4">
|
|
||||||
<div class="mb-4">
|
|
||||||
<span class="text-sm notification-label">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.ALERT_TYPE.TITLE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
id="audio_enable_alert_none"
|
|
||||||
v-model="enableAudioAlerts"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="radio"
|
|
||||||
value="none"
|
|
||||||
@input="handleAudioInput"
|
|
||||||
/>
|
|
||||||
<label for="audio_enable_alert_none">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.ALERT_TYPE.NONE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
id="audio_enable_alert_mine"
|
|
||||||
v-model="enableAudioAlerts"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="radio"
|
|
||||||
value="mine"
|
|
||||||
@input="handleAudioInput"
|
|
||||||
/>
|
|
||||||
<label for="audio_enable_alert_mine">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.ALERT_TYPE.ASSIGNED'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
id="audio_enable_alert_all"
|
|
||||||
v-model="enableAudioAlerts"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="radio"
|
|
||||||
value="all"
|
|
||||||
@input="handleAudioInput"
|
|
||||||
/>
|
|
||||||
<label for="audio_enable_alert_all">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.ALERT_TYPE.ALL_CONVERSATIONS'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<span class="text-sm notification-label">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.DEFAULT_TONE.TITLE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
<div>
|
|
||||||
<select
|
|
||||||
v-model="notificationTone"
|
|
||||||
class="tone-selector mb-0"
|
|
||||||
@change="handleAudioToneChange"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="tone in notificationAlertTones"
|
|
||||||
:key="tone.value"
|
|
||||||
:value="tone.value"
|
|
||||||
>
|
|
||||||
{{ tone.label }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-1">
|
|
||||||
<span class="text-sm notification-label">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.CONDITIONS.TITLE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
id="audio_alert_when_tab_is_inactive"
|
|
||||||
v-model="playAudioWhenTabIsInactive"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="tab_is_inactive"
|
|
||||||
@input="handleAudioAlertConditions"
|
|
||||||
/>
|
|
||||||
<label for="audio_alert_when_tab_is_inactive">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.CONDITIONS.CONDITION_ONE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
id="audio_alert_until_all_conversations_are_read"
|
|
||||||
v-model="alertIfUnreadConversationExist"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="conversations_are_read"
|
|
||||||
@input="handleAudioAlertConditions"
|
|
||||||
/>
|
|
||||||
<label for="audio_alert_until_all_conversations_are_read">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.CONDITIONS.CONDITION_TWO'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="profile--settings--row text-black-900 dark:text-slate-300 flex items-center"
|
|
||||||
>
|
|
||||||
<div class="w-1/4">
|
|
||||||
<h4 class="text-lg text-black-900 dark:text-slate-200">
|
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.TITLE') }}
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.NOTE') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="w-[45%] p-4">
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedEmailFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="email_conversation_creation"
|
|
||||||
@input="handleEmailInput"
|
|
||||||
/>
|
|
||||||
<label for="conversation_creation">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.CONVERSATION_CREATION'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedEmailFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="email_conversation_assignment"
|
|
||||||
@input="handleEmailInput"
|
|
||||||
/>
|
|
||||||
<label for="conversation_assignment">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.CONVERSATION_ASSIGNMENT'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedEmailFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="email_conversation_mention"
|
|
||||||
@input="handleEmailInput"
|
|
||||||
/>
|
|
||||||
<label for="conversation_mention">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.CONVERSATION_MENTION'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedEmailFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="email_assigned_conversation_new_message"
|
|
||||||
@input="handleEmailInput"
|
|
||||||
/>
|
|
||||||
<label for="assigned_conversation_new_message">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.ASSIGNED_CONVERSATION_NEW_MESSAGE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedEmailFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="email_participating_conversation_new_message"
|
|
||||||
@input="handleEmailInput"
|
|
||||||
/>
|
|
||||||
<label for="assigned_conversation_new_message">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.PARTICIPATING_CONVERSATION_NEW_MESSAGE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div v-if="isSLAEnabled" class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedEmailFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="email_sla_missed_first_response"
|
|
||||||
@input="handleEmailInput"
|
|
||||||
/>
|
|
||||||
<label for="sla_missed_first_response">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.SLA_MISSED_FIRST_RESPONSE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div v-if="isSLAEnabled" class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedEmailFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="email_sla_missed_next_response"
|
|
||||||
@input="handleEmailInput"
|
|
||||||
/>
|
|
||||||
<label for="sla_missed_next_response">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.SLA_MISSED_NEXT_RESPONSE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div v-if="isSLAEnabled" class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedEmailFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="email_sla_missed_resolution"
|
|
||||||
@input="handleEmailInput"
|
|
||||||
/>
|
|
||||||
<label for="sla_missed_resolution">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.EMAIL_NOTIFICATIONS_SECTION.SLA_MISSED_RESOLUTION'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="vapidPublicKey && hasPushAPISupport"
|
|
||||||
class="profile--settings--row text-black-900 dark:text-slate-300 flex items-center push-row"
|
|
||||||
>
|
|
||||||
<div class="w-1/4">
|
|
||||||
<h4 class="text-lg text-black-900 dark:text-slate-200">
|
|
||||||
{{ $t('PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.TITLE') }}
|
|
||||||
</h4>
|
|
||||||
<p>{{ $t('PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.NOTE') }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="w-[45%] p-4">
|
|
||||||
<p v-if="hasEnabledPushPermissions">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.HAS_ENABLED_PUSH'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
<div v-else class="push-notification--button">
|
|
||||||
<woot-submit-button
|
|
||||||
:button-text="
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.REQUEST_PUSH'
|
|
||||||
)
|
|
||||||
"
|
|
||||||
class="button nice small"
|
|
||||||
type="button"
|
|
||||||
@click="onRequestPermissions"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedPushFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="push_conversation_creation"
|
|
||||||
@input="handlePushInput"
|
|
||||||
/>
|
|
||||||
<label for="conversation_creation">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.CONVERSATION_CREATION'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedPushFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="push_conversation_assignment"
|
|
||||||
@input="handlePushInput"
|
|
||||||
/>
|
|
||||||
<label for="conversation_assignment">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.CONVERSATION_ASSIGNMENT'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedPushFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="push_conversation_mention"
|
|
||||||
@input="handlePushInput"
|
|
||||||
/>
|
|
||||||
<label for="conversation_mention">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.CONVERSATION_MENTION'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedPushFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="push_assigned_conversation_new_message"
|
|
||||||
@input="handlePushInput"
|
|
||||||
/>
|
|
||||||
<label for="assigned_conversation_new_message">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.ASSIGNED_CONVERSATION_NEW_MESSAGE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedPushFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="push_participating_conversation_new_message"
|
|
||||||
@input="handlePushInput"
|
|
||||||
/>
|
|
||||||
<label for="assigned_conversation_new_message">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.PARTICIPATING_CONVERSATION_NEW_MESSAGE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="isSLAEnabled" class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedPushFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="push_sla_missed_first_response"
|
|
||||||
@input="handlePushInput"
|
|
||||||
/>
|
|
||||||
<label for="sla_missed_first_response">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.SLA_MISSED_FIRST_RESPONSE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="isSLAEnabled" class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedPushFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="push_sla_missed_next_response"
|
|
||||||
@input="handlePushInput"
|
|
||||||
/>
|
|
||||||
<label for="sla_missed_next_response">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.SLA_MISSED_NEXT_RESPONSE'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="isSLAEnabled" class="flex items-center gap-2 mb-1">
|
|
||||||
<input
|
|
||||||
v-model="selectedPushFlags"
|
|
||||||
class="notification--checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
value="push_sla_missed_resolution"
|
|
||||||
@input="handlePushInput"
|
|
||||||
/>
|
|
||||||
<label for="sla_missed_resolution">
|
|
||||||
{{
|
|
||||||
$t(
|
|
||||||
'PROFILE_SETTINGS.FORM.PUSH_NOTIFICATIONS_SECTION.SLA_MISSED_RESOLUTION'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapGetters } from 'vuex';
|
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
|
||||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
|
||||||
import {
|
|
||||||
hasPushPermissions,
|
|
||||||
requestPushPermissions,
|
|
||||||
verifyServiceWorkerExistence,
|
|
||||||
} from '../../../../helper/pushHelper';
|
|
||||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mixins: [alertMixin, configMixin, uiSettingsMixin],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
selectedEmailFlags: [],
|
|
||||||
selectedPushFlags: [],
|
|
||||||
enableAudioAlerts: false,
|
|
||||||
hasEnabledPushPermissions: false,
|
|
||||||
playAudioWhenTabIsInactive: false,
|
|
||||||
alertIfUnreadConversationExist: false,
|
|
||||||
notificationTone: 'ding',
|
|
||||||
notificationAlertTones: [
|
|
||||||
{
|
|
||||||
value: 'ding',
|
|
||||||
label: 'Ding',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'bell',
|
|
||||||
label: 'Bell',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters({
|
|
||||||
accountId: 'getCurrentAccountId',
|
|
||||||
emailFlags: 'userNotificationSettings/getSelectedEmailFlags',
|
|
||||||
pushFlags: 'userNotificationSettings/getSelectedPushFlags',
|
|
||||||
uiSettings: 'getUISettings',
|
|
||||||
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
|
|
||||||
}),
|
|
||||||
hasPushAPISupport() {
|
|
||||||
return !!('Notification' in window);
|
|
||||||
},
|
|
||||||
isSLAEnabled() {
|
|
||||||
return this.isFeatureEnabledonAccount(this.accountId, FEATURE_FLAGS.SLA);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
emailFlags(value) {
|
|
||||||
this.selectedEmailFlags = value;
|
|
||||||
},
|
|
||||||
pushFlags(value) {
|
|
||||||
this.selectedPushFlags = value;
|
|
||||||
},
|
|
||||||
uiSettings(value) {
|
|
||||||
this.notificationUISettings(value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
if (hasPushPermissions()) {
|
|
||||||
this.getPushSubscription();
|
|
||||||
}
|
|
||||||
this.notificationUISettings(this.uiSettings);
|
|
||||||
this.$store.dispatch('userNotificationSettings/get');
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
notificationUISettings(uiSettings) {
|
|
||||||
const {
|
|
||||||
enable_audio_alerts: enableAudio = false,
|
|
||||||
always_play_audio_alert: alwaysPlayAudioAlert,
|
|
||||||
alert_if_unread_assigned_conversation_exist:
|
|
||||||
alertIfUnreadConversationExist,
|
|
||||||
notification_tone: notificationTone,
|
|
||||||
} = uiSettings;
|
|
||||||
this.enableAudioAlerts = enableAudio;
|
|
||||||
this.playAudioWhenTabIsInactive = !alwaysPlayAudioAlert;
|
|
||||||
this.alertIfUnreadConversationExist = alertIfUnreadConversationExist;
|
|
||||||
this.notificationTone = notificationTone || 'ding';
|
|
||||||
},
|
|
||||||
onRegistrationSuccess() {
|
|
||||||
this.hasEnabledPushPermissions = true;
|
|
||||||
},
|
|
||||||
onRequestPermissions() {
|
|
||||||
requestPushPermissions({
|
|
||||||
onSuccess: this.onRegistrationSuccess,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getPushSubscription() {
|
|
||||||
verifyServiceWorkerExistence(registration =>
|
|
||||||
registration.pushManager
|
|
||||||
.getSubscription()
|
|
||||||
.then(subscription => {
|
|
||||||
if (!subscription) {
|
|
||||||
this.hasEnabledPushPermissions = false;
|
|
||||||
} else {
|
|
||||||
this.hasEnabledPushPermissions = true;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
.catch(error => console.log(error))
|
|
||||||
);
|
|
||||||
},
|
|
||||||
async updateNotificationSettings() {
|
|
||||||
try {
|
|
||||||
this.$store.dispatch('userNotificationSettings/update', {
|
|
||||||
selectedEmailFlags: this.selectedEmailFlags,
|
|
||||||
selectedPushFlags: this.selectedPushFlags,
|
|
||||||
});
|
|
||||||
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_SUCCESS'));
|
|
||||||
} catch (error) {
|
|
||||||
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_ERROR'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleEmailInput(e) {
|
|
||||||
this.selectedEmailFlags = this.toggleInput(
|
|
||||||
this.selectedEmailFlags,
|
|
||||||
e.target.value
|
|
||||||
);
|
|
||||||
|
|
||||||
this.updateNotificationSettings();
|
|
||||||
},
|
|
||||||
handlePushInput(e) {
|
|
||||||
this.selectedPushFlags = this.toggleInput(
|
|
||||||
this.selectedPushFlags,
|
|
||||||
e.target.value
|
|
||||||
);
|
|
||||||
|
|
||||||
this.updateNotificationSettings();
|
|
||||||
},
|
|
||||||
handleAudioInput(e) {
|
|
||||||
this.enableAudioAlerts = e.target.value;
|
|
||||||
this.updateUISettings({
|
|
||||||
enable_audio_alerts: this.enableAudioAlerts,
|
|
||||||
});
|
|
||||||
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_SUCCESS'));
|
|
||||||
},
|
|
||||||
handleAudioAlertConditions(e) {
|
|
||||||
let condition = e.target.value;
|
|
||||||
if (condition === 'tab_is_inactive') {
|
|
||||||
this.updateUISettings({
|
|
||||||
always_play_audio_alert: !e.target.checked,
|
|
||||||
});
|
|
||||||
} else if (condition === 'conversations_are_read') {
|
|
||||||
this.updateUISettings({
|
|
||||||
alert_if_unread_assigned_conversation_exist: e.target.checked,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_SUCCESS'));
|
|
||||||
},
|
|
||||||
handleAudioToneChange(e) {
|
|
||||||
this.updateUISettings({ notification_tone: e.target.value });
|
|
||||||
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_SUCCESS'));
|
|
||||||
},
|
|
||||||
toggleInput(selected, current) {
|
|
||||||
if (selected.includes(current)) {
|
|
||||||
const newSelectedFlags = selected.filter(flag => flag !== current);
|
|
||||||
return newSelectedFlags;
|
|
||||||
}
|
|
||||||
return [...selected, current];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
@import '~dashboard/assets/scss/variables.scss';
|
|
||||||
|
|
||||||
.notification--checkbox {
|
|
||||||
font-size: $font-size-large;
|
|
||||||
}
|
|
||||||
|
|
||||||
.push-notification--button {
|
|
||||||
margin-bottom: var(--space-one);
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-label {
|
|
||||||
display: flex;
|
|
||||||
font-weight: var(--font-weight-bold);
|
|
||||||
margin-bottom: var(--space-small);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tone-selector {
|
|
||||||
height: var(--space-large);
|
|
||||||
padding-bottom: var(--space-micro);
|
|
||||||
padding-top: var(--space-micro);
|
|
||||||
width: var(--space-mega);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="flex flex-col justify-between flex-1 h-full m-0 overflow-auto bg-white dark:bg-slate-900"
|
||||||
|
>
|
||||||
|
<keep-alive v-if="keepAlive">
|
||||||
|
<router-view />
|
||||||
|
</keep-alive>
|
||||||
|
<router-view v-else />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
keepAlive: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { frontendURL } from '../../../../helper/URLHelper';
|
import { frontendURL } from '../../../../helper/URLHelper';
|
||||||
|
|
||||||
const SettingsContent = () => import('../Wrapper.vue');
|
const SettingsContent = () => import('./Wrapper.vue');
|
||||||
const Index = () => import('./Index.vue');
|
const Index = () => import('./Index.vue');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -10,12 +10,6 @@ export default {
|
|||||||
name: 'profile_settings',
|
name: 'profile_settings',
|
||||||
roles: ['administrator', 'agent'],
|
roles: ['administrator', 'agent'],
|
||||||
component: SettingsContent,
|
component: SettingsContent,
|
||||||
props: {
|
|
||||||
headerTitle: 'PROFILE_SETTINGS.TITLE',
|
|
||||||
icon: 'edit',
|
|
||||||
showNewButton: false,
|
|
||||||
showSidemenuIcon: false,
|
|
||||||
},
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'settings',
|
path: 'settings',
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ import integrationapps from './integrationapps/integrations.routes';
|
|||||||
import integrations from './integrations/integrations.routes';
|
import integrations from './integrations/integrations.routes';
|
||||||
import labels from './labels/labels.routes';
|
import labels from './labels/labels.routes';
|
||||||
import macros from './macros/macros.routes';
|
import macros from './macros/macros.routes';
|
||||||
import profile from './profile/profile.routes';
|
|
||||||
import reports from './reports/reports.routes';
|
import reports from './reports/reports.routes';
|
||||||
import store from '../../../store';
|
import store from '../../../store';
|
||||||
import sla from './sla/sla.routes';
|
import sla from './sla/sla.routes';
|
||||||
import teams from './teams/teams.routes';
|
import teams from './teams/teams.routes';
|
||||||
import personal from './personal/personal.routes';
|
import profile from './profile/profile.routes';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
routes: [
|
routes: [
|
||||||
@@ -47,10 +46,9 @@ export default {
|
|||||||
...integrations.routes,
|
...integrations.routes,
|
||||||
...labels.routes,
|
...labels.routes,
|
||||||
...macros.routes,
|
...macros.routes,
|
||||||
...profile.routes,
|
|
||||||
...reports.routes,
|
...reports.routes,
|
||||||
...sla.routes,
|
...sla.routes,
|
||||||
...teams.routes,
|
...teams.routes,
|
||||||
...personal.routes,
|
...profile.routes,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
v-model="checked"
|
v-model="checked"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:value="value"
|
:value="value"
|
||||||
class="flex-shrink-0 mt-0.5 border-ash-200 border checked:border-none checked:bg-primary-600 dark:checked:bg-primary-600 shadow appearance-none rounded-[4px] w-4 h-4 focus:ring-1 after:content-[''] after:text-white checked:after:content-['✓'] after:flex after:items-center after:justify-center after:text-center after:text-xs after:font-bold after:relative"
|
class="flex-shrink-0 mt-0.5 border-ash-200 border bg-ash-50 checked:border-none checked:bg-primary-600 dark:checked:bg-primary-600 shadow-sm appearance-none rounded-[4px] w-4 h-4 focus:ring-1 after:content-[''] after:text-white checked:after:content-['✓'] after:flex after:items-center after:justify-center after:text-center after:text-xs after:font-bold after:relative"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user