diff --git a/app/javascript/dashboard/api/customViews.js b/app/javascript/dashboard/api/customViews.js index bb09047ba..03bb9dee8 100644 --- a/app/javascript/dashboard/api/customViews.js +++ b/app/javascript/dashboard/api/customViews.js @@ -6,8 +6,8 @@ class CustomViewsAPI extends ApiClient { super('custom_filters', { accountScoped: true }); } - getCustomViews() { - return axios.get(this.url); + getCustomViewsByFilterType(type) { + return axios.get(`${this.url}?filter_type=${type}`); } } diff --git a/app/javascript/dashboard/components/layout/Sidebar.vue b/app/javascript/dashboard/components/layout/Sidebar.vue index 448603c6f..ee64f4949 100644 --- a/app/javascript/dashboard/components/layout/Sidebar.vue +++ b/app/javascript/dashboard/components/layout/Sidebar.vue @@ -88,12 +88,31 @@ export default { currentUser: 'getCurrentUser', globalConfig: 'globalConfig/get', inboxes: 'inboxes/getInboxes', - customViews: 'customViews/getCustomViews', accountId: 'getCurrentAccountId', currentRole: 'getCurrentRole', labels: 'labels/getLabelsOnSidebar', teams: 'teams/getMyTeams', }), + activeCustomView() { + if (this.activePrimaryMenu.key === 'contacts') { + return 'contact'; + } + if (this.activePrimaryMenu.key === 'conversations') { + return 'conversation'; + } + return ''; + }, + customViews() { + return this.$store.getters['customViews/getCustomViewsByFilterType']( + this.activeCustomView + ); + }, + isConversationOrContactActive() { + return ( + this.activePrimaryMenu.key === 'contacts' || + this.activePrimaryMenu.key === 'conversations' + ); + }, sideMenuConfig() { return getSidebarItems(this.accountId); }, @@ -121,16 +140,27 @@ export default { return activePrimaryMenu; }, }, + + watch: { + activeCustomView() { + this.fetchCustomViews(); + }, + }, mounted() { this.$store.dispatch('labels/get'); this.$store.dispatch('inboxes/get'); - this.$store.dispatch('customViews/get'); this.$store.dispatch('notifications/unReadCount'); this.$store.dispatch('teams/get'); this.$store.dispatch('attributes/get'); + this.fetchCustomViews(); }, methods: { + fetchCustomViews() { + if (this.isConversationOrContactActive) { + this.$store.dispatch('customViews/get', this.activeCustomView); + } + }, toggleKeyShortcutModal() { this.showShortcutModal = true; }, diff --git a/app/javascript/dashboard/components/layout/config/sidebarItems/contacts.js b/app/javascript/dashboard/components/layout/config/sidebarItems/contacts.js index 0729abcfe..723b3fbdf 100644 --- a/app/javascript/dashboard/components/layout/config/sidebarItems/contacts.js +++ b/app/javascript/dashboard/components/layout/config/sidebarItems/contacts.js @@ -5,6 +5,7 @@ const contacts = accountId => ({ routes: [ 'contacts_dashboard', 'contact_profile_dashboard', + 'contacts_through_custom_view', 'contacts_labels_dashboard', ], menuItems: [ diff --git a/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue b/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue index 72f559fc1..9c41f9946 100644 --- a/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue +++ b/app/javascript/dashboard/components/layout/sidebarComponents/Secondary.vue @@ -57,6 +57,9 @@ export default { hasSecondaryMenu() { return this.menuConfig.menuItems && this.menuConfig.menuItems.length; }, + contactCustomViews() { + return this.customViews.filter(view => view.filter_type === 'contact'); + }, accessibleMenuItems() { if (!this.currentRole) { return []; @@ -157,7 +160,7 @@ export default { customViewsSection() { return { icon: 'folder', - label: 'CUSTOM_VIEWS', + label: 'CUSTOM_VIEWS_FOLDER', hasSubMenu: true, key: 'custom_view', children: this.customViews @@ -172,8 +175,27 @@ export default { })), }; }, + contactCustomViewsSection() { + return { + icon: 'folder', + label: 'CUSTOM_VIEWS_SEGMENTS', + hasSubMenu: true, + key: 'custom_view', + children: this.customViews + .filter(view => view.filter_type === 'contact') + .map(view => ({ + id: view.id, + label: view.name, + truncateLabel: true, + toState: frontendURL( + `accounts/${this.accountId}/contacts/custom_view/${view.id}` + ), + })), + }; + }, additionalSecondaryMenuItems() { let conversationMenuItems = [this.inboxSection, this.labelSection]; + let contactMenuItems = [this.contactLabelSection]; if (this.teams.length) { conversationMenuItems = [this.teamSection, ...conversationMenuItems]; } @@ -183,9 +205,15 @@ export default { ...conversationMenuItems, ]; } + if (this.contactCustomViews.length) { + contactMenuItems = [ + this.contactCustomViewsSection, + ...contactMenuItems, + ]; + } return { conversations: conversationMenuItems, - contacts: [this.contactLabelSection], + contacts: contactMenuItems, }; }, }, diff --git a/app/javascript/dashboard/i18n/locale/en/contact.json b/app/javascript/dashboard/i18n/locale/en/contact.json index 976f4c53e..76e47697c 100644 --- a/app/javascript/dashboard/i18n/locale/en/contact.json +++ b/app/javascript/dashboard/i18n/locale/en/contact.json @@ -179,6 +179,7 @@ "SEARCH_BUTTON": "Search", "SEARCH_INPUT_PLACEHOLDER": "Search for contacts", "FILTER_CONTACTS": "Filter", + "FILTER_CONTACTS_SAVE": "Save filter", "LIST": { "LOADING_MESSAGE": "Loading contacts...", "404": "No contacts matches your search 🔍", diff --git a/app/javascript/dashboard/i18n/locale/en/settings.json b/app/javascript/dashboard/i18n/locale/en/settings.json index ceacb2486..f46b6e2e7 100644 --- a/app/javascript/dashboard/i18n/locale/en/settings.json +++ b/app/javascript/dashboard/i18n/locale/en/settings.json @@ -149,7 +149,8 @@ "CUSTOM_ATTRIBUTES": "Custom Attributes", "AUTOMATION": "Automation", "TEAMS": "Teams", - "CUSTOM_VIEWS": "Folders", + "CUSTOM_VIEWS_FOLDER": "Folders", + "CUSTOM_VIEWS_SEGMENTS": "Segments", "ALL_CONTACTS": "All Contacts", "TAGGED_WITH": "Tagged with", "NEW_LABEL": "New label", diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsView.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsView.vue index 1c7489e71..6ca94b3cf 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsView.vue +++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsView.vue @@ -3,13 +3,15 @@
+ view.id === Number(this.customViewsId) + ); + return firstValue; + } + return undefined; + }, }, watch: { label() { this.fetchContacts(DEFAULT_PAGE); }, + activeCustomView() { + if (this.hasActiveCustomViews) { + const payload = this.activeCustomView.query; + this.fetchSavedFilteredContact(payload); + } else { + this.fetchContacts(DEFAULT_PAGE); + } + }, }, mounted() { this.fetchContacts(this.pageParameter); @@ -177,6 +229,14 @@ export default { }); } }, + fetchSavedFilteredContact(payload) { + if (this.hasAppliedFilters) { + this.clearFilters(); + } + this.$store.dispatch('contacts/filter', { + queryPayload: payload, + }); + }, onInputSearch(event) { const newQuery = event.target.value; const refetchAllContacts = !!this.searchQuery && newQuery === ''; @@ -206,6 +266,12 @@ export default { onToggleCreate() { this.showCreateModal = !this.showCreateModal; }, + onToggleSaveFilters() { + this.showAddCustomViewsModal = true; + }, + onCloseAddCustomViewsModal() { + this.showAddCustomViewsModal = false; + }, onToggleImport() { this.showImportModal = !this.showImportModal; }, @@ -218,6 +284,7 @@ export default { }, onApplyFilter(payload) { this.closeContactInfoPanel(); + this.customViewsQuery = { payload }; this.$store.dispatch('contacts/filter', { queryPayload: filterQueryGenerator(payload), }); diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/Header.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/Header.vue index 2758da76d..8d2ddc848 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/components/Header.vue +++ b/app/javascript/dashboard/routes/dashboard/contacts/components/Header.vue @@ -3,7 +3,7 @@

- {{ headerTitle ? `#${headerTitle}` : $t('CONTACTS_PAGE.HEADER') }} + {{ headerTitle }}

@@ -26,7 +26,7 @@ {{ $t('CONTACTS_PAGE.SEARCH_BUTTON') }}
-
+
+ + {{ $t('CONTACTS_PAGE.FILTER_CONTACTS_SAVE') }} + {}, @@ -111,6 +125,14 @@ export default { hasAppliedFilters() { return this.getAppliedContactFilters.length; }, + hasActiveCustomViews() { + return this.customViewsId !== 0; + }, + }, + methods: { + onToggleCustomViewsModal() { + this.$emit('on-toggle-save-filter'); + }, }, }; diff --git a/app/javascript/dashboard/routes/dashboard/contacts/routes.js b/app/javascript/dashboard/routes/dashboard/contacts/routes.js index 054b16d7c..0901489bf 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/routes.js +++ b/app/javascript/dashboard/routes/dashboard/contacts/routes.js @@ -10,6 +10,15 @@ export const routes = [ roles: ['administrator', 'agent'], component: ContactsView, }, + { + path: frontendURL('accounts/:accountId/contacts/custom_view/:id'), + name: 'contacts_through_custom_view', + roles: ['administrator', 'agent'], + component: ContactsView, + props: route => { + return { customViewsId: route.params.id }; + }, + }, { path: frontendURL('accounts/:accountId/labels/:label/contacts'), name: 'contacts_labels_dashboard', diff --git a/app/javascript/dashboard/store/modules/customViews.js b/app/javascript/dashboard/store/modules/customViews.js index f1c04f01c..252db4107 100644 --- a/app/javascript/dashboard/store/modules/customViews.js +++ b/app/javascript/dashboard/store/modules/customViews.js @@ -18,13 +18,18 @@ export const getters = { getCustomViews(_state) { return _state.records; }, + getCustomViewsByFilterType: _state => filterType => { + return _state.records.filter(record => record.filter_type === filterType); + }, }; export const actions = { - get: async function getCustomViews({ commit }) { + get: async function getCustomViews({ commit }, filterType) { commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isFetching: true }); try { - const response = await CustomViewsAPI.getCustomViews(); + const response = await CustomViewsAPI.getCustomViewsByFilterType( + filterType + ); commit(types.SET_CUSTOM_VIEW, response.data); } catch (error) { // Ignore error diff --git a/app/javascript/dashboard/store/modules/specs/customViews/fixtures.js b/app/javascript/dashboard/store/modules/specs/customViews/fixtures.js index 54917a0b0..1a620add9 100644 --- a/app/javascript/dashboard/store/modules/specs/customViews/fixtures.js +++ b/app/javascript/dashboard/store/modules/specs/customViews/fixtures.js @@ -1,7 +1,7 @@ export default [ { name: 'Custom view', - filter_type: 'conversation', + filter_type: 0, query: { payload: [ { @@ -21,20 +21,14 @@ export default [ }, { name: 'Custom view 1', - filter_type: 'conversation', + filter_type: 1, query: { payload: [ { - attribute_key: 'browser_language', + attribute_key: 'name', filter_operator: 'equal_to', - values: ['eng'], - query_operator: 'or', - }, - { - attribute_key: 'campaign_id', - filter_operator: 'equal_to', - values: [15], - query_operator: 'and', + values: ['john doe'], + query_operator: null, }, ], }, diff --git a/app/javascript/dashboard/store/modules/specs/customViews/getters.spec.js b/app/javascript/dashboard/store/modules/specs/customViews/getters.spec.js index e71e8fd58..ec50a7d5d 100644 --- a/app/javascript/dashboard/store/modules/specs/customViews/getters.spec.js +++ b/app/javascript/dashboard/store/modules/specs/customViews/getters.spec.js @@ -7,7 +7,7 @@ describe('#getters', () => { expect(getters.getCustomViews(state)).toEqual([ { name: 'Custom view', - filter_type: 'conversation', + filter_type: 0, query: { payload: [ { @@ -27,20 +27,34 @@ describe('#getters', () => { }, { name: 'Custom view 1', - filter_type: 'conversation', + filter_type: 1, query: { payload: [ { - attribute_key: 'browser_language', + attribute_key: 'name', filter_operator: 'equal_to', - values: ['eng'], - query_operator: 'or', + values: ['john doe'], + query_operator: null, }, + ], + }, + }, + ]); + }); + + it('getCustomViewsByFilterType', () => { + const state = { records: customViewList }; + expect(getters.getCustomViewsByFilterType(state)(1)).toEqual([ + { + name: 'Custom view 1', + filter_type: 1, + query: { + payload: [ { - attribute_key: 'campaign_id', + attribute_key: 'name', filter_operator: 'equal_to', - values: [15], - query_operator: 'and', + values: ['john doe'], + query_operator: null, }, ], },