From 285f7bbeb5837bc294e08bb2ec9bd877e53c2b5a Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Thu, 22 Apr 2021 23:17:29 +0530 Subject: [PATCH] Enhancement: Ability to assign administrators as conversation assignees (#2142) * Enhancement: Ability to assign administrators as conversation assignee Co-authored-by: Nithin David Thomas Co-authored-by: Muhsin Keloth --- app/javascript/dashboard/api/inboxes.js | 5 ++ .../widgets/conversation/ConversationBox.vue | 2 +- .../conversation/ConversationHeader.vue | 4 +- .../dashboard/conversation/ContactPanel.vue | 4 +- app/javascript/dashboard/store/index.js | 2 + .../store/modules/inboxAssignableAgents.js | 62 +++++++++++++++++++ .../inboxAssignableMembers/actions.spec.js | 36 +++++++++++ .../specs/inboxAssignableMembers/fixtures.js | 28 +++++++++ .../inboxAssignableMembers/getters.spec.js | 24 +++++++ .../inboxAssignableMembers/mutations.spec.js | 16 +++++ 10 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 app/javascript/dashboard/store/modules/inboxAssignableAgents.js create mode 100644 app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/actions.spec.js create mode 100644 app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/fixtures.js create mode 100644 app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/getters.spec.js create mode 100644 app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/mutations.spec.js diff --git a/app/javascript/dashboard/api/inboxes.js b/app/javascript/dashboard/api/inboxes.js index b5cea1d01..73400b78c 100644 --- a/app/javascript/dashboard/api/inboxes.js +++ b/app/javascript/dashboard/api/inboxes.js @@ -1,9 +1,14 @@ +/* global axios */ import ApiClient from './ApiClient'; class Inboxes extends ApiClient { constructor() { super('inboxes', { accountScoped: true }); } + + getAssignableAgents(inboxId) { + return axios.get(`${this.url}/${inboxId}/assignable_agents`); + } } export default new Inboxes(); diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationBox.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationBox.vue index bb700a86c..a4512cbbc 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationBox.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationBox.vue @@ -63,7 +63,7 @@ export default { watch: { 'currentChat.inbox_id'(inboxId) { if (inboxId) { - this.$store.dispatch('inboxMembers/fetch', { inboxId }); + this.$store.dispatch('inboxAssignableAgents/fetch', { inboxId }); } }, }, diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue index 5b4f40077..0b2057a61 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationHeader.vue @@ -93,8 +93,8 @@ export default { computed: { ...mapGetters({ - getAgents: 'inboxMembers/getMembersByInbox', - uiFlags: 'inboxMembers/getUIFlags', + getAgents: 'inboxAssignableAgents/getAssignableAgents', + uiFlags: 'inboxAssignableAgents/getUIFlags', currentChat: 'getSelectedChat', }), diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue b/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue index 9d20d90af..b89f71137 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue @@ -155,8 +155,8 @@ export default { ...mapGetters({ currentChat: 'getSelectedChat', teams: 'teams/getTeams', - getAgents: 'inboxMembers/getMembersByInbox', - uiFlags: 'inboxMembers/getUIFlags', + getAgents: 'inboxAssignableAgents/getAssignableAgents', + uiFlags: 'inboxAssignableAgents/getUIFlags', }), currentConversationMetaData() { return this.$store.getters[ diff --git a/app/javascript/dashboard/store/index.js b/app/javascript/dashboard/store/index.js index 851d65a67..c5a5a29af 100755 --- a/app/javascript/dashboard/store/index.js +++ b/app/javascript/dashboard/store/index.js @@ -18,6 +18,7 @@ import conversationTypingStatus from './modules/conversationTypingStatus'; import globalConfig from 'shared/store/globalConfig'; import inboxes from './modules/inboxes'; import inboxMembers from './modules/inboxMembers'; +import inboxAssignableAgents from './modules/inboxAssignableAgents'; import integrations from './modules/integrations'; import labels from './modules/labels'; import reports from './modules/reports'; @@ -46,6 +47,7 @@ export default new Vuex.Store({ globalConfig, inboxes, inboxMembers, + inboxAssignableAgents, integrations, labels, reports, diff --git a/app/javascript/dashboard/store/modules/inboxAssignableAgents.js b/app/javascript/dashboard/store/modules/inboxAssignableAgents.js new file mode 100644 index 000000000..31a2d98d0 --- /dev/null +++ b/app/javascript/dashboard/store/modules/inboxAssignableAgents.js @@ -0,0 +1,62 @@ +import Vue from 'vue'; + +import InboxesAPI from 'dashboard/api/inboxes.js'; + +const state = { + records: {}, + uiFlags: { + isFetching: false, + }, +}; + +export const types = { + SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG: 'SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG', + SET_INBOX_ASSIGNABLE_AGENTS: 'SET_INBOX_ASSIGNABLE_AGENTS', +}; + +export const getters = { + getAssignableAgents: $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_ASSIGNABLE_AGENTS_UI_FLAG, { isFetching: true }); + try { + const { + data: { payload }, + } = await InboxesAPI.getAssignableAgents(inboxId); + commit(types.SET_INBOX_ASSIGNABLE_AGENTS, { inboxId, members: payload }); + } catch (error) { + throw new Error(error); + } finally { + commit(types.SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG, { isFetching: false }); + } + }, +}; + +export const mutations = { + [types.SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG]($state, data) { + $state.uiFlags = { + ...$state.uiFlags, + ...data, + }; + }, + [types.SET_INBOX_ASSIGNABLE_AGENTS]: ($state, { inboxId, members }) => { + Vue.set($state.records, inboxId, members); + }, +}; + +export default { + namespaced: true, + state, + getters, + actions, + mutations, +}; diff --git a/app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/actions.spec.js b/app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/actions.spec.js new file mode 100644 index 000000000..480ee657f --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/actions.spec.js @@ -0,0 +1,36 @@ +import axios from 'axios'; +import { actions, types } from '../../inboxAssignableAgents'; +import agentsData 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: agentsData }, + }); + await actions.fetch({ commit }, { inboxId: 1 }); + expect(commit.mock.calls).toEqual([ + [types.SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG, { isFetching: true }], + [ + types.SET_INBOX_ASSIGNABLE_AGENTS, + { inboxId: 1, members: agentsData }, + ], + [types.SET_INBOX_ASSIGNABLE_AGENTS_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_ASSIGNABLE_AGENTS_UI_FLAG, { isFetching: true }], + [types.SET_INBOX_ASSIGNABLE_AGENTS_UI_FLAG, { isFetching: false }], + ]); + }); + }); +}); diff --git a/app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/fixtures.js b/app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/fixtures.js new file mode 100644 index 000000000..c7b968e33 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/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/inboxAssignableMembers/getters.spec.js b/app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/getters.spec.js new file mode 100644 index 000000000..ac287e2b2 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/getters.spec.js @@ -0,0 +1,24 @@ +import { getters } from '../../teamMembers'; +import agentsData from './fixtures'; + +describe('#getters', () => { + it('getAssignableAgents', () => { + const state = { + records: { + 1: [agentsData[0]], + }, + }; + expect(getters.getTeamMembers(state)(1)).toEqual([agentsData[0]]); + }); + + it('getUIFlags', () => { + const state = { + uiFlags: { + isFetching: false, + }, + }; + expect(getters.getUIFlags(state)).toEqual({ + isFetching: false, + }); + }); +}); diff --git a/app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/mutations.spec.js new file mode 100644 index 000000000..da0bb2a17 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/inboxAssignableMembers/mutations.spec.js @@ -0,0 +1,16 @@ +import { mutations, types } from '../../inboxAssignableAgents'; +import agentsData from './fixtures'; + +describe('#mutations', () => { + describe('#SET_INBOX_ASSIGNABLE_AGENTS', () => { + it('Adds inbox members to records', () => { + const state = { records: {} }; + mutations[types.SET_INBOX_ASSIGNABLE_AGENTS](state, { + members: [...agentsData], + inboxId: 1, + }); + + expect(state.records).toEqual({ 1: agentsData }); + }); + }); +});