mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-30 02:32:29 +00:00
Merge branch 'develop' into feat/openai-integration
This commit is contained in:
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
.multiselect--active {
|
.multiselect--active {
|
||||||
>.multiselect__tags {
|
>.multiselect__tags {
|
||||||
border-color: $color-woot;
|
border-color: var(--w-500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,16 +75,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.multiselect__option--selected {
|
&.multiselect__option--selected {
|
||||||
background: var(--w-400);
|
background: var(--w-75);
|
||||||
color: var(--white);
|
|
||||||
|
|
||||||
&.multiselect__option--highlight:hover {
|
&.multiselect__option--highlight:hover {
|
||||||
background: var(--w-600);
|
background: var(--w-75);
|
||||||
color: var(--white);
|
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--white);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after:hover {
|
&::after:hover {
|
||||||
@@ -196,6 +193,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
max-height: 3.8rem;
|
||||||
padding: var(--space-smaller) var(--space-micro);
|
padding: var(--space-smaller) var(--space-micro);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import InboxDropdownItem from './InboxDropdownItem';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/DropDowns/InboxDropdownItem',
|
||||||
|
component: InboxDropdownItem,
|
||||||
|
argTypes: {
|
||||||
|
name: {
|
||||||
|
defaultValue: 'My new inbox',
|
||||||
|
control: {
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
inboxIdentifier: {
|
||||||
|
defaultValue: 'nithin@mail.com',
|
||||||
|
control: {
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
channelType: {
|
||||||
|
defaultValue: 'email',
|
||||||
|
control: {
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Template = (args, { argTypes }) => ({
|
||||||
|
props: Object.keys(argTypes),
|
||||||
|
components: { InboxDropdownItem },
|
||||||
|
template: '<inbox-dropdown-item v-bind="$props" ></inbox-dropdown-item>',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Banner = Template.bind({});
|
||||||
|
Banner.args = {};
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
<template>
|
||||||
|
<div class="option-item--inbox">
|
||||||
|
<span class="badge--icon">
|
||||||
|
<fluent-icon :icon="computedInboxIcon" size="14" />
|
||||||
|
</span>
|
||||||
|
<div class="option__user-data">
|
||||||
|
<h5 class="option__title">
|
||||||
|
{{ name }}
|
||||||
|
</h5>
|
||||||
|
<p class="option__body text-truncate" :title="inboxIdentifier">
|
||||||
|
{{ inboxIdentifier || computedInboxType }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {
|
||||||
|
getInboxClassByType,
|
||||||
|
getReadableInboxByType,
|
||||||
|
} from 'dashboard/helper/inbox';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {},
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
inboxIdentifier: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
channelType: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
computedInboxIcon() {
|
||||||
|
if (!this.channelType) return 'chat';
|
||||||
|
const classByType = getInboxClassByType(
|
||||||
|
this.channelType,
|
||||||
|
this.inboxIdentifier
|
||||||
|
);
|
||||||
|
return classByType;
|
||||||
|
},
|
||||||
|
computedInboxType() {
|
||||||
|
if (!this.channelType) return 'chat';
|
||||||
|
const classByType = getReadableInboxByType(
|
||||||
|
this.channelType,
|
||||||
|
this.inboxIdentifier
|
||||||
|
);
|
||||||
|
return classByType;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.option-item--inbox {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 3.8rem;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0 var(--space-smaller);
|
||||||
|
}
|
||||||
|
.badge--icon {
|
||||||
|
display: inline-flex;
|
||||||
|
border-radius: var(--border-radius-small);
|
||||||
|
margin-right: var(--space-smaller);
|
||||||
|
background: var(--s-25);
|
||||||
|
padding: var(--space-micro);
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
justify-content: center;
|
||||||
|
width: var(--space-medium);
|
||||||
|
height: var(--space-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option__user-data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
margin-left: var(--space-smaller);
|
||||||
|
margin-right: var(--space-smaller);
|
||||||
|
}
|
||||||
|
.option__body {
|
||||||
|
display: inline-block;
|
||||||
|
color: var(--s-600);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
line-height: 1.3;
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.option__title {
|
||||||
|
line-height: 1.1;
|
||||||
|
font-size: var(--font-size-mini);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,55 @@
|
|||||||
import { INBOX_TYPES } from 'shared/mixins/inboxMixin';
|
import { INBOX_TYPES } from 'shared/mixins/inboxMixin';
|
||||||
|
|
||||||
|
export const getInboxSource = (type, phoneNumber, inbox) => {
|
||||||
|
switch (type) {
|
||||||
|
case INBOX_TYPES.WEB:
|
||||||
|
return inbox.website_url || '';
|
||||||
|
|
||||||
|
case INBOX_TYPES.TWILIO:
|
||||||
|
case INBOX_TYPES.WHATSAPP:
|
||||||
|
return phoneNumber || '';
|
||||||
|
|
||||||
|
case INBOX_TYPES.EMAIL:
|
||||||
|
return inbox.email || '';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getReadableInboxByType = (type, phoneNumber) => {
|
||||||
|
switch (type) {
|
||||||
|
case INBOX_TYPES.WEB:
|
||||||
|
return 'livechat';
|
||||||
|
|
||||||
|
case INBOX_TYPES.FB:
|
||||||
|
return 'facebook';
|
||||||
|
|
||||||
|
case INBOX_TYPES.TWITTER:
|
||||||
|
return 'twitter';
|
||||||
|
|
||||||
|
case INBOX_TYPES.TWILIO:
|
||||||
|
return phoneNumber?.startsWith('whatsapp') ? 'whatsapp' : 'sms';
|
||||||
|
|
||||||
|
case INBOX_TYPES.WHATSAPP:
|
||||||
|
return 'whatsapp';
|
||||||
|
|
||||||
|
case INBOX_TYPES.API:
|
||||||
|
return 'api';
|
||||||
|
|
||||||
|
case INBOX_TYPES.EMAIL:
|
||||||
|
return 'email';
|
||||||
|
|
||||||
|
case INBOX_TYPES.TELEGRAM:
|
||||||
|
return 'telegram';
|
||||||
|
|
||||||
|
case INBOX_TYPES.LINE:
|
||||||
|
return 'line';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 'chat';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const getInboxClassByType = (type, phoneNumber) => {
|
export const getInboxClassByType = (type, phoneNumber) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case INBOX_TYPES.WEB:
|
case INBOX_TYPES.WEB:
|
||||||
|
|||||||
@@ -182,7 +182,8 @@
|
|||||||
"LABEL": "To"
|
"LABEL": "To"
|
||||||
},
|
},
|
||||||
"INBOX": {
|
"INBOX": {
|
||||||
"LABEL": "Inbox",
|
"LABEL": "Via Inbox",
|
||||||
|
"PLACEHOLDER": "Choose source inbox",
|
||||||
"ERROR": "Select an inbox"
|
"ERROR": "Select an inbox"
|
||||||
},
|
},
|
||||||
"SUBJECT": {
|
"SUBJECT": {
|
||||||
|
|||||||
@@ -8,17 +8,43 @@
|
|||||||
<div v-else>
|
<div v-else>
|
||||||
<div class="row gutter-small">
|
<div class="row gutter-small">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<label :class="{ error: $v.targetInbox.$error }">
|
<label>
|
||||||
{{ $t('NEW_CONVERSATION.FORM.INBOX.LABEL') }}
|
{{ $t('NEW_CONVERSATION.FORM.INBOX.LABEL') }}
|
||||||
<select v-model="targetInbox">
|
</label>
|
||||||
<option
|
<div class="multiselect-wrap--small">
|
||||||
v-for="contactableInbox in inboxes"
|
<multiselect
|
||||||
:key="contactableInbox.inbox.id"
|
v-model="targetInbox"
|
||||||
:value="contactableInbox"
|
track-by="id"
|
||||||
>
|
label="name"
|
||||||
{{ contactableInbox.inbox.name }}
|
:placeholder="$t('FORMS.MULTISELECT.SELECT')"
|
||||||
</option>
|
selected-label=""
|
||||||
</select>
|
select-label=""
|
||||||
|
deselect-label=""
|
||||||
|
:max-height="160"
|
||||||
|
:close-on-select="true"
|
||||||
|
:options="[...inboxes]"
|
||||||
|
>
|
||||||
|
<template slot="singleLabel" slot-scope="{ option }">
|
||||||
|
<inbox-dropdown-item
|
||||||
|
v-if="option.name"
|
||||||
|
:name="option.name"
|
||||||
|
:inbox-identifier="computedInboxSource(option)"
|
||||||
|
:channel-type="option.channel_type"
|
||||||
|
/>
|
||||||
|
<span v-else>
|
||||||
|
{{ $t('NEW_CONVERSATION.FORM.INBOX.PLACEHOLDER') }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template slot="option" slot-scope="{ option }">
|
||||||
|
<inbox-dropdown-item
|
||||||
|
:name="option.name"
|
||||||
|
:inbox-identifier="computedInboxSource(option)"
|
||||||
|
:channel-type="option.channel_type"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</multiselect>
|
||||||
|
</div>
|
||||||
|
<label :class="{ error: $v.targetInbox.$error }">
|
||||||
<span v-if="$v.targetInbox.$error" class="message">
|
<span v-if="$v.targetInbox.$error" class="message">
|
||||||
{{ $t('NEW_CONVERSATION.FORM.INBOX.ERROR') }}
|
{{ $t('NEW_CONVERSATION.FORM.INBOX.ERROR') }}
|
||||||
</span>
|
</span>
|
||||||
@@ -129,10 +155,12 @@ import Thumbnail from 'dashboard/components/widgets/Thumbnail';
|
|||||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
|
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
|
||||||
import ReplyEmailHead from 'dashboard/components/widgets/conversation/ReplyEmailHead';
|
import ReplyEmailHead from 'dashboard/components/widgets/conversation/ReplyEmailHead';
|
||||||
import CannedResponse from 'dashboard/components/widgets/conversation/CannedResponse.vue';
|
import CannedResponse from 'dashboard/components/widgets/conversation/CannedResponse.vue';
|
||||||
|
import InboxDropdownItem from 'dashboard/components/widgets/InboxDropdownItem';
|
||||||
import WhatsappTemplates from './WhatsappTemplates.vue';
|
import WhatsappTemplates from './WhatsappTemplates.vue';
|
||||||
import alertMixin from 'shared/mixins/alertMixin';
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
import { INBOX_TYPES } from 'shared/mixins/inboxMixin';
|
import { INBOX_TYPES } from 'shared/mixins/inboxMixin';
|
||||||
import { ExceptionWithMessage } from 'shared/helpers/CustomErrors';
|
import { ExceptionWithMessage } from 'shared/helpers/CustomErrors';
|
||||||
|
import { getInboxSource } from 'dashboard/helper/inbox';
|
||||||
import { required, requiredIf } from 'vuelidate/lib/validators';
|
import { required, requiredIf } from 'vuelidate/lib/validators';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -142,6 +170,7 @@ export default {
|
|||||||
ReplyEmailHead,
|
ReplyEmailHead,
|
||||||
CannedResponse,
|
CannedResponse,
|
||||||
WhatsappTemplates,
|
WhatsappTemplates,
|
||||||
|
InboxDropdownItem,
|
||||||
},
|
},
|
||||||
mixins: [alertMixin],
|
mixins: [alertMixin],
|
||||||
props: {
|
props: {
|
||||||
@@ -161,9 +190,9 @@ export default {
|
|||||||
message: '',
|
message: '',
|
||||||
showCannedResponseMenu: false,
|
showCannedResponseMenu: false,
|
||||||
cannedResponseSearchKey: '',
|
cannedResponseSearchKey: '',
|
||||||
selectedInbox: '',
|
|
||||||
bccEmails: '',
|
bccEmails: '',
|
||||||
ccEmails: '',
|
ccEmails: '',
|
||||||
|
targetInbox: {},
|
||||||
whatsappTemplateSelected: false,
|
whatsappTemplateSelected: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -186,8 +215,8 @@ export default {
|
|||||||
}),
|
}),
|
||||||
emailMessagePayload() {
|
emailMessagePayload() {
|
||||||
const payload = {
|
const payload = {
|
||||||
inboxId: this.targetInbox.inbox.id,
|
inboxId: this.targetInbox.id,
|
||||||
sourceId: this.targetInbox.source_id,
|
sourceId: this.targetInbox.sourceId,
|
||||||
contactId: this.contact.id,
|
contactId: this.contact.id,
|
||||||
message: { content: this.message },
|
message: { content: this.message },
|
||||||
mailSubject: this.subject,
|
mailSubject: this.subject,
|
||||||
@@ -202,12 +231,17 @@ export default {
|
|||||||
}
|
}
|
||||||
return payload;
|
return payload;
|
||||||
},
|
},
|
||||||
targetInbox: {
|
selectedInbox: {
|
||||||
get() {
|
get() {
|
||||||
return this.selectedInbox || {};
|
const inboxList = this.contact.contactableInboxes || [];
|
||||||
|
return (
|
||||||
|
inboxList.find(inbox => inbox.inbox.id === this.targetInbox.id) || {
|
||||||
|
inbox: {},
|
||||||
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
this.selectedInbox = value;
|
this.targetInbox = value.inbox;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
showNoInboxAlert() {
|
showNoInboxAlert() {
|
||||||
@@ -217,7 +251,11 @@ export default {
|
|||||||
return this.inboxes.length === 0 && !this.uiFlags.isFetchingInboxes;
|
return this.inboxes.length === 0 && !this.uiFlags.isFetchingInboxes;
|
||||||
},
|
},
|
||||||
inboxes() {
|
inboxes() {
|
||||||
return this.contact.contactableInboxes || [];
|
const inboxList = this.contact.contactableInboxes || [];
|
||||||
|
return inboxList.map(inbox => ({
|
||||||
|
...inbox.inbox,
|
||||||
|
sourceId: inbox.source_id,
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
isAnEmailInbox() {
|
isAnEmailInbox() {
|
||||||
return (
|
return (
|
||||||
@@ -267,8 +305,8 @@ export default {
|
|||||||
},
|
},
|
||||||
prepareWhatsAppMessagePayload({ message: content, templateParams }) {
|
prepareWhatsAppMessagePayload({ message: content, templateParams }) {
|
||||||
const payload = {
|
const payload = {
|
||||||
inboxId: this.targetInbox.inbox.id,
|
inboxId: this.targetInbox.id,
|
||||||
sourceId: this.targetInbox.source_id,
|
sourceId: this.targetInbox.sourceId,
|
||||||
contactId: this.contact.id,
|
contactId: this.contact.id,
|
||||||
message: { content, template_params: templateParams },
|
message: { content, template_params: templateParams },
|
||||||
assigneeId: this.currentUser.id,
|
assigneeId: this.currentUser.id,
|
||||||
@@ -311,6 +349,18 @@ export default {
|
|||||||
const payload = this.prepareWhatsAppMessagePayload(messagePayload);
|
const payload = this.prepareWhatsAppMessagePayload(messagePayload);
|
||||||
await this.createConversation(payload);
|
await this.createConversation(payload);
|
||||||
},
|
},
|
||||||
|
inboxReadableIdentifier(inbox) {
|
||||||
|
return `${inbox.name} (${inbox.channel_type})`;
|
||||||
|
},
|
||||||
|
computedInboxSource(inbox) {
|
||||||
|
if (!inbox.channel_type) return '';
|
||||||
|
const classByType = getInboxSource(
|
||||||
|
inbox.channel_type,
|
||||||
|
inbox.phone_number,
|
||||||
|
inbox
|
||||||
|
);
|
||||||
|
return classByType;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -352,11 +402,23 @@ export default {
|
|||||||
gap: var(--space-small);
|
gap: var(--space-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .mention--box {
|
::v-deep {
|
||||||
left: 0;
|
.mention--box {
|
||||||
margin: auto;
|
left: 0;
|
||||||
right: 0;
|
margin: auto;
|
||||||
top: unset;
|
right: 0;
|
||||||
height: fit-content;
|
top: unset;
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Remove when have standardized a component out of multiselect */
|
||||||
|
.multiselect .multiselect__content .multiselect__option span {
|
||||||
|
display: inline-flex;
|
||||||
|
width: var(--space-medium);
|
||||||
|
color: var(--s-600);
|
||||||
|
}
|
||||||
|
.multiselect .multiselect__content .multiselect__option {
|
||||||
|
padding: var(--space-micro) var(--space-smaller);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ class ActionService
|
|||||||
end
|
end
|
||||||
|
|
||||||
def send_email_transcript(emails)
|
def send_email_transcript(emails)
|
||||||
|
emails = emails[0].gsub(/\s+/, '').split(',')
|
||||||
|
|
||||||
emails.each do |email|
|
emails.each do |email|
|
||||||
ConversationReplyMailer.with(account: @conversation.account).conversation_transcript(@conversation, email)&.deliver_later
|
ConversationReplyMailer.with(account: @conversation.account).conversation_transcript(@conversation, email)&.deliver_later
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ def fetch_git_sha
|
|||||||
sha.strip
|
sha.strip
|
||||||
elsif File.exist?('.git_sha')
|
elsif File.exist?('.git_sha')
|
||||||
File.read('.git_sha').strip
|
File.read('.git_sha').strip
|
||||||
|
# This is for Heroku. Ensure heroku labs:enable runtime-dyno-metadata is turned on.
|
||||||
|
elsif ENV.fetch('HEROKU_SLUG_COMMIT', nil).present?
|
||||||
|
ENV.fetch('HEROKU_SLUG_COMMIT', nil)
|
||||||
else
|
else
|
||||||
'unknown'
|
'unknown'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -121,18 +121,6 @@ describe AutomationRuleListener do
|
|||||||
expect(conversation.assignee).to eq(user_1)
|
expect(conversation.assignee).to eq(user_1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'triggers automation rule send email transcript to the mentioned email' do
|
|
||||||
mailer = double
|
|
||||||
|
|
||||||
expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation)
|
|
||||||
|
|
||||||
listener.conversation_updated(event)
|
|
||||||
|
|
||||||
conversation.reload
|
|
||||||
|
|
||||||
allow(mailer).to receive(:conversation_transcript)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'triggers automation rule send message to the contacts' do
|
it 'triggers automation rule send message to the contacts' do
|
||||||
expect(conversation.messages).to be_empty
|
expect(conversation.messages).to be_empty
|
||||||
|
|
||||||
@@ -253,15 +241,6 @@ describe AutomationRuleListener do
|
|||||||
expect(conversation.assignee).to eq(user_1)
|
expect(conversation.assignee).to eq(user_1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'triggers automation rule send email transcript to the mentioned email' do
|
|
||||||
mailer = double
|
|
||||||
expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation)
|
|
||||||
listener.conversation_updated(event)
|
|
||||||
conversation.reload
|
|
||||||
|
|
||||||
allow(mailer).to receive(:conversation_transcript)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'triggers automation rule send email to the team' do
|
it 'triggers automation rule send email to the team' do
|
||||||
message_delivery = instance_double(ActionMailer::MessageDelivery)
|
message_delivery = instance_double(ActionMailer::MessageDelivery)
|
||||||
|
|
||||||
@@ -457,15 +436,6 @@ describe AutomationRuleListener do
|
|||||||
expect(conversation.assignee).to eq(user_1)
|
expect(conversation.assignee).to eq(user_1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'triggers automation rule send email transcript to the mentioned email' do
|
|
||||||
mailer = double
|
|
||||||
expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation)
|
|
||||||
listener.conversation_opened(event)
|
|
||||||
conversation.reload
|
|
||||||
|
|
||||||
allow(mailer).to receive(:conversation_transcript)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'triggers automation rule send email to the team' do
|
it 'triggers automation rule send email to the team' do
|
||||||
message_delivery = instance_double(ActionMailer::MessageDelivery)
|
message_delivery = instance_double(ActionMailer::MessageDelivery)
|
||||||
|
|
||||||
@@ -577,15 +547,6 @@ describe AutomationRuleListener do
|
|||||||
|
|
||||||
expect(conversation.assignee).to eq(user_1)
|
expect(conversation.assignee).to eq(user_1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'triggers automation rule send email transcript to the mentioned email' do
|
|
||||||
mailer = double
|
|
||||||
expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation)
|
|
||||||
listener.message_created(event)
|
|
||||||
conversation.reload
|
|
||||||
|
|
||||||
allow(mailer).to receive(:conversation_transcript)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -616,17 +577,6 @@ describe AutomationRuleListener do
|
|||||||
end
|
end
|
||||||
|
|
||||||
context 'when rule matches' do
|
context 'when rule matches' do
|
||||||
it 'triggers automation rule send email transcript to the mentioned email' do
|
|
||||||
mailer = double
|
|
||||||
allow(ConversationReplyMailer).to receive(:with).and_return(mailer)
|
|
||||||
allow(mailer).to receive(:conversation_transcript)
|
|
||||||
|
|
||||||
listener.message_created(event)
|
|
||||||
conversation.reload
|
|
||||||
|
|
||||||
expect(mailer).to have_received(:conversation_transcript).with(conversation, 'new_agent@example.com')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'triggers automation rule send message to the contacts' do
|
it 'triggers automation rule send message to the contacts' do
|
||||||
expect(conversation.messages.count).to eq(1)
|
expect(conversation.messages.count).to eq(1)
|
||||||
listener.message_created(event)
|
listener.message_created(event)
|
||||||
@@ -710,18 +660,6 @@ describe AutomationRuleListener do
|
|||||||
end
|
end
|
||||||
|
|
||||||
context 'when rule matches' do
|
context 'when rule matches' do
|
||||||
it 'triggers automation rule send email transcript to the mentioned email' do
|
|
||||||
mailer = double
|
|
||||||
allow(ConversationReplyMailer).to receive(:with).and_return(mailer)
|
|
||||||
allow(mailer).to receive(:conversation_transcript)
|
|
||||||
|
|
||||||
listener.conversation_created(event)
|
|
||||||
|
|
||||||
conversation.reload
|
|
||||||
|
|
||||||
expect(mailer).to have_received(:conversation_transcript).with(conversation, 'new_agent@example.com')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'triggers automation rule send message to the contacts' do
|
it 'triggers automation rule send message to the contacts' do
|
||||||
expect(conversation.messages.count).to eq(1)
|
expect(conversation.messages.count).to eq(1)
|
||||||
|
|
||||||
@@ -781,15 +719,6 @@ describe AutomationRuleListener do
|
|||||||
let(:event) { Events::Base.new('message_created', Time.zone.now, { conversation: tweet, message: message }) }
|
let(:event) { Events::Base.new('message_created', Time.zone.now, { conversation: tweet, message: message }) }
|
||||||
let!(:message) { create(:message, account: account, conversation: tweet, message_type: 'incoming') }
|
let!(:message) { create(:message, account: account, conversation: tweet, message_type: 'incoming') }
|
||||||
|
|
||||||
it 'triggers automation rule except send_message and send_attachment' do
|
|
||||||
mailer = double
|
|
||||||
allow(ConversationReplyMailer).to receive(:with).and_return(mailer)
|
|
||||||
allow(mailer).to receive(:conversation_transcript)
|
|
||||||
|
|
||||||
listener.message_created(event)
|
|
||||||
expect(mailer).to have_received(:conversation_transcript).with(tweet, 'new_agent@example.com')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not triggers automation rule send message or send attachment' do
|
it 'does not triggers automation rule send message or send attachment' do
|
||||||
expect(tweet.messages.count).to eq(1)
|
expect(tweet.messages.count).to eq(1)
|
||||||
|
|
||||||
|
|||||||
@@ -87,5 +87,21 @@ RSpec.describe AutomationRules::ActionService do
|
|||||||
described_class.new(rule, account, conversation).perform
|
described_class.new(rule, account, conversation).perform
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#perform with send_email_transcript action' do
|
||||||
|
before do
|
||||||
|
rule.actions << { action_name: 'send_email_transcript', action_params: ['contact@example.com, agent@example.com,agent1@example.com'] }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'will send email to transcript to action params emails' do
|
||||||
|
mailer = double
|
||||||
|
allow(ConversationReplyMailer).to receive(:with).and_return(mailer)
|
||||||
|
allow(mailer).to receive(:conversation_transcript).with(conversation, 'contact@example.com')
|
||||||
|
allow(mailer).to receive(:conversation_transcript).with(conversation, 'agent@example.com')
|
||||||
|
allow(mailer).to receive(:conversation_transcript).with(conversation, 'agent1@example.com')
|
||||||
|
|
||||||
|
described_class.new(rule, account, conversation).perform
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -310,8 +310,6 @@
|
|||||||
- $ref: '#/parameters/inbox_id'
|
- $ref: '#/parameters/inbox_id'
|
||||||
get:
|
get:
|
||||||
$ref: ./application/inboxes/inbox_members/show.yml
|
$ref: ./application/inboxes/inbox_members/show.yml
|
||||||
delete:
|
|
||||||
$ref: ./application/inboxes/inbox_members/delete.yml
|
|
||||||
|
|
||||||
/api/v1/accounts/{account_id}/inbox_members:
|
/api/v1/accounts/{account_id}/inbox_members:
|
||||||
parameters:
|
parameters:
|
||||||
@@ -320,6 +318,8 @@
|
|||||||
$ref: ./application/inboxes/inbox_members/create.yml
|
$ref: ./application/inboxes/inbox_members/create.yml
|
||||||
patch:
|
patch:
|
||||||
$ref: ./application/inboxes/inbox_members/update.yml
|
$ref: ./application/inboxes/inbox_members/update.yml
|
||||||
|
delete:
|
||||||
|
$ref: ./application/inboxes/inbox_members/delete.yml
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3369,62 +3369,6 @@
|
|||||||
"description": "Access denied"
|
"description": "Access denied"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"delete": {
|
|
||||||
"tags": [
|
|
||||||
"Inboxes"
|
|
||||||
],
|
|
||||||
"operationId": "delete-agent-in-inbox",
|
|
||||||
"summary": "Remove an Agent from Inbox",
|
|
||||||
"description": "Remove an Agent from Inbox",
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"userApiKey": [
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "data",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"inbox_id",
|
|
||||||
"user_ids"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"inbox_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The ID of the inbox"
|
|
||||||
},
|
|
||||||
"user_ids": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"description": "IDs of users to be deleted from the inbox"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Success"
|
|
||||||
},
|
|
||||||
"404": {
|
|
||||||
"description": "Inbox not found"
|
|
||||||
},
|
|
||||||
"403": {
|
|
||||||
"description": "Access denied"
|
|
||||||
},
|
|
||||||
"422": {
|
|
||||||
"description": "User must exist"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/accounts/{account_id}/inbox_members": {
|
"/api/v1/accounts/{account_id}/inbox_members": {
|
||||||
@@ -3558,6 +3502,62 @@
|
|||||||
"description": "User must exist"
|
"description": "User must exist"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"tags": [
|
||||||
|
"Inboxes"
|
||||||
|
],
|
||||||
|
"operationId": "delete-agent-in-inbox",
|
||||||
|
"summary": "Remove an Agent from Inbox",
|
||||||
|
"description": "Remove an Agent from Inbox",
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"userApiKey": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "data",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"inbox_id",
|
||||||
|
"user_ids"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"inbox_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The ID of the inbox"
|
||||||
|
},
|
||||||
|
"user_ids": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"description": "IDs of users to be deleted from the inbox"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Success"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Inbox not found"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Access denied"
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "User must exist"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/accounts/{account_id}/conversations/{conversation_id}/messages": {
|
"/api/v1/accounts/{account_id}/conversations/{conversation_id}/messages": {
|
||||||
|
|||||||
Reference in New Issue
Block a user