feat: New automation actions (#4033)
This commit is contained in:
parent
cffc984ef6
commit
c674393c02
9 changed files with 192 additions and 35 deletions
|
@ -34,7 +34,7 @@ class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseCont
|
||||||
params.permit(
|
params.permit(
|
||||||
:name, :description, :event_name, :account_id, :active,
|
:name, :description, :event_name, :account_id, :active,
|
||||||
conditions: [:attribute_key, :filter_operator, :query_operator, { values: [] }],
|
conditions: [:attribute_key, :filter_operator, :query_operator, { values: [] }],
|
||||||
actions: [:action_name, { action_params: [{}] }]
|
actions: [:action_name, { action_params: [] }]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
<select
|
<select
|
||||||
v-model="action_name"
|
v-model="action_name"
|
||||||
class="action__question"
|
class="action__question"
|
||||||
@change="resetFilter()"
|
:class="{ 'full-width': !inputType }"
|
||||||
|
@change="resetAction()"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="attribute in actionTypes"
|
v-for="attribute in actionTypes"
|
||||||
|
@ -18,19 +19,38 @@
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="filter__answer--wrap">
|
<div class="filter__answer--wrap">
|
||||||
<div class="multiselect-wrap--small">
|
<div v-if="inputType">
|
||||||
<multiselect
|
<div
|
||||||
|
v-if="inputType === 'multi_select'"
|
||||||
|
class="multiselect-wrap--small"
|
||||||
|
>
|
||||||
|
<multiselect
|
||||||
|
v-model="action_params"
|
||||||
|
track-by="id"
|
||||||
|
label="name"
|
||||||
|
:placeholder="'Select'"
|
||||||
|
:multiple="true"
|
||||||
|
selected-label
|
||||||
|
:select-label="$t('FORMS.MULTISELECT.ENTER_TO_SELECT')"
|
||||||
|
deselect-label=""
|
||||||
|
:max-height="160"
|
||||||
|
:options="dropdownValues"
|
||||||
|
:allow-empty="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
v-else-if="inputType === 'email'"
|
||||||
v-model="action_params"
|
v-model="action_params"
|
||||||
track-by="id"
|
type="email"
|
||||||
label="name"
|
class="answer--text-input"
|
||||||
:placeholder="'Select'"
|
placeholder="Enter email"
|
||||||
:multiple="true"
|
/>
|
||||||
selected-label
|
<input
|
||||||
:select-label="$t('FORMS.MULTISELECT.ENTER_TO_SELECT')"
|
v-else-if="inputType === 'url'"
|
||||||
deselect-label=""
|
v-model="action_params"
|
||||||
:max-height="160"
|
type="url"
|
||||||
:options="dropdownValues"
|
class="answer--text-input"
|
||||||
:allow-empty="false"
|
placeholder="Enter url"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -91,13 +111,17 @@ export default {
|
||||||
this.$emit('input', { ...payload, action_params: value });
|
this.$emit('input', { ...payload, action_params: value });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
inputType() {
|
||||||
|
return this.actionTypes.find(action => action.key === this.action_name)
|
||||||
|
.inputType;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
removeAction() {
|
removeAction() {
|
||||||
this.$emit('removeAction');
|
this.$emit('removeAction');
|
||||||
},
|
},
|
||||||
resetFilter() {
|
resetAction() {
|
||||||
this.$emit('resetFilter');
|
this.$emit('resetAction');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -136,6 +160,10 @@ export default {
|
||||||
max-width: 50%;
|
max-width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action__question.full-width {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.filter__answer--wrap {
|
.filter__answer--wrap {
|
||||||
margin-right: var(--space-smaller);
|
margin-right: var(--space-smaller);
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|
|
@ -101,6 +101,7 @@
|
||||||
getActionDropdownValues(automation.actions[i].action_name)
|
getActionDropdownValues(automation.actions[i].action_name)
|
||||||
"
|
"
|
||||||
:v="$v.automation.actions.$each[i]"
|
:v="$v.automation.actions.$each[i]"
|
||||||
|
@resetAction="resetAction(i)"
|
||||||
@removeAction="removeAction(i)"
|
@removeAction="removeAction(i)"
|
||||||
/>
|
/>
|
||||||
<div class="filter-actions">
|
<div class="filter-actions">
|
||||||
|
@ -187,7 +188,13 @@ export default {
|
||||||
required,
|
required,
|
||||||
$each: {
|
$each: {
|
||||||
action_params: {
|
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) {
|
getActionDropdownValues(type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'assign_team':
|
case 'assign_team':
|
||||||
case 'send_email_to_team':
|
|
||||||
return this.$store.getters['teams/getTeams'];
|
return this.$store.getters['teams/getTeams'];
|
||||||
case 'add_label':
|
case 'add_label':
|
||||||
return this.$store.getters['labels/getLabels'].map(i => {
|
return this.$store.getters['labels/getLabels'].map(i => {
|
||||||
|
@ -424,6 +430,9 @@ export default {
|
||||||
).filterOperators[0].value;
|
).filterOperators[0].value;
|
||||||
this.automation.conditions[index].values = '';
|
this.automation.conditions[index].values = '';
|
||||||
},
|
},
|
||||||
|
resetAction(index) {
|
||||||
|
this.automation.actions[index].action_params = [];
|
||||||
|
},
|
||||||
showUserInput(operatorType) {
|
showUserInput(operatorType) {
|
||||||
if (operatorType === 'is_present' || operatorType === 'is_not_present')
|
if (operatorType === 'is_present' || operatorType === 'is_not_present')
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -351,7 +351,6 @@ export default {
|
||||||
getActionDropdownValues(type) {
|
getActionDropdownValues(type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'assign_team':
|
case 'assign_team':
|
||||||
case 'send_email_to_team':
|
|
||||||
return this.$store.getters['teams/getTeams'];
|
return this.$store.getters['teams/getTeams'];
|
||||||
case 'add_label':
|
case 'add_label':
|
||||||
return this.$store.getters['labels/getLabels'].map(i => {
|
return this.$store.getters['labels/getLabels'].map(i => {
|
||||||
|
|
|
@ -79,8 +79,33 @@ export const AUTOMATIONS = {
|
||||||
// {
|
// {
|
||||||
// key: 'send_email_to_team',
|
// key: 'send_email_to_team',
|
||||||
// name: 'Send an 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: {
|
conversation_created: {
|
||||||
|
@ -120,15 +145,40 @@ export const AUTOMATIONS = {
|
||||||
name: 'Assign a team',
|
name: 'Assign a team',
|
||||||
attributeI18nKey: 'ASSIGN_TEAM',
|
attributeI18nKey: 'ASSIGN_TEAM',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'assign_agent',
|
||||||
|
name: 'Assign an agent',
|
||||||
|
attributeI18nKey: 'ASSIGN_AGENT',
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// key: 'send_email_to_team',
|
// key: 'send_email_to_team',
|
||||||
// name: 'Send an email to team',
|
// name: 'Send an email to team',
|
||||||
// attributeI18nKey: 'SEND_MESSAGE',
|
// attributeI18nKey: 'SEND_MESSAGE',
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
key: 'assign_agent',
|
key: 'send_email_transcript',
|
||||||
name: 'Assign an agent',
|
name: 'Send an email transcript',
|
||||||
attributeI18nKey: 'ASSIGN_AGENT',
|
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',
|
name: 'Assign a team',
|
||||||
attributeI18nKey: 'ASSIGN_TEAM',
|
attributeI18nKey: 'ASSIGN_TEAM',
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// key: 'send_email_to_team',
|
|
||||||
// name: 'Send an email to team',
|
|
||||||
// attributeI18nKey: 'SEND_EMAIL_TO_TEAM',
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
key: 'assign_agent',
|
key: 'assign_agent',
|
||||||
name: 'Assign an agent',
|
name: 'Assign an agent',
|
||||||
attributeI18nKey: 'ASSIGN_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',
|
key: 'assign_team',
|
||||||
label: 'Assign a team',
|
label: 'Assign a team',
|
||||||
|
inputType: 'multi_select',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'add_label',
|
key: 'add_label',
|
||||||
label: 'Add a label',
|
label: 'Add a label',
|
||||||
|
inputType: 'multi_select',
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// key: 'send_email_to_team',
|
// key: 'send_email_to_team',
|
||||||
// label: 'Send an 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',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -10,6 +10,8 @@ module ActivityMessageHandler
|
||||||
end
|
end
|
||||||
|
|
||||||
def status_change_activity(user_name)
|
def status_change_activity(user_name)
|
||||||
|
return send_automation_activity if Current.executed_by.present?
|
||||||
|
|
||||||
create_status_change_message(user_name)
|
create_status_change_message(user_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -29,6 +31,11 @@ module ActivityMessageHandler
|
||||||
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
::Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||||
end
|
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 = [])
|
def create_label_added(user_name, labels = [])
|
||||||
return unless labels.size.positive?
|
return unless labels.size.positive?
|
||||||
|
|
||||||
|
|
|
@ -21,21 +21,27 @@ class AutomationRules::ActionService
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def send_email_transcript(email)
|
def send_email_transcript(emails)
|
||||||
ConversationReplyMailer.with(account: conversation.account).conversation_transcript(@conversation, email)&.deliver_later
|
emails.each do |email|
|
||||||
|
ConversationReplyMailer.with(account: @conversation.account).conversation_transcript(@conversation, email)&.deliver_later
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def mute_conversation(_params)
|
def mute_conversation(_params)
|
||||||
@conversation.mute!
|
@conversation.mute!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def snooze_conversation(_params)
|
||||||
|
@conversation.ensure_snooze_until_reset
|
||||||
|
end
|
||||||
|
|
||||||
def change_status(status)
|
def change_status(status)
|
||||||
@conversation.update!(status: status[0])
|
@conversation.update!(status: status[0])
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_webhook_events(webhook_url)
|
def send_webhook_event(webhook_url)
|
||||||
payload = @conversation.webhook_data.merge(event: "automation_event: #{@rule.event_name}")
|
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
|
end
|
||||||
|
|
||||||
def send_message(message)
|
def send_message(message)
|
||||||
|
|
|
@ -28,7 +28,7 @@ class FilterService
|
||||||
@filter_values["value_#{current_index}"] = filter_values(query_hash)
|
@filter_values["value_#{current_index}"] = filter_values(query_hash)
|
||||||
equals_to_filter_string(query_hash[:filter_operator], current_index)
|
equals_to_filter_string(query_hash[:filter_operator], current_index)
|
||||||
when 'contains', 'does_not_contain'
|
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)
|
like_filter_string(query_hash[:filter_operator], current_index)
|
||||||
when 'is_present'
|
when 'is_present'
|
||||||
@filter_values["value_#{current_index}"] = 'IS NOT NULL'
|
@filter_values["value_#{current_index}"] = 'IS NOT NULL'
|
||||||
|
@ -57,6 +57,12 @@ class FilterService
|
||||||
end
|
end
|
||||||
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)
|
def lt_gt_filter_values(query_hash)
|
||||||
attribute_key = query_hash[:attribute_key]
|
attribute_key = query_hash[:attribute_key]
|
||||||
attribute_type = custom_attribute(attribute_key).try(:attribute_display_type)
|
attribute_type = custom_attribute(attribute_key).try(:attribute_display_type)
|
||||||
|
|
|
@ -31,7 +31,7 @@ describe AutomationRuleListener do
|
||||||
},
|
},
|
||||||
{ 'action_name' => 'assign_team', 'action_params' => [team.id] },
|
{ 'action_name' => 'assign_team', 'action_params' => [team.id] },
|
||||||
{ 'action_name' => 'add_label', 'action_params' => %w[support priority_customer] },
|
{ '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' => 'assign_best_agent', 'action_params' => [user_1.id] },
|
||||||
{ 'action_name' => 'send_email_transcript', 'action_params' => 'new_agent@example.com' },
|
{ 'action_name' => 'send_email_transcript', 'action_params' => 'new_agent@example.com' },
|
||||||
{ 'action_name' => 'mute_conversation', 'action_params' => nil },
|
{ 'action_name' => 'mute_conversation', 'action_params' => nil },
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue