Merge branch 'develop' into chore/update-rails

This commit is contained in:
Sojan Jose
2023-04-27 15:33:50 +05:30
committed by GitHub
24 changed files with 418 additions and 182 deletions

View File

@@ -1,78 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'Bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See the error
**Expected behavior**
Share a clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Browser logs**
Share the browser logs to debug the issue further.
**Server logs**
Share the server logs to debug the issue further.
**Environment**
Describe whether you are using Chatwoot Cloud (app.chatwoot.com) or a self-hosted installation of Chatwoot. If you are using a self-hosted installation of Chatwoot, describe the type of deployment (Docker/Linux VM installation/Heroku/Kubernetes/Other).
- [ ] app.chatwoot.com (Chatwoot Cloud)
- [ ] Self-hosted
- - [ ] Linux VM
- - [ ] Docker
- - [ ] Kubernetes
- - [ ] Heroku
- - [ ] Other (Please specify)
**Desktop (please complete the following information)** (If applicable)
- OS: [e.g. Linux, Windows, MacOS]
- Browser [e.g. chrome, firefox, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information)** (If applicable)
- Device: [e.g. iPhone6, Pixel7]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, firefox, safari]
- Version [e.g. 22]
**Docker** (If applicable)
Please share the output of the following.
- `docker version`
- `docker info`
- `docker-compose version`
**Cloud Provider** (If applicable)
- [ ] AWS
- [ ] GCP
- [ ] Azure
- [ ] DigitalOcean
- [ ] Others
**Additional context**
Add any other context about the problem here.

78
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,78 @@
name: 🐞 Bug report
description: Create a report to help us improve
labels: 'Bug'
body:
- type: textarea
attributes:
label: Describe the bug
description: A concise description of what you expected to happen along with screenshots if applicable.
validations:
required: true
- type: textarea
attributes:
label: To Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. In this environment...
2. With this config...
3. Run '...'
4. See error...
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: A concise description of what you expected to happen.
- type: dropdown
id: environment
attributes:
label: Environment
description: Describe whether you are using Chatwoot Cloud (app.chatwoot.com) or a self-hosted installation of Chatwoot. If you are using a self-hosted installation of Chatwoot, describe the type of deployment (Docker/Linux VM installation/Heroku/Kubernetes/Other).
options:
- app.chatwoot.com
- Linux VM
- Docker
- Kubernetes
- Heroku
- Other [please specify in the description]
validations:
required: true
- type: dropdown
id: provider
attributes:
label: Cloud Provider
description:
options:
- AWS
- GCP
- Azure
- DigitalOcean
- Other [please specify in the description]
- type: dropdown
id: platform
attributes:
label: Platform
description: Describe the platform you are using
options:
- Browser
- Mobile
- type: input
attributes:
label: Operating system
description: The operating system and the version you are using.
- type: input
attributes:
label: Browser and version
description: The name of the browser and version you are using.
- type: textarea
attributes:
label: Docker (if applicable)
description: |
Please share the output of the following.
- `docker version`
- `docker info`
- `docker-compose version`
- type: textarea
attributes:
label: Additional context
description: Add any other context about the problem here.

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Report a security issue
url: https://www.chatwoot.com/docs/contributing-guide/security-reports/
about: Guidelines and steps to report a security vulnerability. Please report security vulnerabilities here.
- name: Product Documentation
url: https://www.chatwoot.com/help-center
about: If you have questions, are confused, or just want to understand our product better, please check out our documentation.

View File

@@ -1,20 +0,0 @@
---
name: Enhancement request
about: Suggest any enhancements for this project
title: ''
labels: ''
assignees: ''
---
**Is your enhancement request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions you've considered.
**Additional context**
Add any other context or screenshots about the enhancement request here.

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,28 @@
name: 🧙 Feature request
description: Suggest an idea for this project
labels: 'feature-request'
body:
- type: textarea
attributes:
label: Is your feature or enhancement related to a problem? Please describe.
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: false
- type: textarea
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
validations:
required: false

View File

@@ -1,2 +1,5 @@
class AccountDrop < BaseDrop
def name
@obj.try(:name)
end
end

View File

