-
-
-
-
+
+
+
+
-
-
diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/components/SLA/helpers/SLAFilterHelpers.js b/app/javascript/dashboard/routes/dashboard/settings/reports/components/SLA/helpers/SLAFilterHelpers.js
new file mode 100644
index 000000000..cac7e93c3
--- /dev/null
+++ b/app/javascript/dashboard/routes/dashboard/settings/reports/components/SLA/helpers/SLAFilterHelpers.js
@@ -0,0 +1,37 @@
+export const buildFilterList = (items, type) =>
+ // Build the filter list for the dropdown
+ items.map(item => ({
+ id: item.id,
+ name: type === 'labels' ? item.title : item.name,
+ type,
+ }));
+
+export const getActiveFilter = (filters, type, key) => {
+ // Method is used to get the active filter from the filter list
+ return filters.find(filterItem =>
+ type === 'labels'
+ ? filterItem.title === key
+ : filterItem.id.toString() === key.toString()
+ );
+};
+
+export const getFilterType = (input, direction) => {
+ // Method is used to map the filter key to the filter type
+ const filterMap = {
+ keyToType: {
+ assigned_agent_id: 'agents',
+ inbox_id: 'inboxes',
+ team_id: 'teams',
+ sla_policy_id: 'sla',
+ label_list: 'labels',
+ },
+ typeToKey: {
+ agents: 'assigned_agent_id',
+ inboxes: 'inbox_id',
+ teams: 'team_id',
+ sla: 'sla_policy_id',
+ labels: 'label_list',
+ },
+ };
+ return filterMap[direction][input];
+};
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index 61d6dec8d..deeb88f0a 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -31,6 +31,7 @@ import VueDOMPurifyHTML from 'vue-dompurify-html';
import { domPurifyConfig } from '../shared/helpers/HTMLSanitizer';
import AnalyticsPlugin from '../dashboard/helper/AnalyticsHelper/plugin';
import resizeDirective from '../dashboard/helper/directives/resize.js';
+import { directive as onClickaway } from 'vue-clickaway';
Vue.config.env = process.env;
@@ -80,6 +81,7 @@ Vue.component('woot-wizard', WootWizard);
Vue.component('fluent-icon', FluentIcon);
Vue.directive('resize', resizeDirective);
+Vue.directive('on-clickaway', onClickaway);
const i18nConfig = new VueI18n({
locale: 'en',
messages: i18n,
diff --git a/app/javascript/sdk/sdk.js b/app/javascript/sdk/sdk.js
index 313bbb88a..95d132258 100644
--- a/app/javascript/sdk/sdk.js
+++ b/app/javascript/sdk/sdk.js
@@ -23,6 +23,7 @@ export const SDK_CSS = `
.woot-widget-holder iframe {
border: 0;
+ color-scheme: normal;
height: 100% !important;
width: 100% !important;
max-height: 100vh !important;
diff --git a/app/javascript/widget/api/conversation.js b/app/javascript/widget/api/conversation.js
index 5cae5bc13..1060b285d 100755
--- a/app/javascript/widget/api/conversation.js
+++ b/app/javascript/widget/api/conversation.js
@@ -38,10 +38,9 @@ const setUserLastSeenAt = async ({ lastSeen }) => {
{ contact_last_seen_at: lastSeen }
);
};
-const sendEmailTranscript = async ({ email }) => {
+const sendEmailTranscript = async () => {
return API.post(
- `/api/v1/widget/conversations/transcript${window.location.search}`,
- { email }
+ `/api/v1/widget/conversations/transcript${window.location.search}`
);
};
const toggleStatus = async () => {
diff --git a/app/javascript/widget/components/ChatFooter.vue b/app/javascript/widget/components/ChatFooter.vue
index 2a8ef36dc..20d3d820f 100755
--- a/app/javascript/widget/components/ChatFooter.vue
+++ b/app/javascript/widget/components/ChatFooter.vue
@@ -87,7 +87,10 @@ export default {
return !allowMessagesAfterResolved && status === 'resolved';
},
showEmailTranscriptButton() {
- return this.currentUser && this.currentUser.email;
+ return this.hasEmail;
+ },
+ hasEmail() {
+ return this.currentUser && this.currentUser.has_email;
},
hasReplyTo() {
return (
@@ -141,12 +144,9 @@ export default {
this.inReplyTo = message;
},
async sendTranscript() {
- const { email } = this.currentUser;
- if (email) {
+ if (this.hasEmail) {
try {
- await sendEmailTranscript({
- email,
- });
+ await sendEmailTranscript();
window.bus.$emit(BUS_EVENTS.SHOW_ALERT, {
message: this.$t('EMAIL_TRANSCRIPT.SEND_EMAIL_SUCCESS'),
type: 'success',
diff --git a/app/javascript/widget/components/PreChat/Form.vue b/app/javascript/widget/components/PreChat/Form.vue
index 394b338d5..3ff597c67 100644
--- a/app/javascript/widget/components/PreChat/Form.vue
+++ b/app/javascript/widget/components/PreChat/Form.vue
@@ -1,7 +1,7 @@
{
it('getCurrentUser', () => {
const user = {
- email: 'thoma@sphadikam.com',
- name: 'Adu Thoma',
+ has_email: true,
+ has_name: true,
avatar_url: '',
identifier_hash: 'malana_hash',
};
@@ -12,8 +12,8 @@ describe('#getters', () => {
currentUser: user,
};
expect(getters.getCurrentUser(state)).toEqual({
- email: 'thoma@sphadikam.com',
- name: 'Adu Thoma',
+ has_email: true,
+ has_name: true,
avatar_url: '',
identifier_hash: 'malana_hash',
});
diff --git a/app/javascript/widget/store/modules/specs/contact/mutations.spec.js b/app/javascript/widget/store/modules/specs/contact/mutations.spec.js
index fed202bf2..5778be2fd 100644
--- a/app/javascript/widget/store/modules/specs/contact/mutations.spec.js
+++ b/app/javascript/widget/store/modules/specs/contact/mutations.spec.js
@@ -4,8 +4,8 @@ describe('#mutations', () => {
describe('#SET_CURRENT_USER', () => {
it('set current user', () => {
const user = {
- email: 'thoma@sphadikam.com',
- name: 'Adu Thoma',
+ has_email: true,
+ has_name: true,
avatar_url: '',
identifier_hash: 'malana_hash',
};
diff --git a/app/services/imap/base_fetch_email_service.rb b/app/services/imap/base_fetch_email_service.rb
index 4f49dc980..1af3bdb5d 100644
--- a/app/services/imap/base_fetch_email_service.rb
+++ b/app/services/imap/base_fetch_email_service.rb
@@ -3,10 +3,17 @@ require 'net/imap'
class Imap::BaseFetchEmailService
pattr_initialize [:channel!]
- def perform
+ def fetch_emails
# Override this method
end
+ def perform
+ inbound_emails = fetch_emails
+ terminate_imap_connection
+
+ inbound_emails
+ end
+
private
def authentication_type
@@ -105,6 +112,13 @@ class Imap::BaseFetchEmailService
imap
end
+ def terminate_imap_connection
+ imap_client.logout
+ rescue Net::IMAP::Error => e
+ Rails.logger.info "Logout failed for #{channel.email} - #{e.message}."
+ imap_client.disconnect
+ end
+
def build_mail_from_string(raw_email_content)
Mail.read_from_string(raw_email_content)
end
diff --git a/app/services/imap/fetch_email_service.rb b/app/services/imap/fetch_email_service.rb
index f6d8de774..f602b3375 100644
--- a/app/services/imap/fetch_email_service.rb
+++ b/app/services/imap/fetch_email_service.rb
@@ -1,5 +1,5 @@
class Imap::FetchEmailService < Imap::BaseFetchEmailService
- def perform
+ def fetch_emails
fetch_mail_for_channel
end
diff --git a/app/services/imap/microsoft_fetch_email_service.rb b/app/services/imap/microsoft_fetch_email_service.rb
index b6d3c03b8..ed3eb20f1 100644
--- a/app/services/imap/microsoft_fetch_email_service.rb
+++ b/app/services/imap/microsoft_fetch_email_service.rb
@@ -1,5 +1,5 @@
class Imap::MicrosoftFetchEmailService < Imap::BaseFetchEmailService
- def perform
+ def fetch_emails
return if channel.provider_config['access_token'].blank?
fetch_mail_for_channel
diff --git a/app/views/api/v1/widget/contacts/set_user.json.jbuilder b/app/views/api/v1/widget/contacts/set_user.json.jbuilder
index 8c770fe4f..e6283fa4f 100644
--- a/app/views/api/v1/widget/contacts/set_user.json.jbuilder
+++ b/app/views/api/v1/widget/contacts/set_user.json.jbuilder
@@ -1,5 +1,5 @@
json.id @contact.id
-json.name @contact.name
-json.email @contact.email
-json.phone_number @contact.phone_number
+json.has_email @contact.email.present?
+json.has_name @contact.name.present?
+json.has_phone_number @contact.phone_number.present?
json.widget_auth_token @widget_auth_token if @widget_auth_token.present?
diff --git a/app/views/api/v1/widget/contacts/show.json.jbuilder b/app/views/api/v1/widget/contacts/show.json.jbuilder
index 2e7a38277..26be98a3e 100644
--- a/app/views/api/v1/widget/contacts/show.json.jbuilder
+++ b/app/views/api/v1/widget/contacts/show.json.jbuilder
@@ -1,5 +1,5 @@
json.id @contact.id
-json.name @contact.name
-json.email @contact.email
-json.phone_number @contact.phone_number
+json.has_email @contact.email.present?
+json.has_name @contact.name.present?
+json.has_phone_number @contact.phone_number.present?
json.identifier @contact.identifier
diff --git a/app/views/api/v1/widget/contacts/update.json.jbuilder b/app/views/api/v1/widget/contacts/update.json.jbuilder
index d6228cbfe..21f3c37b7 100644
--- a/app/views/api/v1/widget/contacts/update.json.jbuilder
+++ b/app/views/api/v1/widget/contacts/update.json.jbuilder
@@ -1,4 +1,4 @@
json.id @contact.id
-json.name @contact.name
-json.email @contact.email
-json.phone_number @contact.phone_number
+json.has_email @contact.email.present?
+json.has_name @contact.name.present?
+json.has_phone_number @contact.phone_number.present?
diff --git a/enterprise/app/models/applied_sla.rb b/enterprise/app/models/applied_sla.rb
index 0ae634536..6adeee036 100644
--- a/enterprise/app/models/applied_sla.rb
+++ b/enterprise/app/models/applied_sla.rb
@@ -30,14 +30,14 @@ class AppliedSla < ApplicationRecord
enum sla_status: { active: 0, hit: 1, missed: 2, active_with_misses: 3 }
scope :filter_by_date_range, ->(range) { where(created_at: range) if range.present? }
- scope :filter_by_inbox_id, ->(inbox_id) { where(inbox_id: inbox_id) if inbox_id.present? }
- scope :filter_by_team_id, ->(team_id) { where(team_id: team_id) if team_id.present? }
+ scope :filter_by_inbox_id, ->(inbox_id) { joins(:conversation).where(conversations: { inbox_id: inbox_id }) if inbox_id.present? }
+ scope :filter_by_team_id, ->(team_id) { joins(:conversation).where(conversations: { team_id: team_id }) if team_id.present? }
scope :filter_by_sla_policy_id, ->(sla_policy_id) { where(sla_policy_id: sla_policy_id) if sla_policy_id.present? }
- scope :filter_by_label_list, ->(label_list) { joins(:conversation).where(conversations: { cached_label_list: label_list }) if label_list.present? }
+ scope :filter_by_label_list, lambda { |label_list|
+ joins(:conversation).where('conversations.cached_label_list LIKE ?', "%#{label_list}%") if label_list.present?
+ }
scope :filter_by_assigned_agent_id, lambda { |assigned_agent_id|
- if assigned_agent_id.present?
- joins(:conversation).where(conversations: { assigned_agent_id: assigned_agent_id })
- end
+ joins(:conversation).where(conversations: { assignee_id: assigned_agent_id }) if assigned_agent_id.present?
}
scope :missed, -> { where(sla_status: %i[missed active_with_misses]) }
diff --git a/spec/controllers/api/v1/widget/contacts_controller_spec.rb b/spec/controllers/api/v1/widget/contacts_controller_spec.rb
index 336d9d9ca..7abbcf22e 100644
--- a/spec/controllers/api/v1/widget/contacts_controller_spec.rb
+++ b/spec/controllers/api/v1/widget/contacts_controller_spec.rb
@@ -48,7 +48,9 @@ RSpec.describe '/api/v1/widget/contacts', type: :request do
headers: { 'X-Auth-Token' => token },
as: :json
body = response.parsed_body
- expect(body['phone_number']).to eq('+745623239')
+ expect(body['has_phone_number']).to be true
+ contact.reload
+ expect(contact.phone_number).to eq('+745623239')
expect(response).to have_http_status(:success)
end
@@ -58,7 +60,9 @@ RSpec.describe '/api/v1/widget/contacts', type: :request do
headers: { 'X-Auth-Token' => token },
as: :json
body = response.parsed_body
- expect(body['phone_number']).to eq('+245623239')
+ expect(body['has_phone_number']).to be true
+ contact.reload
+ expect(contact.phone_number).to eq('+245623239')
expect(response).to have_http_status(:success)
end
@@ -68,7 +72,33 @@ RSpec.describe '/api/v1/widget/contacts', type: :request do
headers: { 'X-Auth-Token' => token },
as: :json
body = response.parsed_body
- expect(body['email']).to eq('test@test.com')
+ expect(body['has_email']).to be true
+ contact.reload
+ expect(contact.email).to eq('test@test.com')
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'dont update email if empty value email passed' do
+ patch '/api/v1/widget/contact',
+ params: params.merge({ email: '' }),
+ headers: { 'X-Auth-Token' => token },
+ as: :json
+ body = response.parsed_body
+ expect(body['has_email']).to be true
+ contact.reload
+ expect(contact.email).to eq('test@test.com')
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'dont update email if nil value email passed' do
+ patch '/api/v1/widget/contact',
+ params: params.merge({ email: nil }),
+ headers: { 'X-Auth-Token' => token },
+ as: :json
+ body = response.parsed_body
+ expect(body['has_email']).to be true
+ contact.reload
+ expect(contact.email).to eq('test@test.com')
expect(response).to have_http_status(:success)
end
@@ -78,7 +108,9 @@ RSpec.describe '/api/v1/widget/contacts', type: :request do
headers: { 'X-Auth-Token' => token },
as: :json
body = response.parsed_body
- expect(body['email']).to eq('test-1@test.com')
+ expect(body['has_email']).to be true
+ contact.reload
+ expect(contact.email).to eq('test-1@test.com')
expect(response).to have_http_status(:success)
end
end
diff --git a/spec/controllers/api/v1/widget/conversations_controller_spec.rb b/spec/controllers/api/v1/widget/conversations_controller_spec.rb
index c3fd7ff26..e4f5a4bde 100644
--- a/spec/controllers/api/v1/widget/conversations_controller_spec.rb
+++ b/spec/controllers/api/v1/widget/conversations_controller_spec.rb
@@ -190,17 +190,19 @@ RSpec.describe '/api/v1/widget/conversations/toggle_typing', type: :request do
describe 'POST /api/v1/widget/conversations/transcript' do
context 'with a conversation' do
it 'sends transcript email' do
+ contact.update(email: 'test@test.com')
mailer = double
allow(ConversationReplyMailer).to receive(:with).and_return(mailer)
allow(mailer).to receive(:conversation_transcript)
post '/api/v1/widget/conversations/transcript',
headers: { 'X-Auth-Token' => token },
- params: { website_token: web_widget.website_token, email: 'test@test.com' },
+ params: { website_token: web_widget.website_token },
as: :json
expect(response).to have_http_status(:success)
expect(mailer).to have_received(:conversation_transcript).with(conversation, 'test@test.com')
+ contact.update(email: nil)
end
end
end
diff --git a/spec/enterprise/controllers/api/v1/accounts/applied_slas_controller_spec.rb b/spec/enterprise/controllers/api/v1/accounts/applied_slas_controller_spec.rb
index 34389dad4..423f65711 100644
--- a/spec/enterprise/controllers/api/v1/accounts/applied_slas_controller_spec.rb
+++ b/spec/enterprise/controllers/api/v1/accounts/applied_slas_controller_spec.rb
@@ -92,7 +92,7 @@ RSpec.describe 'Applied SLAs API', type: :request do
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation3, created_at: 3.days.ago)
get "/api/v1/accounts/#{account.id}/applied_slas/metrics",
- params: { label_list: ['label1'] },
+ params: { label_list: 'label1' },
headers: administrator.create_new_auth_token
expect(response).to have_http_status(:success)
body = JSON.parse(response.body)
@@ -205,7 +205,7 @@ RSpec.describe 'Applied SLAs API', type: :request do
create(:applied_sla, sla_policy: sla_policy1, conversation: conversation3, created_at: 3.days.ago, sla_status: 'missed')
get "/api/v1/accounts/#{account.id}/applied_slas",
- params: { label_list: ['label1'] },
+ params: { label_list: 'label1' },
headers: administrator.create_new_auth_token
expect(response).to have_http_status(:success)
body = JSON.parse(response.body)
diff --git a/spec/services/imap/fetch_email_service_spec.rb b/spec/services/imap/fetch_email_service_spec.rb
index 28a173951..46336bf0f 100644
--- a/spec/services/imap/fetch_email_service_spec.rb
+++ b/spec/services/imap/fetch_email_service_spec.rb
@@ -30,6 +30,7 @@ RSpec.describe Imap::FetchEmailService do
allow(imap).to receive(:search).with(%w[SINCE 25-Oct-2020]).and_return([1])
allow(imap).to receive(:fetch).with([1], 'BODY.PEEK[HEADER]').and_return([email_header])
allow(imap).to receive(:fetch).with(1, 'RFC822').and_return([imap_fetch_mail])
+ allow(imap).to receive(:logout)
result = described_class.new(channel: imap_email_channel).perform
@@ -39,6 +40,7 @@ RSpec.describe Imap::FetchEmailService do
expect(imap).to have_received(:fetch).with([1], 'BODY.PEEK[HEADER]')
expect(imap).to have_received(:fetch).with(1, 'RFC822')
expect(logger).to have_received(:info).with("[IMAP::FETCH_EMAIL_SERVICE] Fetching mails from #{imap_email_channel.email}, found 1.")
+ expect(imap).to have_received(:logout)
end
end
@@ -51,6 +53,7 @@ RSpec.describe Imap::FetchEmailService do
allow(imap).to receive(:search).with(%w[SINCE 25-Oct-2020]).and_return([1])
allow(imap).to receive(:fetch).with([1], 'BODY.PEEK[HEADER]').and_return([email_header])
+ allow(imap).to receive(:logout)
result = described_class.new(channel: imap_email_channel).perform
diff --git a/spec/services/imap/microsoft_fetch_email_service_spec.rb b/spec/services/imap/microsoft_fetch_email_service_spec.rb
index 8f79b86b5..3a13ce0c6 100644
--- a/spec/services/imap/microsoft_fetch_email_service_spec.rb
+++ b/spec/services/imap/microsoft_fetch_email_service_spec.rb
@@ -35,6 +35,7 @@ RSpec.describe Imap::MicrosoftFetchEmailService do
allow(imap).to receive(:search).with(%w[SINCE 25-Oct-2020]).and_return([1])
allow(imap).to receive(:fetch).with([1], 'BODY.PEEK[HEADER]').and_return([email_header])
allow(imap).to receive(:fetch).with(1, 'RFC822').and_return([imap_fetch_mail])
+ allow(imap).to receive(:logout)
result = described_class.new(channel: microsoft_channel).perform