From 050df4378971a384fc525917301644eb3dd8d2c9 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Mon, 18 Oct 2021 18:39:04 +0530 Subject: [PATCH 01/59] feat: Adds sound alert notification for new messages on the widget (#3181) * feat: Adds sound alert notification for new messages on the widget * Review fixes * uses absolute path * Minor fixes Co-authored-by: Muhsin Keloth --- app/javascript/packs/widget.js | 2 ++ app/javascript/shared/helpers/AudioNotificationHelper.js | 4 ++++ app/javascript/widget/helpers/actionCable.js | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/app/javascript/packs/widget.js b/app/javascript/packs/widget.js index e4aa8cfd1f06..7aeaf4545c20 100644 --- a/app/javascript/packs/widget.js +++ b/app/javascript/packs/widget.js @@ -4,6 +4,7 @@ import VueI18n from 'vue-i18n'; import store from '../widget/store'; import App from '../widget/App.vue'; import ActionCableConnector from '../widget/helpers/actionCable'; +import { getAlertAudio } from 'shared/helpers/AudioNotificationHelper'; import i18n from '../widget/i18n'; Vue.use(VueI18n); @@ -30,4 +31,5 @@ window.onload = () => { window.WOOT_WIDGET, window.chatwootPubsubToken ); + getAlertAudio(); }; diff --git a/app/javascript/shared/helpers/AudioNotificationHelper.js b/app/javascript/shared/helpers/AudioNotificationHelper.js index 07d4bafb0f9f..9896d6b8e7c6 100644 --- a/app/javascript/shared/helpers/AudioNotificationHelper.js +++ b/app/javascript/shared/helpers/AudioNotificationHelper.js @@ -94,3 +94,7 @@ export const newMessageNotification = data => { showBadgeOnFavicon(); } }; + +export const playNewMessageNotificationInWidget = () => { + window.playAudioAlert(); +}; diff --git a/app/javascript/widget/helpers/actionCable.js b/app/javascript/widget/helpers/actionCable.js index c4cf483c6616..1bdbba850311 100644 --- a/app/javascript/widget/helpers/actionCable.js +++ b/app/javascript/widget/helpers/actionCable.js @@ -1,4 +1,5 @@ import BaseActionCableConnector from '../../shared/helpers/BaseActionCableConnector'; +import { playNewMessageNotificationInWidget } from 'shared/helpers/AudioNotificationHelper'; class ActionCableConnector extends BaseActionCableConnector { constructor(app, pubsubToken) { @@ -36,6 +37,9 @@ class ActionCableConnector extends BaseActionCableConnector { .then(() => { window.bus.$emit('on-agent-message-received'); }); + if (data.sender_type === 'User') { + playNewMessageNotificationInWidget(); + } }; onMessageUpdated = data => { From b0041ccf3fcb66e450f57940676d29a13e1f438e Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Wed, 20 Oct 2021 09:05:08 +0530 Subject: [PATCH 02/59] fix: Close button click action in contact details sidepanel (#3245) --- .../routes/dashboard/contacts/components/ContactInfoPanel.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue index 47d48d96935d..041052a09a6b 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue +++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue @@ -94,6 +94,7 @@ export default { top: 3.6rem; font-size: var(--font-size-big); color: var(--s-500); + z-index: 1; .close-icon { margin-right: var(--space-smaller); From 658a511b884a72e92c1d9b4de16139b8701ed91d Mon Sep 17 00:00:00 2001 From: Tejaswini Chile Date: Wed, 20 Oct 2021 18:14:56 +0530 Subject: [PATCH 03/59] feat: Conversation and contact endpoint (#3198) --- .../api/v1/accounts/contacts_controller.rb | 4 + .../v1/accounts/conversations_controller.rb | 6 +- app/policies/contact_policy.rb | 4 + .../v1/accounts/contacts/filter.json.jbuilder | 3 + .../conversations/filter.json.jbuilder | 3 + .../conversations/search.json.jbuilder | 22 +---- .../api/v1/models/_conversation.json.jbuilder | 21 +++++ config/routes.rb | 8 +- lib/filters/conversation_filters.json | 92 +++++++++++++++++++ .../v1/accounts/contacts_controller_spec.rb | 27 ++++++ .../accounts/conversations_controller_spec.rb | 34 +++++++ 11 files changed, 200 insertions(+), 24 deletions(-) create mode 100644 app/views/api/v1/accounts/contacts/filter.json.jbuilder create mode 100644 app/views/api/v1/accounts/conversations/filter.json.jbuilder create mode 100644 app/views/api/v1/models/_conversation.json.jbuilder create mode 100644 lib/filters/conversation_filters.json diff --git a/app/controllers/api/v1/accounts/contacts_controller.rb b/app/controllers/api/v1/accounts/contacts_controller.rb index 561b224c30fb..0c9341b08c8a 100644 --- a/app/controllers/api/v1/accounts/contacts_controller.rb +++ b/app/controllers/api/v1/accounts/contacts_controller.rb @@ -50,6 +50,10 @@ def active def show; end + def filter + @contacts = Current.account.contacts.limit(10) + end + def contactable_inboxes @all_contactable_inboxes = Contacts::ContactableInboxesService.new(contact: @contact).get @contactable_inboxes = @all_contactable_inboxes.select { |contactable_inbox| policy(contactable_inbox[:inbox]).show? } diff --git a/app/controllers/api/v1/accounts/conversations_controller.rb b/app/controllers/api/v1/accounts/conversations_controller.rb index 89d48e544a1d..db9f65b414ce 100644 --- a/app/controllers/api/v1/accounts/conversations_controller.rb +++ b/app/controllers/api/v1/accounts/conversations_controller.rb @@ -2,7 +2,7 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro include Events::Types include DateRangeHelper - before_action :conversation, except: [:index, :meta, :search, :create] + before_action :conversation, except: [:index, :meta, :search, :create, :filter] before_action :contact_inbox, only: [:create] def index @@ -31,6 +31,10 @@ def create def show; end + def filter + @conversations = Current.account.conversations.limit(10) + end + def mute @conversation.mute! head :ok diff --git a/app/policies/contact_policy.rb b/app/policies/contact_policy.rb index 9013014d740b..67ab23c5d87d 100644 --- a/app/policies/contact_policy.rb +++ b/app/policies/contact_policy.rb @@ -15,6 +15,10 @@ def search? true end + def filter? + true + end + def update? true end diff --git a/app/views/api/v1/accounts/contacts/filter.json.jbuilder b/app/views/api/v1/accounts/contacts/filter.json.jbuilder new file mode 100644 index 000000000000..87d953b5b250 --- /dev/null +++ b/app/views/api/v1/accounts/contacts/filter.json.jbuilder @@ -0,0 +1,3 @@ +json.array! @contacts do |contact| + json.partial! 'api/v1/models/contact.json.jbuilder', resource: contact +end diff --git a/app/views/api/v1/accounts/conversations/filter.json.jbuilder b/app/views/api/v1/accounts/conversations/filter.json.jbuilder new file mode 100644 index 000000000000..6b8baf3cef39 --- /dev/null +++ b/app/views/api/v1/accounts/conversations/filter.json.jbuilder @@ -0,0 +1,3 @@ +json.array! @conversations do |conversation| + json.partial! 'api/v1/models/conversation.json.jbuilder', conversation: conversation +end diff --git a/app/views/api/v1/accounts/conversations/search.json.jbuilder b/app/views/api/v1/accounts/conversations/search.json.jbuilder index ab9114ad7d54..ac70d3a55e48 100644 --- a/app/views/api/v1/accounts/conversations/search.json.jbuilder +++ b/app/views/api/v1/accounts/conversations/search.json.jbuilder @@ -5,26 +5,6 @@ json.meta do end json.payload do json.array! @conversations do |conversation| - json.id conversation.display_id - json.created_at conversation.created_at.to_i - json.contact do - json.id conversation.contact.id - json.name conversation.contact.name - end - json.inbox do - json.id conversation.inbox.id - json.name conversation.inbox.name - json.channel_type conversation.inbox.channel_type - end - json.messages do - json.array! conversation.messages do |message| - json.content message.content - json.id message.id - json.sender_name message.sender.name if message.sender - json.message_type message.message_type_before_type_cast - json.created_at message.created_at.to_i - end - end - json.account_id conversation.account_id + json.partial! 'api/v1/models/conversation.json.jbuilder', conversation: conversation end end diff --git a/app/views/api/v1/models/_conversation.json.jbuilder b/app/views/api/v1/models/_conversation.json.jbuilder new file mode 100644 index 000000000000..0951892a61f0 --- /dev/null +++ b/app/views/api/v1/models/_conversation.json.jbuilder @@ -0,0 +1,21 @@ +json.id conversation.display_id +json.created_at conversation.created_at.to_i +json.contact do + json.id conversation.contact.id + json.name conversation.contact.name +end +json.inbox do + json.id conversation.inbox.id + json.name conversation.inbox.name + json.channel_type conversation.inbox.channel_type +end +json.messages do + json.array! conversation.messages do |message| + json.content message.content + json.id message.id + json.sender_name message.sender.name if message.sender + json.message_type message.message_type_before_type_cast + json.created_at message.created_at.to_i + end +end +json.account_id conversation.account_id diff --git a/config/routes.rb b/config/routes.rb index 3d629d2fd5f3..61a5d15a5410 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -58,8 +58,11 @@ resource :twilio_channel, only: [:create] end resources :conversations, only: [:index, :create, :show] do - get 'meta', on: :collection - get 'search', on: :collection + collection do + get :meta + get :search + get :filter + end scope module: :conversations do resources :messages, only: [:index, :create, :destroy] resources :assignments, only: [:create] @@ -80,6 +83,7 @@ collection do get :active get :search + get :filter post :import end member do diff --git a/lib/filters/conversation_filters.json b/lib/filters/conversation_filters.json new file mode 100644 index 000000000000..6e493e57aff2 --- /dev/null +++ b/lib/filters/conversation_filters.json @@ -0,0 +1,92 @@ +{ + "conversations": [ + { + "attribute_key": "status", + "attribute_name": "Status", + "input_type": "multi_select", + "data_type": "text", + "filter_operators": [ "equal_to", "not_equal_to" ], + "attribute_type": "standard" + }, + { + "attribute_key": "assigne", + "attribute_name": "Assignee Name", + "input_type": "search_box with name tags/plain text", + "data_type": "text", + "filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ], + "attribute_type": "standard" + }, + { + "attribute_key": "contact", + "attribute_name": "Contact Name", + "input_type": "plain_text", + "data_type": "text", + "filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ], + "attribute_type": "standard" + }, + { + "attribute_key": "inbox", + "attribute_name": "Inbox Name", + "input_type": "search_box", + "data_type": "text", + "filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ], + "attribute_type": "standard" + }, + { + "attribute_key": "team_id", + "attribute_name": "Team Name", + "input_type": "search_box", + "data_type": "number", + "filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ], + "attribute_type": "standard" + }, + { + "attribute_key": "id", + "attribute_name": "Conversation Identifier", + "input_type": "textbox", + "data_type": "Number", + "filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ], + "attribute_type": "standard" + }, + { + "attribute_key": "campaign_id", + "attribute_name": "Campaign Name", + "input_type": "textbox", + "data_type": "Number", + "filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "is_present", "is_not_present" ], + "attribute_type": "standard" + }, + { + "attribute_key": "labels", + "attribute_name": "Labels", + "input_type": "tags", + "data_type": "text", + "filter_operators": ["exactly_equal_to", "contains", "does_not_contain" ], + "attribute_type": "standard" + }, + { + "attribute_key": "browser", + "attribute_name": "browser", + "input_type": "textbox", + "data_type": "text", + "filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain" ], + "attribute_type": "additional_attributes" + }, + { + "attribute_key": "country_code", + "attribute_name": "Country Name", + "input_type": "textbox", + "data_type": "text", + "filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "present", "is_not_present" ], + "attribute_type": "additional_attributes" + }, + { + "attribute_key": "referer", + "attribute_name": "Referer link", + "input_type": "textbox", + "data_type": "link", + "filter_operators": [ "equal_to", "not_equal_to", "contains", "does_not_contain", "present", "is_not_present" ], + "attribute_type": "additional_attributes" + } + ] +} diff --git a/spec/controllers/api/v1/accounts/contacts_controller_spec.rb b/spec/controllers/api/v1/accounts/contacts_controller_spec.rb index 13d2649f3993..dc7d81146698 100644 --- a/spec/controllers/api/v1/accounts/contacts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/contacts_controller_spec.rb @@ -218,6 +218,33 @@ end end + describe 'GET /api/v1/accounts/{account.id}/contacts/filter' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v1/accounts/#{account.id}/contacts/filter" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + let(:admin) { create(:user, account: account, role: :administrator) } + let!(:contact1) { create(:contact, :with_email, account: account) } + let!(:contact2) { create(:contact, :with_email, name: 'testcontact', account: account, email: 'test@test.com') } + + it 'returns all contacts when query is empty' do + get "/api/v1/accounts/#{account.id}/contacts/filter", + params: { q: [] }, + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(response.body).to include(contact2.email) + expect(response.body).to include(contact1.email) + end + end + end + describe 'GET /api/v1/accounts/{account.id}/contacts/:id' do let!(:contact) { create(:contact, account: account) } diff --git a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb index 025932175b37..dafec9ea7bbc 100644 --- a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb @@ -109,6 +109,40 @@ end end + describe 'GET /api/v1/accounts/{account.id}/conversations/filter' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v1/accounts/#{account.id}/conversations/filter", params: { q: 'test' } + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + let(:agent) { create(:user, account: account, role: :agent) } + + before do + conversation = create(:conversation, account: account) + create(:message, conversation: conversation, account: account, content: 'test1') + create(:message, conversation: conversation, account: account, content: 'test2') + create(:inbox_member, user: agent, inbox: conversation.inbox) + end + + it 'returns all conversations with empty query' do + get "/api/v1/accounts/#{account.id}/conversations/filter", + headers: agent.create_new_auth_token, + params: { q: 'test1' }, + as: :json + + expect(response).to have_http_status(:success) + response_data = JSON.parse(response.body, symbolize_names: true) + + expect(response_data.count).to eq(1) + expect(response_data[0][:messages][0][:content]).to include(Message.first.content) + end + end + end + describe 'GET /api/v1/accounts/{account.id}/conversations/:id' do let(:conversation) { create(:conversation, account: account) } From 09643b63b0c628d1750a340985507a83f0c98880 Mon Sep 17 00:00:00 2001 From: Jan-David Date: Fri, 22 Oct 2021 07:12:13 +0200 Subject: [PATCH 04/59] fix: Use correct German translation of 'disabled' (#3255) --- app/javascript/dashboard/i18n/locale/de/inboxMgmt.json | 6 +++--- .../dashboard/i18n/locale/de/integrationApps.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/javascript/dashboard/i18n/locale/de/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/de/inboxMgmt.json index d2bd065e250f..c496e69db38b 100644 --- a/app/javascript/dashboard/i18n/locale/de/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/de/inboxMgmt.json @@ -292,15 +292,15 @@ }, "AUTO_ASSIGNMENT": { "ENABLED": "Aktiviert", - "DISABLED": "Behindert" + "DISABLED": "Deaktiviert" }, "EMAIL_COLLECT_BOX": { "ENABLED": "Aktiviert", - "DISABLED": "Behindert" + "DISABLED": "Deaktiviert" }, "ENABLE_CSAT": { "ENABLED": "Aktiviert", - "DISABLED": "Behindert" + "DISABLED": "Deaktiviert" }, "ENABLE_HMAC": { "LABEL": "Enable" diff --git a/app/javascript/dashboard/i18n/locale/de/integrationApps.json b/app/javascript/dashboard/i18n/locale/de/integrationApps.json index e6090ad5d78b..c3f10e2107c2 100644 --- a/app/javascript/dashboard/i18n/locale/de/integrationApps.json +++ b/app/javascript/dashboard/i18n/locale/de/integrationApps.json @@ -5,7 +5,7 @@ "HEADER": "Anwendungen", "STATUS": { "ENABLED": "Aktiviert", - "DISABLED": "Behindert" + "DISABLED": "Deaktiviert" }, "CONFIGURE": "Konfigurieren", "ADD_BUTTON": "Neuen Hook hinzufügen", From 19855a90e2daf123009888eb8cad229aece1445e Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Fri, 22 Oct 2021 19:23:05 +0530 Subject: [PATCH 05/59] fix: Remove duplicate attachment downloads (#3262) --- .../dashboard/components/widgets/conversation/bubble/File.vue | 2 +- app/javascript/widget/components/FileBubble.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/dashboard/components/widgets/conversation/bubble/File.vue b/app/javascript/dashboard/components/widgets/conversation/bubble/File.vue index e1cf8c7a54aa..fb761742f756 100644 --- a/app/javascript/dashboard/components/widgets/conversation/bubble/File.vue +++ b/app/javascript/dashboard/components/widgets/conversation/bubble/File.vue @@ -1,5 +1,5 @@