From d1a62fe6ab9473db6a6123bc6e440ac76bf8d406 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Sat, 13 Nov 2021 12:32:53 +0530 Subject: [PATCH] feat: Add the option for changing the contact/conversation sidebar items order (#3362) --- .../dashboard/mixins/specs/uiSettings.spec.js | 46 ++- app/javascript/dashboard/mixins/uiSettings.js | 20 +- .../contacts/components/ContactInfoPanel.vue | 120 ++++--- .../dashboard/conversation/ContactPanel.vue | 305 ++++++------------ .../conversation/ConversationAction.vue | 189 +++++++++++ package.json | 1 + yarn.lock | 12 + 7 files changed, 450 insertions(+), 243 deletions(-) create mode 100644 app/javascript/dashboard/routes/dashboard/conversation/ConversationAction.vue diff --git a/app/javascript/dashboard/mixins/specs/uiSettings.spec.js b/app/javascript/dashboard/mixins/specs/uiSettings.spec.js index fcde4a291..75e33d387 100644 --- a/app/javascript/dashboard/mixins/specs/uiSettings.spec.js +++ b/app/javascript/dashboard/mixins/specs/uiSettings.spec.js @@ -1,7 +1,9 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; -import uiSettingsMixin from '../uiSettings'; +import uiSettingsMixin, { + DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER, + DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER, +} from '../uiSettings'; import Vuex from 'vuex'; - const localVue = createLocalVue(); localVue.use(Vuex); @@ -17,6 +19,8 @@ describe('uiSettingsMixin', () => { display_rich_content_editor: false, enter_to_send_enabled: false, is_ct_labels_open: true, + conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER, + contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER, }), }; store = new Vuex.Store({ actions, getters }); @@ -33,6 +37,8 @@ describe('uiSettingsMixin', () => { display_rich_content_editor: false, enter_to_send_enabled: false, is_ct_labels_open: true, + conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER, + contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER, }); }); @@ -52,6 +58,8 @@ describe('uiSettingsMixin', () => { display_rich_content_editor: false, enter_to_send_enabled: true, is_ct_labels_open: true, + conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER, + contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER, }, }, undefined @@ -75,6 +83,8 @@ describe('uiSettingsMixin', () => { display_rich_content_editor: false, enter_to_send_enabled: false, is_ct_labels_open: false, + conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER, + contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER, }, }, undefined @@ -98,4 +108,36 @@ describe('uiSettingsMixin', () => { ).toEqual(false); }); }); + + describe('#conversationSidebarItemsOrder', () => { + it('returns correct values', () => { + const Component = { + render() {}, + title: 'TestComponent', + mixins: [uiSettingsMixin], + }; + const wrapper = shallowMount(Component, { store, localVue }); + expect(wrapper.vm.conversationSidebarItemsOrder).toEqual([ + { name: 'conversation_info' }, + { name: 'contact_attributes' }, + { name: 'previous_conversation' }, + { name: 'conversation_actions' }, + ]); + }); + }); + describe('#contactSidebarItemsOrder', () => { + it('returns correct values', () => { + const Component = { + render() {}, + title: 'TestComponent', + mixins: [uiSettingsMixin], + }; + const wrapper = shallowMount(Component, { store, localVue }); + expect(wrapper.vm.contactSidebarItemsOrder).toEqual([ + { name: 'contact_attributes' }, + { name: 'contact_labels' }, + { name: 'previous_conversation' }, + ]); + }); + }); }); diff --git a/app/javascript/dashboard/mixins/uiSettings.js b/app/javascript/dashboard/mixins/uiSettings.js index 3518f4497..b33f4df50 100644 --- a/app/javascript/dashboard/mixins/uiSettings.js +++ b/app/javascript/dashboard/mixins/uiSettings.js @@ -1,10 +1,28 @@ import { mapGetters } from 'vuex'; - +export const DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER = [ + { name: 'conversation_info' }, + { name: 'contact_attributes' }, + { name: 'previous_conversation' }, + { name: 'conversation_actions' }, +]; +export const DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER = [ + { name: 'contact_attributes' }, + { name: 'contact_labels' }, + { name: 'previous_conversation' }, +]; export default { computed: { ...mapGetters({ uiSettings: 'getUISettings', }), + conversationSidebarItemsOrder() { + const { conversation_sidebar_items_order: itemsOrder } = this.uiSettings; + return itemsOrder || DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER; + }, + contactSidebarItemsOrder() { + const { contact_sidebar_items_order: itemsOrder } = this.uiSettings; + return itemsOrder || DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER; + }, }, methods: { updateUISettings(uiSettings = {}) { diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue index cba08b2d1..647baf65a 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue +++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactInfoPanel.vue @@ -12,42 +12,71 @@ :contact="contact" @panel-close="onClose" /> - - - - - - - - - - + +
+
+ + + + +
+
+ + + +
+
+ + + +
+
+
+ @@ -58,7 +87,7 @@ import ContactInfo from 'dashboard/routes/dashboard/conversation/contact/Contact import ContactLabel from 'dashboard/routes/dashboard/contacts/components/ContactLabels.vue'; import CustomAttributes from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributes.vue'; import CustomAttributeSelector from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributeSelector.vue'; - +import draggable from 'vuedraggable'; import uiSettingsMixin from 'dashboard/mixins/uiSettings'; export default { @@ -69,6 +98,7 @@ export default { ContactLabel, CustomAttributes, CustomAttributeSelector, + draggable, }, mixins: [uiSettingsMixin], props: { @@ -85,12 +115,30 @@ export default { default: true, }, }, + data() { + return { + dragEnabled: true, + contactSidebarItems: [], + dragging: false, + }; + }, computed: { hasContactAttributes() { const { custom_attributes: customAttributes } = this.contact; return customAttributes && Object.keys(customAttributes).length; }, }, + mounted() { + this.contactSidebarItems = this.contactSidebarItemsOrder; + }, + methods: { + onDragEnd() { + this.dragging = false; + this.updateUISettings({ + contact_sidebar_items_order: this.contactSidebarItems, + }); + }, + }, }; diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue b/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue index 3540c9b9f..f5a540834 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue @@ -4,118 +4,95 @@ -
- -
-
- + +
+
+ - - - + +
-
- + - toggleSidebarUIState('is_conv_details_open', value) " - :no-search-result=" - $t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.TEAM') - " - :input-placeholder=" - $t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.TEAM') - " - @click="onClickAssignTeam" - /> + > + + + +
+
+ + + + +
+
+ + +
- -
- -
- - - - - - - - - - - - + +
@@ -126,27 +103,25 @@ import agentMixin from '../../../mixins/agentMixin'; import AccordionItem from 'dashboard/components/Accordion/AccordionItem'; import ContactConversations from './ContactConversations.vue'; -import ContactDetailsItem from './ContactDetailsItem.vue'; +import ConversationAction from './ConversationAction.vue'; + import ContactInfo from './contact/ContactInfo'; import ConversationInfo from './ConversationInfo'; -import ConversationLabels from './labels/LabelBox.vue'; -import MultiselectDropdown from 'shared/components/ui/MultiselectDropdown.vue'; import CustomAttributes from './customAttributes/CustomAttributes.vue'; import CustomAttributeSelector from './customAttributes/CustomAttributeSelector.vue'; - +import draggable from 'vuedraggable'; import uiSettingsMixin from 'dashboard/mixins/uiSettings'; export default { components: { AccordionItem, ContactConversations, - ContactDetailsItem, ContactInfo, ConversationInfo, - ConversationLabels, - MultiselectDropdown, CustomAttributes, CustomAttributeSelector, + ConversationAction, + draggable, }, mixins: [alertMixin, agentMixin, uiSettingsMixin], props: { @@ -163,6 +138,13 @@ export default { default: () => {}, }, }, + data() { + return { + dragEnabled: true, + conversationSidebarItems: [], + dragging: false, + }; + }, computed: { ...mapGetters({ currentChat: 'getSelectedChat', @@ -194,61 +176,6 @@ export default { const { custom_attributes: customAttributes } = this.contact; return customAttributes && Object.keys(customAttributes).length; }, - teamsList() { - if (this.assignedTeam) { - return [ - { - id: 0, - name: 'None', - }, - ...this.teams, - ]; - } - return this.teams; - }, - assignedAgent: { - get() { - return this.currentChat.meta.assignee; - }, - set(agent) { - const agentId = agent ? agent.id : 0; - this.$store.dispatch('setCurrentChatAssignee', agent); - this.$store - .dispatch('assignAgent', { - conversationId: this.currentChat.id, - agentId, - }) - .then(() => { - this.showAlert(this.$t('CONVERSATION.CHANGE_AGENT')); - }); - }, - }, - assignedTeam: { - get() { - return this.currentChat.meta.team; - }, - set(team) { - const teamId = team ? team.id : 0; - this.$store.dispatch('setCurrentChatTeam', team); - this.$store - .dispatch('assignTeam', { - conversationId: this.currentChat.id, - teamId, - }) - .then(() => { - this.showAlert(this.$t('CONVERSATION.CHANGE_TEAM')); - }); - }, - }, - showSelfAssign() { - if (!this.assignedAgent) { - return true; - } - if (this.assignedAgent.id !== this.currentUser.id) { - return true; - } - return false; - }, }, watch: { conversationId(newConversationId, prevConversationId) { @@ -261,6 +188,7 @@ export default { }, }, mounted() { + this.conversationSidebarItems = this.conversationSidebarItemsOrder; this.getContactDetails(); this.$store.dispatch('attributes/get', 0); }, @@ -281,43 +209,12 @@ export default { openTranscriptModal() { this.showTranscriptModal = true; }, - onSelfAssign() { - const { - account_id, - availability_status, - available_name, - email, - id, - name, - role, - avatar_url, - } = this.currentUser; - const selfAssign = { - account_id, - availability_status, - available_name, - email, - id, - name, - role, - thumbnail: avatar_url, - }; - this.assignedAgent = selfAssign; - }, - onClickAssignAgent(selectedItem) { - if (this.assignedAgent && this.assignedAgent.id === selectedItem.id) { - this.assignedAgent = null; - } else { - this.assignedAgent = selectedItem; - } - }, - onClickAssignTeam(selectedItemTeam) { - if (this.assignedTeam && this.assignedTeam.id === selectedItemTeam.id) { - this.assignedTeam = null; - } else { - this.assignedTeam = selectedItemTeam; - } + onDragEnd() { + this.dragging = false; + this.updateUISettings({ + conversation_sidebar_items_order: this.conversationSidebarItems, + }); }, }, }; diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ConversationAction.vue b/app/javascript/dashboard/routes/dashboard/conversation/ConversationAction.vue new file mode 100644 index 000000000..940e1d1b0 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/conversation/ConversationAction.vue @@ -0,0 +1,189 @@ + + + diff --git a/package.json b/package.json index b54d995f5..d74e1f537 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "vue-template-compiler": "2.6.12", "vue-upload-component": "2.8.22", "vue2-datepicker": "^3.9.1", + "vuedraggable": "^2.24.3", "vuelidate": "0.7.6", "vuex": "~2.1.1", "vuex-router-sync": "~4.1.2" diff --git a/yarn.lock b/yarn.lock index 04287bddb..aa0ae5567 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13540,6 +13540,11 @@ sort-keys@^1.0.0: dependencies: is-plain-obj "^1.0.0" +sortablejs@1.10.2: + version "1.10.2" + resolved "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290" + integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A== + source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -15112,6 +15117,13 @@ vue@2.6.12, vue@^2.6.12: resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123" integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg== +vuedraggable@^2.24.3: + version "2.24.3" + resolved "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19" + integrity sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g== + dependencies: + sortablejs "1.10.2" + vuelidate@0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/vuelidate/-/vuelidate-0.7.6.tgz#84100c13b943470660d0416642845cd2a1edf4b2"