feat: Adds email signature form to profile settings (#3906)
This commit is contained in:
committed by
GitHub
parent
d5c9193d1a
commit
351a3dc372
@@ -142,9 +142,9 @@ export default {
|
||||
}) {
|
||||
const formData = new FormData();
|
||||
Object.keys(profileAttributes).forEach(key => {
|
||||
const value = profileAttributes[key];
|
||||
if (value) {
|
||||
formData.append(`profile[${key}]`, value);
|
||||
const hasValue = profileAttributes[key] === undefined;
|
||||
if (!hasValue) {
|
||||
formData.append(`profile[${key}]`, profileAttributes[key]);
|
||||
}
|
||||
});
|
||||
formData.append('profile[display_name]', displayName || '');
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<div class="bottom-box" :class="wrapClass">
|
||||
<div class="left-wrap">
|
||||
<woot-button
|
||||
v-tooltip.top-end="$t('CONVERSATION.REPLYBOX.TIP_EMOJI_ICON')"
|
||||
:title="$t('CONVERSATION.REPLYBOX.TIP_EMOJI_ICON')"
|
||||
icon="emoji"
|
||||
emoji="😊"
|
||||
@@ -14,6 +15,7 @@
|
||||
<!-- ensure the same validations for attachment types are implemented in backend models as well -->
|
||||
<file-upload
|
||||
ref="upload"
|
||||
v-tooltip.top-end="$t('CONVERSATION.REPLYBOX.TIP_ATTACH_ICON')"
|
||||
:size="4096 * 4096"
|
||||
:accept="allowedFileTypes"
|
||||
:multiple="enableMultipleFileUpload"
|
||||
@@ -38,6 +40,7 @@
|
||||
</file-upload>
|
||||
<woot-button
|
||||
v-if="enableRichEditor && !isOnPrivateNote"
|
||||
v-tooltip.top-end="$t('CONVERSATION.REPLYBOX.TIP_FORMAT_ICON')"
|
||||
icon="quote"
|
||||
emoji="🖊️"
|
||||
color-scheme="secondary"
|
||||
@@ -46,6 +49,16 @@
|
||||
:title="$t('CONVERSATION.REPLYBOX.TIP_FORMAT_ICON')"
|
||||
@click="toggleFormatMode"
|
||||
/>
|
||||
<woot-button
|
||||
v-if="showMessageSignatureButton"
|
||||
v-tooltip.top-end="signatureToggleTooltip"
|
||||
icon="signature"
|
||||
color-scheme="secondary"
|
||||
variant="smooth"
|
||||
size="small"
|
||||
:title="signatureToggleTooltip"
|
||||
@click="toggleMessageSignature"
|
||||
/>
|
||||
<transition name="modal-fade">
|
||||
<div
|
||||
v-show="$refs.upload && $refs.upload.dropActive"
|
||||
@@ -90,13 +103,16 @@ import {
|
||||
hasPressedAltAndAKey,
|
||||
} from 'shared/helpers/KeyboardHelpers';
|
||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
|
||||
import { ALLOWED_FILE_TYPES } from 'shared/constants/messages';
|
||||
|
||||
import { REPLY_EDITOR_MODES } from './constants';
|
||||
export default {
|
||||
name: 'ReplyTopPanel',
|
||||
components: { FileUpload },
|
||||
mixins: [eventListenerMixins],
|
||||
mixins: [eventListenerMixins, uiSettingsMixin, inboxMixin],
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
@@ -110,6 +126,10 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
inbox: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
showFileUpload: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@@ -175,6 +195,18 @@ export default {
|
||||
allowedFileTypes() {
|
||||
return ALLOWED_FILE_TYPES;
|
||||
},
|
||||
showMessageSignatureButton() {
|
||||
return !this.isPrivate && this.isAnEmailChannel;
|
||||
},
|
||||
sendWithSignature() {
|
||||
const { send_with_signature: isEnabled } = this.uiSettings;
|
||||
return isEnabled;
|
||||
},
|
||||
signatureToggleTooltip() {
|
||||
return this.sendWithSignature
|
||||
? this.$t('CONVERSATION.FOOTER.DISABLE_SIGN_TOOLTIP')
|
||||
: this.$t('CONVERSATION.FOOTER.ENABLE_SIGN_TOOLTIP');
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
ActiveStorage.start();
|
||||
@@ -194,6 +226,11 @@ export default {
|
||||
toggleEnterToSend() {
|
||||
this.$emit('toggleEnterToSend', !this.enterToSendEnabled);
|
||||
},
|
||||
toggleMessageSignature() {
|
||||
this.updateUISettings({
|
||||
send_with_signature: !this.sendWithSignature,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -66,8 +66,16 @@
|
||||
:remove-attachment="removeAttachment"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="showMessageSignature"
|
||||
v-tooltip="$t('CONVERSATION.FOOTER.MESSAGE_SIGN_TOOLTIP')"
|
||||
class="message-signature-wrap"
|
||||
>
|
||||
<p class="message-signature" v-html="formatMessage(messageSignature)" />
|
||||
</div>
|
||||
<reply-bottom-panel
|
||||
:mode="replyType"
|
||||
:inbox="inbox"
|
||||
:send-button-text="replyButtonLabel"
|
||||
:on-direct-file-upload="onDirectFileUpload"
|
||||
:show-file-upload="showFileUpload"
|
||||
@@ -101,6 +109,7 @@ import ReplyBottomPanel from 'dashboard/components/widgets/WootWriter/ReplyBotto
|
||||
import Banner from 'dashboard/components/ui/Banner.vue';
|
||||
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
|
||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
|
||||
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
|
||||
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
||||
import { MAXIMUM_FILE_UPLOAD_SIZE } from 'shared/constants/messages';
|
||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||
@@ -128,7 +137,13 @@ export default {
|
||||
WootMessageEditor,
|
||||
Banner,
|
||||
},
|
||||
mixins: [clickaway, inboxMixin, uiSettingsMixin, alertMixin],
|
||||
mixins: [
|
||||
clickaway,
|
||||
inboxMixin,
|
||||
uiSettingsMixin,
|
||||
alertMixin,
|
||||
messageFormatterMixin,
|
||||
],
|
||||
props: {
|
||||
selectedTweet: {
|
||||
type: [Object, String],
|
||||
@@ -162,6 +177,7 @@ export default {
|
||||
computed: {
|
||||
...mapGetters({
|
||||
currentChat: 'getSelectedChat',
|
||||
messageSignature: 'getMessageSignature',
|
||||
currentUser: 'getCurrentUser',
|
||||
}),
|
||||
|
||||
@@ -333,6 +349,13 @@ export default {
|
||||
enableMultipleFileUpload() {
|
||||
return this.isAnEmailChannel || this.isAWebWidgetInbox || this.isAPIInbox;
|
||||
},
|
||||
showMessageSignature() {
|
||||
return !this.isPrivate && this.isAnEmailChannel && this.sendWithSignature;
|
||||
},
|
||||
sendWithSignature() {
|
||||
const { send_with_signature: isEnabled } = this.uiSettings;
|
||||
return isEnabled;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentChat(conversation) {
|
||||
@@ -444,7 +467,10 @@ export default {
|
||||
return;
|
||||
}
|
||||
if (!this.showMentions) {
|
||||
const newMessage = this.message;
|
||||
let newMessage = this.message;
|
||||
if (this.sendWithSignature && this.messageSignature) {
|
||||
newMessage += '\n\n' + this.messageSignature;
|
||||
}
|
||||
const messagePayload = this.getMessagePayload(newMessage);
|
||||
this.clearMessage();
|
||||
try {
|
||||
@@ -623,6 +649,25 @@ export default {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.message-signature-wrap {
|
||||
margin: 0 var(--space-normal);
|
||||
padding: var(--space-small);
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
border: 1px dashed var(--s-100);
|
||||
border-radius: var(--border-radius-small);
|
||||
|
||||
&:hover {
|
||||
background: var(--s-25);
|
||||
}
|
||||
}
|
||||
|
||||
.message-signature {
|
||||
width: fit-content;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.attachment-preview-box {
|
||||
padding: 0 var(--space-normal);
|
||||
background: transparent;
|
||||
|
||||
@@ -57,6 +57,9 @@
|
||||
}
|
||||
},
|
||||
"FOOTER": {
|
||||
"MESSAGE_SIGN_TOOLTIP": "Message signature",
|
||||
"ENABLE_SIGN_TOOLTIP": "Enable signature",
|
||||
"DISABLE_SIGN_TOOLTIP": "Disable signature",
|
||||
"MSG_INPUT": "Shift + enter for new line. Start with '/' to select a Canned Response.",
|
||||
"PRIVATE_MSG_INPUT": "Shift + enter for new line. This will be visible only to Agents"
|
||||
},
|
||||
|
||||
@@ -19,6 +19,18 @@
|
||||
"TITLE": "Profile",
|
||||
"NOTE": "Your email address is your identity and is used to log in."
|
||||
},
|
||||
"MESSAGE_SIGNATURE_SECTION": {
|
||||
"TITLE": "Personal message signature",
|
||||
"NOTE": "Create a personal message signature that would be added to all the messages you send from the platform. Use the rich content editor to create a highly personalised signature.",
|
||||
"BTN_TEXT": "Save message signature",
|
||||
"API_ERROR":"Couldn't save signature! Try again",
|
||||
"API_SUCCESS": "Signature saved successfully"
|
||||
},
|
||||
"MESSAGE_SIGNATURE": {
|
||||
"LABEL": "Message Signature",
|
||||
"ERROR": "Message Signature cannot be empty",
|
||||
"PLACEHOLDER": "Insert your personal message signature here."
|
||||
},
|
||||
"PASSWORD_SECTION": {
|
||||
"TITLE": "Password",
|
||||
"NOTE": "Updating your password would reset your logins in multiple devices.",
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<message-signature />
|
||||
<change-password />
|
||||
<notification-settings />
|
||||
<div class="profile--settings--row row">
|
||||
@@ -95,13 +96,15 @@ import { mapGetters } from 'vuex';
|
||||
import { clearCookiesOnLogout } from '../../../../store/utils/api';
|
||||
import NotificationSettings from './NotificationSettings';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import ChangePassword from './ChangePassword.vue';
|
||||
import ChangePassword from './ChangePassword';
|
||||
import MessageSignature from './MessageSignature';
|
||||
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NotificationSettings,
|
||||
ChangePassword,
|
||||
MessageSignature,
|
||||
},
|
||||
mixins: [alertMixin, globalConfigMixin],
|
||||
data() {
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<form @submit.prevent="updateSignature()">
|
||||
<div class="profile--settings--row row">
|
||||
<div class="columns small-3">
|
||||
<h4 class="block-title">
|
||||
{{ $t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.TITLE') }}
|
||||
</h4>
|
||||
<p>{{ $t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.NOTE') }}</p>
|
||||
</div>
|
||||
<div class="columns small-9 medium-5">
|
||||
<div>
|
||||
<label for="message-signature-input">{{
|
||||
$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE.LABEL')
|
||||
}}</label>
|
||||
<woot-message-editor
|
||||
id="message-signature-input"
|
||||
v-model="messageSignature"
|
||||
class="message-editor"
|
||||
:is-format-mode="true"
|
||||
:placeholder="
|
||||
$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE.PLACEHOLDER')
|
||||
"
|
||||
@blur="$v.messageSignature.$touch"
|
||||
/>
|
||||
</div>
|
||||
<woot-button
|
||||
:is-loading="isUpdating"
|
||||
type="submit"
|
||||
:is-disabled="$v.messageSignature.$invalid"
|
||||
>
|
||||
{{ $t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.BTN_TEXT') }}
|
||||
</woot-button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { required } from 'vuelidate/lib/validators';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
WootMessageEditor,
|
||||
},
|
||||
mixins: [alertMixin],
|
||||
data() {
|
||||
return {
|
||||
messageSignature: '',
|
||||
enableMessageSignature: false,
|
||||
isUpdating: false,
|
||||
errorMessage: '',
|
||||
};
|
||||
},
|
||||
validations: {
|
||||
messageSignature: {
|
||||
required,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
currentUser: 'getCurrentUser',
|
||||
currentUserId: 'getCurrentUserID',
|
||||
}),
|
||||
},
|
||||
watch: {
|
||||
currentUser() {
|
||||
this.initValues();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initValues();
|
||||
},
|
||||
methods: {
|
||||
initValues() {
|
||||
const { message_signature: messageSignature } = this.currentUser;
|
||||
this.messageSignature = messageSignature;
|
||||
},
|
||||
async updateSignature() {
|
||||
this.$v.$touch();
|
||||
if (this.$v.$invalid) {
|
||||
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.ERROR'));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.$store.dispatch('updateProfile', {
|
||||
message_signature: this.messageSignature,
|
||||
message_signature_enabled: true,
|
||||
});
|
||||
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.showAlert(this.errorMessage);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.profile--settings--row {
|
||||
.ProseMirror-woot-style {
|
||||
height: 8rem;
|
||||
}
|
||||
|
||||
.editor-root {
|
||||
background: var(--white);
|
||||
margin-bottom: var(--space-normal);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -65,6 +65,12 @@ export const getters = {
|
||||
getCurrentUser(_state) {
|
||||
return _state.currentUser;
|
||||
},
|
||||
|
||||
getMessageSignature(_state) {
|
||||
const { message_signature: messageSignature } = _state.currentUser;
|
||||
|
||||
return messageSignature || '';
|
||||
},
|
||||
};
|
||||
|
||||
// actions
|
||||
|
||||
@@ -37,4 +37,20 @@ describe('#getters', () => {
|
||||
})
|
||||
).toEqual({ is_contact_sidebar_open: true });
|
||||
});
|
||||
describe('#getMessageSignature', () => {
|
||||
it('Return signature when signature is present', () => {
|
||||
expect(
|
||||
getters.getMessageSignature({
|
||||
currentUser: { message_signature: 'Thanks' },
|
||||
})
|
||||
).toEqual('Thanks');
|
||||
});
|
||||
it('Return empty string when signature is not present', () => {
|
||||
expect(
|
||||
getters.getMessageSignature({
|
||||
currentUser: {},
|
||||
})
|
||||
).toEqual('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user