feat: Adds the ability to have custom view for conversations (#3666)

* feat: Adds the ability to save custom filters and display folders on the sidebar

* Minor fixes

* Review fixes

* Review fixes

* i18n fixes

* Shows conversations when the user click on the folder sidebar item

* Spacing fixes

* Review fixes

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Sivin Varghese 2022-01-17 09:18:54 +05:30 committed by GitHub
parent 290196d43b
commit 4398734bdf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 594 additions and 23 deletions

View file

@ -1,26 +1,39 @@
l<template>
<template>
<div class="conversations-list-wrap">
<slot></slot>
<div class="chat-list__top" :class="{ filter__applied: hasAppliedFilters }">
<div
class="chat-list__top"
:class="{ filter__applied: hasAppliedFiltersOrActiveCustomViews }"
>
<h1 class="page-title text-truncate" :title="pageTitle">
{{ pageTitle }}
</h1>
<div class="filter--actions">
<chat-filter
v-if="!hasAppliedFilters"
v-if="!hasAppliedFiltersOrActiveCustomViews"
@statusFilterChange="updateStatusType"
/>
<div v-if="hasAppliedFilters && !hasActiveCustomViews">
<woot-button
v-tooltip.top-end="$t('FILTER.CUSTOM_VIEWS.ADD.SAVE_BUTTON')"
size="tiny"
variant="smooth"
color-scheme="secondary"
icon="save"
@click="onClickOpenAddCustomViewsModal"
/>
<woot-button
v-tooltip.top-end="$t('FILTER.CLEAR_BUTTON_LABEL')"
size="tiny"
variant="smooth"
color-scheme="alert"
icon="dismiss-circle"
@click="resetAndFetchData"
/>
</div>
<woot-button
v-else
size="small"
variant="clear"
color-scheme="alert"
@click="resetAndFetchData"
>
{{ $t('FILTER.CLEAR_BUTTON_LABEL') }}
</woot-button>
<woot-button
v-if="!hasActiveCustomViews"
v-tooltip.top-end="$t('FILTER.TOOLTIP_LABEL')"
variant="clear"
color-scheme="secondary"
@ -33,8 +46,14 @@ l<template>
</div>
</div>
<add-custom-views
v-if="showAddCustomViewsModal"
:custom-views-query="customViewsQuery"
@close="onCloseAddCustomViewsModal"
/>
<chat-type-tabs
v-if="!hasAppliedFilters"
v-if="!hasAppliedFiltersOrActiveCustomViews"
:items="assigneeTabItems"
:active-tab="activeAssigneeTab"
class="tab--chat-type"
@ -51,6 +70,7 @@ l<template>
:key="chat.id"
:active-label="label"
:team-id="teamId"
:custom-views-id="customViewsId"
:chat="chat"
:conversation-type="conversationType"
:show-assignee="showAssigneeInConversationCard"
@ -108,6 +128,7 @@ import conversationMixin from '../mixins/conversations';
import wootConstants from '../constants';
import advancedFilterTypes from './widgets/conversation/advancedFilterItems';
import filterQueryGenerator from '../helper/filterQueryGenerator.js';
import AddCustomViews from 'dashboard/routes/dashboard/customviews/AddCustomViews';
import {
hasPressedAltAndJKey,
@ -116,6 +137,7 @@ import {
export default {
components: {
AddCustomViews,
ChatTypeTabs,
ConversationCard,
ChatFilter,
@ -139,6 +161,10 @@ export default {
type: String,
default: '',
},
customViewsId: {
type: [String, Number],
default: 0,
},
},
data() {
return {
@ -149,6 +175,8 @@ export default {
...filter,
attributeName: this.$t(`FILTER.ATTRIBUTES.${filter.attributeI18nKey}`),
})),
customViewsQuery: {},
showAddCustomViewsModal: false,
};
},
computed: {
@ -163,10 +191,24 @@ export default {
activeInbox: 'getSelectedInbox',
conversationStats: 'conversationStats/getStats',
appliedFilters: 'getAppliedConversationFilters',
customViews: 'customViews/getCustomViews',
}),
hasAppliedFilters() {
return this.appliedFilters.length;
},
hasActiveCustomViews() {
return this.activeCustomView.length > 0 && this.customViewsId !== 0;
},
hasAppliedFiltersOrActiveCustomViews() {
return this.hasAppliedFilters || this.hasActiveCustomViews;
},
savedCustomViewsValue() {
if (this.hasActiveCustomViews) {
const payload = this.activeCustomView[0].query;
this.fetchSavedFilteredConversations(payload);
}
return {};
},
assigneeTabItems() {
return this.$t('CHAT_LIST.ASSIGNEE_TYPE_TABS').map(item => {
const count = this.conversationStats[item.COUNT_KEY] || 0;
@ -189,7 +231,9 @@ export default {
);
},
currentPageFilterKey() {
return this.hasAppliedFilters ? 'appliedFilters' : this.activeAssigneeTab;
return this.hasAppliedFiltersOrActiveCustomViews
? 'appliedFilters'
: this.activeAssigneeTab;
},
currentFiltersPage() {
return this.$store.getters['conversationPage/getCurrentPageFilter'](
@ -212,6 +256,9 @@ export default {
conversationType: this.conversationType
? this.conversationType
: undefined,
customViews: this.hasActiveCustomViews
? this.savedCustomViewsValue
: undefined,
};
},
pageTitle() {
@ -227,11 +274,14 @@ export default {
if (this.conversationType === 'mention') {
return this.$t('CHAT_LIST.MENTION_HEADING');
}
if (this.hasActiveCustomViews) {
return this.activeCustomView[0].name;
}
return this.$t('CHAT_LIST.TAB_HEADING');
},
conversationList() {
let conversationList = [];
if (!this.hasAppliedFilters) {
if (!this.hasAppliedFiltersOrActiveCustomViews) {
const filters = this.conversationFilters;
if (this.activeAssigneeTab === 'me') {
conversationList = [...this.mineChatsList(filters)];
@ -246,6 +296,14 @@ export default {
return conversationList;
},
activeCustomView() {
if (this.customViewsId) {
return this.customViews.filter(
view => view.id === Number(this.customViewsId)
);
}
return [];
},
activeTeam() {
if (this.teamId) {
return this.$store.getters['teams/getTeam'](this.teamId);
@ -266,6 +324,9 @@ export default {
conversationType() {
this.resetAndFetchData();
},
activeCustomView() {
this.resetAndFetchData();
},
},
mounted() {
this.$store.dispatch('setChatFilter', this.activeStatus);
@ -280,10 +341,17 @@ export default {
if (this.$route.name !== 'home') {
this.$router.push({ name: 'home' });
}
this.customViewsQuery = { payload: payload };
this.$store.dispatch('conversationPage/reset');
this.$store.dispatch('emptyAllConversations');
this.fetchFilteredConversations(payload);
},
onClickOpenAddCustomViewsModal() {
this.showAddCustomViewsModal = true;
},
onCloseAddCustomViewsModal() {
this.showAddCustomViewsModal = false;
},
onToggleAdvanceFiltersModal() {
this.showAdvancedFilters = !this.showAdvancedFilters;
},
@ -335,6 +403,13 @@ export default {
this.$store.dispatch('conversationPage/reset');
this.$store.dispatch('emptyAllConversations');
this.$store.dispatch('clearConversationFilters');
if (this.hasActiveCustomViews) {
const payload = this.activeCustomView[0].query;
this.fetchSavedFilteredConversations(payload);
}
if (this.customViewsId) {
return;
}
this.fetchConversations();
},
fetchConversations() {
@ -343,8 +418,12 @@ export default {
.then(() => this.$emit('conversation-load'));
},
loadMoreConversations() {
if (!this.hasAppliedFilters) {
if (!this.hasAppliedFiltersOrActiveCustomViews) {
this.fetchConversations();
}
if (this.hasActiveCustomViews) {
const payload = this.activeCustomView[0].query;
this.fetchSavedFilteredConversations(payload);
} else {
this.fetchFilteredConversations(this.appliedFilters);
}
@ -359,6 +438,15 @@ export default {
.then(() => this.$emit('conversation-load'));
this.showAdvancedFilters = false;
},
fetchSavedFilteredConversations(payload) {
let page = this.currentFiltersPage + 1;
this.$store
.dispatch('fetchFilteredConversations', {
queryData: payload,
page,
})
.then(() => this.$emit('conversation-load'));
},
updateAssigneeTab(selectedTab) {
if (this.activeAssigneeTab !== selectedTab) {
bus.$emit('clearSearchInput');
@ -413,7 +501,7 @@ export default {
}
.filter__applied {
padding: var(--space-slab) 0 !important;
padding: 0 0 var(--space-slab) 0 !important;
border-bottom: 1px solid var(--color-border);
}
</style>

View file

@ -14,6 +14,7 @@
:inboxes="inboxes"
:labels="labels"
:teams="teams"
:custom-views="customViews"
:menu-config="activeSecondaryMenu"
:current-role="currentRole"
@add-label="showAddLabelPopup"
@ -87,6 +88,7 @@ export default {
currentUser: 'getCurrentUser',
globalConfig: 'globalConfig/get',
inboxes: 'inboxes/getInboxes',
customViews: 'customViews/getCustomViews',
accountId: 'getCurrentAccountId',
currentRole: 'getCurrentRole',
labels: 'labels/getLabelsOnSidebar',
@ -122,6 +124,7 @@ export default {
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');

View file

@ -14,6 +14,8 @@ const conversations = accountId => ({
'conversations_through_team',
'conversation_mentions',
'conversation_through_mentions',
'custom_view_conversations',
'conversations_through_custom_view',
],
menuItems: [
{

View file

@ -40,6 +40,10 @@ export default {
type: Array,
default: () => [],
},
customViews: {
type: Array,
default: () => [],
},
menuConfig: {
type: Object,
default: () => {},
@ -150,11 +154,35 @@ export default {
})),
};
},
customViewsSection() {
return {
icon: 'folder',
label: 'CUSTOM_VIEWS',
hasSubMenu: true,
key: 'custom_view',
children: this.customViews
.filter(view => view.filter_type === 'conversation')
.map(view => ({
id: view.id,
label: view.name,
truncateLabel: true,
toState: frontendURL(
`accounts/${this.accountId}/custom_view/${view.id}`
),
})),
};
},
additionalSecondaryMenuItems() {
let conversationMenuItems = [this.inboxSection, this.labelSection];
if (this.teams.length) {
conversationMenuItems = [this.teamSection, ...conversationMenuItems];
}
if (this.customViews.length) {
conversationMenuItems = [
this.customViewsSection,
...conversationMenuItems,
];
}
return {
conversations: conversationMenuItems,
contacts: [this.contactLabelSection],

View file

@ -7,7 +7,7 @@
v-else
class="secondary-menu--title secondary-menu--link fs-small"
:class="computedClass"
:to="menuItem.toState"
:to="menuItem && menuItem.toState"
>
<fluent-icon
:icon="menuItem.icon"

View file

@ -130,6 +130,10 @@ export default {
type: [String, Number],
default: 0,
},
customViewsId: {
type: [String, Number],
default: 0,
},
showAssignee: {
type: Boolean,
default: false,
@ -248,6 +252,7 @@ export default {
id: chat.id,
label: this.activeLabel,
teamId: this.teamId,
customViewsId: this.customViewsId,
conversationType: this.conversationType,
});
router.push({ path: frontendURL(path) });