feat: Ability to specify a subject line for outbound emails (#3168)

This commit is contained in:
Aswin Dev P.S 2021-10-27 13:09:29 +05:30 committed by GitHub
parent 2f2d2b4f73
commit 46867e89cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 14 deletions

View file

@ -155,6 +155,11 @@
"LABEL": "Inbox", "LABEL": "Inbox",
"ERROR": "Select an inbox" "ERROR": "Select an inbox"
}, },
"SUBJECT": {
"LABEL": "Subject",
"PLACEHOLDER": "Subject",
"ERROR": "Subject can't be empty"
},
"MESSAGE": { "MESSAGE": {
"LABEL": "Message", "LABEL": "Message",
"PLACEHOLDER": "Write your message here", "PLACEHOLDER": "Write your message here",

View file

@ -41,6 +41,22 @@
</label> </label>
</div> </div>
</div> </div>
<div class="row" v-if="isAnEmailInbox">
<div class="columns">
<label :class="{ error: $v.message.$error }">
{{ $t('NEW_CONVERSATION.FORM.SUBJECT.LABEL') }}
<input
v-model="subject"
type="text"
:placeholder="$t('NEW_CONVERSATION.FORM.SUBJECT.PLACEHOLDER')"
@input="$v.message.$touch"
/>
<span v-if="$v.message.$error" class="message">
{{ $t('NEW_CONVERSATION.FORM.SUBJECT.ERROR') }}
</span>
</label>
</div>
</div>
<div class="row"> <div class="row">
<div class="columns"> <div class="columns">
<label :class="{ error: $v.message.$error }"> <label :class="{ error: $v.message.$error }">
@ -75,6 +91,7 @@ import { mapGetters } from 'vuex';
import Thumbnail from 'dashboard/components/widgets/Thumbnail'; import Thumbnail from 'dashboard/components/widgets/Thumbnail';
import alertMixin from 'shared/mixins/alertMixin'; import alertMixin from 'shared/mixins/alertMixin';
import { INBOX_TYPES } from 'shared/mixins/inboxMixin';
import { ExceptionWithMessage } from 'shared/helpers/CustomErrors'; import { ExceptionWithMessage } from 'shared/helpers/CustomErrors';
import { required } from 'vuelidate/lib/validators'; import { required } from 'vuelidate/lib/validators';
@ -96,11 +113,15 @@ export default {
data() { data() {
return { return {
name: '', name: '',
subject: '',
message: '', message: '',
selectedInbox: '', selectedInbox: '',
}; };
}, },
validations: { validations: {
subject: {
required,
},
message: { message: {
required, required,
}, },
@ -119,6 +140,7 @@ export default {
sourceId: this.targetInbox.source_id, sourceId: this.targetInbox.source_id,
contactId: this.contact.id, contactId: this.contact.id,
message: { content: this.message }, message: { content: this.message },
mailSubject: this.subject,
}; };
}, },
targetInbox: { targetInbox: {
@ -138,6 +160,12 @@ export default {
inboxes() { inboxes() {
return this.contact.contactableInboxes || []; return this.contact.contactableInboxes || [];
}, },
isAnEmailInbox() {
return (
this.selectedInbox &&
this.selectedInbox.inbox.channel_type === INBOX_TYPES.EMAIL
);
},
}, },
methods: { methods: {
onCancel() { onCancel() {
@ -154,7 +182,6 @@ export default {
} }
try { try {
const payload = this.getNewConversation; const payload = this.getNewConversation;
await this.onSubmit(payload); await this.onSubmit(payload);
this.onSuccess(); this.onSuccess();
this.showAlert(this.$t('NEW_CONVERSATION.FORM.SUCCESS_MESSAGE')); this.showAlert(this.$t('NEW_CONVERSATION.FORM.SUCCESS_MESSAGE'));

View file

@ -24,12 +24,15 @@ export const actions = {
commit(types.default.SET_CONTACT_CONVERSATIONS_UI_FLAG, { commit(types.default.SET_CONTACT_CONVERSATIONS_UI_FLAG, {
isCreating: true, isCreating: true,
}); });
const { inboxId, message, contactId, sourceId } = params; const { inboxId, message, contactId, sourceId, mailSubject } = params;
try { try {
const { data } = await ConversationApi.create({ const { data } = await ConversationApi.create({
inbox_id: inboxId, inbox_id: inboxId,
contact_id: contactId, contact_id: contactId,
source_id: sourceId, source_id: sourceId,
additional_attributes: {
mail_subject: mailSubject,
},
message, message,
}); });
commit(types.default.ADD_CONTACT_CONVERSATION, { commit(types.default.ADD_CONTACT_CONVERSATION, {

View file

@ -44,7 +44,13 @@ describe('#actions', () => {
axios.post.mockResolvedValue({ data: conversationList[0] }); axios.post.mockResolvedValue({ data: conversationList[0] });
await actions.create( await actions.create(
{ commit }, { commit },
{ inboxId: 1, message: { content: 'hi' }, contactId: 4, sourceId: 5 } {
inboxId: 1,
message: { content: 'hi' },
contactId: 4,
sourceId: 5,
mailSubject: 'Mail Subject',
}
); );
expect(commit.mock.calls).toEqual([ expect(commit.mock.calls).toEqual([
[types.default.SET_CONTACT_CONVERSATIONS_UI_FLAG, { isCreating: true }], [types.default.SET_CONTACT_CONVERSATIONS_UI_FLAG, { isCreating: true }],
@ -65,7 +71,13 @@ describe('#actions', () => {
await expect( await expect(
actions.create( actions.create(
{ commit }, { commit },
{ inboxId: 1, message: { content: 'hi' }, contactId: 4, sourceId: 5 } {
inboxId: 1,
message: { content: 'hi' },
contactId: 4,
sourceId: 5,
mailSubject: 'Mail Subject',
}
) )
).rejects.toThrow(Error); ).rejects.toThrow(Error);
expect(commit.mock.calls).toEqual([ expect(commit.mock.calls).toEqual([

View file

@ -12,7 +12,6 @@ class ConversationReplyMailer < ApplicationMailer
new_messages = @conversation.messages.chat.where('id >= ?', last_queued_id) new_messages = @conversation.messages.chat.where('id >= ?', last_queued_id)
@messages = recap_messages + new_messages @messages = recap_messages + new_messages
@messages = @messages.select(&:email_reply_summarizable?) @messages = @messages.select(&:email_reply_summarizable?)
mail({ mail({
to: @contact&.email, to: @contact&.email,
from: from_email_with_name, from: from_email_with_name,
@ -110,14 +109,15 @@ class ConversationReplyMailer < ApplicationMailer
end end
def mail_subject def mail_subject
return "Re: #{incoming_mail_subject}" if incoming_mail_subject subject = @conversation.additional_attributes['mail_subject']
return "[##{@conversation.display_id}] #{I18n.t('conversations.reply.email_subject')}" if subject.nil?
subject_line = I18n.t('conversations.reply.email_subject') chat_count = @conversation.messages.chat.count
"[##{@conversation.display_id}] #{subject_line}" if chat_count > 1
end "Re: #{subject}"
else
def incoming_mail_subject subject
@incoming_mail_subject ||= @conversation.additional_attributes['mail_subject'] end
end end
def reply_email def reply_email

View file

@ -25,6 +25,15 @@ RSpec.describe ConversationReplyMailer, type: :mailer do
bcc_emails: 'agent_bcc1@example.com' bcc_emails: 'agent_bcc1@example.com'
}) })
end end
let(:new_message) do
create(:message,
account: account,
conversation: conversation,
content_attributes: {
cc_emails: 'agent_cc2@example.com',
bcc_emails: 'agent_bcc2@example.com'
})
end
let(:cc_message) do let(:cc_message) do
create(:message, create(:message,
account: account, account: account,
@ -39,10 +48,17 @@ RSpec.describe ConversationReplyMailer, type: :mailer do
let(:mail) { described_class.reply_with_summary(message.conversation, message.id).deliver_now } let(:mail) { described_class.reply_with_summary(message.conversation, message.id).deliver_now }
let(:cc_mail) { described_class.reply_with_summary(cc_message.conversation, message.id).deliver_now } let(:cc_mail) { described_class.reply_with_summary(cc_message.conversation, message.id).deliver_now }
it 'renders the subject' do it 'renders the default subject' do
expect(mail.subject).to eq("[##{message.conversation.display_id}] New messages on this conversation") expect(mail.subject).to eq("[##{message.conversation.display_id}] New messages on this conversation")
end end
it 'renders the subject in conversation as reply' do
conversation.additional_attributes = { 'mail_subject': 'Mail Subject' }
conversation.save
new_message.save
expect(mail.subject).to eq('Re: Mail Subject')
end
it 'not have private notes' do it 'not have private notes' do
# make the message private # make the message private
private_message.private = true private_message.private = true
@ -91,10 +107,16 @@ RSpec.describe ConversationReplyMailer, type: :mailer do
message_2.save message_2.save
end end
it 'renders the subject' do it 'renders the default subject' do
expect(mail.subject).to eq("[##{message_2.conversation.display_id}] New messages on this conversation") expect(mail.subject).to eq("[##{message_2.conversation.display_id}] New messages on this conversation")
end end
it 'renders the subject in conversation' do
conversation.additional_attributes = { 'mail_subject': 'Mail Subject' }
conversation.save
expect(mail.subject).to eq('Mail Subject')
end
it 'not have private notes' do it 'not have private notes' do
# make the message private # make the message private
private_message.private = true private_message.private = true