From c674393c0245fa06f0763c176de18f28a6077a82 Mon Sep 17 00:00:00 2001 From: Fayaz Ahmed <15716057+fayazara@users.noreply.github.com> Date: Tue, 29 Mar 2022 13:27:16 +0530 Subject: [PATCH] feat: New automation actions (#4033) --- .../accounts/automation_rules_controller.rb | 2 +- .../widgets/AutomationActionInput.vue | 58 ++++++--- .../settings/automation/AddAutomationRule.vue | 13 +- .../automation/EditAutomationRule.vue | 1 - .../settings/automation/constants.js | 122 ++++++++++++++++-- .../concerns/activity_message_handler.rb | 7 + .../automation_rules/action_service.rb | 14 +- app/services/filter_service.rb | 8 +- .../automation_rule_listener_spec.rb | 2 +- 9 files changed, 192 insertions(+), 35 deletions(-) diff --git a/app/controllers/api/v1/accounts/automation_rules_controller.rb b/app/controllers/api/v1/accounts/automation_rules_controller.rb index 3971dbcaf..2dc71bcec 100644 --- a/app/controllers/api/v1/accounts/automation_rules_controller.rb +++ b/app/controllers/api/v1/accounts/automation_rules_controller.rb @@ -34,7 +34,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont params.permit( :name, :description, :event_name, :account_id, :active, conditions: [:attribute_key, :filter_operator, :query_operator, { values: [] }], - actions: [:action_name, { action_params: [{}] }] + actions: [:action_name, { action_params: [] }] ) end diff --git a/app/javascript/dashboard/components/widgets/AutomationActionInput.vue b/app/javascript/dashboard/components/widgets/AutomationActionInput.vue index 22521af91..0c8178a9b 100644 --- a/app/javascript/dashboard/components/widgets/AutomationActionInput.vue +++ b/app/javascript/dashboard/components/widgets/AutomationActionInput.vue @@ -7,7 +7,8 @@
-
- +
+ +
+ +
@@ -91,13 +111,17 @@ export default { this.$emit('input', { ...payload, action_params: value }); }, }, + inputType() { + return this.actionTypes.find(action => action.key === this.action_name) + .inputType; + }, }, methods: { removeAction() { this.$emit('removeAction'); }, - resetFilter() { - this.$emit('resetFilter'); + resetAction() { + this.$emit('resetAction'); }, }, }; @@ -136,6 +160,10 @@ export default { max-width: 50%; } +.action__question.full-width { + max-width: 100%; +} + .filter__answer--wrap { margin-right: var(--space-smaller); flex-grow: 1; diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue b/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue index 814e90c25..2e10280e5 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue @@ -101,6 +101,7 @@ getActionDropdownValues(automation.actions[i].action_name) " :v="$v.automation.actions.$each[i]" + @resetAction="resetAction(i)" @removeAction="removeAction(i)" />
@@ -187,7 +188,13 @@ export default { required, $each: { action_params: { - required, + required: requiredIf(prop => { + return !( + prop.action_name === 'mute_conversation' || + prop.action_name === 'snooze_convresation' || + prop.action_name === 'resolve_convresation' + ); + }), }, }, }, @@ -351,7 +358,6 @@ export default { getActionDropdownValues(type) { switch (type) { case 'assign_team': - case 'send_email_to_team': return this.$store.getters['teams/getTeams']; case 'add_label': return this.$store.getters['labels/getLabels'].map(i => { @@ -424,6 +430,9 @@ export default { ).filterOperators[0].value; this.automation.conditions[index].values = ''; }, + resetAction(index) { + this.automation.actions[index].action_params = []; + }, showUserInput(operatorType) { if (operatorType === 'is_present' || operatorType === 'is_not_present') return false; diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue b/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue index d55ce419b..6add27ba5 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue @@ -351,7 +351,6 @@ export default { getActionDropdownValues(type) { switch (type) { case 'assign_team': - case 'send_email_to_team': return this.$store.getters['teams/getTeams']; case 'add_label': return this.$store.getters['labels/getLabels'].map(i => { diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js index 90e3a7395..dd11700bd 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js @@ -79,8 +79,33 @@ export const AUTOMATIONS = { // { // key: 'send_email_to_team', // name: 'Send an email to team', - // attributeI18nKey: 'SEND_EMAIL_TO_TEAM', + // attributeI18nKey: 'SEND_MESSAGE', // }, + { + key: 'send_email_transcript', + name: 'Send an email transcript', + attributeI18nKey: 'SEND_EMAIL_TRANSCRIPT', + }, + { + key: 'mute_conversation', + name: 'Mute conversation', + attributeI18nKey: 'MUTE_CONVERSATION', + }, + { + key: 'snooze_convresation', + name: 'Snooze conversation', + attributeI18nKey: 'MUTE_CONVERSATION', + }, + { + key: 'resolve_convresation', + name: 'Resolve conversation', + attributeI18nKey: 'RESOLVE_CONVERSATION', + }, + { + key: 'send_webhook_event', + name: 'Send Webhook Event', + attributeI18nKey: 'SEND_WEBHOOK_EVENT', + }, ], }, conversation_created: { @@ -120,15 +145,40 @@ export const AUTOMATIONS = { name: 'Assign a team', attributeI18nKey: 'ASSIGN_TEAM', }, + { + key: 'assign_agent', + name: 'Assign an agent', + attributeI18nKey: 'ASSIGN_AGENT', + }, // { // key: 'send_email_to_team', // name: 'Send an email to team', // attributeI18nKey: 'SEND_MESSAGE', // }, { - key: 'assign_agent', - name: 'Assign an agent', - attributeI18nKey: 'ASSIGN_AGENT', + key: 'send_email_transcript', + name: 'Send an email transcript', + attributeI18nKey: 'SEND_EMAIL_TRANSCRIPT', + }, + { + key: 'mute_conversation', + name: 'Mute conversation', + attributeI18nKey: 'MUTE_CONVERSATION', + }, + { + key: 'snooze_convresation', + name: 'Snooze conversation', + attributeI18nKey: 'MUTE_CONVERSATION', + }, + { + key: 'resolve_convresation', + name: 'Resolve conversation', + attributeI18nKey: 'RESOLVE_CONVERSATION', + }, + { + key: 'send_webhook_event', + name: 'Send Webhook Event', + attributeI18nKey: 'SEND_WEBHOOK_EVENT', }, ], }, @@ -183,16 +233,40 @@ export const AUTOMATIONS = { name: 'Assign a team', attributeI18nKey: 'ASSIGN_TEAM', }, - // { - // key: 'send_email_to_team', - // name: 'Send an email to team', - // attributeI18nKey: 'SEND_EMAIL_TO_TEAM', - // }, { key: 'assign_agent', name: 'Assign an agent', attributeI18nKey: 'ASSIGN_AGENT', - attributeKey: 'assignee_id', + }, + // { + // key: 'send_email_to_team', + // name: 'Send an email to team', + // attributeI18nKey: 'SEND_MESSAGE', + // }, + { + key: 'send_email_transcript', + name: 'Send an email transcript', + attributeI18nKey: 'SEND_EMAIL_TRANSCRIPT', + }, + { + key: 'mute_conversation', + name: 'Mute conversation', + attributeI18nKey: 'MUTE_CONVERSATION', + }, + { + key: 'snooze_convresation', + name: 'Snooze conversation', + attributeI18nKey: 'MUTE_CONVERSATION', + }, + { + key: 'resolve_convresation', + name: 'Resolve conversation', + attributeI18nKey: 'RESOLVE_CONVERSATION', + }, + { + key: 'send_webhook_event', + name: 'Send Webhook Event', + attributeI18nKey: 'SEND_WEBHOOK_EVENT', }, ], }, @@ -217,13 +291,41 @@ export const AUTOMATION_ACTION_TYPES = [ { key: 'assign_team', label: 'Assign a team', + inputType: 'multi_select', }, { key: 'add_label', label: 'Add a label', + inputType: 'multi_select', }, // { // key: 'send_email_to_team', // label: 'Send an email to team', + // inputType: 'multi_select', // }, + { + key: 'send_email_transcript', + label: 'Send an email transcript', + inputType: 'email', + }, + { + key: 'mute_conversation', + label: 'Mute conversation', + inputType: null, + }, + { + key: 'snooze_convresation', + label: 'Snooze conversation', + inputType: null, + }, + { + key: 'resolve_convresation', + label: 'Resolve conversation', + inputType: null, + }, + { + key: 'send_webhook_event', + label: 'Send Webhook Event', + inputType: 'url', + }, ]; diff --git a/app/models/concerns/activity_message_handler.rb b/app/models/concerns/activity_message_handler.rb index 6ccdfa7a9..4a2b4f22e 100644 --- a/app/models/concerns/activity_message_handler.rb +++ b/app/models/concerns/activity_message_handler.rb @@ -10,6 +10,8 @@ module ActivityMessageHandler end def status_change_activity(user_name) + return send_automation_activity if Current.executed_by.present? + create_status_change_message(user_name) end @@ -29,6 +31,11 @@ module ActivityMessageHandler ::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content end + def send_automation_activity + content = I18n.t("conversations.activity.status.#{status}", user_name: 'Automation System') + ::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content + end + def create_label_added(user_name, labels = []) return unless labels.size.positive? diff --git a/app/services/automation_rules/action_service.rb b/app/services/automation_rules/action_service.rb index 158ac59c5..3848e30f5 100644 --- a/app/services/automation_rules/action_service.rb +++ b/app/services/automation_rules/action_service.rb @@ -21,21 +21,27 @@ class AutomationRules::ActionService private - def send_email_transcript(email) - ConversationReplyMailer.with(account: conversation.account).conversation_transcript(@conversation, email)&.deliver_later + def send_email_transcript(emails) + emails.each do |email| + ConversationReplyMailer.with(account: @conversation.account).conversation_transcript(@conversation, email)&.deliver_later + end end def mute_conversation(_params) @conversation.mute! end + def snooze_conversation(_params) + @conversation.ensure_snooze_until_reset + end + def change_status(status) @conversation.update!(status: status[0]) end - def send_webhook_events(webhook_url) + def send_webhook_event(webhook_url) payload = @conversation.webhook_data.merge(event: "automation_event: #{@rule.event_name}") - WebhookJob.perform_later(webhook_url, payload) + WebhookJob.perform_later(webhook_url[0], payload) end def send_message(message) diff --git a/app/services/filter_service.rb b/app/services/filter_service.rb index bf2d7d6a0..e0167b5af 100644 --- a/app/services/filter_service.rb +++ b/app/services/filter_service.rb @@ -28,7 +28,7 @@ class FilterService @filter_values["value_#{current_index}"] = filter_values(query_hash) equals_to_filter_string(query_hash[:filter_operator], current_index) when 'contains', 'does_not_contain' - @filter_values["value_#{current_index}"] = "%#{filter_values(query_hash)}%" + @filter_values["value_#{current_index}"] = "%#{string_filter_values(query_hash)}%" like_filter_string(query_hash[:filter_operator], current_index) when 'is_present' @filter_values["value_#{current_index}"] = 'IS NOT NULL' @@ -57,6 +57,12 @@ class FilterService end end + def string_filter_values(query_hash) + return query_hash['values'][0] if query_hash['values'].is_a?(Array) + + query_hash['values'] + end + def lt_gt_filter_values(query_hash) attribute_key = query_hash[:attribute_key] attribute_type = custom_attribute(attribute_key).try(:attribute_display_type) diff --git a/spec/listeners/automation_rule_listener_spec.rb b/spec/listeners/automation_rule_listener_spec.rb index ff289a6dd..66a9a68ac 100644 --- a/spec/listeners/automation_rule_listener_spec.rb +++ b/spec/listeners/automation_rule_listener_spec.rb @@ -31,7 +31,7 @@ describe AutomationRuleListener do }, { 'action_name' => 'assign_team', 'action_params' => [team.id] }, { 'action_name' => 'add_label', 'action_params' => %w[support priority_customer] }, - { 'action_name' => 'send_webhook_events', 'action_params' => 'https://www.example.com' }, + { 'action_name' => 'send_webhook_event', 'action_params' => ['https://www.example.com'] }, { 'action_name' => 'assign_best_agent', 'action_params' => [user_1.id] }, { 'action_name' => 'send_email_transcript', 'action_params' => 'new_agent@example.com' }, { 'action_name' => 'mute_conversation', 'action_params' => nil },