From e44e9fc0254d9c3fa3d43a1e6ef4231f1da3937b Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Tue, 16 Mar 2021 19:40:49 +0530 Subject: [PATCH 1/9] chore: Fix SMTP mailer config defaults (#1923) --- config/initializers/mailer.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/initializers/mailer.rb b/config/initializers/mailer.rb index 2ed599105..54a22f0dc 100644 --- a/config/initializers/mailer.rb +++ b/config/initializers/mailer.rb @@ -13,16 +13,16 @@ Rails.application.configure do # Config related to smtp smtp_settings = { - domain: ENV.fetch('SMTP_DOMAIN', ''), address: ENV.fetch('SMTP_ADDRESS', 'localhost'), port: ENV.fetch('SMTP_PORT', 587) } - smtp_settings[:authentication] = ENV.fetch('SMTP_AUTHENTICATION', 'login').to_sym if ENV.fetch('SMTP_AUTHENTICATION', '').present? + smtp_settings[:authentication] = ENV.fetch('SMTP_AUTHENTICATION', 'login').to_sym if ENV['SMTP_AUTHENTICATION'].present? + smtp_settings[:domain] = ENV['SMTP_DOMAIN'] if ENV['SMTP_DOMAIN'].present? smtp_settings[:user_name] = ENV['SMTP_USERNAME'] smtp_settings[:password] = ENV['SMTP_PASSWORD'] smtp_settings[:enable_starttls_auto] = ActiveModel::Type::Boolean.new.cast(ENV.fetch('SMTP_ENABLE_STARTTLS_AUTO', true)) - smtp_settings[:openssl_verify_mode] = ENV.fetch('SMTP_OPENSSL_VERIFY_MODE', 'peer') + smtp_settings[:openssl_verify_mode] = ENV['SMTP_OPENSSL_VERIFY_MODE'] if ENV['SMTP_OPENSSL_VERIFY_MODE'].present? smtp_settings[:ssl] = ActiveModel::Type::Boolean.new.cast(ENV.fetch('SMTP_SSL', true)) if ENV['SMTP_SSL'] smtp_settings[:tls] = ActiveModel::Type::Boolean.new.cast(ENV.fetch('SMTP_TLS', true)) if ENV['SMTP_TLS'] From 54d578676b25383fea9a8e8e5219aadb95f9e655 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Tue, 16 Mar 2021 19:44:50 +0530 Subject: [PATCH 2/9] fix: Use team conversationURL in team section (#1921) Fixes: #1917 --- .../dashboard/components/ChatList.vue | 17 ++++++++++++----- .../widgets/conversation/ConversationCard.vue | 5 +++++ app/javascript/dashboard/helper/URLHelper.js | 11 ++++++++++- .../dashboard/helper/specs/URLHelper.spec.js | 5 +++++ .../dashboard/conversation/ConversationView.vue | 8 +------- 5 files changed, 33 insertions(+), 13 deletions(-) diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index 6603a25c6..3a5abf952 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -25,6 +25,7 @@ v-for="chat in conversationList" :key="chat.id" :active-label="label" + :team-id="teamId" :chat="chat" /> @@ -76,14 +77,14 @@ export default { type: [String, Number], default: 0, }, + teamId: { + type: [String, Number], + default: 0, + }, label: { type: String, default: '', }, - activeTeam: { - type: Object, - default: () => {}, - }, }, data() { return { @@ -132,7 +133,7 @@ export default { status: this.activeStatus, page: this.currentPage + 1, labels: this.label ? [this.label] : undefined, - teamId: this.activeTeam.name ? this.activeTeam.id : undefined, + teamId: this.teamId ? this.teamId : undefined, }; }, pageTitle() { @@ -168,6 +169,12 @@ export default { return labels.includes(this.label); }); }, + activeTeam() { + if (this.teamId) { + return this.$store.getters['teams/getTeam'](this.teamId); + } + return {}; + }, }, watch: { activeTeam() { diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue index 4b8193699..43cf1817d 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue @@ -85,6 +85,10 @@ export default { type: Boolean, default: false, }, + teamId: { + type: [String, Number], + default: 0, + }, }, computed: { @@ -171,6 +175,7 @@ export default { activeInbox, id: chat.id, label: this.activeLabel, + teamId: this.teamId, }); router.push({ path: frontendURL(path) }); }, diff --git a/app/javascript/dashboard/helper/URLHelper.js b/app/javascript/dashboard/helper/URLHelper.js index 97744e43b..e9124e097 100644 --- a/app/javascript/dashboard/helper/URLHelper.js +++ b/app/javascript/dashboard/helper/URLHelper.js @@ -5,13 +5,22 @@ export const frontendURL = (path, params) => { return `/app/${path}${stringifiedParams}`; }; -export const conversationUrl = ({ accountId, activeInbox, id, label }) => { +export const conversationUrl = ({ + accountId, + activeInbox, + id, + label, + teamId, +}) => { if (activeInbox) { return `accounts/${accountId}/inbox/${activeInbox}/conversations/${id}`; } if (label) { return `accounts/${accountId}/label/${label}/conversations/${id}`; } + if (teamId) { + return `accounts/${accountId}/team/${teamId}/conversations/${id}`; + } return `accounts/${accountId}/conversations/${id}`; }; diff --git a/app/javascript/dashboard/helper/specs/URLHelper.spec.js b/app/javascript/dashboard/helper/specs/URLHelper.spec.js index 354cffcd6..8b4a5266d 100644 --- a/app/javascript/dashboard/helper/specs/URLHelper.spec.js +++ b/app/javascript/dashboard/helper/specs/URLHelper.spec.js @@ -21,6 +21,11 @@ describe('#URL Helpers', () => { conversationUrl({ accountId: 1, label: 'customer-support', id: 1 }) ).toBe('accounts/1/label/customer-support/conversations/1'); }); + it('should return correct conversation URL if team Id is available', () => { + expect(conversationUrl({ accountId: 1, teamId: 1, id: 1 })).toBe( + 'accounts/1/team/1/conversations/1' + ); + }); }); describe('frontendURL', () => { diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue b/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue index 0e1bfd464..f66ef9587 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/ConversationView.vue @@ -3,7 +3,7 @@ @@ -68,12 +68,6 @@ export default { } return false; }, - activeTeam() { - if (this.teamId) { - return this.$store.getters['teams/getTeam'](this.teamId); - } - return {}; - }, }, mounted() { From 6afa1fb556b6b2208314e702b7b23f1709db9a41 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Fri, 19 Mar 2021 12:55:05 +0530 Subject: [PATCH 3/9] Fix: Error when assigns a conversation to user (#1926) --- app/javascript/dashboard/helper/actionCable.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/javascript/dashboard/helper/actionCable.js b/app/javascript/dashboard/helper/actionCable.js index 9bed8e7d5..6cb1f7229 100644 --- a/app/javascript/dashboard/helper/actionCable.js +++ b/app/javascript/dashboard/helper/actionCable.js @@ -48,11 +48,7 @@ class ActionCableConnector extends BaseActionCableConnector { }; onAssigneeChanged = payload => { - const { meta = {}, id } = payload; - const { assignee } = meta || {}; - if (id) { - this.app.$store.dispatch('updateAssignee', { id, assignee }); - } + this.app.$store.dispatch('updateConversation', payload); this.fetchConversationStats(); }; From e289106c5b7abe75379076b0cc86230cc4da7fc5 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+sivin-git@users.noreply.github.com> Date: Sat, 20 Mar 2021 13:08:56 +0530 Subject: [PATCH 4/9] Chore: Hover color for status dropdown (#1906) --- .../dashboard/assets/scss/_animations.scss | 6 +- .../assets/scss/plugins/_dropdown.scss | 51 +++++++---- .../assets/scss/widgets/_sidemenu.scss | 10 --- .../components/layout/AvailabilityStatus.vue | 88 +++++++++---------- .../dashboard/components/layout/Sidebar.vue | 8 -- .../layout/sidebarComponents/OptionsMenu.vue | 28 +++--- .../widgets/conversation/MoreActions.vue | 81 ++++++----------- .../components/ui/dropdown/DropdownItem.vue | 46 ++++++++++ .../components/ui/dropdown/DropdownMenu.vue | 21 +++++ 9 files changed, 190 insertions(+), 149 deletions(-) create mode 100644 app/javascript/shared/components/ui/dropdown/DropdownItem.vue create mode 100644 app/javascript/shared/components/ui/dropdown/DropdownMenu.vue diff --git a/app/javascript/dashboard/assets/scss/_animations.scss b/app/javascript/dashboard/assets/scss/_animations.scss index 144c9fc74..3a26d5bf2 100644 --- a/app/javascript/dashboard/assets/scss/_animations.scss +++ b/app/javascript/dashboard/assets/scss/_animations.scss @@ -54,8 +54,10 @@ opacity: 0; } -.menu-slide-enter-active, .menu-slide-leave-active { - transition: all .15s $ease-in-cubic; +.menu-slide-enter-active, +.menu-slide-leave-active { + transform: translateY(0); + transition: transform 0.25s $ease-in-cubic, opacity 0.15s $ease-in-cubic; } .menu-slide-enter, .menu-slide-leave-to diff --git a/app/javascript/dashboard/assets/scss/plugins/_dropdown.scss b/app/javascript/dashboard/assets/scss/plugins/_dropdown.scss index b4dada351..9f70dec13 100644 --- a/app/javascript/dashboard/assets/scss/plugins/_dropdown.scss +++ b/app/javascript/dashboard/assets/scss/plugins/_dropdown.scss @@ -1,27 +1,42 @@ -.dropdown-pane.sleek { +.dropdown-pane { @include elegant-card; @include border-light; - padding-left: 0; - padding-right: 0; - right: -12px; - top: 48px; - width: auto; + z-index: 999; - &::before { - @include arrow(top, var(--color-border-light), 14px); - position: absolute; - right: 6px; - top: -14px; + &.dropdown-pane--open { + display: block; + visibility: visible; } - &::after { - @include arrow(top, $color-white, var(--space-slab)); - position: absolute; - right: var(--space-small); - top: -12px; + &.dropdowm--bottom { + &::before { + @include arrow(top, var(--color-border-light), 14px); + position: absolute; + right: 6px; + top: -14px; + } + + &::after { + @include arrow(top, $color-white, var(--space-slab)); + position: absolute; + right: var(--space-small); + top: -12px; + } } - .dropdown>li>a:hover { - background: var(--color-background); + &.dropdowm--top { + &::before { + @include arrow(bottom, var(--color-border-light), 14px); + bottom: -14px; + position: absolute; + right: 6px; + } + + &::after { + @include arrow(bottom, $color-white, var(--space-slab)); + bottom: -12px; + position: absolute; + right: var(--space-small); + } } } diff --git a/app/javascript/dashboard/assets/scss/widgets/_sidemenu.scss b/app/javascript/dashboard/assets/scss/widgets/_sidemenu.scss index 65abd09c7..099d32287 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_sidemenu.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_sidemenu.scss @@ -68,21 +68,11 @@ position: relative; .dropdown-pane { - @include elegant-card; - @include border-light; bottom: 6rem; display: block; left: 5rem; visibility: visible; width: 80%; - z-index: 999; - - &::before { - @include arrow(bottom, $color-white, $space-slab); - bottom: -$space-slab; - position: absolute; - right: $space-slab; - } } .active { diff --git a/app/javascript/dashboard/components/layout/AvailabilityStatus.vue b/app/javascript/dashboard/components/layout/AvailabilityStatus.vue index d19e3ba0e..469129339 100644 --- a/app/javascript/dashboard/components/layout/AvailabilityStatus.vue +++ b/app/javascript/dashboard/components/layout/AvailabilityStatus.vue @@ -15,25 +15,24 @@ @@ -47,9 +46,17 @@ + diff --git a/app/javascript/shared/components/ui/dropdown/DropdownMenu.vue b/app/javascript/shared/components/ui/dropdown/DropdownMenu.vue new file mode 100644 index 000000000..12a485151 --- /dev/null +++ b/app/javascript/shared/components/ui/dropdown/DropdownMenu.vue @@ -0,0 +1,21 @@ + + From 4657e5c7132a45d7d45ba2623e36a53dca2c62cf Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Sat, 20 Mar 2021 17:12:36 +0530 Subject: [PATCH 5/9] Fix: Refactors error prone checks (#1947) --- .../dashboard/components/widgets/conversation/MessagesView.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue index da31bf55b..a4656d89b 100644 --- a/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue +++ b/app/javascript/dashboard/components/widgets/conversation/MessagesView.vue @@ -162,7 +162,7 @@ export default { }, shouldShowSpinner() { return ( - this.getMessages.dataFetched === undefined || + (this.getMessages && this.getMessages.dataFetched === undefined) || (!this.listLoadingStatus && this.isLoadingPrevious) ); }, From 484c32fae3477008bfa69684c054515b751f1f38 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Sat, 20 Mar 2021 17:42:29 +0530 Subject: [PATCH 6/9] Fix: sidebar filters not applying bug for chatlist (#1938) --- .../dashboard/components/ChatList.vue | 18 +-- .../store/modules/conversations/getters.js | 49 ++++---- .../store/modules/conversations/helpers.js | 29 +++++ .../specs/conversations/getters.spec.js | 80 ++++++++----- .../specs/conversations/helpers.spec.js | 106 +++++++++++++++--- 5 files changed, 201 insertions(+), 81 deletions(-) diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index 3a5abf952..a21fb15a7 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -150,24 +150,16 @@ export default { }, conversationList() { let conversationList = []; + const filters = this.conversationFilters; if (this.activeAssigneeTab === 'me') { - conversationList = this.mineChatsList.slice(); + conversationList = [...this.mineChatsList(filters)]; } else if (this.activeAssigneeTab === 'unassigned') { - conversationList = this.unAssignedChatsList.slice(); + conversationList = [...this.unAssignedChatsList(filters)]; } else { - conversationList = this.allChatList.slice(); + conversationList = [...this.allChatList(filters)]; } - if (!this.label) { - return conversationList; - } - - return conversationList.filter(conversation => { - const labels = this.$store.getters[ - 'conversationLabels/getConversationLabels' - ](conversation.id); - return labels.includes(this.label); - }); + return conversationList; }, activeTeam() { if (this.teamId) { diff --git a/app/javascript/dashboard/store/modules/conversations/getters.js b/app/javascript/dashboard/store/modules/conversations/getters.js index 04d7f58c3..de13f0572 100644 --- a/app/javascript/dashboard/store/modules/conversations/getters.js +++ b/app/javascript/dashboard/store/modules/conversations/getters.js @@ -1,4 +1,5 @@ import authAPI from '../../../api/auth'; +import { applyPageFilters } from './helpers'; export const getSelectedChatConversation = ({ allConversations, @@ -18,24 +19,30 @@ const getters = { ); return selectedChat || {}; }, - getMineChats(_state) { + getMineChats: _state => activeFilters => { const currentUserID = authAPI.getCurrentUser().id; - return _state.allConversations.filter(chat => - !chat.meta.assignee - ? false - : chat.status === _state.chatStatusFilter && - chat.meta.assignee.id === currentUserID - ); + + return _state.allConversations.filter(conversation => { + const { assignee } = conversation.meta; + const isAssignedToMe = assignee && assignee.id === currentUserID; + const shouldFilter = applyPageFilters(conversation, activeFilters); + const isChatMine = isAssignedToMe && shouldFilter; + + return isChatMine; + }); }, - getUnAssignedChats(_state) { - return _state.allConversations.filter( - chat => !chat.meta.assignee && chat.status === _state.chatStatusFilter - ); + getUnAssignedChats: _state => activeFilters => { + return _state.allConversations.filter(conversation => { + const isUnAssigned = !conversation.meta.assignee; + const shouldFilter = applyPageFilters(conversation, activeFilters); + return isUnAssigned && shouldFilter; + }); }, - getAllStatusChats(_state) { - return _state.allConversations.filter( - chat => chat.status === _state.chatStatusFilter - ); + getAllStatusChats: _state => activeFilters => { + return _state.allConversations.filter(conversation => { + const shouldFilter = applyPageFilters(conversation, activeFilters); + return shouldFilter; + }); }, getChatListLoadingStatus: ({ listLoadingStatus }) => listLoadingStatus, getAllMessagesLoaded(_state) { @@ -56,18 +63,6 @@ const getters = { }, getChatStatusFilter: ({ chatStatusFilter }) => chatStatusFilter, getSelectedInbox: ({ currentInbox }) => currentInbox, - getNextChatConversation: _state => { - const [selectedChat] = getSelectedChatConversation(_state); - const conversations = getters.getAllStatusChats(_state); - if (conversations.length <= 1) { - return null; - } - const currentIndex = conversations.findIndex( - conversation => conversation.id === selectedChat.id - ); - const nextIndex = (currentIndex + 1) % conversations.length; - return conversations[nextIndex]; - }, }; export default getters; diff --git a/app/javascript/dashboard/store/modules/conversations/helpers.js b/app/javascript/dashboard/store/modules/conversations/helpers.js index 3f40ea5cc..8492da7ca 100644 --- a/app/javascript/dashboard/store/modules/conversations/helpers.js +++ b/app/javascript/dashboard/store/modules/conversations/helpers.js @@ -4,3 +4,32 @@ export const findPendingMessageIndex = (chat, message) => { m => m.id === message.id || m.id === tempMessageId ); }; + +export const applyPageFilters = (conversation, filters) => { + const { inboxId, status, labels = [], teamId } = filters; + const { + status: chatStatus, + inbox_id: chatInboxId, + labels: chatLabels = [], + meta = {}, + } = conversation; + const { team = {} } = meta; + const { id: chatTeamId } = team; + const filterByStatus = chatStatus === status; + let shouldFilter = filterByStatus; + + if (inboxId) { + const filterByInbox = Number(inboxId) === chatInboxId; + shouldFilter = shouldFilter && filterByInbox; + } + if (teamId) { + const filterByTeam = Number(teamId) === chatTeamId; + shouldFilter = shouldFilter && filterByTeam; + } + if (labels.length) { + const filterByLabels = labels.every(label => chatLabels.includes(label)); + shouldFilter = shouldFilter && filterByLabels; + } + + return shouldFilter; +}; diff --git a/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js index 1492ebdb6..8d9ad1711 100644 --- a/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js @@ -47,33 +47,59 @@ describe('#getters', () => { ]); }); }); - describe('#getNextChatConversation', () => { - it('return the next chat', () => { - const state = { - allConversations: [ - { - id: 1, - }, - { - id: 2, - }, - ], - selectedChatId: 1, - }; - expect(getters.getNextChatConversation(state)).toEqual({ - id: 2, - }); - }); - it('return null when there is only one chat', () => { - const state = { - allConversations: [ - { - id: 1, - }, - ], - selectedChatId: 1, - }; - expect(getters.getNextChatConversation(state)).toBeNull(); + describe('#getUnAssignedChats', () => { + it('order returns only chats assigned to user', () => { + const conversationList = [ + { + id: 1, + inbox_id: 2, + status: 1, + meta: { assignee: { id: 1 } }, + labels: ['sales', 'dev'], + }, + { + id: 2, + inbox_id: 2, + status: 1, + meta: {}, + labels: ['dev'], + }, + { + id: 11, + inbox_id: 3, + status: 1, + meta: { assignee: { id: 1 } }, + labels: [], + }, + { + id: 22, + inbox_id: 4, + status: 1, + meta: { team: { id: 5 } }, + labels: ['sales'], + }, + ]; + + expect( + getters.getUnAssignedChats({ allConversations: conversationList })({ + status: 1, + }) + ).toEqual([ + { + id: 2, + inbox_id: 2, + status: 1, + meta: {}, + labels: ['dev'], + }, + { + id: 22, + inbox_id: 4, + status: 1, + meta: { team: { id: 5 } }, + labels: ['sales'], + }, + ]); }); }); }); diff --git a/app/javascript/dashboard/store/modules/specs/conversations/helpers.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/helpers.spec.js index 9a1f59a12..6f3463666 100644 --- a/app/javascript/dashboard/store/modules/specs/conversations/helpers.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversations/helpers.spec.js @@ -1,4 +1,38 @@ -import { findPendingMessageIndex } from '../../conversations/helpers'; +import { + findPendingMessageIndex, + applyPageFilters, +} from '../../conversations/helpers'; + +const conversationList = [ + { + id: 1, + inbox_id: 2, + status: 1, + meta: {}, + labels: ['sales', 'dev'], + }, + { + id: 2, + inbox_id: 2, + status: 1, + meta: {}, + labels: ['dev'], + }, + { + id: 11, + inbox_id: 3, + status: 1, + meta: { team: { id: 5 } }, + labels: [], + }, + { + id: 22, + inbox_id: 4, + status: 1, + meta: { team: { id: 5 } }, + labels: ['sales'], + }, +]; describe('#findPendingMessageIndex', () => { it('returns the correct index of pending message with id', () => { @@ -18,20 +52,64 @@ describe('#findPendingMessageIndex', () => { }); }); -describe('#addOrUpdateChat', () => { - it('returns the correct index of pending message with id', () => { - const chat = { - messages: [{ id: 1, status: 'progress' }], - }; - const message = { echo_id: 1 }; - expect(findPendingMessageIndex(chat, message)).toEqual(0); +describe('#applyPageFilters', () => { + describe('#filter-team', () => { + it('returns true if conversation has team and team filter is active', () => { + const filters = { + status: 1, + teamId: 5, + }; + expect(applyPageFilters(conversationList[3], filters)).toEqual(true); + }); + it('returns true if conversation has no team and team filter is active', () => { + const filters = { + status: 1, + teamId: 5, + }; + expect(applyPageFilters(conversationList[0], filters)).toEqual(false); + }); }); - it('returns -1 if pending message with id is not present', () => { - const chat = { - messages: [{ id: 1, status: 'progress' }], - }; - const message = { echo_id: 2 }; - expect(findPendingMessageIndex(chat, message)).toEqual(-1); + describe('#filter-inbox', () => { + it('returns true if conversation has inbox and inbox filter is active', () => { + const filters = { + status: 1, + inboxId: 4, + }; + expect(applyPageFilters(conversationList[3], filters)).toEqual(true); + }); + it('returns true if conversation has no inbox and inbox filter is active', () => { + const filters = { + status: 1, + inboxId: 5, + }; + expect(applyPageFilters(conversationList[0], filters)).toEqual(false); + }); + }); + + describe('#filter-labels', () => { + it('returns true if conversation has labels and labels filter is active', () => { + const filters = { + status: 1, + labels: ['dev'], + }; + expect(applyPageFilters(conversationList[0], filters)).toEqual(true); + }); + it('returns true if conversation has no inbox and inbox filter is active', () => { + const filters = { + status: 1, + labels: ['dev'], + }; + expect(applyPageFilters(conversationList[2], filters)).toEqual(false); + }); + }); + + describe('#filter-status', () => { + it('returns true if conversation has status and status filter is active', () => { + const filters = { + status: 1, + }; + expect(applyPageFilters(conversationList[1], filters)).toEqual(true); + }); }); }); From 3043ee505885074aa1598abd41be1ad6eb2a27cc Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Sat, 20 Mar 2021 17:44:20 +0530 Subject: [PATCH 7/9] Fix: Hide prechat for sessions inititated with setUser (#1914) --- .../api/v1/widget/contacts_controller.rb | 5 ++- app/javascript/widget/App.vue | 1 + app/javascript/widget/api/contacts.js | 3 ++ .../widget/store/modules/contacts.js | 34 +++++++++++++++++-- .../modules/specs/contact/actions.spec.js | 21 ++++++++++++ .../modules/specs/contact/getters.spec.js | 21 ++++++++++++ .../modules/specs/contact/mutations.spec.js | 17 ++++++++++ app/javascript/widget/views/Home.vue | 4 ++- .../api/v1/widget/contacts/show.json.jbuilder | 3 ++ config/routes.rb | 2 +- 10 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 app/javascript/widget/store/modules/specs/contact/actions.spec.js create mode 100644 app/javascript/widget/store/modules/specs/contact/getters.spec.js create mode 100644 app/javascript/widget/store/modules/specs/contact/mutations.spec.js create mode 100644 app/views/api/v1/widget/contacts/show.json.jbuilder diff --git a/app/controllers/api/v1/widget/contacts_controller.rb b/app/controllers/api/v1/widget/contacts_controller.rb index ad818d7ac..135d2ced3 100644 --- a/app/controllers/api/v1/widget/contacts_controller.rb +++ b/app/controllers/api/v1/widget/contacts_controller.rb @@ -1,6 +1,9 @@ class Api::V1::Widget::ContactsController < Api::V1::Widget::BaseController + before_action :process_hmac + + def show; end + def update - process_hmac contact_identify_action = ContactIdentifyAction.new( contact: @contact, params: permitted_params.to_h.deep_symbolize_keys diff --git a/app/javascript/widget/App.vue b/app/javascript/widget/App.vue index 48e89096d..e0e08911c 100755 --- a/app/javascript/widget/App.vue +++ b/app/javascript/widget/App.vue @@ -150,6 +150,7 @@ export default { this.setPopoutDisplay(message.showPopoutButton); this.fetchAvailableAgents(websiteToken); this.setHideMessageBubble(message.hideMessageBubble); + this.$store.dispatch('contacts/get'); } else if (message.event === 'widget-visible') { this.scrollConversationToBottom(); } else if (message.event === 'set-current-url') { diff --git a/app/javascript/widget/api/contacts.js b/app/javascript/widget/api/contacts.js index a72bb4910..a246e6dc2 100644 --- a/app/javascript/widget/api/contacts.js +++ b/app/javascript/widget/api/contacts.js @@ -3,6 +3,9 @@ import { API } from 'widget/helpers/axios'; const buildUrl = endPoint => `/api/v1/${endPoint}${window.location.search}`; export default { + get() { + return API.get(buildUrl('widget/contact')); + }, update(identifier, userObject) { return API.patch(buildUrl('widget/contact'), { identifier, diff --git a/app/javascript/widget/store/modules/contacts.js b/app/javascript/widget/store/modules/contacts.js index 0ecae3a55..582b78414 100644 --- a/app/javascript/widget/store/modules/contacts.js +++ b/app/javascript/widget/store/modules/contacts.js @@ -1,7 +1,27 @@ import ContactsAPI from '../../api/contacts'; import { refreshActionCableConnector } from '../../helpers/actionCable'; +const state = { + currentUser: {}, +}; + +const SET_CURRENT_USER = 'SET_CURRENT_USER'; + +export const getters = { + getCurrentUser(_state) { + return _state.currentUser; + }, +}; + export const actions = { + get: async ({ commit }) => { + try { + const { data } = await ContactsAPI.get(); + commit(SET_CURRENT_USER, data); + } catch (error) { + // Ignore error + } + }, update: async ({ dispatch }, { identifier, user: userObject }) => { try { const user = { @@ -14,6 +34,7 @@ export const actions = { data: { pubsub_token: pubsubToken }, } = await ContactsAPI.update(identifier, user); + dispatch('get'); if (userObject.identifier_hash) { dispatch('conversation/clearConversations', {}, { root: true }); dispatch('conversation/fetchOldConversations', {}, { root: true }); @@ -33,10 +54,17 @@ export const actions = { }, }; +export const mutations = { + [SET_CURRENT_USER]($state, user) { + const { currentUser } = $state; + $state.currentUser = { ...currentUser, ...user }; + }, +}; + export default { namespaced: true, - state: {}, - getters: {}, + state, + getters, actions, - mutations: {}, + mutations, }; diff --git a/app/javascript/widget/store/modules/specs/contact/actions.spec.js b/app/javascript/widget/store/modules/specs/contact/actions.spec.js new file mode 100644 index 000000000..c16b0eb20 --- /dev/null +++ b/app/javascript/widget/store/modules/specs/contact/actions.spec.js @@ -0,0 +1,21 @@ +import { API } from 'widget/helpers/axios'; +import { actions } from '../../contacts'; + +const commit = jest.fn(); +jest.mock('widget/helpers/axios'); + +describe('#actions', () => { + describe('#update', () => { + it('sends correct actions', async () => { + const user = { + email: 'thoma@sphadikam.com', + name: 'Adu Thoma', + avatar_url: '', + identifier_hash: 'malana_hash', + }; + API.patch.mockResolvedValue({ data: { pubsub_token: 'token' } }); + await actions.update({ commit }, { identifier: 1, user }); + expect(commit.mock.calls).toEqual([]); + }); + }); +}); diff --git a/app/javascript/widget/store/modules/specs/contact/getters.spec.js b/app/javascript/widget/store/modules/specs/contact/getters.spec.js new file mode 100644 index 000000000..43b0f15d7 --- /dev/null +++ b/app/javascript/widget/store/modules/specs/contact/getters.spec.js @@ -0,0 +1,21 @@ +import { getters } from '../../contacts'; + +describe('#getters', () => { + it('getCurrentUser', () => { + const user = { + email: 'thoma@sphadikam.com', + name: 'Adu Thoma', + avatar_url: '', + identifier_hash: 'malana_hash', + }; + const state = { + currentUser: user, + }; + expect(getters.getCurrentUser(state)).toEqual({ + email: 'thoma@sphadikam.com', + name: 'Adu Thoma', + 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 new file mode 100644 index 000000000..fed202bf2 --- /dev/null +++ b/app/javascript/widget/store/modules/specs/contact/mutations.spec.js @@ -0,0 +1,17 @@ +import { mutations } from '../../contacts'; + +describe('#mutations', () => { + describe('#SET_CURRENT_USER', () => { + it('set current user', () => { + const user = { + email: 'thoma@sphadikam.com', + name: 'Adu Thoma', + avatar_url: '', + identifier_hash: 'malana_hash', + }; + const state = { currentUser: {} }; + mutations.SET_CURRENT_USER(state, user); + expect(state.currentUser).toEqual(user); + }); + }); +}); diff --git a/app/javascript/widget/views/Home.vue b/app/javascript/widget/views/Home.vue index 9753c5cd2..32f784ec7 100755 --- a/app/javascript/widget/views/Home.vue +++ b/app/javascript/widget/views/Home.vue @@ -114,13 +114,15 @@ export default { conversationSize: 'conversation/getConversationSize', groupedMessages: 'conversation/getGroupedConversation', isFetchingList: 'conversation/getIsFetchingList', + currentUser: 'contacts/getCurrentUser', }), currentView() { + const { email: currentUserEmail = '' } = this.currentUser; if (this.isHeaderCollapsed) { if (this.conversationSize) { return 'messageView'; } - if (this.preChatFormEnabled) { + if (this.preChatFormEnabled && !currentUserEmail) { return 'preChatFormView'; } return 'messageView'; diff --git a/app/views/api/v1/widget/contacts/show.json.jbuilder b/app/views/api/v1/widget/contacts/show.json.jbuilder new file mode 100644 index 000000000..9853ccbee --- /dev/null +++ b/app/views/api/v1/widget/contacts/show.json.jbuilder @@ -0,0 +1,3 @@ +json.id @contact.id +json.name @contact.name +json.email @contact.email diff --git a/config/routes.rb b/config/routes.rb index d5e1162fa..cedb58a12 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -150,7 +150,7 @@ Rails.application.routes.draw do post :transcript end end - resource :contact, only: [:update] + resource :contact, only: [:show, :update] resources :inbox_members, only: [:index] resources :labels, only: [:create, :destroy] end From 763d8d37df34d2f4cb6f4f7e4e47e29c2bb36a78 Mon Sep 17 00:00:00 2001 From: Siddharth Ramesh Date: Sat, 20 Mar 2021 17:44:58 +0530 Subject: [PATCH 8/9] Fix error messages during login for non-verified users (#1945) --- app/javascript/dashboard/routes/login/Login.vue | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/javascript/dashboard/routes/login/Login.vue b/app/javascript/dashboard/routes/login/Login.vue index 24541da90..a56a02ad6 100644 --- a/app/javascript/dashboard/routes/login/Login.vue +++ b/app/javascript/dashboard/routes/login/Login.vue @@ -149,7 +149,13 @@ export default { } if (response && response.status === 401) { - this.showAlert(this.$t('LOGIN.API.UNAUTH')); + const { errors } = response.data; + const hasAuthErrorMsg = errors && errors.length && errors[0] && typeof errors[0] === 'string'; + if (hasAuthErrorMsg) { + this.showAlert(errors[0]); + } else { + this.showAlert(this.$t('LOGIN.API.UNAUTH')); + } return; } this.showAlert(this.$t('LOGIN.API.ERROR_MESSAGE')); From b94e87584be75b7c69eecb88aab5112a5d532776 Mon Sep 17 00:00:00 2001 From: Sojan Date: Sun, 21 Mar 2021 01:45:13 +0530 Subject: [PATCH 9/9] Bump version to 1.14.1 --- config/app.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/app.yml b/config/app.yml index 1d82a9db3..7164aff76 100644 --- a/config/app.yml +++ b/config/app.yml @@ -1,5 +1,5 @@ shared: &shared - version: '1.14.0' + version: '1.14.1' development: <<: *shared diff --git a/package.json b/package.json index 8257a2b8c..52174feed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chatwoot/chatwoot", - "version": "1.14.0", + "version": "1.14.1", "license": "MIT", "scripts": { "eslint": "eslint app/javascript --fix",