From 1ce36539aa37839a772dc11debf6f0841929e790 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Mon, 29 Mar 2021 11:09:06 +0530 Subject: [PATCH 01/41] fix: Show inbox name only if more > 1 inbox present (#2004) --- .../components/widgets/conversation/ConversationCard.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue index bfea8c86c..003784afb 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue @@ -18,7 +18,7 @@ size="40px" />
- + {{ inboxName }} @@ -161,7 +161,11 @@ export default { }, showInboxName() { - return !this.hideInboxName && this.isInboxNameVisible; + return ( + !this.hideInboxName && + this.isInboxNameVisible && + this.inboxesList.length > 1 + ); }, inboxName() { const stateInbox = this.chatInbox; From 24fea5fbe03ee5c2202f749b38cb950e10b38d2a Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Mon, 29 Mar 2021 13:05:51 +0530 Subject: [PATCH 02/41] chore(docs): Add docker pull count badge (#2005) Co-authored-by: Pranav Raj S --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bf868799f..a9938f01c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@

- Woot-logo + Woot-logo -

A simple and elegant live chat software
-
An opensource alternative to Intercom, Zendesk, Drift, Crisp etc.
+

Customer engagement suite, an open-source alternative to Intercom, Zendesk, Salesforce Service Cloud etc.

@@ -16,6 +15,7 @@ ___

Maintainability CircleCI Badge + Docker Pull Badge Docker Build Badge License Commits-per-month From 6bb026621d26b061149f7df07d8cb42c8f6fc773 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Mon, 29 Mar 2021 23:26:20 +0530 Subject: [PATCH 03/41] fix: Current.user nil issue when using access tokens (#2012) --- .rubocop.yml | 1 + .../api/v1/accounts/contacts/conversations_controller.rb | 6 +++--- app/controllers/concerns/access_token_auth_helper.rb | 3 ++- app/models/contact.rb | 1 + spec/factories/contacts.rb | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 01951faea..0fca6f54b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -105,6 +105,7 @@ Rails/UniqueValidationWithoutIndex: Exclude: - 'app/models/channel/twitter_profile.rb' - 'app/models/webhook.rb' + - 'app/models/contact.rb' Rails/RenderInline: Exclude: - 'app/controllers/swagger_controller.rb' diff --git a/app/controllers/api/v1/accounts/contacts/conversations_controller.rb b/app/controllers/api/v1/accounts/contacts/conversations_controller.rb index 8a9199b6b..576fbaa2f 100644 --- a/app/controllers/api/v1/accounts/contacts/conversations_controller.rb +++ b/app/controllers/api/v1/accounts/contacts/conversations_controller.rb @@ -8,10 +8,10 @@ class Api::V1::Accounts::Contacts::ConversationsController < Api::V1::Accounts:: private def inbox_ids - if current_user.administrator? + if Current.user.administrator? Current.account.inboxes.pluck(:id) - elsif current_user.agent? - current_user.assigned_inboxes.pluck(:id) + elsif Current.user.agent? + Current.user.assigned_inboxes.pluck(:id) else [] end diff --git a/app/controllers/concerns/access_token_auth_helper.rb b/app/controllers/concerns/access_token_auth_helper.rb index 3d6f55674..91bd461aa 100644 --- a/app/controllers/concerns/access_token_auth_helper.rb +++ b/app/controllers/concerns/access_token_auth_helper.rb @@ -14,6 +14,7 @@ module AccessTokenAuthHelper render_unauthorized('Invalid Access Token') && return if @access_token.blank? @resource = @access_token.owner + Current.user = @resource if current_user.is_a?(User) end def super_admin? @@ -21,7 +22,7 @@ module AccessTokenAuthHelper end def validate_bot_access_token! - return if current_user.is_a?(User) + return if Current.user.is_a?(User) return if super_admin? return if agent_bot_accessible? diff --git a/app/models/contact.rb b/app/models/contact.rb index f62ffc1f8..3d7eedf9a 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -31,6 +31,7 @@ class Contact < ApplicationRecord validates :account_id, presence: true validates :email, allow_blank: true, uniqueness: { scope: [:account_id], case_sensitive: false } validates :identifier, allow_blank: true, uniqueness: { scope: [:account_id] } + validates :phone_number, allow_blank: true, uniqueness: { scope: [:account_id] } belongs_to :account has_many :conversations, dependent: :destroy diff --git a/spec/factories/contacts.rb b/spec/factories/contacts.rb index eaecd67e9..68ca981e0 100644 --- a/spec/factories/contacts.rb +++ b/spec/factories/contacts.rb @@ -4,7 +4,7 @@ FactoryBot.define do factory :contact do sequence(:name) { |n| "Contact #{n}" } sequence(:email) { |n| "contact-#{n}@example.com" } - phone_number { '+123456789011' } + phone_number { Faker::PhoneNumber.cell_phone_in_e164 } avatar { fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') } account end From c95ad8dd4f6080f5a7db790b561026c92e56ae04 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Tue, 30 Mar 2021 16:02:41 +0530 Subject: [PATCH 04/41] chore: Remove validation on team name (#2014) --- app/models/team.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/models/team.rb b/app/models/team.rb index 605267ce4..9f6c2f3ed 100644 --- a/app/models/team.rb +++ b/app/models/team.rb @@ -20,8 +20,6 @@ # fk_rails_... (account_id => accounts.id) # class Team < ApplicationRecord - include RegexHelper - belongs_to :account has_many :team_members, dependent: :destroy has_many :members, through: :team_members, source: :user @@ -29,7 +27,6 @@ class Team < ApplicationRecord validates :name, presence: { message: 'must not be blank' }, - format: { with: UNICODE_CHARACTER_NUMBER_HYPHEN_UNDERSCORE }, uniqueness: { scope: :account_id } before_validation do From 42534e1df423abf62efcae16c2202b3418319d22 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Tue, 30 Mar 2021 18:16:17 +0530 Subject: [PATCH 05/41] chore: Add fail safes for Redis connections (#2016) --- config/initializers/redis.rb | 4 ---- lib/redis/config.rb | 4 +++- spec/lib/redis/config_spec.rb | 8 ++++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/config/initializers/redis.rb b/config/initializers/redis.rb index d1aa25b2e..1566098c9 100644 --- a/config/initializers/redis.rb +++ b/config/initializers/redis.rb @@ -4,7 +4,3 @@ redis = Rails.env.test? ? MockRedis.new : Redis.new(Redis::Config.app) # Add here as you use it for more features # Used for Round Robin, Conversation Emails & Online Presence $alfred = Redis::Namespace.new('alfred', redis: redis, warning: true) - -# https://github.com/mperham/sidekiq/issues/4591 -# TODO once sidekiq remove we can remove this -Redis.exists_returns_integer = false diff --git a/lib/redis/config.rb b/lib/redis/config.rb index 888ae89c3..35ea3e617 100644 --- a/lib/redis/config.rb +++ b/lib/redis/config.rb @@ -18,7 +18,9 @@ module Redis::Config def base_config { url: ENV.fetch('REDIS_URL', 'redis://127.0.0.1:6379'), - password: ENV.fetch('REDIS_PASSWORD', nil).presence + password: ENV.fetch('REDIS_PASSWORD', nil).presence, + reconnect_attempts: 2, + network_timeout: 5 } end diff --git a/spec/lib/redis/config_spec.rb b/spec/lib/redis/config_spec.rb index fa4f4c74c..2b919696f 100644 --- a/spec/lib/redis/config_spec.rb +++ b/spec/lib/redis/config_spec.rb @@ -16,14 +16,14 @@ describe ::Redis::Config do it 'checks for app redis config' do app_config = described_class.app - expect(app_config.keys).to match_array([:url, :password]) + expect(app_config.keys).to match_array([:url, :password, :network_timeout, :reconnect_attempts]) expect(app_config[:url]).to eq(redis_url) expect(app_config[:password]).to eq(redis_pasword) end it 'checks for sidekiq redis config' do sidekiq_config = described_class.sidekiq - expect(sidekiq_config.keys).to match_array([:url, :password, :size]) + expect(sidekiq_config.keys).to match_array([:url, :password, :size, :network_timeout, :reconnect_attempts]) expect(sidekiq_config[:url]).to eq redis_url expect(sidekiq_config[:password]).to eq redis_pasword expect(sidekiq_config[:size]).to eq described_class::SIDEKIQ_SIZE @@ -54,13 +54,13 @@ describe ::Redis::Config do end it 'checks for app redis config' do - expect(described_class.app.keys).to match_array([:url, :password, :sentinels]) + expect(described_class.app.keys).to match_array([:url, :password, :sentinels, :network_timeout, :reconnect_attempts]) expect(described_class.app[:url]).to eq("redis://#{redis_master_name}") expect(described_class.app[:sentinels]).to match_array(expected_sentinels) end it 'checks for sidekiq redis config' do - expect(described_class.sidekiq.keys).to match_array([:url, :password, :sentinels, :size]) + expect(described_class.sidekiq.keys).to match_array([:url, :password, :sentinels, :size, :network_timeout, :reconnect_attempts]) expect(described_class.sidekiq[:size]).to eq described_class::SIDEKIQ_SIZE end end From 71214b59d8d2b46fae8e9f03befb94f1276d9423 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Tue, 30 Mar 2021 20:00:17 +0530 Subject: [PATCH 06/41] fix: Add missing translations in agent reports (#2017) --- app/views/api/v2/accounts/reports/agents.csv.erb | 8 +++++++- config/locales/en.yml | 5 +++++ config/locales/ml.yml | 5 +++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/views/api/v2/accounts/reports/agents.csv.erb b/app/views/api/v2/accounts/reports/agents.csv.erb index 3675c0a66..ca0a7e1f4 100644 --- a/app/views/api/v2/accounts/reports/agents.csv.erb +++ b/app/views/api/v2/accounts/reports/agents.csv.erb @@ -1,4 +1,10 @@ -<% headers = ['Agent name', 'Conversations count', 'Avg first response time (Minutes)', 'Avg resolution time (Minutes)'] %> +<% headers = [ + I18n.t('reports.agent_csv.agent_name'), + I18n.t('reports.agent_csv.conversations_count'), + I18n.t('reports.agent_csv.avg_first_response_time'), + I18n.t('reports.agent_csv.avg_resolution_time') + ] +%> <%= CSV.generate_line headers %> <% Current.account.users.each do |agent| %> <% agent_report = V2::ReportBuilder.new(Current.account, { diff --git a/config/locales/en.yml b/config/locales/en.yml index a344118a0..35aac041d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -44,6 +44,11 @@ en: reports: period: Reporting period %{since} to %{until} + agent_csv: + agent_name: Agent name + conversations_count: Conversations count + avg_first_response_time: Avg first response time (Minutes) + avg_resolution_time: Avg resolution time (Minutes) notifications: notification_title: diff --git a/config/locales/ml.yml b/config/locales/ml.yml index 1340afc54..294665698 100644 --- a/config/locales/ml.yml +++ b/config/locales/ml.yml @@ -29,6 +29,11 @@ ml: failed: സൈനപ്പ് പരാജയപ്പെട്ടു reports: period: Reporting period %{since} to %{until} + agent_csv: + agent_name: ഏജന്റിന്റെ പേര് + conversations_count: സംഭാഷണങ്ങളുടെ എണ്ണം + avg_first_response_time: ശരാശരി ആദ്യ പ്രതികരണ സമയം (മിനിറ്റ്) + avg_resolution_time: ശരാശരി മിഴിവ് സമയം (മിനിറ്റ്) notifications: notification_title: conversation_creation: "[New conversation] - #%{display_id} has been created in %{inbox_name}" From bb37f7b263e997f3eb77f3f359e2b62c16bf45cc Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Tue, 30 Mar 2021 21:06:31 +0530 Subject: [PATCH 07/41] chore: Handle Sentry Errors (#2020) If a contract contains an invalid email address, the sidekiq jobs for conversation continuity would error out. handling this exception gracefully. --- app/mailers/application_mailer.rb | 7 +++++++ lib/exception_list.rb | 7 +++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 12341e30c..9b5c876d6 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -16,12 +16,19 @@ class ApplicationMailer < ActionMailer::Base end end + rescue_from(*ExceptionList::SMTP_EXCEPTIONS, with: :handle_smtp_exceptions) + def smtp_config_set_or_development? ENV.fetch('SMTP_ADDRESS', nil).present? || Rails.env.development? end private + def handle_smtp_exceptions(message) + Rails.logger.info 'Failed to send Email' + Rails.logger.info "Exception: #{message}" + end + def send_mail_with_liquid(*args) mail(*args) do |format| # explored sending a multipart email containing both text type and html diff --git a/lib/exception_list.rb b/lib/exception_list.rb index 6ea285bd0..3909e4e22 100644 --- a/lib/exception_list.rb +++ b/lib/exception_list.rb @@ -1,5 +1,8 @@ module ExceptionList - URI_EXCEPTIONS = [Errno::ETIMEDOUT, Errno::ECONNREFUSED, URI::InvalidURIError, Net::OpenTimeout, SocketError].freeze + URI_EXCEPTIONS = [Errno::ETIMEDOUT, Errno::ECONNREFUSED, URI::InvalidURIError, Net::OpenTimeout, SocketError, OpenURI::HTTPError].freeze REST_CLIENT_EXCEPTIONS = [RestClient::NotFound, RestClient::GatewayTimeout, RestClient::BadRequest, - RestClient::MethodNotAllowed, RestClient::Forbidden].freeze + RestClient::MethodNotAllowed, RestClient::Forbidden, RestClient::InternalServerError, RestClient::PayloadTooLarge].freeze + SMTP_EXCEPTIONS = [ + Net::SMTPSyntaxError + ].freeze end From f5a961b27cc4cebe1604f43143dcfeee1a83af16 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Mar 2021 14:42:00 +0530 Subject: [PATCH 08/41] chore: Bump y18n from 4.0.0 to 4.0.1 (#2022) Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/yargs/y18n/releases) - [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md) - [Commits](https://github.com/yargs/y18n/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 011be7572..7db98eccc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11709,9 +11709,9 @@ xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1: integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" - integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== yallist@^2.1.2: version "2.1.2" @@ -11790,4 +11790,4 @@ yauzl@2.10.0, yauzl@^2.10.0: integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= dependencies: buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" \ No newline at end of file + fd-slicer "~1.1.0" From c8b81b066b64a3436b55f1ebf570e6c393cd4380 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Wed, 31 Mar 2021 16:39:57 +0530 Subject: [PATCH 09/41] chore: Add timeouts to requests (#2024) --- .../conversation_notifications_mailer.rb | 6 +++--- lib/local_resource.rb | 3 ++- lib/webhooks/trigger.rb | 7 ++++++- spec/lib/webhooks/trigger_spec.rb | 13 ++++++++++--- .../conversation_notifications_mailer_spec.rb | 5 +++-- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/app/mailers/agent_notifications/conversation_notifications_mailer.rb b/app/mailers/agent_notifications/conversation_notifications_mailer.rb index 60e5401da..877c68ab5 100644 --- a/app/mailers/agent_notifications/conversation_notifications_mailer.rb +++ b/app/mailers/agent_notifications/conversation_notifications_mailer.rb @@ -29,13 +29,13 @@ class AgentNotifications::ConversationNotificationsMailer < ApplicationMailer send_mail_with_liquid(to: @agent.email, subject: subject) and return end - def assigned_conversation_new_message(conversation, agent) + def assigned_conversation_new_message(message, agent) return unless smtp_config_set_or_development? # Don't spam with email notifications if agent is online - return if ::OnlineStatusTracker.get_presence(conversation.account.id, 'User', agent.id) + return if ::OnlineStatusTracker.get_presence(message.account_id, 'User', agent.id) @agent = agent - @conversation = conversation + @conversation = message.conversation subject = "#{@agent.available_name}, New message in your assigned conversation [ID - #{@conversation.display_id}]." @action_url = app_account_conversation_url(account_id: @conversation.account_id, id: @conversation.display_id) send_mail_with_liquid(to: @agent.email, subject: subject) and return diff --git a/lib/local_resource.rb b/lib/local_resource.rb index 530861ca8..e7fa28106 100644 --- a/lib/local_resource.rb +++ b/lib/local_resource.rb @@ -16,7 +16,8 @@ class LocalResource end def io - @io ||= uri.open + # TODO: should we use RestClient here too ? + @io ||= uri.open(read_timeout: 5) end def encoding diff --git a/lib/webhooks/trigger.rb b/lib/webhooks/trigger.rb index c3f0f95f2..8bbaa05a4 100644 --- a/lib/webhooks/trigger.rb +++ b/lib/webhooks/trigger.rb @@ -1,6 +1,11 @@ class Webhooks::Trigger def self.execute(url, payload) - RestClient.post(url, payload.to_json, { content_type: :json, accept: :json }) + RestClient::Request.execute( + method: :post, + url: url, payload: payload.to_json, + headers: { content_type: :json, accept: :json }, + timeout: 5 + ) rescue *ExceptionList::REST_CLIENT_EXCEPTIONS, *ExceptionList::URI_EXCEPTIONS => e Rails.logger.info "Exception: invalid webhook url #{url} : #{e.message}" rescue StandardError => e diff --git a/spec/lib/webhooks/trigger_spec.rb b/spec/lib/webhooks/trigger_spec.rb index 7608634ac..b5a2dcbd2 100644 --- a/spec/lib/webhooks/trigger_spec.rb +++ b/spec/lib/webhooks/trigger_spec.rb @@ -5,11 +5,18 @@ describe Webhooks::Trigger do describe '#execute' do it 'triggers webhook' do - params = { hello: :hello } + payload = { hello: :hello } url = 'https://test.com' - expect(RestClient).to receive(:post).with(url, params.to_json, { accept: :json, content_type: :json }).once - trigger.execute(url, params) + expect(RestClient::Request).to receive(:execute) + .with( + method: :post, + url: url, + payload: payload.to_json, + headers: { content_type: :json, accept: :json }, + timeout: 5 + ).once + trigger.execute(url, payload) end end end diff --git a/spec/mailers/agent_notifications/conversation_notifications_mailer_spec.rb b/spec/mailers/agent_notifications/conversation_notifications_mailer_spec.rb index d44824892..3f909fb97 100644 --- a/spec/mailers/agent_notifications/conversation_notifications_mailer_spec.rb +++ b/spec/mailers/agent_notifications/conversation_notifications_mailer_spec.rb @@ -52,10 +52,11 @@ RSpec.describe AgentNotifications::ConversationNotificationsMailer, type: :maile end describe 'assigned_conversation_new_message' do - let(:mail) { described_class.assigned_conversation_new_message(conversation, agent).deliver_now } + let(:message) { create(:message, conversation: conversation, account: account) } + let(:mail) { described_class.assigned_conversation_new_message(message, agent).deliver_now } it 'renders the subject' do - expect(mail.subject).to eq("#{agent.available_name}, New message in your assigned conversation [ID - #{conversation.display_id}].") + expect(mail.subject).to eq("#{agent.available_name}, New message in your assigned conversation [ID - #{message.conversation.display_id}].") end it 'renders the receiver email' do From de72530022cfb13ead1b4128276bb8b4f1f48bfb Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+sivin-git@users.noreply.github.com> Date: Thu, 1 Apr 2021 20:57:43 +0530 Subject: [PATCH 10/41] Fix: responsive issue conversations hamburger menu (#2036) --- app/javascript/dashboard/components/ChatList.vue | 1 - app/javascript/dashboard/components/SidemenuIcon.vue | 6 +++++- .../routes/dashboard/conversation/search/PopOverSearch.vue | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index a21fb15a7..392b0a7a4 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -3,7 +3,6 @@

- {{ pageTitle }}

diff --git a/app/javascript/dashboard/components/SidemenuIcon.vue b/app/javascript/dashboard/components/SidemenuIcon.vue index 1ac0b0370..f17a1c865 100644 --- a/app/javascript/dashboard/components/SidemenuIcon.vue +++ b/app/javascript/dashboard/components/SidemenuIcon.vue @@ -3,7 +3,6 @@ + diff --git a/app/javascript/dashboard/routes/dashboard/conversation/search/PopOverSearch.vue b/app/javascript/dashboard/routes/dashboard/conversation/search/PopOverSearch.vue index fd97c371d..c6960c9e2 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/search/PopOverSearch.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/search/PopOverSearch.vue @@ -1,6 +1,7 @@ + diff --git a/app/javascript/dashboard/i18n/locale/en/conversation.json b/app/javascript/dashboard/i18n/locale/en/conversation.json index 91247105d..e8aa521f1 100644 --- a/app/javascript/dashboard/i18n/locale/en/conversation.json +++ b/app/javascript/dashboard/i18n/locale/en/conversation.json @@ -29,10 +29,14 @@ "HEADER": { "RESOLVE_ACTION": "Resolve", "REOPEN_ACTION": "Reopen", + "OPEN_ACTION": "Open", "OPEN": "More", "CLOSE": "Close", "DETAILS": "details" }, + "RESOLVE_DROPDOWN": { + "OPEN_BOT": "Open with bot" + }, "FOOTER": { "MSG_INPUT": "Shift + enter for new line. Start with '/' to select a Canned Response.", "PRIVATE_MSG_INPUT": "Shift + enter for new line. This will be visible only to Agents" diff --git a/app/javascript/dashboard/store/modules/conversations/actions.js b/app/javascript/dashboard/store/modules/conversations/actions.js index 8e4e20954..e983ca2f2 100644 --- a/app/javascript/dashboard/store/modules/conversations/actions.js +++ b/app/javascript/dashboard/store/modules/conversations/actions.js @@ -135,9 +135,12 @@ const actions = { commit(types.default.ASSIGN_TEAM, team); }, - toggleStatus: async ({ commit }, data) => { + toggleStatus: async ({ commit }, { conversationId, status }) => { try { - const response = await ConversationApi.toggleStatus(data); + const response = await ConversationApi.toggleStatus({ + conversationId, + status, + }); commit( types.default.RESOLVE_CONVERSATION, response.data.payload.current_status diff --git a/config/locales/en.yml b/config/locales/en.yml index 35aac041d..b0aa0ef53 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -63,6 +63,7 @@ en: status: resolved: "Conversation was marked resolved by %{user_name}" open: "Conversation was reopened by %{user_name}" + bot: "Conversation was transferred to bot by %{user_name}" auto_resolved: "Conversation was marked resolved by system due to %{duration} days of inactivity" assignee: self_assigned: "%{user_name} self-assigned this conversation" From a377da902883a189c123fc0031c6794359770940 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Wed, 7 Apr 2021 11:45:33 +0530 Subject: [PATCH 19/41] chore: Change size of assignee dropdown in header (#2027) * Chore: Change size of assignee dropdown in header * Fix: prevent overflow of agent name in multselect --- .../dashboard/assets/scss/plugins/_multiselect.scss | 2 ++ .../dashboard/assets/scss/widgets/_conv-header.scss | 8 +++++--- .../widgets/conversation/ConversationHeader.vue | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/javascript/dashboard/assets/scss/plugins/_multiselect.scss b/app/javascript/dashboard/assets/scss/plugins/_multiselect.scss index 36df6af14..bc024e024 100644 --- a/app/javascript/dashboard/assets/scss/plugins/_multiselect.scss +++ b/app/javascript/dashboard/assets/scss/plugins/_multiselect.scss @@ -118,6 +118,8 @@ } .multiselect__single { + @include text-ellipsis; + display: inline-block; margin-bottom: 0; padding: var(--space-slab) var(--space-one); } diff --git a/app/javascript/dashboard/assets/scss/widgets/_conv-header.scss b/app/javascript/dashboard/assets/scss/widgets/_conv-header.scss index 23616f8de..dc2a4c2d1 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_conv-header.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_conv-header.scss @@ -11,11 +11,12 @@ $resolve-button-width: 13.2rem; .multiselect-box { @include flex; @include flex-align($x: justify, $y: middle); - @include border-light; - border-radius: $space-smaller; + border: 1px solid var(--color-border); + border-radius: var(--space-smaller); margin-right: var(--space-small); + width: 20.2rem; - &::before { + .icon { color: $medium-gray; font-size: $font-size-default; line-height: 3.8rem; @@ -25,6 +26,7 @@ $resolve-button-width: 13.2rem; .multiselect { margin: 0; + min-width: 0; .multiselect__tags { border: 0; diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue index e8f032a36..228030a45 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue @@ -30,7 +30,8 @@ class="header-actions-wrap" :class="{ 'has-open-sidebar': isContactPanelOpen }" > -
+
+ Date: Wed, 7 Apr 2021 12:28:50 +0530 Subject: [PATCH 20/41] enhancement: Profile settings accessibility improvements (#2054) * profile settings in to separate form * update locale texts * replce woot-submit-button with woot-button * disable button if value is empty * change condition * code climate fixes --- .../dashboard/i18n/locale/en/settings.json | 12 ++- .../dashboard/settings/profile/Index.vue | 74 ++++++++++++------- 2 files changed, 52 insertions(+), 34 deletions(-) diff --git a/app/javascript/dashboard/i18n/locale/en/settings.json b/app/javascript/dashboard/i18n/locale/en/settings.json index fd915fb0c..314bbbbda 100644 --- a/app/javascript/dashboard/i18n/locale/en/settings.json +++ b/app/javascript/dashboard/i18n/locale/en/settings.json @@ -3,6 +3,8 @@ "LINK": "Profile Settings", "TITLE": "Profile Settings", "BTN_TEXT": "Update Profile", + "UPDATE_SUCCESS": "Your profile has been updated successfully", + "PASSWORD_UPDATE_SUCCESS": "Your password has been changed successfully", "AFTER_EMAIL_CHANGED": "Your profile has been updated successfully, please login again as your login credentials are changed", "FORM": { "AVATAR": "Profile Image", @@ -16,7 +18,8 @@ }, "PASSWORD_SECTION": { "TITLE": "Password", - "NOTE": "Updating your password would reset your logins in multiple devices." + "NOTE": "Updating your password would reset your logins in multiple devices.", + "BTN_TEXT": "Change password" }, "ACCESS_TOKEN": { "TITLE": "Access Token", @@ -64,11 +67,7 @@ }, "AVAILABILITY": { "LABEL": "Availability", - "STATUSES_LIST": [ - "Online", - "Busy", - "Offline" - ] + "STATUSES_LIST": ["Online", "Busy", "Offline"] }, "EMAIL": { "LABEL": "Your email address", @@ -147,6 +146,5 @@ }, "SUBMIT": "Submit" } - } } diff --git a/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue index d84134749..90d6f7012 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue @@ -1,8 +1,8 @@ @@ -130,7 +138,8 @@ export default { email: '', password: '', passwordConfirmation: '', - isUpdating: false, + isProfileUpdating: false, + isPasswordChanging: false, }; }, validations: { @@ -181,13 +190,17 @@ export default { this.avatarUrl = this.currentUser.avatar_url; this.displayName = this.currentUser.display_name; }, - async updateUser() { + async updateUser(type) { this.$v.$touch(); if (this.$v.$invalid) { this.showAlert(this.$t('PROFILE_SETTINGS.FORM.ERROR')); return; } - this.isUpdating = true; + if (type === 'profile') { + this.isProfileUpdating = true; + } else if (type === 'password') { + this.isPasswordChanging = true; + } const hasEmailChanged = this.currentUser.email !== this.email; try { await this.$store.dispatch('updateProfile', { @@ -198,13 +211,20 @@ export default { displayName: this.displayName, password_confirmation: this.passwordConfirmation, }); - this.isUpdating = false; + this.isProfileUpdating = false; + this.isPasswordChanging = false; if (hasEmailChanged) { clearCookiesOnLogout(); this.showAlert(this.$t('PROFILE_SETTINGS.AFTER_EMAIL_CHANGED')); } + if (type === 'profile') { + this.showAlert(this.$t('PROFILE_SETTINGS.UPDATE_SUCCESS')); + } else if (type === 'password') { + this.showAlert(this.$t('PROFILE_SETTINGS.PASSWORD_UPDATE_SUCCESS')); + } } catch (error) { - this.isUpdating = false; + this.isProfileUpdating = false; + this.isPasswordChanging = false; } }, handleImageUpload({ file, url }) { From 012502a2d1cba6c8f4225bf06f41d02450f541ad Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+sivin-git@users.noreply.github.com> Date: Thu, 8 Apr 2021 11:44:23 +0530 Subject: [PATCH 21/41] chore: Refactor shouldShowInboxes function name (#2065) --- app/javascript/dashboard/components/layout/Sidebar.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/javascript/dashboard/components/layout/Sidebar.vue b/app/javascript/dashboard/components/layout/Sidebar.vue index 0bb416456..509c34696 100644 --- a/app/javascript/dashboard/components/layout/Sidebar.vue +++ b/app/javascript/dashboard/components/layout/Sidebar.vue @@ -19,12 +19,12 @@ :menu-item="teamSection" /> @@ -128,11 +128,11 @@ export default { currentRoute() { return this.$store.state.route.name; }, - shouldShowInboxes() { + shouldShowSidebarItem() { return this.sidemenuItems.common.routes.includes(this.currentRoute); }, shouldShowTeams() { - return this.shouldShowInboxes && this.teams.length; + return this.shouldShowSidebarItem && this.teams.length; }, inboxSection() { return { From 3e03b08ad0f6e855a5ad650b8271986423ef4cc2 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+sivin-git@users.noreply.github.com> Date: Thu, 8 Apr 2021 16:01:46 +0530 Subject: [PATCH 22/41] Enhancement: Adds a new button that redirect to inbox config page (#2066) * Adds a new button that redirect to inbox config page * Review fixes --- .../dashboard/i18n/locale/en/inboxMgmt.json | 4 +- .../dashboard/settings/inbox/FinishSetup.vue | 38 ++++++++++++++----- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json index e63a40656..a1184f5db 100644 --- a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json @@ -5,7 +5,8 @@ "LIST": { "404": "There are no inboxes attached to this account." }, - "CREATE_FLOW": [{ + "CREATE_FLOW": [ + { "title": "Choose Channel", "route": "settings_inbox_new", "body": "Choose the provider you want to integrate with Chatwoot." @@ -193,6 +194,7 @@ "TITLE": "Your Inbox is ready!", "MESSAGE": "You can now engage with your customers through your new Channel. Happy supporting ", "BUTTON_TEXT": "Take me there", + "MORE_SETTINGS": "More settings", "WEBSITE_SUCCESS": "You have successfully finished creating a website channel. Copy the code shown below and paste it on your website. Next time a customer use the live chat, the conversation will automatically appear on your inbox." }, "REAUTH": "Reauthorize", diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue index 630bc0652..80b37079c 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue @@ -29,15 +29,26 @@ >
- - {{ $t('INBOX_MGMT.FINISH.BUTTON_TEXT') }} - +
@@ -90,4 +101,13 @@ export default { margin: $space-normal auto; max-width: 70%; } + +.footer { + display: flex; + justify-content: center; +} + +.settings-button { + margin-right: var(--space-small); +} From 2140443f07df47b81c497297fecc914efa55a7ba Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Thu, 8 Apr 2021 19:41:26 +0530 Subject: [PATCH 23/41] chore: Add huntr.dev badge (#2069) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a9938f01c..030b13dcd 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ ___ Commits-per-month Discord + Huntr

Chat dashboard From 28fb6469a380941595065ce10919f5850a8e01a7 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+sivin-git@users.noreply.github.com> Date: Thu, 8 Apr 2021 19:42:03 +0530 Subject: [PATCH 24/41] Fix: Alignment issue in activity message (#2057) * Fixes alignment issue in activity message * Codeclimate fixes Co-authored-by: Nithin David Thomas --- .../scss/widgets/_conversation-view.scss | 28 +++++++++++++------ .../widgets/conversation/bubble/Actions.vue | 12 ++++++-- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss b/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss index cd639b940..730cbb9b0 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss @@ -249,17 +249,27 @@ } .activity-wrap { - @include flex; - @include margin($space-small auto); - @include padding($space-small $space-normal); - @include flex-align($x: center, $y: null); - background: lighten($warning-color, 32%); - border: 1px solid lighten($warning-color, 22%); - border-radius: $space-smaller; - font-size: $font-size-small; + background: var(--s-50); + border: 1px solid var(--s-100); + border-radius: var(--border-radius-medium); + display: flex; + font-size: var(--font-size-small); + justify-content: center; + margin: var(--space-small) var(--space-normal); + padding: var(--space-small) var(--space-normal); - .message-text__wrap { + @include breakpoint(xlarge up) { + margin: var(--space-small) auto; + } + + .is-text { display: inline-block; + text-align: start; + + @include breakpoint(xlarge up) { + display: flex; + text-align: center; + } } } } diff --git a/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue b/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue index f055b97cd..9c96d44a5 100644 --- a/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue +++ b/app/javascript/dashboard/components/widgets/conversation/bubble/Actions.vue @@ -95,6 +95,8 @@ export default { diff --git a/app/javascript/dashboard/components/widgets/conversation/MoreActions.vue b/app/javascript/dashboard/components/widgets/conversation/MoreActions.vue index 883b27a2c..2c73969e5 100644 --- a/app/javascript/dashboard/components/widgets/conversation/MoreActions.vue +++ b/app/javascript/dashboard/components/widgets/conversation/MoreActions.vue @@ -112,7 +112,6 @@ export default { .dropdown-pane { right: var(--space-minus-small); top: 48px; - width: auto; } .icon { From 03bd34be86e84af6159df93c007dfe174f563821 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Fri, 9 Apr 2021 14:32:29 +0530 Subject: [PATCH 26/41] Fix: Populate only agents part of the inbox in conversations (#2060) * Fix: Show only agents part of the inbox in conversations * Show only verified agents Co-authored-by: Muhsin Keloth --- .../widgets/conversation/ConversationBox.vue | 8 +++ .../conversation/ConversationHeader.vue | 8 ++- .../dashboard/conversation/ContactPanel.vue | 9 +++- .../dashboard/store/modules/inboxMembers.js | 52 +++++++++++++++++-- .../specs/inboxMembers/actions.spec.js | 31 +++++++++++ .../modules/specs/inboxMembers/fixtures.js | 28 ++++++++++ .../specs/inboxMembers/getters.spec.js | 24 +++++++++ .../specs/inboxMembers/mutations.spec.js | 16 ++++++ 8 files changed, 168 insertions(+), 8 deletions(-) create mode 100644 app/javascript/dashboard/store/modules/specs/inboxMembers/actions.spec.js create mode 100644 app/javascript/dashboard/store/modules/specs/inboxMembers/fixtures.js create mode 100644 app/javascript/dashboard/store/modules/specs/inboxMembers/getters.spec.js create mode 100644 app/javascript/dashboard/store/modules/specs/inboxMembers/mutations.spec.js diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationBox.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationBox.vue index d6d5bbd85..bb700a86c 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationBox.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationBox.vue @@ -19,6 +19,7 @@
@@ -59,6 +60,13 @@ export default { return this.isContactPanelOpen && this.currentChat.id; }, }, + watch: { + 'currentChat.inbox_id'(inboxId) { + if (inboxId) { + this.$store.dispatch('inboxMembers/fetch', { inboxId }); + } + }, + }, methods: { onToggleContactPanel() { this.$emit('contact-panel-toggle'); diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue index 228030a45..4d62e8dd9 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue @@ -34,6 +34,7 @@ {}, @@ -135,8 +139,9 @@ export default { computed: { ...mapGetters({ currentChat: 'getSelectedChat', - agents: 'agents/getVerifiedAgents', teams: 'teams/getTeams', + getAgents: 'inboxMembers/getMembersByInbox', + uiFlags: 'inboxMembers/getUIFlags', }), currentConversationMetaData() { return this.$store.getters[ @@ -201,7 +206,7 @@ export default { return this.$store.getters['contacts/getContact'](this.contactId); }, agentsList() { - return [{ id: 0, name: 'None' }, ...this.agents]; + return [{ id: 0, name: 'None' }, ...this.getAgents(this.inboxId)]; }, teamsList() { return [{ id: 0, name: 'None' }, ...this.teams]; diff --git a/app/javascript/dashboard/store/modules/inboxMembers.js b/app/javascript/dashboard/store/modules/inboxMembers.js index 4661e0f78..071def998 100644 --- a/app/javascript/dashboard/store/modules/inboxMembers.js +++ b/app/javascript/dashboard/store/modules/inboxMembers.js @@ -1,10 +1,44 @@ +import Vue from 'vue'; + import InboxMembersAPI from '../../api/inboxMembers'; -const state = {}; +const state = { + records: {}, + uiFlags: { + isFetching: false, + }, +}; -const getters = {}; +export const types = { + SET_INBOX_MEMBERS_UI_FLAG: 'SET_INBOX_MEMBERS_UI_FLAG', + SET_INBOX_MEMBERS: 'SET_INBOX_MEMBERS', +}; -const actions = { +export const getters = { + getMembersByInbox: $state => inboxId => { + const allAgents = $state.records[inboxId] || []; + const verifiedAgents = allAgents.filter(record => record.confirmed); + return verifiedAgents; + }, + getUIFlags($state) { + return $state.uiFlags; + }, +}; + +export const actions = { + async fetch({ commit }, { inboxId }) { + commit(types.SET_INBOX_MEMBERS_UI_FLAG, { isFetching: true }); + try { + const { + data: { payload }, + } = await InboxMembersAPI.show(inboxId); + commit(types.SET_INBOX_MEMBERS, { inboxId, members: payload }); + } catch (error) { + throw new Error(error); + } finally { + commit(types.SET_INBOX_MEMBERS_UI_FLAG, { isFetching: false }); + } + }, get(_, { inboxId }) { return InboxMembersAPI.show(inboxId); }, @@ -13,7 +47,17 @@ const actions = { }, }; -const mutations = {}; +export const mutations = { + [types.SET_INBOX_MEMBERS_UI_FLAG]($state, data) { + $state.uiFlags = { + ...$state.uiFlags, + ...data, + }; + }, + [types.SET_INBOX_MEMBERS]: ($state, { inboxId, members }) => { + Vue.set($state.records, inboxId, members); + }, +}; export default { namespaced: true, diff --git a/app/javascript/dashboard/store/modules/specs/inboxMembers/actions.spec.js b/app/javascript/dashboard/store/modules/specs/inboxMembers/actions.spec.js new file mode 100644 index 000000000..af2b63cd2 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/inboxMembers/actions.spec.js @@ -0,0 +1,31 @@ +import axios from 'axios'; +import { actions, types } from '../../inboxMembers'; +import inboxMembers from './fixtures'; + +const commit = jest.fn(); +global.axios = axios; +jest.mock('axios'); + +describe('#actions', () => { + describe('#fetch', () => { + it('sends correct actions if API is success', async () => { + axios.get.mockResolvedValue({ data: { payload: inboxMembers } }); + await actions.fetch({ commit }, { inboxId: 1 }); + expect(commit.mock.calls).toEqual([ + [types.SET_INBOX_MEMBERS_UI_FLAG, { isFetching: true }], + [types.SET_INBOX_MEMBERS, { inboxId: 1, members: inboxMembers }], + [types.SET_INBOX_MEMBERS_UI_FLAG, { isFetching: false }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.get.mockRejectedValue({ message: 'Incorrect header' }); + await expect(actions.fetch({ commit }, { inboxId: 1 })).rejects.toThrow( + Error + ); + expect(commit.mock.calls).toEqual([ + [types.SET_INBOX_MEMBERS_UI_FLAG, { isFetching: true }], + [types.SET_INBOX_MEMBERS_UI_FLAG, { isFetching: false }], + ]); + }); + }); +}); diff --git a/app/javascript/dashboard/store/modules/specs/inboxMembers/fixtures.js b/app/javascript/dashboard/store/modules/specs/inboxMembers/fixtures.js new file mode 100644 index 000000000..c7b968e33 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/inboxMembers/fixtures.js @@ -0,0 +1,28 @@ +export default [ + { + id: 1, + provider: 'email', + uid: 'agent1@chatwoot.com', + name: 'Agent1', + email: 'agent1@chatwoot.com', + account_id: 1, + created_at: '2019-11-18T02:21:06.225Z', + updated_at: '2019-12-20T07:43:35.794Z', + pubsub_token: 'random-1', + role: 'agent', + confirmed: true, + }, + { + id: 2, + provider: 'email', + uid: 'agent2@chatwoot.com', + name: 'Agent2', + email: 'agent2@chatwoot.com', + account_id: 1, + created_at: '2019-11-18T02:21:06.225Z', + updated_at: '2019-12-20T07:43:35.794Z', + pubsub_token: 'random-2', + role: 'agent', + confirmed: true, + }, +]; diff --git a/app/javascript/dashboard/store/modules/specs/inboxMembers/getters.spec.js b/app/javascript/dashboard/store/modules/specs/inboxMembers/getters.spec.js new file mode 100644 index 000000000..9b83c548e --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/inboxMembers/getters.spec.js @@ -0,0 +1,24 @@ +import { getters } from '../../teamMembers'; +import teamMembers from './fixtures'; + +describe('#getters', () => { + it('getMembersByInbox', () => { + const state = { + records: { + 1: [teamMembers[0]], + }, + }; + expect(getters.getTeamMembers(state)(1)).toEqual([teamMembers[0]]); + }); + + it('getUIFlags', () => { + const state = { + uiFlags: { + isFetching: false, + }, + }; + expect(getters.getUIFlags(state)).toEqual({ + isFetching: false, + }); + }); +}); diff --git a/app/javascript/dashboard/store/modules/specs/inboxMembers/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/inboxMembers/mutations.spec.js new file mode 100644 index 000000000..11d59ef93 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/inboxMembers/mutations.spec.js @@ -0,0 +1,16 @@ +import { mutations, types } from '../../inboxMembers'; +import inboxMembers from './fixtures'; + +describe('#mutations', () => { + describe('#SET_INBOX_MEMBERS', () => { + it('Adds inbox members to records', () => { + const state = { records: {} }; + mutations[types.SET_INBOX_MEMBERS](state, { + members: [...inboxMembers], + inboxId: 1, + }); + + expect(state.records).toEqual({ 1: inboxMembers }); + }); + }); +}); From 7eb766a45727b608099c190c9d5fb53dce8d4c0a Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+sivin-git@users.noreply.github.com> Date: Sat, 10 Apr 2021 20:03:45 +0530 Subject: [PATCH 27/41] Fix: Dropdown width issue in safari browser. (#2080) --- app/javascript/dashboard/assets/scss/plugins/_dropdown.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/javascript/dashboard/assets/scss/plugins/_dropdown.scss b/app/javascript/dashboard/assets/scss/plugins/_dropdown.scss index 8ca486221..7009a077f 100644 --- a/app/javascript/dashboard/assets/scss/plugins/_dropdown.scss +++ b/app/javascript/dashboard/assets/scss/plugins/_dropdown.scss @@ -1,6 +1,7 @@ .dropdown-pane { @include elegant-card; @include border-light; + box-sizing: content-box; width: fit-content; z-index: 999; From 04a9dc630a48c7b82d947997ec6d8ed76bdddd04 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Mon, 12 Apr 2021 16:15:11 +0530 Subject: [PATCH 28/41] enhancement: Enable reply box for whatsapp template message (#2082) * remove 24-hours reply restriction in WhatsApp * remove the console.logs * move reply policy to separate constants file * fix spelling mistake --- .../widgets/conversation/MessagesView.vue | 44 +++++++++++++++---- .../widgets/conversation/ReplyBox.vue | 6 +-- .../dashboard/i18n/locale/el/inboxMgmt.json | 4 +- .../i18n/locale/en/conversation.json | 3 +- .../dashboard/i18n/locale/id/inboxMgmt.json | 2 +- app/javascript/shared/constants/links.js | 6 +++ 6 files changed, 49 insertions(+), 16 deletions(-) create mode 100644 app/javascript/shared/constants/links.js diff --git a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue index a4656d89b..0b89fb8c4 100644 --- a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue +++ b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue @@ -1,10 +1,13 @@ @@ -120,7 +152,7 @@ export default { @import '~dashboard/assets/scss/mixins'; .contact--profile { align-items: flex-start; - padding: var(--space-normal) var(--space-normal) var(--space-large); + padding: var(--space-normal) var(--space-normal); .user-thumbnail-box { margin-right: $space-normal; @@ -164,8 +196,21 @@ export default { font-size: $font-weight-normal; } } +.contact-actions { + margin: var(--space-small) 0; +} +.button.edit-contact { + margin-left: var(--space-two); + padding-left: var(--space-micro); +} -.edit-contact { - margin-left: var(--space-slab); +.button.new-message { + margin-right: var(--space-small); +} + +.contact-actions { + display: flex; + align-items: center; + width: 100%; } diff --git a/app/javascript/dashboard/routes/dashboard/conversation/contact/ConversationForm.vue b/app/javascript/dashboard/routes/dashboard/conversation/contact/ConversationForm.vue new file mode 100644 index 000000000..75e333a73 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/conversation/contact/ConversationForm.vue @@ -0,0 +1,210 @@ +