From fc9699d9931087e77858464f17bcc97f91f6ea12 Mon Sep 17 00:00:00 2001 From: Fayaz Ahmed <15716057+fayazara@users.noreply.github.com> Date: Fri, 5 Aug 2022 10:57:58 +0530 Subject: [PATCH] feat: Add assign team option to the context menu (#5153) Co-authored-by: Pranav Raj S --- .../dashboard/components/ChatList.vue | 50 ++++++++++++++++++- .../widgets/conversation/ConversationCard.vue | 5 ++ .../conversation/contextMenu/Index.vue | 23 +++++++-- .../conversation/contextMenu/menuItem.vue | 10 ++-- .../i18n/locale/en/conversation.json | 17 ++++++- .../conversation/ConversationAction.vue | 8 ++- .../store/modules/conversations/actions.js | 6 +-- .../store/modules/conversations/index.js | 4 +- .../specs/conversations/actions.spec.js | 7 ++- .../specs/conversations/mutations.spec.js | 13 +++++ .../FluentIcon/dashboard-icons.json | 1 + 11 files changed, 121 insertions(+), 23 deletions(-) diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index 61df0bf74..16865d0dc 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -121,6 +121,7 @@ @select-conversation="selectConversation" @de-select-conversation="deSelectConversation" @assign-agent="onAssignAgent" + @assign-team="onAssignTeam" @assign-label="onAssignLabels" @update-conversation-status="toggleConversationStatus" @context-menu-toggle="onContextMenuToggle" @@ -618,11 +619,44 @@ export default { }, }); this.selectedConversations = []; - this.showAlert(this.$t('BULK_ACTION.ASSIGN_SUCCESFUL')); + if (conversationId) { + this.showAlert( + this.$t( + 'CONVERSATION.CARD_CONTEXT_MENU.API.AGENT_ASSIGNMENT.SUCCESFUL', + { + agentName: agent.name, + conversationId, + } + ) + ); + } else { + this.showAlert(this.$t('BULK_ACTION.ASSIGN_SUCCESFUL')); + } } catch (err) { this.showAlert(this.$t('BULK_ACTION.ASSIGN_FAILED')); } }, + async onAssignTeam(team, conversationId = null) { + try { + await this.$store.dispatch('assignTeam', { + conversationId, + teamId: team.id, + }); + this.showAlert( + this.$t( + 'CONVERSATION.CARD_CONTEXT_MENU.API.TEAM_ASSIGNMENT.SUCCESFUL', + { + team: team.name, + conversationId, + } + ) + ); + } catch (error) { + this.showAlert( + this.$t('CONVERSATION.CARD_CONTEXT_MENU.API.TEAM_ASSIGNMENT.FAILED') + ); + } + }, // Same method used in context menu, conversationId being passed from there. async onAssignLabels(labels, conversationId = null) { try { @@ -634,7 +668,19 @@ export default { }, }); this.selectedConversations = []; - this.showAlert(this.$t('BULK_ACTION.LABELS.ASSIGN_SUCCESFUL')); + if (conversationId) { + this.showAlert( + this.$t( + 'CONVERSATION.CARD_CONTEXT_MENU.API.LABEL_ASSIGNMENT.SUCCESFUL', + { + labelName: labels[0], + conversationId, + } + ) + ); + } else { + this.showAlert(this.$t('BULK_ACTION.LABELS.ASSIGN_SUCCESFUL')); + } } catch (err) { this.showAlert(this.$t('BULK_ACTION.LABELS.ASSIGN_FAILED')); } diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue index b3c36928c..78e8c1988 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue @@ -105,6 +105,7 @@ @update-conversation="onUpdateConversation" @assign-agent="onAssignAgent" @assign-label="onAssignLabel" + @assign-team="onAssignTeam" /> @@ -352,6 +353,10 @@ export default { this.$emit('assign-label', [label.title], [this.chat.id]); this.closeContextMenu(); }, + async onAssignTeam(team) { + this.$emit('assign-team', team, this.chat.id); + this.closeContextMenu(); + }, }, }; diff --git a/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue b/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue index 32a62544d..dc5a8887f 100644 --- a/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue +++ b/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue @@ -6,7 +6,7 @@ :key="option.key" :option="option" variant="icon" - @click.native="toggleStatus(option.key, null)" + @click="toggleStatus(option.key, null)" /> @@ -14,7 +14,7 @@ v-for="(option, i) in snoozeMenuConfig.options" :key="i" :option="option" - @click.native="snoozeConversation(option.snoozedUntil)" + @click="snoozeConversation(option.snoozedUntil)" /> @@ -24,7 +24,7 @@ :key="label.id" :option="generateMenuLabelConfig(label, 'label')" variant="label" - @click.native="$emit('assign-label', label)" + @click="$emit('assign-label', label)" /> @@ -36,10 +36,18 @@ :key="agent.id" :option="generateMenuLabelConfig(agent, 'agent')" variant="agent" - @click.native="$emit('assign-agent', agent)" + @click="$emit('assign-agent', agent)" /> + + + @@ -119,11 +127,17 @@ export default { icon: 'person-add', label: this.$t('CONVERSATION.CARD_CONTEXT_MENU.ASSIGN_AGENT'), }, + teamMenuConfig: { + key: 'team', + icon: 'people-team-add', + label: this.$t('CONVERSATION.CARD_CONTEXT_MENU.ASSIGN_TEAM'), + }, }; }, computed: { ...mapGetters({ labels: 'labels/getLabels', + teams: 'teams/getTeams', assignableAgentsUiFlags: 'inboxAssignableAgents/getUIFlags', }), assignableAgents() { @@ -160,6 +174,7 @@ export default { ...(type === 'text' && { label: option.label }), ...(type === 'label' && { label: option.title }), ...(type === 'agent' && { label: option.name }), + ...(type === 'team' && { label: option.name }), }; }, }, diff --git a/app/javascript/dashboard/components/widgets/conversation/contextMenu/menuItem.vue b/app/javascript/dashboard/components/widgets/conversation/contextMenu/menuItem.vue index eb4a71c6f..7534430d5 100644 --- a/app/javascript/dashboard/components/widgets/conversation/contextMenu/menuItem.vue +++ b/app/javascript/dashboard/components/widgets/conversation/contextMenu/menuItem.vue @@ -1,5 +1,5 @@ @@ -45,14 +45,16 @@ export default { .menu { display: flex; align-items: center; - width: 100%; + flex-wrap: nowrap; + width: calc(var(--space-mega) * 2); padding: var(--space-smaller); border-radius: var(--border-radius-small); - min-width: calc(var(--space-mega) * 2); + overflow: hidden; .menu-label { margin: 0; font-size: var(--font-size-mini); + flex-shrink: 0; } .menu-icon { diff --git a/app/javascript/dashboard/i18n/locale/en/conversation.json b/app/javascript/dashboard/i18n/locale/en/conversation.json index 9dfc75bde..7adac123f 100644 --- a/app/javascript/dashboard/i18n/locale/en/conversation.json +++ b/app/javascript/dashboard/i18n/locale/en/conversation.json @@ -72,7 +72,22 @@ }, "ASSIGN_AGENT": "Assign agent", "ASSIGN_LABEL": "Assign label", - "AGENTS_LOADING": "Loading agents..." + "AGENTS_LOADING": "Loading agents...", + "ASSIGN_TEAM": "Assign team", + "API": { + "AGENT_ASSIGNMENT": { + "SUCCESFUL": "Conversation id %{conversationId} assigned to \"%{agentName}\"", + "FAILED": "Couldn't assign agent. Please try again." + }, + "LABEL_ASSIGNMENT": { + "SUCCESFUL": "Assigned label #%{labelName} to conversation id %{conversationId}", + "FAILED": "Couldn't assign label. Please try again." + }, + "TEAM_ASSIGNMENT": { + "SUCCESFUL": "Assigned team \"%{team}\" to conversation id %{conversationId}", + "FAILED": "Couldn't assign team. Please try again." + } + } }, "FOOTER": { "MESSAGE_SIGN_TOOLTIP": "Message signature", diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ConversationAction.vue b/app/javascript/dashboard/routes/dashboard/conversation/ConversationAction.vue index f6249325e..d71a397c5 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/ConversationAction.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/ConversationAction.vue @@ -111,13 +111,11 @@ export default { return this.currentChat.meta.team; }, set(team) { + const conversationId = this.currentChat.id; const teamId = team ? team.id : 0; - this.$store.dispatch('setCurrentChatTeam', team); + this.$store.dispatch('setCurrentChatTeam', { team, conversationId }); this.$store - .dispatch('assignTeam', { - conversationId: this.currentChat.id, - teamId, - }) + .dispatch('assignTeam', { conversationId, teamId }) .then(() => { this.showAlert(this.$t('CONVERSATION.CHANGE_TEAM')); }); diff --git a/app/javascript/dashboard/store/modules/conversations/actions.js b/app/javascript/dashboard/store/modules/conversations/actions.js index 2012a6edd..22c6721c1 100644 --- a/app/javascript/dashboard/store/modules/conversations/actions.js +++ b/app/javascript/dashboard/store/modules/conversations/actions.js @@ -121,14 +121,14 @@ const actions = { conversationId, teamId, }); - dispatch('setCurrentChatTeam', response.data); + dispatch('setCurrentChatTeam', { team: response.data, conversationId }); } catch (error) { // Handle error } }, - setCurrentChatTeam({ commit }, team) { - commit(types.ASSIGN_TEAM, team); + setCurrentChatTeam({ commit }, { team, conversationId }) { + commit(types.ASSIGN_TEAM, { team, conversationId }); }, toggleStatus: async ( diff --git a/app/javascript/dashboard/store/modules/conversations/index.js b/app/javascript/dashboard/store/modules/conversations/index.js index e05743dbe..8df08089c 100644 --- a/app/javascript/dashboard/store/modules/conversations/index.js +++ b/app/javascript/dashboard/store/modules/conversations/index.js @@ -68,8 +68,8 @@ export const mutations = { Vue.set(chat.meta, 'assignee', assignee); }, - [types.ASSIGN_TEAM](_state, team) { - const [chat] = getSelectedChatConversation(_state); + [types.ASSIGN_TEAM](_state, { team, conversationId }) { + const [chat] = _state.allConversations.filter(c => c.id === conversationId); Vue.set(chat.meta, 'team', team); }, diff --git a/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js index 91cbfa626..d566b1df7 100644 --- a/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js @@ -327,10 +327,13 @@ describe('#actions', () => { axios.post.mockResolvedValue({ data: { id: 1, name: 'Team' }, }); - await actions.setCurrentChatTeam({ commit }, { id: 1, name: 'Team' }); + await actions.setCurrentChatTeam( + { commit }, + { team: { id: 1, name: 'Team' }, conversationId: 1 } + ); expect(commit).toHaveBeenCalledTimes(1); expect(commit.mock.calls).toEqual([ - ['ASSIGN_TEAM', { id: 1, name: 'Team' }], + ['ASSIGN_TEAM', { team: { id: 1, name: 'Team' }, conversationId: 1 }], ]); }); }); diff --git a/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js index 8d4535b21..1cba8e973 100644 --- a/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js @@ -37,6 +37,19 @@ describe('#mutations', () => { }); }); + describe('#ASSIGN_TEAM', () => { + it('clears current chat window', () => { + const state = { allConversations: [{ id: 1, meta: {} }] }; + mutations[types.ASSIGN_TEAM](state, { + team: { id: 1, name: 'Team 1' }, + conversationId: 1, + }); + expect(state.allConversations).toEqual([ + { id: 1, meta: { team: { id: 1, name: 'Team 1' } } }, + ]); + }); + }); + describe('#SET_CURRENT_CHAT_WINDOW', () => { it('set current chat window', () => { const state = { selectedChatId: 1 }; diff --git a/app/javascript/shared/components/FluentIcon/dashboard-icons.json b/app/javascript/shared/components/FluentIcon/dashboard-icons.json index 832e95a0d..a4066aa71 100644 --- a/app/javascript/shared/components/FluentIcon/dashboard-icons.json +++ b/app/javascript/shared/components/FluentIcon/dashboard-icons.json @@ -107,6 +107,7 @@ "panel-contract-outline": "M14.193 14.751a.75.75 0 0 0 1.059.056l2.5-2.25a.75.75 0 0 0 0-1.114l-2.5-2.25a.75.75 0 0 0-1.004 1.115l1.048.942H11.75a.75.75 0 1 0 0 1.5h3.546l-1.048.942a.75.75 0 0 0-.055 1.06ZM2 6.75A2.75 2.75 0 0 1 4.75 4h14.5A2.75 2.75 0 0 1 22 6.75v10.5A2.75 2.75 0 0 1 19.25 20H4.75A2.75 2.75 0 0 1 2 17.25V6.75ZM9 5.5v13h10.25c.69 0 1.25-.56 1.25-1.25V6.75c0-.69-.56-1.25-1.25-1.25H9Z", "pen-outline": "M7.5 2.75a.75.75 0 0 0-1.5 0v3a1.75 1.75 0 0 0 1.543 1.738L6.527 9.993a3.868 3.868 0 0 0 .119 3.143l3.99 7.95c.283.566.803.914 1.364.914s1.08-.348 1.365-.913l3.99-7.951c.481-.96.526-2.137.118-3.143l-1.016-2.505A1.75 1.75 0 0 0 18 5.75v-3a.75.75 0 0 0-1.5 0v3a.25.25 0 0 1-.25.25h-8.5a.25.25 0 0 1-.25-.25v-3Zm7.343 4.75 1.24 3.057c.247.61.217 1.336-.07 1.906l-3.263 6.504v-6.668a1.5 1.5 0 1 0-1.5 0v6.668l-3.264-6.504a2.368 2.368 0 0 1-.069-1.906L9.157 7.5h5.686Z", "people-outline": "M4 13.999 13 14a2 2 0 0 1 1.995 1.85L15 16v1.5C14.999 21 11.284 22 8.5 22c-2.722 0-6.335-.956-6.495-4.27L2 17.5v-1.501c0-1.054.816-1.918 1.85-1.995L4 14ZM15.22 14H20c1.054 0 1.918.816 1.994 1.85L22 16v1c-.001 3.062-2.858 4-5 4a7.16 7.16 0 0 1-2.14-.322c.336-.386.607-.827.802-1.327A6.19 6.19 0 0 0 17 19.5l.267-.006c.985-.043 3.086-.363 3.226-2.289L20.5 17v-1a.501.501 0 0 0-.41-.492L20 15.5h-4.051a2.957 2.957 0 0 0-.595-1.34L15.22 14H20h-4.78ZM4 15.499l-.1.01a.51.51 0 0 0-.254.136.506.506 0 0 0-.136.253l-.01.101V17.5c0 1.009.45 1.722 1.417 2.242.826.445 2.003.714 3.266.753l.317.005.317-.005c1.263-.039 2.439-.308 3.266-.753.906-.488 1.359-1.145 1.412-2.057l.005-.186V16a.501.501 0 0 0-.41-.492L13 15.5l-9-.001ZM8.5 3a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9Zm9 2a3.5 3.5 0 1 1 0 7 3.5 3.5 0 0 1 0-7Zm-9-.5c-1.654 0-3 1.346-3 3s1.346 3 3 3 3-1.346 3-3-1.346-3-3-3Zm9 2c-1.103 0-2 .897-2 2s.897 2 2 2 2-.897 2-2-.897-2-2-2Z", + "people-team-add-outline": "M17.5 12a5.5 5.5 0 1 1 0 11a5.5 5.5 0 0 1 0-11Zm0 2l-.09.007a.5.5 0 0 0-.402.402L17 14.5V17h-2.502l-.09.008a.5.5 0 0 0-.402.402l-.008.09l.008.09a.5.5 0 0 0 .402.402l.09.008H17v2.503l.008.09a.5.5 0 0 0 .402.402l.09.008l.09-.008a.5.5 0 0 0 .402-.402l.008-.09V18l2.504.001l.09-.008a.5.5 0 0 0 .402-.402l.008-.09l-.008-.09a.5.5 0 0 0-.403-.402l-.09-.008H18v-2.5l-.008-.09a.5.5 0 0 0-.402-.403L17.5 14Zm-3.246-4c.835 0 1.563.454 1.951 1.13a6.44 6.44 0 0 0-1.518.509a.736.736 0 0 0-.433-.139H9.752a.75.75 0 0 0-.75.75v4.249c0 1.41.974 2.594 2.286 2.915a6.42 6.42 0 0 0 .735 1.587l-.02-.001a4.501 4.501 0 0 1-4.501-4.501V12.25A2.25 2.25 0 0 1 9.752 10h4.502Zm-6.848 0a3.243 3.243 0 0 0-.817 1.5H4.25a.75.75 0 0 0-.75.75v2.749a2.501 2.501 0 0 0 3.082 2.433c.085.504.24.985.453 1.432A4.001 4.001 0 0 1 2 14.999V12.25a2.25 2.25 0 0 1 2.096-2.245L4.25 10h3.156Zm12.344 0A2.25 2.25 0 0 1 22 12.25v.56A6.478 6.478 0 0 0 17.5 11l-.245.005A3.21 3.21 0 0 0 16.6 10h3.15ZM18.5 4a2.5 2.5 0 1 1 0 5a2.5 2.5 0 0 1 0-5ZM12 3a3 3 0 1 1 0 6a3 3 0 0 1 0-6ZM5.5 4a2.5 2.5 0 1 1 0 5a2.5 2.5 0 0 1 0-5Zm13 1.5a1 1 0 1 0 0 2a1 1 0 0 0 0-2Zm-6.5-1a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3Zm-6.5 1a1 1 0 1 0 0 2a1 1 0 0 0 0-2Z", "people-team-outline": "M14.754 10c.966 0 1.75.784 1.75 1.75v4.749a4.501 4.501 0 0 1-9.002 0V11.75c0-.966.783-1.75 1.75-1.75h5.502Zm0 1.5H9.252a.25.25 0 0 0-.25.25v4.749a3.001 3.001 0 0 0 6.002 0V11.75a.25.25 0 0 0-.25-.25ZM3.75 10h3.381a2.738 2.738 0 0 0-.618 1.5H3.75a.25.25 0 0 0-.25.25v3.249a2.501 2.501 0 0 0 3.082 2.433c.085.504.24.985.453 1.432A4.001 4.001 0 0 1 2 14.999V11.75c0-.966.784-1.75 1.75-1.75Zm13.125 0h3.375c.966 0 1.75.784 1.75 1.75V15a4 4 0 0 1-5.03 3.866c.214-.448.369-.929.455-1.433A2.5 2.5 0 0 0 20.5 15v-3.25a.25.25 0 0 0-.25-.25h-2.757a2.738 2.738 0 0 0-.618-1.5ZM12 3a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm6.5 1a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm-13 0a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm6.5.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm6.5 1a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm-13 0a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z", "person-add-outline": "M17.5 12a5.5 5.5 0 1 1 0 11a5.5 5.5 0 0 1 0-11zm-5.477 2a6.47 6.47 0 0 0-.709 1.5H4.253a.749.749 0 0 0-.75.75v.577c0 .535.192 1.053.54 1.46c1.253 1.469 3.22 2.214 5.957 2.214c.597 0 1.157-.035 1.68-.106c.246.495.553.954.912 1.367c-.795.16-1.66.24-2.592.24c-3.146 0-5.532-.906-7.098-2.74a3.75 3.75 0 0 1-.898-2.435v-.578A2.249 2.249 0 0 1 4.253 14h7.77zm5.477 0l-.09.008a.5.5 0 0 0-.402.402L17 14.5V17h-2.496l-.09.008a.5.5 0 0 0-.402.402l-.008.09l.008.09a.5.5 0 0 0 .402.402l.09.008H17L17 20.5l.008.09a.5.5 0 0 0 .402.402l.09.008l.09-.008a.5.5 0 0 0 .402-.402L18 20.5V18h2.504l.09-.008a.5.5 0 0 0 .402-.402l.008-.09l-.008-.09a.5.5 0 0 0-.402-.402l-.09-.008H18L18 14.5l-.008-.09a.5.5 0 0 0-.402-.402L17.5 14zM10 2.005a5 5 0 1 1 0 10a5 5 0 0 1 0-10zm0 1.5a3.5 3.5 0 1 0 0 7a3.5 3.5 0 0 0 0-7z", "person-assign-outline": "M11.313 15.5a6.471 6.471 0 0 1 .709-1.5h-7.77a2.249 2.249 0 0 0-2.249 2.25v.577c0 .892.319 1.756.899 2.435c1.566 1.834 3.952 2.74 7.098 2.74c.931 0 1.796-.08 2.592-.24a6.51 6.51 0 0 1-.913-1.366c-.524.07-1.083.105-1.68.105c-2.737 0-4.703-.745-5.957-2.213a2.25 2.25 0 0 1-.539-1.461v-.578a.75.75 0 0 1 .75-.749h7.06ZM10 2.005a5 5 0 1 1 0 10a5 5 0 0 1 0-10Zm0 1.5a3.5 3.5 0 1 0 0 7a3.5 3.5 0 0 0 0-7ZM23 17.5a5.5 5.5 0 1 1-11 0a5.5 5.5 0 0 1 11 0Zm-4.647-2.853a.5.5 0 0 0-.707.707L19.293 17H15a.5.5 0 1 0 0 1h4.293l-1.647 1.647a.5.5 0 0 0 .707.707l2.5-2.5a.497.497 0 0 0 .147-.345V17.5a.498.498 0 0 0-.15-.357l-2.497-2.496Z",