import AutomationActionTeamMessageInput from './AutomationActionTeamMessageInput.vue';
+import AutomationActionFileInput from './AutomationFileInput.vue';
export default {
components: {
AutomationActionTeamMessageInput,
+ AutomationActionFileInput,
},
props: {
value: {
@@ -109,6 +116,10 @@ export default {
type: Boolean,
default: true,
},
+ initialFileName: {
+ type: String,
+ default: '',
+ },
},
computed: {
action_name: {
@@ -187,6 +198,7 @@ export default {
.filter__answer--wrap {
margin-right: var(--space-smaller);
flex-grow: 1;
+ max-width: 50%;
input {
margin-bottom: 0;
diff --git a/app/javascript/dashboard/components/widgets/AutomationFileInput.vue b/app/javascript/dashboard/components/widgets/AutomationFileInput.vue
new file mode 100644
index 000000000..595faa8fb
--- /dev/null
+++ b/app/javascript/dashboard/components/widgets/AutomationFileInput.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/helper/actionQueryGenerator.js b/app/javascript/dashboard/helper/actionQueryGenerator.js
index 7ef5fa56f..965c5b238 100644
--- a/app/javascript/dashboard/helper/actionQueryGenerator.js
+++ b/app/javascript/dashboard/helper/actionQueryGenerator.js
@@ -1,7 +1,15 @@
+const allElementsString = arr => {
+ return arr.every(elem => typeof elem === 'string');
+};
+
+const allElementsNumbers = arr => {
+ return arr.every(elem => typeof elem === 'number');
+};
+
const formatArray = params => {
if (params.length <= 0) {
params = [];
- } else if (params.every(elem => typeof elem === 'string')) {
+ } else if (allElementsString(params) || allElementsNumbers(params)) {
params = [...params];
} else {
params = params.map(val => val.id);
diff --git a/app/javascript/dashboard/i18n/locale/en/automation.json b/app/javascript/dashboard/i18n/locale/en/automation.json
index 0d2076b65..5d291814e 100644
--- a/app/javascript/dashboard/i18n/locale/en/automation.json
+++ b/app/javascript/dashboard/i18n/locale/en/automation.json
@@ -104,6 +104,13 @@
"DEACTIVATION_ERROR": "Could not Deactivate Automation, Please try again later",
"CONFIRMATION_LABEL": "Yes",
"CANCEL_LABEL": "No"
+ },
+ "ATTACHMENT": {
+ "UPLOAD_ERROR": "Could not upload attachment, Please try again",
+ "LABEL_IDLE": "Upload Attachment",
+ "LABEL_UPLOADING": "Uploading...",
+ "LABEL_UPLOADED": "Succesfully Uploaded",
+ "LABEL_UPLOAD_FAILED": "Upload Failed"
}
}
}
diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue b/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue
index 6c5d03962..668b44d51 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue
@@ -101,6 +101,12 @@
showActionInput(automation.actions[i].action_name)
"
:v="$v.automation.actions.$each[i]"
+ :initial-file-name="
+ getFileName(
+ automation.actions[i].action_params[0],
+ automation.actions[i].action_name
+ )
+ "
@removeAction="removeAction(i)"
/>
@@ -507,6 +513,15 @@ export default {
if (type === null) return false;
return true;
},
+ getFileName(id, actionType) {
+ if (!id) return '';
+ if (actionType === 'send_attachment') {
+ const file = this.automation.files.find(item => item.blob_id === id);
+ // replace `blob_id.toString()` with file name once api is fixed.
+ if (file) return file.filename.toString();
+ }
+ return '';
+ },
},
};
diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js
index 3d1dac355..387d0d407 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js
+++ b/app/javascript/dashboard/routes/dashboard/settings/automation/constants.js
@@ -119,11 +119,11 @@ export const AUTOMATIONS = {
name: 'Send Webhook Event',
attributeI18nKey: 'SEND_WEBHOOK_EVENT',
},
- // {
- // key: 'send_attachment',
- // name: 'Send Attachment',
- // attributeI18nKey: 'SEND_ATTACHMENT',
- // },
+ {
+ key: 'send_attachment',
+ name: 'Send Attachment',
+ attributeI18nKey: 'SEND_ATTACHMENT',
+ },
],
},
conversation_created: {
@@ -210,11 +210,11 @@ export const AUTOMATIONS = {
name: 'Send Webhook Event',
attributeI18nKey: 'SEND_WEBHOOK_EVENT',
},
- // {
- // key: 'send_attachment',
- // name: 'Send Attachment',
- // attributeI18nKey: 'SEND_ATTACHMENT',
- // },
+ {
+ key: 'send_attachment',
+ name: 'Send Attachment',
+ attributeI18nKey: 'SEND_ATTACHMENT',
+ },
],
},
conversation_updated: {
@@ -315,11 +315,11 @@ export const AUTOMATIONS = {
name: 'Send Webhook Event',
attributeI18nKey: 'SEND_WEBHOOK_EVENT',
},
- // {
- // key: 'send_attachment',
- // name: 'Send Attachment',
- // attributeI18nKey: 'SEND_ATTACHMENT',
- // },
+ {
+ key: 'send_attachment',
+ name: 'Send Attachment',
+ attributeI18nKey: 'SEND_ATTACHMENT',
+ },
],
},
};
@@ -380,11 +380,11 @@ export const AUTOMATION_ACTION_TYPES = [
label: 'Send Webhook Event',
inputType: 'url',
},
- // {
- // key: 'send_attachment',
- // label: 'Send Attachment',
- // inputType: 'file',
- // },
+ {
+ key: 'send_attachment',
+ label: 'Send Attachment',
+ inputType: 'attachment',
+ },
{
key: 'send_message',
label: 'Send a message',
diff --git a/app/javascript/dashboard/store/modules/automations.js b/app/javascript/dashboard/store/modules/automations.js
index a588d9ae7..7d9f4cb69 100644
--- a/app/javascript/dashboard/store/modules/automations.js
+++ b/app/javascript/dashboard/store/modules/automations.js
@@ -76,6 +76,14 @@ export const actions = {
commit(types.SET_AUTOMATION_UI_FLAG, { isCloning: false });
}
},
+ uploadAttachment: async (_, file) => {
+ try {
+ const { data } = await AutomationAPI.attachment(file);
+ return data.blob_id;
+ } catch (error) {
+ throw new Error(error);
+ }
+ },
};
export const mutations = {
diff --git a/app/javascript/shared/components/FluentIcon/dashboard-icons.json b/app/javascript/shared/components/FluentIcon/dashboard-icons.json
index f145159d1..54dc0b627 100644
--- a/app/javascript/shared/components/FluentIcon/dashboard-icons.json
+++ b/app/javascript/shared/components/FluentIcon/dashboard-icons.json
@@ -68,6 +68,7 @@
"emoji-outline": "M12 1.999c5.524 0 10.002 4.478 10.002 10.002 0 5.523-4.478 10.001-10.002 10.001-5.524 0-10.002-4.478-10.002-10.001C1.998 6.477 6.476 1.999 12 1.999Zm0 1.5a8.502 8.502 0 1 0 0 17.003A8.502 8.502 0 0 0 12 3.5ZM8.462 14.784A4.491 4.491 0 0 0 12 16.502a4.492 4.492 0 0 0 3.535-1.714.75.75 0 1 1 1.177.93A5.991 5.991 0 0 1 12 18.002a5.991 5.991 0 0 1-4.716-2.29.75.75 0 0 1 1.178-.928ZM9 8.75a1.25 1.25 0 1 1 0 2.499A1.25 1.25 0 0 1 9 8.75Zm6 0a1.25 1.25 0 1 1 0 2.499 1.25 1.25 0 0 1 0-2.499Z",
"error-circle-outline": "M12 2c5.523 0 10 4.478 10 10s-4.477 10-10 10S2 17.522 2 12 6.477 2 12 2Zm0 1.667c-4.595 0-8.333 3.738-8.333 8.333 0 4.595 3.738 8.333 8.333 8.333 4.595 0 8.333-3.738 8.333-8.333 0-4.595-3.738-8.333-8.333-8.333Zm-.001 10.835a.999.999 0 1 1 0 1.998.999.999 0 0 1 0-1.998ZM11.994 7a.75.75 0 0 1 .744.648l.007.101.004 4.502a.75.75 0 0 1-1.493.103l-.007-.102-.004-4.501a.75.75 0 0 1 .75-.751Z",
"filter-outline": "M13.5 16a.75.75 0 0 1 0 1.5h-3a.75.75 0 0 1 0-1.5h3Zm3-5a.75.75 0 0 1 0 1.5h-9a.75.75 0 0 1 0-1.5h9Zm3-5a.75.75 0 0 1 0 1.5h-15a.75.75 0 0 1 0-1.5h15Z",
+ "file-upload-outline": "M6 2a2 2 0 0 0-2 2v5.207a5.48 5.48 0 0 1 1-.185V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-3.6a5.507 5.507 0 0 1-.657 1H14a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6Zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7ZM5.5 19a4.5 4.5 0 1 0 0-9a4.5 4.5 0 0 0 0 9Zm2.354-4.854a.5.5 0 1 1-.708.708L6 13.707V16.5a.5.5 0 0 1-1 0v-2.793l-1.146 1.147a.5.5 0 1 1-.708-.707l2-2A.5.5 0 0 1 5.497 12h.006a.498.498 0 0 1 .348.144l.003.003l2 2Z",
"flash-on-outline": "m8.294 14-1.767 7.068c-.187.746.736 1.256 1.269.701L19.79 9.27A.75.75 0 0 0 19.25 8h-4.46l1.672-5.013A.75.75 0 0 0 15.75 2h-7a.75.75 0 0 0-.721.544l-3 10.5A.75.75 0 0 0 5.75 14h2.544Zm4.745-5.487a.75.75 0 0 0 .711.987h3.74l-8.824 9.196 1.316-5.264a.75.75 0 0 0-.727-.932h-2.51l2.57-9h5.394l-1.67 5.013Z",
"folder-outline": "M8.207 4c.46 0 .908.141 1.284.402l.156.12L12.022 6.5h7.728a2.25 2.25 0 0 1 2.229 1.938l.016.158.005.154v9a2.25 2.25 0 0 1-2.096 2.245L19.75 20H4.25a2.25 2.25 0 0 1-2.245-2.096L2 17.75V6.25a2.25 2.25 0 0 1 2.096-2.245L4.25 4h3.957Zm1.44 5.979a2.25 2.25 0 0 1-1.244.512l-.196.009-4.707-.001v7.251c0 .38.282.694.648.743l.102.007h15.5a.75.75 0 0 0 .743-.648l.007-.102v-9a.75.75 0 0 0-.648-.743L19.75 8h-7.729L9.647 9.979ZM8.207 5.5H4.25a.75.75 0 0 0-.743.648L3.5 6.25v2.749L8.207 9a.75.75 0 0 0 .395-.113l.085-.06 1.891-1.578-1.89-1.575a.75.75 0 0 0-.377-.167L8.207 5.5Z",
"globe-desktop-outline": "M22.002 12C22.002 6.477 17.524 2 12 2 6.476 1.999 2 6.477 2 12.001c0 5.186 3.947 9.45 9.001 9.952V20.11c-.778-.612-1.478-1.905-1.939-3.61h1.94V15H8.737a18.969 18.969 0 0 1-.135-5h6.794c.068.64.105 1.31.105 2h1.5c0-.684-.033-1.353-.095-2h3.358c.154.64.237 1.31.237 2h1.5ZM4.786 16.5h2.722l.102.396c.317 1.17.748 2.195 1.27 3.015a8.532 8.532 0 0 1-4.094-3.41ZM3.736 10h3.358a20.847 20.847 0 0 0-.095 2c0 1.043.075 2.051.217 3H4.043a8.483 8.483 0 0 1-.544-3c0-.682.08-1.347.232-1.983L3.736 10Zm5.122-5.902.023-.008C8.16 5.222 7.611 6.748 7.298 8.5H4.25c.905-2 2.56-3.587 4.608-4.402Zm3.026-.594L12 3.5l.126.006c1.262.126 2.48 2.125 3.045 4.995H8.83c.568-2.878 1.79-4.88 3.055-4.996Zm3.343.76-.107-.174.291.121a8.533 8.533 0 0 1 4.339 4.29h-3.048c-.298-1.665-.806-3.125-1.475-4.237Z M12 19a1 1 0 0 0 1 1h3v2h-.5a.5.5 0 1 0 0 1h4a.5.5 0 0 0 0-1H19v-2h3a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1h-9a1 1 0 0 0-1 1v5Z",
diff --git a/app/models/attachment.rb b/app/models/attachment.rb
index 43ae583ea..2a8e575f3 100644
--- a/app/models/attachment.rb
+++ b/app/models/attachment.rb
@@ -28,7 +28,6 @@ class Attachment < ApplicationRecord
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
application/vnd.openxmlformats-officedocument.wordprocessingml.document
].freeze
-
belongs_to :account
belongs_to :message
has_one_attached :file
diff --git a/app/models/automation_rule.rb b/app/models/automation_rule.rb
index 8211de98b..d76f2058e 100644
--- a/app/models/automation_rule.rb
+++ b/app/models/automation_rule.rb
@@ -18,6 +18,8 @@
# index_automation_rules_on_account_id (account_id)
#
class AutomationRule < ApplicationRecord
+ include Rails.application.routes.url_helpers
+
belongs_to :account
has_many_attached :files
@@ -28,7 +30,21 @@ class AutomationRule < ApplicationRecord
scope :active, -> { where(active: true) }
CONDITIONS_ATTRS = %w[content email country_code status message_type browser_language assignee_id team_id referer city company inbox_id].freeze
- ACTIONS_ATTRS = %w[send_message add_label send_email_to_team assign_team assign_best_agents send_attachments].freeze
+ ACTIONS_ATTRS = %w[send_message add_label send_email_to_team assign_team assign_best_agents send_attachment].freeze
+
+ def file_base_data
+ files.map do |file|
+ {
+ id: file.id,
+ automation_rule_id: id,
+ file_type: file.content_type,
+ account_id: account_id,
+ file_url: url_for(file),
+ blob_id: file.blob_id,
+ filename: file.filename.to_s
+ }
+ end
+ end
private
diff --git a/app/policies/automation_rule_policy.rb b/app/policies/automation_rule_policy.rb
index 431b32964..b0e33437d 100644
--- a/app/policies/automation_rule_policy.rb
+++ b/app/policies/automation_rule_policy.rb
@@ -7,6 +7,10 @@ class AutomationRulePolicy < ApplicationPolicy
@account_user.administrator?
end
+ def attach_file?
+ @account_user.administrator?
+ end
+
def show?
@account_user.administrator?
end
diff --git a/app/services/automation_rules/action_service.rb b/app/services/automation_rules/action_service.rb
index 887e82b38..e88ca06dd 100644
--- a/app/services/automation_rules/action_service.rb
+++ b/app/services/automation_rules/action_service.rb
@@ -21,9 +21,14 @@ class AutomationRules::ActionService
private
- def send_attachments(_file_params)
- blobs = @rule.files.map { |file, _| file.blob }
- params = { content: nil, private: false, attachments: blobs }
+ def send_attachment(blob_ids)
+ return unless @rule.files.attached?
+
+ blob = ActiveStorage::Blob.find(blob_ids)
+
+ return if blob.blank?
+
+ params = { content: nil, private: false, attachments: blob }
mb = Messages::MessageBuilder.new(nil, @conversation, params)
mb.perform
end
@@ -51,7 +56,7 @@ class AutomationRules::ActionService
end
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[0], payload)
end
diff --git a/app/views/api/v1/accounts/automation_rules/partials/_automation_rule.json.jbuilder b/app/views/api/v1/accounts/automation_rules/partials/_automation_rule.json.jbuilder
index b0e01c824..a1047c82f 100644
--- a/app/views/api/v1/accounts/automation_rules/partials/_automation_rule.json.jbuilder
+++ b/app/views/api/v1/accounts/automation_rules/partials/_automation_rule.json.jbuilder
@@ -7,3 +7,4 @@ json.conditions automation_rule.conditions
json.actions automation_rule.actions
json.created_on automation_rule.created_at.to_i
json.active automation_rule.active?
+json.files automation_rule.file_base_data if automation_rule.files.any?
diff --git a/config/routes.rb b/config/routes.rb
index a2afa4f09..7ed667160 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -55,6 +55,7 @@ Rails.application.routes.draw do
resources :canned_responses, only: [:index, :create, :update, :destroy]
resources :automation_rules, only: [:index, :create, :show, :update, :destroy] do
post :clone
+ post :attach_file, on: :collection
end
resources :campaigns, only: [:index, :create, :show, :update, :destroy]
diff --git a/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb b/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb
index 2039daf15..4513773bc 100644
--- a/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb
@@ -133,29 +133,35 @@ RSpec.describe 'Api::V1::Accounts::AutomationRulesController', type: :request do
it 'Saves file in the automation actions to send an attachments' do
file = fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png')
- params[:attachments] = [file]
+
+ expect(account.automation_rules.count).to eq(0)
+
+ post "/api/v1/accounts/#{account.id}/automation_rules/attach_file",
+ headers: administrator.create_new_auth_token,
+ params: { attachment: file }
+
+ expect(response).to have_http_status(:success)
+
+ blob = JSON.parse(response.body)
+
+ expect(blob['blob_key']).to be_present
+ expect(blob['blob_id']).to be_present
+
params[:actions] = [
{
action_name: :send_message,
action_params: ['Welcome to the chatwoot platform.']
},
{
- action_name: :update_additional_attributes,
- action_params: [{ intiated_at: '2021-12-03 17:25:26.844536 +0530' }]
- },
- {
- action_name: :send_attachments
+ action_name: :send_attachment,
+ action_params: [blob['blob_id']]
}
]
- expect(account.automation_rules.count).to eq(0)
-
post "/api/v1/accounts/#{account.id}/automation_rules",
headers: administrator.create_new_auth_token,
params: params
- expect(response).to have_http_status(:success)
- expect(account.automation_rules.count).to eq(1)
automation_rule = account.automation_rules.first
expect(automation_rule.files.presence).to be_truthy
expect(automation_rule.files.count).to eq(1)
@@ -163,32 +169,36 @@ RSpec.describe 'Api::V1::Accounts::AutomationRulesController', type: :request do
it 'Saves files in the automation actions to send multiple attachments' do
file_1 = fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png')
- file_2 = fixture_file_upload(Rails.root.join('spec/assets/sample.pdf'), 'image/png')
- params[:attachments] = [file_1, file_2]
+ file_2 = fixture_file_upload(Rails.root.join('spec/assets/sample.png'), 'image/png')
+
+ post "/api/v1/accounts/#{account.id}/automation_rules/attach_file",
+ headers: administrator.create_new_auth_token,
+ params: { attachment: file_1 }
+
+ blob_1 = JSON.parse(response.body)
+
+ post "/api/v1/accounts/#{account.id}/automation_rules/attach_file",
+ headers: administrator.create_new_auth_token,
+ params: { attachment: file_2 }
+
+ blob_2 = JSON.parse(response.body)
+
params[:actions] = [
{
- action_name: :send_message,
- action_params: ['Welcome to the chatwoot platform.']
+ action_name: :send_attachment,
+ action_params: [blob_1['blob_id']]
},
{
- action_name: :update_additional_attributes,
- action_params: [{ intiated_at: '2021-12-03 17:25:26.844536 +0530' }]
- },
- {
- action_name: :send_attachments
+ action_name: :send_attachment,
+ action_params: [blob_2['blob_id']]
}
]
- expect(account.automation_rules.count).to eq(0)
-
post "/api/v1/accounts/#{account.id}/automation_rules",
headers: administrator.create_new_auth_token,
params: params
- expect(response).to have_http_status(:success)
- expect(account.automation_rules.count).to eq(1)
automation_rule = account.automation_rules.first
- expect(automation_rule.files.presence).to be_truthy
expect(automation_rule.files.count).to eq(2)
end
end
diff --git a/spec/listeners/automation_rule_listener_spec.rb b/spec/listeners/automation_rule_listener_spec.rb
index 079b9bbd5..6213e4450 100644
--- a/spec/listeners/automation_rule_listener_spec.rb
+++ b/spec/listeners/automation_rule_listener_spec.rb
@@ -128,6 +128,15 @@ describe AutomationRuleListener do
expect(conversation.messages.first.content).to eq('Send this message.')
end
+ it 'triggers automation rule to mute conversation' do
+ expect(conversation).not_to be_muted
+
+ listener.conversation_updated(event)
+
+ conversation.reload
+ expect(conversation).to be_muted
+ end
+
it 'triggers automation_rule with contact standard attributes' do
automation_rule.update!(
conditions: [