feat: Adds email signature form to profile settings (#3906)

This commit is contained in:
Nithin David Thomas
2022-02-15 10:38:24 +05:30
committed by GitHub
parent d5c9193d1a
commit 351a3dc372
10 changed files with 254 additions and 7 deletions

View File

@@ -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 || '');

View File

@@ -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>

View File

@@ -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;

View File

@@ -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"
},

View File

@@ -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.",

View File

@@ -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() {

View File

@@ -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>

View File

@@ -65,6 +65,12 @@ export const getters = {
getCurrentUser(_state) {
return _state.currentUser;
},
getMessageSignature(_state) {
const { message_signature: messageSignature } = _state.currentUser;
return messageSignature || '';
},
};
// actions

View File

@@ -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('');
});
});
});