@@ -10,4 +10,26 @@ module EmailHelper
def normalize_email_with_plus_addressing(email)
"#{email.split('@').first.split('+').first}@#{email.split('@').last}".downcase
end
def parse_email_variables(conversation, email)
case email
when modified_liquid_content(email)
template = Liquid::Template.parse(modified_liquid_content(email))
template.render(message_drops(conversation))
when URI::MailTo::EMAIL_REGEXP
email
end
end
def modified_liquid_content(email)
# This regex is used to match the code blocks in the content
# We don't want to process liquid in code blocks
email.gsub(/`(.*?)`/m, '{% raw %}`\\1`{% endraw %}')
end
def message_drops(conversation)
{
'contact' => ContactDrop.new(conversation.contact)
}
end
end

View File

@@ -443,11 +443,11 @@ export default {
.conversation-metadata-attributes {
display: flex;
gap: var(--space-small);
margin-left: var(--space-small);
}
.assignee-label {
display: inline-flex;
margin-left: var(--space-small);
max-width: 50%;
}
}

View File

@@ -93,7 +93,9 @@ import { mapGetters } from 'vuex';
import ReplyBox from './ReplyBox';
import Message from './Message';
import conversationMixin from '../../../mixins/conversations';
import conversationMixin, {
filterDuplicateSourceMessages,
} from '../../../mixins/conversations';
import Banner from 'dashboard/components/ui/Banner.vue';
import { getTypingUsersText } from '../../../helper/commons';
import { BUS_EVENTS } from 'shared/constants/busEvents';
@@ -171,24 +173,28 @@ export default {
return '';
},
getMessages() {
const [chat] = this.allConversations.filter(
c => c.id === this.currentChat.id
);
return chat;
const messages = this.currentChat.messages || [];
if (this.isAWhatsAppChannel) {
return filterDuplicateSourceMessages(messages);
}
return messages;
},
getReadMessages() {
const chat = this.getMessages;
return chat === undefined ? null : this.readMessages(chat);
return this.readMessages(
this.getMessages,
this.currentChat.agent_last_seen_at
);
},
getUnReadMessages() {
const chat = this.getMessages;
return chat === undefined ? null : this.unReadMessages(chat);
return this.unReadMessages(
this.getMessages,
this.currentChat.agent_last_seen_at
);
},
shouldShowSpinner() {
return (
(this.getMessages && this.getMessages.dataFetched === undefined) ||
(this.currentChat && this.currentChat.dataFetched === undefined) ||
(!this.listLoadingStatus && this.isLoadingPrevious)
);
},
@@ -208,7 +214,7 @@ export default {
selectedTweet() {
if (this.selectedTweetId) {
const { messages = [] } = this.getMessages;
const { messages = [] } = this.currentChat;
const [selectedMessage] = messages.filter(
message => message.id === this.selectedTweetId
);
@@ -360,7 +366,7 @@ export default {
async fetchPreviousMessages(scrollTop = 0) {
this.setScrollParams();
const shouldLoadMoreMessages =
this.getMessages.dataFetched === true &&
this.currentChat.dataFetched === true &&
!this.listLoadingStatus &&
!this.isLoadingPrevious;
@@ -373,7 +379,7 @@ export default {
try {
await this.$store.dispatch('fetchPreviousMessages', {
conversationId: this.currentChat.id,
before: this.getMessages.messages[0].id,
before: this.currentChat.messages[0].id,
});
const heightDifference =
this.conversationPanel.scrollHeight - this.heightBeforeLoad;

View File

@@ -14,8 +14,8 @@
"EMPTY_STATE": "No %{item} found for query '%{query}'",
"EMPTY_STATE_FULL": "No results found for query '%{query}'",
"PLACEHOLDER_KEYBINDING": "/ to focus",
"INPUT_PLACEHOLDER": "Search messages, contacts or conversations",
"EMPTY_STATE_DEFAULT": "Search by conversation id, email, phone number, messages for better search results.",
"INPUT_PLACEHOLDER": "Type 3 or more characters to search",
"EMPTY_STATE_DEFAULT": "Search by conversation id, email, phone number, messages for better search results. ",
"BOT_LABEL": "Bot",
"READ_MORE": "Read more",
"WROTE": "wrote:"

View File

@@ -12,6 +12,26 @@ const getLastNonActivityMessage = (messageInStore, messageFromAPI) => {
return messageInStore || messageFromAPI;
};
export const filterDuplicateSourceMessages = (messages = []) => {
const messagesWithoutDuplicates = [];
// We cannot use Map or any short hand method as it returns the last message with the duplicate ID
// We should return the message with smaller id when there is a duplicate
messages.forEach(m1 => {
if (m1.source_id) {
if (
messagesWithoutDuplicates.findIndex(
m2 => m1.source_id === m2.source_id
) < 0
) {
messagesWithoutDuplicates.push(m1);
}
} else {
messagesWithoutDuplicates.push(m1);
}
});
return messagesWithoutDuplicates;
};
export default {
methods: {
lastMessage(m) {
@@ -34,14 +54,14 @@ export default {
lastNonActivityMessageFromAPI
);
},
readMessages(m) {
return m.messages.filter(
chat => chat.created_at * 1000 <= m.agent_last_seen_at * 1000
readMessages(messages, agentLastSeenAt) {
return messages.filter(
message => message.created_at * 1000 <= agentLastSeenAt * 1000
);
},
unReadMessages(m) {
return m.messages.filter(
chat => chat.created_at * 1000 > m.agent_last_seen_at * 1000
unReadMessages(messages, agentLastSeenAt) {
return messages.filter(
message => message.created_at * 1000 > agentLastSeenAt * 1000
);
},
},

View File

@@ -1,17 +1,50 @@
import conversationMixin from '../conversations';
import conversationMixin, {
filterDuplicateSourceMessages,
} from '../conversations';
import conversationFixture from './conversationFixtures';
import commonHelpers from '../../helper/commons';
commonHelpers();
describe('#filterDuplicateSourceMessages', () => {
it('returns messages without duplicate source_id and all messages without source_id', () => {
expect(
filterDuplicateSourceMessages([
{ source_id: null, id: 1 },
{ source_id: '', id: 2 },
{ id: 3 },
{ source_id: 'wa_1', id: 4 },
{ source_id: 'wa_1', id: 5 },
{ source_id: 'wa_1', id: 6 },
{ source_id: 'wa_2', id: 7 },
{ source_id: 'wa_2', id: 8 },
{ source_id: 'wa_3', id: 9 },
])
).toEqual([
{ source_id: null, id: 1 },
{ source_id: '', id: 2 },
{ id: 3 },
{ source_id: 'wa_1', id: 4 },
{ source_id: 'wa_2', id: 7 },
{ source_id: 'wa_3', id: 9 },
]);
});
});
describe('#conversationMixin', () => {
it('should return read messages if conversation is passed', () => {
expect(
conversationMixin.methods.readMessages(conversationFixture.conversation)
conversationMixin.methods.readMessages(
conversationFixture.conversation.messages,
conversationFixture.conversation.agent_last_seen_at
)
).toEqual(conversationFixture.readMessages);
});
it('should return read messages if conversation is passed', () => {
expect(
conversationMixin.methods.unReadMessages(conversationFixture.conversation)
conversationMixin.methods.unReadMessages(
conversationFixture.conversation.messages,
conversationFixture.conversation.agent_last_seen_at
)
).toEqual(conversationFixture.unReadMessages);
});

View File

@@ -18,12 +18,23 @@ export default {
type: Array,
default: () => [],
},
selectedTab: {
type: Number,
default: 0,
},
},
data() {
return {
activeTab: 0,
};
},
watch: {
selectedTab(value, oldValue) {
if (value !== oldValue) {
this.activeTab = this.selectedTab;
}
},
},
methods: {
onTabChange(index) {
this.activeTab = index;

View File

@@ -17,11 +17,12 @@
<search-tabs
v-if="query"
:tabs="tabs"
:selected-tab="activeTabIndex"
@tab-change="tab => (selectedTab = tab)"
/>
</header>
<div class="search-results">
<div v-if="all.length">
<div v-if="showResultsSection">
<search-result-contacts-list
v-if="filterContacts"
:is-fetching="uiFlags.contact.isFetching"
@@ -46,7 +47,7 @@
:show-title="isSelectedTabAll"
/>
</div>
<div v-else-if="showEmptySearchResults && !all.length" class="empty">
<div v-else-if="showEmptySearchResults" class="empty">
<fluent-icon icon="info" size="16px" class="icon" />
<p class="empty-state__text">
{{ $t('SEARCH.EMPTY_STATE_FULL', { query }) }}
@@ -157,9 +158,23 @@ export default {
},
];
},
activeTabIndex() {
const index = this.tabs.findIndex(tab => tab.key === this.selectedTab);
return index >= 0 ? index : 0;
},
showEmptySearchResults() {
return (
this.totalSearchResultsCount === 0 && this.uiFlags.isSearchCompleted
this.totalSearchResultsCount === 0 &&
this.uiFlags.isSearchCompleted &&
!this.uiFlags.isFetching &&
this.query
);
},
showResultsSection() {
return (
(this.uiFlags.isSearchCompleted &&
this.totalSearchResultsCount !== 0) ||
this.uiFlags.isFetching
);
},
isSelectedTabAll() {
@@ -175,6 +190,7 @@ export default {
},
methods: {
onSearch(q) {
this.selectedTab = 'all';
this.query = q;
if (!q) {
this.$store.dispatch('conversationSearch/clearSearchResults');

View File

@@ -17,7 +17,16 @@
<div v-if="isBusinessHoursEnabled" class="business-hours-wrap">
<label class="unavailable-input-wrap">
{{ $t('INBOX_MGMT.BUSINESS_HOURS.UNAVAILABLE_MESSAGE_LABEL') }}
<textarea v-model="unavailableMessage" type="text" />
<label v-if="isRichEditorEnabled" class="richtext">
<woot-message-editor
v-model="unavailableMessage"
:enable-variables="true"
:is-format-mode="true"
class="input"
:min-height="4"
/>
</label>
<textarea v-else v-model="unavailableMessage" type="text" />
</label>
<div class="timezone-input-wrap">
<label>
@@ -61,7 +70,9 @@
<script>
import { mapGetters } from 'vuex';
import alertMixin from 'shared/mixins/alertMixin';
import inboxMixin from 'shared/mixins/inboxMixin';
import SettingsSection from 'dashboard/components/SettingsSection';
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor';
import BusinessDay from './BusinessDay';
import {
timeSlotParse,
@@ -79,8 +90,9 @@ export default {
components: {
SettingsSection,
BusinessDay,
WootMessageEditor,
},
mixins: [alertMixin],
mixins: [alertMixin, inboxMixin],
props: {
inbox: {
type: Object,
@@ -115,6 +127,15 @@ export default {
timeZones() {
return [...timeZoneOptions()];
},
isRichEditorEnabled() {
if (
this.isATwilioChannel ||
this.isATwitterInbox ||
this.isAFacebookInbox
)
return false;
return true;
},
},
watch: {
inbox() {
@@ -190,4 +211,11 @@ export default {
.business-hours-wrap {
margin-bottom: var(--space-medium);
}
.richtext {
padding: 0 var(--space-normal);
border-radius: var(--border-radius-normal);
border: 1px solid var(--color-border);
margin: 0 0 var(--space-normal);
}
</style>

View File

@@ -61,9 +61,11 @@ export const actions = {
isSearchCompleted: false,
});
try {
dispatch('contactSearch', { q });
dispatch('conversationSearch', { q });
dispatch('messageSearch', { q });
await Promise.all([
dispatch('contactSearch', { q }),
dispatch('conversationSearch', { q }),
dispatch('messageSearch', { q }),
]);
} catch (error) {
// Ignore error
} finally {

View File

@@ -4,6 +4,7 @@
<woot-message-editor
v-model="greetingsMessage"
:is-format-mode="true"
:enable-variables="true"
class="input"
:placeholder="placeholder"
:min-height="4"

View File

@@ -13,12 +13,13 @@ module Liquidable
'contact' => ContactDrop.new(conversation.contact),
'agent' => UserDrop.new(sender),
'conversation' => ConversationDrop.new(conversation),
'inbox' => InboxDrop.new(inbox)
'inbox' => InboxDrop.new(inbox),
'account' => AccountDrop.new(conversation.account)
}
end
def liquid_processable_message?
content.present? && message_type == 'outgoing'
content.present? && (message_type == 'outgoing' || message_type == 'template')
end
def process_liquid_in_content

View File

@@ -1,4 +1,6 @@
class ActionService
include EmailHelper
def initialize(conversation)
@conversation = conversation.reload
end
@@ -59,6 +61,7 @@ class ActionService
emails = emails[0].gsub(/\s+/, '').split(',')
emails.each do |email|
email = parse_email_variables(@conversation, email)
ConversationReplyMailer.with(account: @conversation.account).conversation_transcript(@conversation, email)&.deliver_later
end
end

View File

@@ -199,24 +199,33 @@ contacts:
conversations:
- channel: Channel::WebWidget
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I'm having trouble logging in to my account.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: Hi! Sorry to hear that. Can you please provide me with your username and email address so I can look into it for you?
- name: "Tiffanie Cloughton"
email: "tcloughton1@newyorker.test"
gender: 'female'
conversations:
- channel: Channel::FacebookPage
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I need some help with my billing statement.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: Hello! I'd be happy to assist you with that. Can you please tell me which billing statement you're referring to?
- name: "Melonie Keatch"
email: "mkeatch2@reuters.test"
gender: 'female'
conversations:
- channel: Channel::TwitterProfile
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I think I accidentally deleted some important files. Can you help me recover them?
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: Of course! Can you please tell me what type of files they were and where they were located on your device?
- name: "Olin Canniffe"
email: "ocanniffe3@feedburner.test"
gender: 'male'
@@ -224,8 +233,11 @@ contacts:
- channel: Channel::Whatsapp
source_id: "123456723"
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I'm having trouble connecting to the internet.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: I'm sorry to hear that. Have you tried restarting your modem/router? If that doesn't work, please let me know and I can provide further assistance.
- name: "Viviene Corp"
email: "vcorp4@instagram.test"
gender: 'female'
@@ -233,32 +245,44 @@ contacts:
- channel: Channel::Sms
source_id: "+1234567"
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I'm having trouble with the mobile app. It keeps crashing.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: I'm sorry to hear that. Can you please try uninstalling and reinstalling the app and see if that helps? If not, please let me know and I can look into it further.
- name: "Drake Pittway"
email: "dpittway5@chron.test"
gender: 'male'
conversations:
- channel: Channel::Line
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I'm trying to update my account information but it won't save.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: Sorry for the inconvenience. Can you please provide me with the specific information you're trying to update and the error message you're receiving?
- name: "Klaus Crawley"
email: "kcrawley6@narod.ru"
gender: 'male'
conversations:
- channel: Channel::WebWidget
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I need some help setting up my new device.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: No problem! Can you please tell me the make and model of your device and what specifically you need help with?
- name: "Bing Cusworth"
email: "bcusworth7@arstechnica.test"
gender: 'male'
conversations:
- channel: Channel::TwitterProfile
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I accidentally placed an order for the wrong item. Can I cancel it?
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: I'm sorry to hear that. Can you please provide me with your order number and I'll see if I can cancel it for you?
- name: "Claus Jira"
email: "cjira8@comcast.net"
gender: 'male'
@@ -266,8 +290,11 @@ contacts:
- channel: Channel::Whatsapp
source_id: "12323432"
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I'm having trouble with my email. I can't seem to send or receive any messages.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: I'm sorry to hear that. Can you please tell me what email client you're using and if you're receiving any error messages?
- name: "Quent Dalliston"
email: "qdalliston9@zimbio.test"
gender: 'male'
@@ -275,24 +302,33 @@ contacts:
- channel: Channel::Whatsapp
source_id: "12342234324"
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I need some help resetting my password.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: Sure! Can you please provide me with your username or email address and I'll send you a password reset link?
- name: "Coreen Mewett"
email: "cmewetta@home.pl"
gender: 'female'
conversations:
- channel: Channel::FacebookPage
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I think someone may have hacked into my account. What should I do?
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: I'm sorry to hear that. Please change your password immediately and enable two-factor authentication if you haven't already done so. I can also assist you in reviewing your account activity if needed.
- name: "Benyamin Janeway"
email: "bjanewayb@ustream.tv"
gender: 'male'
conversations:
- channel: Channel::Line
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I have a question about your product features.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: Sure thing! What specific feature are you interested in learning more about?
- name: "Cordell Dalinder"
email: "cdalinderc@msn.test"
gender: 'male'
@@ -300,8 +336,11 @@ contacts:
- channel: Channel::Email
source_id: "cdalinderc@msn.test"
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I need help setting up my new printer.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: No problem! Can you please provide me with the make and model of your printer and what type of device you'll be connecting it to?
- name: "Merrile Petruk"
email: "mpetrukd@wunderground.test"
gender: 'female'
@@ -310,8 +349,11 @@ contacts:
source_id: "mpetrukd@wunderground.test"
priority: urgent
messages:
- message_type: incoming
content: hello world
- message_type: incoming
content: Hi, I'm having trouble accessing a file that I shared with someone.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: I'm sorry to hear that. Can you please tell me which file you're having trouble accessing and who you shared it with? I'll do my best to help you regain access.
- name: "Nathaniel Vannuchi"
email: "nvannuchie@photobucket.test"
gender: 'male'
@@ -387,7 +429,9 @@ contacts:
- message_type: incoming
content: "Hey, \n I'm looking for some help to figure out if it is the right product for me."
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: Welcome to PaperLayer. Our Team will be getting back you shortly.
- message_type: outgoing
sender: michael_scott@paperlayer.test
content: How may i help you ?
sender: michael_scott@paperlayer.test

View File

@@ -91,6 +91,7 @@ RSpec.describe AutomationRules::ActionService do
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'] }
rule.save
end
it 'will send email to transcript to action params emails' do
@@ -102,6 +103,17 @@ RSpec.describe AutomationRules::ActionService do
described_class.new(rule, account, conversation).perform
end
it 'will send email to transcript to contacts' do
rule.actions = [{ action_name: 'send_email_transcript', action_params: ['{{contact.email}}'] }]
rule.save
mailer = double
allow(ConversationReplyMailer).to receive(:with).and_return(mailer)
allow(mailer).to receive(:conversation_transcript).with(conversation, conversation.contact.email)
described_class.new(rule.reload, account, conversation).perform
end
end
end
end

View File

@@ -8,5 +8,26 @@ describe ::MessageTemplates::Template::Greeting do
described_class.new(conversation: conversation).perform
expect(conversation.messages.count).to eq(1)
end
it 'creates the greeting messages with template variable' do
conversation.inbox.update!(greeting_message: 'Hey, {{contact.name}} welcome to our board.')
described_class.new(conversation: conversation).perform
expect(conversation.messages.count).to eq(1)
expect(conversation.messages.last.content).to eq("Hey, #{conversation.contact.name} welcome to our board.")
end
it 'creates the greeting messages with more than one variable strings' do
conversation.inbox.update!(greeting_message: 'Hey, {{contact.name}} welcome to our board. - from {{account.name}}')
described_class.new(conversation: conversation).perform
expect(conversation.messages.count).to eq(1)
expect(conversation.messages.last.content).to eq("Hey, #{conversation.contact.name} welcome to our board. - from #{conversation.account.name}")
end
it 'creates the greeting messages' do
conversation.inbox.update!(greeting_message: 'Hello welcome to our board.')
described_class.new(conversation: conversation).perform
expect(conversation.messages.count).to eq(1)
expect(conversation.messages.last.content).to eq('Hello welcome to our board.')
end
end
end

View File

@@ -9,5 +9,22 @@ describe ::MessageTemplates::Template::OutOfOffice do
expect(conversation.messages.template.count).to eq(1)
expect(conversation.messages.template.first.content).to eq(conversation.inbox.out_of_office_message)
end
it 'creates the out of office messages with template variable' do
conversation.inbox.update!(out_of_office_message: 'Hey, {{contact.name}} we are unavailable at the moment.')
described_class.new(conversation: conversation).perform
expect(conversation.messages.count).to eq(1)
expect(conversation.messages.last.content).to eq("Hey, #{conversation.contact.name} we are unavailable at the moment.")
end
it 'creates the out of office messages with more than one variable strings' do
conversation.inbox.update!(out_of_office_message:
'Hey, {{contact.name}} we are unavailable at the moment. - from {{account.name}}')
described_class.new(conversation: conversation).perform
expect(conversation.messages.count).to eq(1)
expect(conversation.messages.last.content).to eq(
"Hey, #{conversation.contact.name} we are unavailable at the moment. - from #{conversation.account.name}"
)
end
end
end