From a2b025b548f34e6a8f5c49d6fd5ec35fc3787594 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Tue, 21 Jan 2020 18:57:44 +0545 Subject: [PATCH] Feature: View conversation labels on sidebar (#436) --- app/javascript/dashboard/api/conversations.js | 18 ++++ .../dashboard/api/specs/conversations.spec.js | 15 ++++ .../dashboard/i18n/locale/en/contact.json | 4 + .../dashboard/conversation/ContactPanel.vue | 6 +- .../conversation/ConversationLabels.vue | 83 +++++++++++++++++++ app/javascript/dashboard/store/index.js | 2 + .../store/modules/conversationLabels.js | 61 ++++++++++++++ .../specs/conversationLabels/actions.spec.js | 35 ++++++++ .../specs/conversationLabels/getters.spec.js | 24 ++++++ .../conversationLabels/mutations.spec.js | 29 +++++++ .../dashboard/store/mutation-types.js | 4 + .../conversations/labels/create.json.jbuilder | 9 +- .../conversations/labels/index.json.jbuilder | 9 +- 13 files changed, 282 insertions(+), 17 deletions(-) create mode 100644 app/javascript/dashboard/api/conversations.js create mode 100644 app/javascript/dashboard/api/specs/conversations.spec.js create mode 100644 app/javascript/dashboard/routes/dashboard/conversation/ConversationLabels.vue create mode 100644 app/javascript/dashboard/store/modules/conversationLabels.js create mode 100644 app/javascript/dashboard/store/modules/specs/conversationLabels/actions.spec.js create mode 100644 app/javascript/dashboard/store/modules/specs/conversationLabels/getters.spec.js create mode 100644 app/javascript/dashboard/store/modules/specs/conversationLabels/mutations.spec.js diff --git a/app/javascript/dashboard/api/conversations.js b/app/javascript/dashboard/api/conversations.js new file mode 100644 index 000000000..02d312388 --- /dev/null +++ b/app/javascript/dashboard/api/conversations.js @@ -0,0 +1,18 @@ +/* global axios */ +import ApiClient from './ApiClient'; + +class ConversationApi extends ApiClient { + constructor() { + super('conversations'); + } + + getLabels(conversationID) { + return axios.get(`${this.url}/${conversationID}/labels`); + } + + createLabels(conversationID) { + return axios.get(`${this.url}/${conversationID}/labels`); + } +} + +export default new ConversationApi(); diff --git a/app/javascript/dashboard/api/specs/conversations.spec.js b/app/javascript/dashboard/api/specs/conversations.spec.js new file mode 100644 index 000000000..71cebae3a --- /dev/null +++ b/app/javascript/dashboard/api/specs/conversations.spec.js @@ -0,0 +1,15 @@ +import conversations from '../conversations'; +import ApiClient from '../ApiClient'; + +describe('#ConversationApi', () => { + it('creates correct instance', () => { + expect(conversations).toBeInstanceOf(ApiClient); + expect(conversations).toHaveProperty('get'); + expect(conversations).toHaveProperty('show'); + expect(conversations).toHaveProperty('create'); + expect(conversations).toHaveProperty('update'); + expect(conversations).toHaveProperty('delete'); + expect(conversations).toHaveProperty('getLabels'); + expect(conversations).toHaveProperty('createLabels'); + }); +}); diff --git a/app/javascript/dashboard/i18n/locale/en/contact.json b/app/javascript/dashboard/i18n/locale/en/contact.json index 5611c5842..9b17e3ddd 100644 --- a/app/javascript/dashboard/i18n/locale/en/contact.json +++ b/app/javascript/dashboard/i18n/locale/en/contact.json @@ -7,6 +7,10 @@ "CONVERSATIONS": { "NO_RECORDS_FOUND": "There are no previous conversations associated to this contact.", "TITLE": "Previous Conversations" + }, + "LABELS": { + "NO_RECORDS_FOUND": "There are no labels associated to this conversation.", + "TITLE": "Labels" } } } diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue b/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue index 8fc660dfe..1a8179b31 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/ContactPanel.vue @@ -59,18 +59,22 @@ :contact-id="contact.id" :conversation-id="conversationId" /> + + + + diff --git a/app/javascript/dashboard/store/index.js b/app/javascript/dashboard/store/index.js index 41fdbdf78..2c848a798 100755 --- a/app/javascript/dashboard/store/index.js +++ b/app/javascript/dashboard/store/index.js @@ -9,6 +9,7 @@ import Channel from './modules/channels'; import contacts from './modules/contacts'; import contactConversations from './modules/contactConversations'; import conversationMetadata from './modules/conversationMetadata'; +import conversationLabels from './modules/conversationLabels'; import conversations from './modules/conversations'; import inboxes from './modules/inboxes'; import inboxMembers from './modules/inboxMembers'; @@ -24,6 +25,7 @@ export default new Vuex.Store({ Channel, contacts, contactConversations, + conversationLabels, conversationMetadata, conversations, inboxes, diff --git a/app/javascript/dashboard/store/modules/conversationLabels.js b/app/javascript/dashboard/store/modules/conversationLabels.js new file mode 100644 index 000000000..059a31566 --- /dev/null +++ b/app/javascript/dashboard/store/modules/conversationLabels.js @@ -0,0 +1,61 @@ +import Vue from 'vue'; +import * as types from '../mutation-types'; +import ConversationAPI from '../../api/conversations'; + +const state = { + records: {}, + uiFlags: { + isFetching: false, + }, +}; + +export const getters = { + getUIFlags($state) { + return $state.uiFlags; + }, + getConversationLabels: $state => id => { + return $state.records[Number(id)] || []; + }, +}; + +export const actions = { + get: async ({ commit }, conversationId) => { + commit(types.default.SET_CONVERSATION_LABELS_UI_FLAG, { + isFetching: true, + }); + try { + const response = await ConversationAPI.getLabels(conversationId); + commit(types.default.SET_CONVERSATION_LABELS, { + id: conversationId, + data: response.data.payload, + }); + commit(types.default.SET_CONVERSATION_LABELS_UI_FLAG, { + isFetching: false, + }); + } catch (error) { + commit(types.default.SET_CONVERSATION_LABELS_UI_FLAG, { + isFetching: false, + }); + } + }, +}; + +export const mutations = { + [types.default.SET_CONVERSATION_LABELS_UI_FLAG]($state, data) { + $state.uiFlags = { + ...$state.uiFlags, + ...data, + }; + }, + [types.default.SET_CONVERSATION_LABELS]: ($state, { id, data }) => { + Vue.set($state.records, id, data); + }, +}; + +export default { + namespaced: true, + state, + getters, + actions, + mutations, +}; diff --git a/app/javascript/dashboard/store/modules/specs/conversationLabels/actions.spec.js b/app/javascript/dashboard/store/modules/specs/conversationLabels/actions.spec.js new file mode 100644 index 000000000..cb6818f27 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/conversationLabels/actions.spec.js @@ -0,0 +1,35 @@ +import axios from 'axios'; +import { actions } from '../../conversationLabels'; +import * as types from '../../../mutation-types'; + +const commit = jest.fn(); +global.axios = axios; +jest.mock('axios'); + +describe('#actions', () => { + describe('#get', () => { + it('sends correct actions if API is success', async () => { + axios.get.mockResolvedValue({ + data: { payload: ['customer-success', 'on-hold'] }, + }); + await actions.get({ commit }, 1); + expect(commit.mock.calls).toEqual([ + [types.default.SET_CONVERSATION_LABELS_UI_FLAG, { isFetching: true }], + + [ + types.default.SET_CONVERSATION_LABELS, + { id: 1, data: ['customer-success', 'on-hold'] }, + ], + [types.default.SET_CONVERSATION_LABELS_UI_FLAG, { isFetching: false }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.get.mockRejectedValue({ message: 'Incorrect header' }); + await actions.get({ commit }); + expect(commit.mock.calls).toEqual([ + [types.default.SET_CONVERSATION_LABELS_UI_FLAG, { isFetching: true }], + [types.default.SET_CONVERSATION_LABELS_UI_FLAG, { isFetching: false }], + ]); + }); + }); +}); diff --git a/app/javascript/dashboard/store/modules/specs/conversationLabels/getters.spec.js b/app/javascript/dashboard/store/modules/specs/conversationLabels/getters.spec.js new file mode 100644 index 000000000..281b90794 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/conversationLabels/getters.spec.js @@ -0,0 +1,24 @@ +import { getters } from '../../conversationLabels'; + +describe('#getters', () => { + it('getConversationLabels', () => { + const state = { + records: { 1: ['customer-success', 'on-hold'] }, + }; + expect(getters.getConversationLabels(state)(1)).toEqual([ + 'customer-success', + 'on-hold', + ]); + }); + + it('getUIFlags', () => { + const state = { + uiFlags: { + isFetching: true, + }, + }; + expect(getters.getUIFlags(state)).toEqual({ + isFetching: true, + }); + }); +}); diff --git a/app/javascript/dashboard/store/modules/specs/conversationLabels/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/conversationLabels/mutations.spec.js new file mode 100644 index 000000000..c22bc40f5 --- /dev/null +++ b/app/javascript/dashboard/store/modules/specs/conversationLabels/mutations.spec.js @@ -0,0 +1,29 @@ +import * as types from '../../../mutation-types'; +import { mutations } from '../../conversationLabels'; + +describe('#mutations', () => { + describe('#SET_CONVERSATION_LABELS_UI_FLAG', () => { + it('set ui flags', () => { + const state = { uiFlags: { isFetching: true } }; + mutations[types.default.SET_CONVERSATION_LABELS_UI_FLAG](state, { + isFetching: false, + }); + expect(state.uiFlags).toEqual({ + isFetching: false, + }); + }); + }); + + describe('#SET_CONVERSATION_LABELS', () => { + it('set contact conversation records', () => { + const state = { records: {} }; + mutations[types.default.SET_CONVERSATION_LABELS](state, { + id: 1, + data: ['customer-success', 'on-hold'], + }); + expect(state.records).toEqual({ + 1: ['customer-success', 'on-hold'], + }); + }); + }); +}); diff --git a/app/javascript/dashboard/store/mutation-types.js b/app/javascript/dashboard/store/mutation-types.js index 12111a3f6..dcd87673a 100755 --- a/app/javascript/dashboard/store/mutation-types.js +++ b/app/javascript/dashboard/store/mutation-types.js @@ -66,6 +66,10 @@ export default { SET_CONTACT_CONVERSATIONS_UI_FLAG: 'SET_CONTACT_CONVERSATIONS_UI_FLAG', SET_CONTACT_CONVERSATIONS: 'SET_CONTACT_CONVERSATIONS', + // Conversation Label + SET_CONVERSATION_LABELS_UI_FLAG: 'SET_CONVERSATION_LABELS_UI_FLAG', + SET_CONVERSATION_LABELS: 'SET_CONVERSATION_LABELS', + // Reports SET_ACCOUNT_REPORTS: 'SET_ACCOUNT_REPORTS', SET_ACCOUNT_SUMMARY: 'SET_ACCOUNT_SUMMARY', diff --git a/app/views/api/v1/conversations/labels/create.json.jbuilder b/app/views/api/v1/conversations/labels/create.json.jbuilder index 13f99090d..b47f0870f 100644 --- a/app/views/api/v1/conversations/labels/create.json.jbuilder +++ b/app/views/api/v1/conversations/labels/create.json.jbuilder @@ -1,8 +1 @@ -json.data do - json.meta do - end - - json.payload do - json.labels @labels - end -end +json.payload @labels diff --git a/app/views/api/v1/conversations/labels/index.json.jbuilder b/app/views/api/v1/conversations/labels/index.json.jbuilder index 13f99090d..b47f0870f 100644 --- a/app/views/api/v1/conversations/labels/index.json.jbuilder +++ b/app/views/api/v1/conversations/labels/index.json.jbuilder @@ -1,8 +1 @@ -json.data do - json.meta do - end - - json.payload do - json.labels @labels - end -end +json.payload @labels