feat: Ability to toggle conversation continuity via email (#3817)
Fixes: #3368 Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
parent
34e8ad9dc5
commit
59deffc7e3
9 changed files with 113 additions and 50 deletions
|
@ -310,6 +310,10 @@
|
|||
"ENABLED": "Enabled",
|
||||
"DISABLED": "Disabled"
|
||||
},
|
||||
"ENABLE_CONTINUITY_VIA_EMAIL": {
|
||||
"ENABLED": "Enabled",
|
||||
"DISABLED": "Disabled"
|
||||
},
|
||||
"ENABLE_HMAC": {
|
||||
"LABEL": "Enable"
|
||||
}
|
||||
|
@ -356,6 +360,8 @@
|
|||
"AUTO_ASSIGNMENT": "Enable auto assignment",
|
||||
"ENABLE_CSAT": "Enable CSAT",
|
||||
"ENABLE_CSAT_SUB_TEXT": "Enable/Disable CSAT(Customer satisfaction) survey after resolving a conversation",
|
||||
"ENABLE_CONTINUITY_VIA_EMAIL": "Enable conversation continuity via email",
|
||||
"ENABLE_CONTINUITY_VIA_EMAIL_SUB_TEXT": "Conversations will continue over email if the contact email address is available.",
|
||||
"INBOX_UPDATE_TITLE": "Inbox Settings",
|
||||
"INBOX_UPDATE_SUB_TEXT": "Update your inbox settings",
|
||||
"AUTO_ASSIGNMENT_SUB_TEXT": "Enable or disable the automatic assignment of new conversations to the agents added to this inbox.",
|
||||
|
|
|
@ -215,7 +215,7 @@
|
|||
</p>
|
||||
</label>
|
||||
|
||||
<label class="medium-9 columns">
|
||||
<label v-if="isAWebWidgetInbox" class="medium-9 columns">
|
||||
{{ $t('INBOX_MGMT.SETTINGS_POPUP.ALLOW_MESSAGES_AFTER_RESOLVED') }}
|
||||
<select v-model="allowMessagesAfterResolved">
|
||||
<option :value="true">
|
||||
|
@ -234,6 +234,25 @@
|
|||
</p>
|
||||
</label>
|
||||
|
||||
<label v-if="isAWebWidgetInbox" class="medium-9 columns">
|
||||
{{ $t('INBOX_MGMT.SETTINGS_POPUP.ENABLE_CONTINUITY_VIA_EMAIL') }}
|
||||
<select v-model="continuityViaEmail">
|
||||
<option :value="true">
|
||||
{{ $t('INBOX_MGMT.EDIT.ENABLE_CONTINUITY_VIA_EMAIL.ENABLED') }}
|
||||
</option>
|
||||
<option :value="false">
|
||||
{{ $t('INBOX_MGMT.EDIT.ENABLE_CONTINUITY_VIA_EMAIL.DISABLED') }}
|
||||
</option>
|
||||
</select>
|
||||
<p class="help-text">
|
||||
{{
|
||||
$t(
|
||||
'INBOX_MGMT.SETTINGS_POPUP.ENABLE_CONTINUITY_VIA_EMAIL_SUB_TEXT'
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</label>
|
||||
|
||||
<label v-if="isAWebWidgetInbox">
|
||||
{{ $t('INBOX_MGMT.FEATURES.LABEL') }}
|
||||
</label>
|
||||
|
@ -440,6 +459,7 @@ export default {
|
|||
isAgentListUpdating: false,
|
||||
csatSurveyEnabled: false,
|
||||
allowMessagesAfterResolved: true,
|
||||
continuityViaEmail: true,
|
||||
selectedInboxName: '',
|
||||
channelWebsiteUrl: '',
|
||||
webhookUrl: '',
|
||||
|
@ -604,6 +624,7 @@ export default {
|
|||
this.emailCollectEnabled = this.inbox.enable_email_collect;
|
||||
this.csatSurveyEnabled = this.inbox.csat_survey_enabled;
|
||||
this.allowMessagesAfterResolved = this.inbox.allow_messages_after_resolved;
|
||||
this.continuityViaEmail = this.inbox.continuity_via_email;
|
||||
this.channelWebsiteUrl = this.inbox.website_url;
|
||||
this.channelWelcomeTitle = this.inbox.welcome_title;
|
||||
this.channelWelcomeTagline = this.inbox.welcome_tagline;
|
||||
|
@ -659,6 +680,7 @@ export default {
|
|||
reply_time: this.replyTime || 'in_a_few_minutes',
|
||||
hmac_mandatory: this.hmacMandatory,
|
||||
tweets_enabled: this.tweetsEnabled,
|
||||
continuity_via_email: this.continuityViaEmail,
|
||||
},
|
||||
};
|
||||
if (this.avatarFile) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# Table name: channel_web_widgets
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# continuity_via_email :boolean default(TRUE), not null
|
||||
# feature_flags :integer default(3), not null
|
||||
# hmac_mandatory :boolean default(FALSE)
|
||||
# hmac_token :string
|
||||
|
@ -29,7 +30,8 @@ class Channel::WebWidget < ApplicationRecord
|
|||
include FlagShihTzu
|
||||
|
||||
self.table_name = 'channel_web_widgets'
|
||||
EDITABLE_ATTRS = [:website_url, :widget_color, :welcome_title, :welcome_tagline, :reply_time, :pre_chat_form_enabled, :hmac_mandatory,
|
||||
EDITABLE_ATTRS = [:website_url, :widget_color, :welcome_title, :welcome_tagline, :reply_time, :pre_chat_form_enabled,
|
||||
:continuity_via_email, :hmac_mandatory,
|
||||
{ pre_chat_form_options: [:pre_chat_message, :require_email] },
|
||||
{ selected_feature_flags: [] }].freeze
|
||||
|
||||
|
|
|
@ -185,6 +185,14 @@ class Message < ApplicationRecord
|
|||
::MessageTemplates::HookExecutionService.new(message: self).perform
|
||||
end
|
||||
|
||||
def email_notifiable_webwidget?
|
||||
inbox.web_widget? && inbox.channel.continuity_via_email
|
||||
end
|
||||
|
||||
def email_notifiable_channel?
|
||||
email_notifiable_webwidget? || %w[Email].include?(inbox.inbox_type)
|
||||
end
|
||||
|
||||
def email_notifiable_message?
|
||||
return false unless outgoing? || input_csat?
|
||||
return false if private?
|
||||
|
@ -194,7 +202,8 @@ class Message < ApplicationRecord
|
|||
|
||||
def can_notify_via_mail?
|
||||
return unless email_notifiable_message?
|
||||
return false if conversation.contact.email.blank? || !(%w[Website Email].include? inbox.inbox_type)
|
||||
return unless email_notifiable_channel?
|
||||
return if conversation.contact.email.blank?
|
||||
|
||||
true
|
||||
end
|
||||
|
|
|
@ -34,36 +34,43 @@ if resource.web_widget?
|
|||
json.hmac_token resource.channel.try(:hmac_token)
|
||||
json.pre_chat_form_enabled resource.channel.try(:pre_chat_form_enabled)
|
||||
json.pre_chat_form_options resource.channel.try(:pre_chat_form_options)
|
||||
json.continuity_via_email resource.channel.try(:continuity_via_email)
|
||||
end
|
||||
|
||||
## Facebook Attributes
|
||||
json.page_id resource.channel.try(:page_id)
|
||||
json.reauthorization_required resource.channel.try(:reauthorization_required?) if resource.facebook?
|
||||
if resource.facebook?
|
||||
json.page_id resource.channel.try(:page_id)
|
||||
json.reauthorization_required resource.channel.try(:reauthorization_required?)
|
||||
end
|
||||
|
||||
## Twilio Attributes
|
||||
json.phone_number resource.channel.try(:phone_number)
|
||||
json.medium resource.channel.try(:medium) if resource.twilio?
|
||||
|
||||
## Email Channel Attributes
|
||||
json.forward_to_email resource.channel.try(:forward_to_email)
|
||||
json.email resource.channel.try(:email) if resource.email?
|
||||
if resource.email?
|
||||
## Email Channel Attributes
|
||||
json.forward_to_email resource.channel.try(:forward_to_email)
|
||||
json.email resource.channel.try(:email)
|
||||
|
||||
## IMAP
|
||||
json.imap_email resource.channel.try(:imap_email) if resource.email?
|
||||
json.imap_password resource.channel.try(:imap_password) if resource.email?
|
||||
json.imap_address resource.channel.try(:imap_address) if resource.email?
|
||||
json.imap_port resource.channel.try(:imap_port) if resource.email?
|
||||
json.imap_enabled resource.channel.try(:imap_enabled) if resource.email?
|
||||
json.imap_enable_ssl resource.channel.try(:imap_enable_ssl) if resource.email?
|
||||
## IMAP
|
||||
json.imap_email resource.channel.try(:imap_email)
|
||||
json.imap_password resource.channel.try(:imap_password)
|
||||
json.imap_address resource.channel.try(:imap_address)
|
||||
json.imap_port resource.channel.try(:imap_port)
|
||||
json.imap_enabled resource.channel.try(:imap_enabled)
|
||||
json.imap_enable_ssl resource.channel.try(:imap_enable_ssl)
|
||||
|
||||
## SMTP
|
||||
json.smtp_email resource.channel.try(:smtp_email) if resource.email?
|
||||
json.smtp_password resource.channel.try(:smtp_password) if resource.email?
|
||||
json.smtp_address resource.channel.try(:smtp_address) if resource.email?
|
||||
json.smtp_port resource.channel.try(:smtp_port) if resource.email?
|
||||
json.smtp_enabled resource.channel.try(:smtp_enabled) if resource.email?
|
||||
json.smtp_domain resource.channel.try(:smtp_domain) if resource.email?
|
||||
## SMTP
|
||||
json.smtp_email resource.channel.try(:smtp_email)
|
||||
json.smtp_password resource.channel.try(:smtp_password)
|
||||
json.smtp_address resource.channel.try(:smtp_address)
|
||||
json.smtp_port resource.channel.try(:smtp_port)
|
||||
json.smtp_enabled resource.channel.try(:smtp_enabled)
|
||||
json.smtp_domain resource.channel.try(:smtp_domain)
|
||||
end
|
||||
|
||||
## API Channel Attributes
|
||||
json.webhook_url resource.channel.try(:webhook_url) if resource.api?
|
||||
json.inbox_identifier resource.channel.try(:identifier) if resource.api?
|
||||
if resource.api?
|
||||
json.webhook_url resource.channel.try(:webhook_url)
|
||||
json.inbox_identifier resource.channel.try(:identifier)
|
||||
end
|
||||
|
|
|
@ -305,11 +305,11 @@ Rails.application.routes.draw do
|
|||
resources :users, only: [:index, :new, :create, :show, :edit, :update]
|
||||
resources :access_tokens, only: [:index, :show]
|
||||
resources :installation_configs, only: [:index, :new, :create, :show, :edit, :update]
|
||||
resources :agent_bots, only: [:index, :new, :create, :show, :edit, :update]
|
||||
resources :platform_apps, only: [:index, :new, :create, :show, :edit, :update]
|
||||
|
||||
# resources that doesn't appear in primary navigation in super admin
|
||||
resources :account_users, only: [:new, :create, :destroy]
|
||||
resources :agent_bots, only: [:index, :new, :create, :show, :edit, :update]
|
||||
resources :platform_apps, only: [:index, :new, :create, :show, :edit, :update]
|
||||
end
|
||||
authenticated :super_admin do
|
||||
mount Sidekiq::Web => '/monitoring/sidekiq'
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class EnableConversationContinuity < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_column :channel_web_widgets, :continuity_via_email, :boolean, null: false, default: true
|
||||
end
|
||||
end
|
|
@ -274,6 +274,7 @@ ActiveRecord::Schema.define(version: 2022_01_21_055444) do
|
|||
t.boolean "pre_chat_form_enabled", default: false
|
||||
t.jsonb "pre_chat_form_options", default: {}
|
||||
t.boolean "hmac_mandatory", default: false
|
||||
t.boolean "continuity_via_email", default: true, null: false
|
||||
t.index ["hmac_token"], name: "index_channel_web_widgets_on_hmac_token", unique: true
|
||||
t.index ["website_token"], name: "index_channel_web_widgets_on_website_token", unique: true
|
||||
end
|
||||
|
|
|
@ -56,33 +56,44 @@ RSpec.describe Message, type: :model do
|
|||
expect(hook_execution_service).to have_received(:perform)
|
||||
end
|
||||
|
||||
it 'calls notify email method on after save for outgoing messages' do
|
||||
allow(ConversationReplyEmailWorker).to receive(:perform_in).and_return(true)
|
||||
message.message_type = 'outgoing'
|
||||
message.save!
|
||||
expect(ConversationReplyEmailWorker).to have_received(:perform_in)
|
||||
end
|
||||
context 'with conversation continuity' do
|
||||
it 'calls notify email method on after save for outgoing messages in website channel' do
|
||||
allow(ConversationReplyEmailWorker).to receive(:perform_in).and_return(true)
|
||||
message.message_type = 'outgoing'
|
||||
message.save!
|
||||
expect(ConversationReplyEmailWorker).to have_received(:perform_in)
|
||||
end
|
||||
|
||||
it 'wont call notify email method for private notes' do
|
||||
message.private = true
|
||||
allow(ConversationReplyEmailWorker).to receive(:perform_in).and_return(true)
|
||||
message.save!
|
||||
expect(ConversationReplyEmailWorker).not_to have_received(:perform_in)
|
||||
end
|
||||
it 'does not call notify email for website channel if continuity is disabled' do
|
||||
message.inbox = create(:inbox, account: message.account,
|
||||
channel: build(:channel_widget, account: message.account, continuity_via_email: false))
|
||||
allow(ConversationReplyEmailWorker).to receive(:perform_in).and_return(true)
|
||||
message.message_type = 'outgoing'
|
||||
message.save!
|
||||
expect(ConversationReplyEmailWorker).not_to have_received(:perform_in)
|
||||
end
|
||||
|
||||
it 'calls EmailReply worker if the channel is email' do
|
||||
message.inbox = create(:inbox, account: message.account, channel: build(:channel_email, account: message.account))
|
||||
allow(EmailReplyWorker).to receive(:perform_in).and_return(true)
|
||||
message.message_type = 'outgoing'
|
||||
message.save!
|
||||
expect(EmailReplyWorker).to have_received(:perform_in).with(1.second, message.id)
|
||||
end
|
||||
it 'wont call notify email method for private notes' do
|
||||
message.private = true
|
||||
allow(ConversationReplyEmailWorker).to receive(:perform_in).and_return(true)
|
||||
message.save!
|
||||
expect(ConversationReplyEmailWorker).not_to have_received(:perform_in)
|
||||
end
|
||||
|
||||
it 'wont call notify email method unless its website or email channel' do
|
||||
message.inbox = create(:inbox, account: message.account, channel: build(:channel_api, account: message.account))
|
||||
allow(ConversationReplyEmailWorker).to receive(:perform_in).and_return(true)
|
||||
message.save!
|
||||
expect(ConversationReplyEmailWorker).not_to have_received(:perform_in)
|
||||
it 'calls EmailReply worker if the channel is email' do
|
||||
message.inbox = create(:inbox, account: message.account, channel: build(:channel_email, account: message.account))
|
||||
allow(EmailReplyWorker).to receive(:perform_in).and_return(true)
|
||||
message.message_type = 'outgoing'
|
||||
message.save!
|
||||
expect(EmailReplyWorker).to have_received(:perform_in).with(1.second, message.id)
|
||||
end
|
||||
|
||||
it 'wont call notify email method unless its website or email channel' do
|
||||
message.inbox = create(:inbox, account: message.account, channel: build(:channel_api, account: message.account))
|
||||
allow(ConversationReplyEmailWorker).to receive(:perform_in).and_return(true)
|
||||
message.save!
|
||||
expect(ConversationReplyEmailWorker).not_to have_received(:perform_in)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue