feat: Inbox list filter (#8880)
* feat: Inbox list filter * fix: routes after delete/unread * fix: Specs * feat: Handle sort in frontend * chore: Minor fixes * chore: Minor fix --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
@@ -306,7 +306,7 @@ export default {
|
||||
});
|
||||
} else if (isAInboxViewRoute(this.$route.name)) {
|
||||
this.$router.push({
|
||||
name: 'inbox-view',
|
||||
name: 'inbox_view',
|
||||
});
|
||||
} else if (this.$route.name !== 'contacts_dashboard') {
|
||||
this.$router.push({
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
class="flex flex-col h-full w-full ltr:border-r border-slate-50 dark:border-slate-800/50"
|
||||
:class="isOnExpandedLayout ? '' : 'min-w-[360px] max-w-[360px]'"
|
||||
>
|
||||
<inbox-list-header />
|
||||
<inbox-list-header @filter="onFilterChange" />
|
||||
<div
|
||||
ref="notificationList"
|
||||
class="flex flex-col w-full h-[calc(100%-56px)] overflow-x-hidden overflow-y-auto"
|
||||
>
|
||||
<inbox-card
|
||||
v-for="notificationItem in records"
|
||||
v-for="notificationItem in notifications"
|
||||
:key="notificationItem.id"
|
||||
:notification-item="notificationItem"
|
||||
@mark-notification-as-read="markNotificationAsRead"
|
||||
@@ -42,18 +42,20 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import InboxCard from './components/InboxCard.vue';
|
||||
import InboxListHeader from './components/InboxListHeader.vue';
|
||||
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
import IntersectionObserver from 'dashboard/components/IntersectionObserver.vue';
|
||||
import alertMixin from 'shared/mixins/alertMixin';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
export default {
|
||||
components: {
|
||||
InboxCard,
|
||||
InboxListHeader,
|
||||
IntersectionObserver,
|
||||
},
|
||||
mixins: [alertMixin],
|
||||
mixins: [alertMixin, uiSettingsMixin],
|
||||
props: {
|
||||
conversationId: {
|
||||
type: [String, Number],
|
||||
@@ -71,38 +73,64 @@ export default {
|
||||
rootMargin: '100px 0px 100px 0px',
|
||||
},
|
||||
page: 1,
|
||||
status: '',
|
||||
type: '',
|
||||
sortOrder: wootConstants.INBOX_SORT_BY.NEWEST,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
accountId: 'getCurrentAccountId',
|
||||
meta: 'notifications/getMeta',
|
||||
records: 'notifications/getNotifications',
|
||||
uiFlags: 'notifications/getUIFlags',
|
||||
notification: 'notifications/getFilteredNotifications',
|
||||
}),
|
||||
inboxFilters() {
|
||||
return {
|
||||
page: this.page,
|
||||
status: this.status,
|
||||
type: this.type,
|
||||
sortOrder: this.sortOrder,
|
||||
};
|
||||
},
|
||||
notifications() {
|
||||
return this.notification(this.inboxFilters);
|
||||
},
|
||||
showEndOfList() {
|
||||
return this.uiFlags.isAllNotificationsLoaded && !this.uiFlags.isFetching;
|
||||
},
|
||||
showEmptyState() {
|
||||
return !this.uiFlags.isFetching && !this.records.length;
|
||||
return !this.uiFlags.isFetching && !this.notifications.length;
|
||||
},
|
||||
showEndOfListMessage() {
|
||||
return this.showEndOfList && this.records.length;
|
||||
return this.showEndOfList && this.notifications.length;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('notifications/clear');
|
||||
this.$store.dispatch('notifications/index', { page: 1 });
|
||||
this.setSavedFilter();
|
||||
this.fetchNotifications();
|
||||
},
|
||||
methods: {
|
||||
fetchNotifications() {
|
||||
this.page = 1;
|
||||
this.$store.dispatch('notifications/clear');
|
||||
const filter = this.inboxFilters;
|
||||
|
||||
this.$store.dispatch('notifications/index', filter);
|
||||
},
|
||||
redirectToInbox() {
|
||||
if (!this.conversationId) return;
|
||||
if (this.$route.name === 'inbox-view') return;
|
||||
this.$router.push({ name: 'inbox-view' });
|
||||
if (this.$route.name === 'inbox_view') return;
|
||||
this.$router.push({ name: 'inbox_view' });
|
||||
},
|
||||
loadMoreNotifications() {
|
||||
if (this.uiFlags.isAllNotificationsLoaded) return;
|
||||
this.$store.dispatch('notifications/index', { page: this.page + 1 });
|
||||
this.$store.dispatch('notifications/index', {
|
||||
page: this.page + 1,
|
||||
status: this.status,
|
||||
type: this.type,
|
||||
sortOrder: this.sortOrder,
|
||||
});
|
||||
this.page += 1;
|
||||
},
|
||||
markNotificationAsRead(notification) {
|
||||
@@ -148,6 +176,25 @@ export default {
|
||||
this.showAlert(this.$t('INBOX.ALERTS.DELETE'));
|
||||
});
|
||||
},
|
||||
onFilterChange(option) {
|
||||
if (option.type === wootConstants.INBOX_FILTER_TYPE.STATUS) {
|
||||
this.status = option.selected ? option.key : '';
|
||||
}
|
||||
if (option.type === wootConstants.INBOX_FILTER_TYPE.TYPE) {
|
||||
this.type = option.selected ? option.key : '';
|
||||
}
|
||||
if (option.type === wootConstants.INBOX_FILTER_TYPE.SORT_ORDER) {
|
||||
this.sortOrder = option.key;
|
||||
}
|
||||
this.fetchNotifications();
|
||||
},
|
||||
setSavedFilter() {
|
||||
const { inbox_filter_by: filterBy = {} } = this.uiSettings;
|
||||
const { status, type, sort_by: sortBy } = filterBy;
|
||||
this.status = status;
|
||||
this.type = type;
|
||||
this.sortOrder = sortBy || wootConstants.INBOX_SORT_BY.NEWEST;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -148,6 +148,7 @@ export default {
|
||||
name: 'home',
|
||||
});
|
||||
}
|
||||
this.$store.dispatch('agents/get');
|
||||
},
|
||||
methods: {
|
||||
async fetchConversationById() {
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
v-if="assigneeMeta"
|
||||
:src="assigneeMeta.thumbnail"
|
||||
:username="assigneeMeta.name"
|
||||
size="20px"
|
||||
size="16px"
|
||||
class="relative bottom-0.5"
|
||||
/>
|
||||
<div class="flex min-w-0">
|
||||
<span
|
||||
|
||||
@@ -38,18 +38,17 @@
|
||||
:key="option.key"
|
||||
role="button"
|
||||
class="flex rounded-[4px] h-5 w-full items-center justify-between p-0.5 gap-1"
|
||||
:class="
|
||||
activeSort === option.key ? 'bg-woot-50 dark:bg-woot-700/50' : ''
|
||||
"
|
||||
@click.stop="onSortOptionClick(option.key)"
|
||||
:class="{
|
||||
'bg-woot-50 dark:bg-woot-700/50': activeSort === option.key,
|
||||
}"
|
||||
@click.stop="onSortOptionClick(option)"
|
||||
>
|
||||
<span
|
||||
class="text-xs font-medium hover:text-woot-600 dark:hover:text-woot-600"
|
||||
:class="
|
||||
activeSort === option.key
|
||||
? 'text-woot-600 dark:text-woot-600'
|
||||
: 'text-slate-600 dark:text-slate-300'
|
||||
"
|
||||
:class="{
|
||||
'text-woot-600 dark:text-woot-600': activeSort === option.key,
|
||||
'text-slate-600 dark:text-slate-300': activeSort !== option.key,
|
||||
}"
|
||||
>
|
||||
{{ option.name }}
|
||||
</span>
|
||||
@@ -74,19 +73,19 @@
|
||||
>
|
||||
<div
|
||||
v-for="option in displayOptions"
|
||||
:key="option.id"
|
||||
:key="option.key"
|
||||
class="flex items-center px-3 py-2 gap-1.5 h-9"
|
||||
>
|
||||
<input
|
||||
:id="option.value"
|
||||
:id="option.key"
|
||||
type="checkbox"
|
||||
:name="option.value"
|
||||
:name="option.key"
|
||||
:checked="option.selected"
|
||||
class="m-0 border-[1.5px] shadow border-slate-200 dark:border-slate-600 appearance-none rounded-[4px] w-4 h-4 dark:bg-slate-800 focus:ring-1 focus:ring-slate-100 dark:focus:ring-slate-700 checked:bg-woot-600 dark:checked:bg-woot-600 after:content-[''] after:text-white checked:after:content-['✓'] after:flex after:items-center after:justify-center checked:border-t checked:border-woot-700 dark:checked:border-woot-300 checked:border-b-0 checked:border-r-0 checked:border-l-0 after:text-center after:text-xs after:font-bold after:relative after:-top-[1.5px]"
|
||||
@change="updateDisplayOption(option)"
|
||||
/>
|
||||
<label
|
||||
:for="option.value"
|
||||
:for="option.key"
|
||||
class="text-xs font-medium text-slate-800 !ml-0 !mr-0 dark:text-slate-100"
|
||||
>
|
||||
{{ option.name }}
|
||||
@@ -98,55 +97,101 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
|
||||
export default {
|
||||
mixins: [uiSettingsMixin],
|
||||
data() {
|
||||
return {
|
||||
showSortMenu: false,
|
||||
displayOptions: [
|
||||
{
|
||||
id: 1,
|
||||
name: this.$t('INBOX.DISPLAY_MENU.DISPLAY_OPTIONS.SNOOZED'),
|
||||
value: 'snoozed',
|
||||
key: wootConstants.INBOX_DISPLAY_BY.SNOOZED,
|
||||
selected: false,
|
||||
type: wootConstants.INBOX_FILTER_TYPE.STATUS,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: this.$t('INBOX.DISPLAY_MENU.DISPLAY_OPTIONS.READ'),
|
||||
value: 'read',
|
||||
selected: true,
|
||||
key: wootConstants.INBOX_DISPLAY_BY.READ,
|
||||
selected: false,
|
||||
type: wootConstants.INBOX_FILTER_TYPE.TYPE,
|
||||
},
|
||||
],
|
||||
sortOptions: [
|
||||
{
|
||||
name: this.$t('INBOX.DISPLAY_MENU.SORT_OPTIONS.NEWEST'),
|
||||
key: 'newest',
|
||||
key: wootConstants.INBOX_SORT_BY.NEWEST,
|
||||
type: wootConstants.INBOX_FILTER_TYPE.SORT_ORDER,
|
||||
},
|
||||
{
|
||||
name: this.$t('INBOX.DISPLAY_MENU.SORT_OPTIONS.OLDEST'),
|
||||
key: 'oldest',
|
||||
key: wootConstants.INBOX_SORT_BY.OLDEST,
|
||||
type: wootConstants.INBOX_FILTER_TYPE.SORT_ORDER,
|
||||
},
|
||||
],
|
||||
activeSort: 'newest',
|
||||
activeSort: wootConstants.INBOX_SORT_BY.NEWEST,
|
||||
activeDisplayFilter: {
|
||||
status: '',
|
||||
type: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
activeSortOption() {
|
||||
return this.sortOptions.find(option => option.key === this.activeSort)
|
||||
.name;
|
||||
return (
|
||||
this.sortOptions.find(option => option.key === this.activeSort)?.name ||
|
||||
''
|
||||
);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setSavedFilter();
|
||||
},
|
||||
methods: {
|
||||
updateDisplayOption(option) {
|
||||
option.selected = !option.selected;
|
||||
// TODO: Update the display options
|
||||
this.displayOptions.forEach(displayOption => {
|
||||
if (displayOption.key === option.key) {
|
||||
displayOption.selected = !option.selected;
|
||||
this.activeDisplayFilter[displayOption.type] = displayOption.selected
|
||||
? displayOption.key
|
||||
: '';
|
||||
this.saveSelectedDisplayFilter();
|
||||
this.$emit('filter', option);
|
||||
}
|
||||
});
|
||||
},
|
||||
openSortMenu() {
|
||||
this.showSortMenu = !this.showSortMenu;
|
||||
},
|
||||
onSortOptionClick(key) {
|
||||
this.activeSort = key;
|
||||
onSortOptionClick(option) {
|
||||
this.activeSort = option.key;
|
||||
this.showSortMenu = false;
|
||||
// TODO: Update the sort options
|
||||
this.saveSelectedDisplayFilter();
|
||||
this.$emit('filter', option);
|
||||
},
|
||||
saveSelectedDisplayFilter() {
|
||||
this.updateUISettings({
|
||||
inbox_filter_by: {
|
||||
...this.activeDisplayFilter,
|
||||
sort_by: this.activeSort || wootConstants.INBOX_SORT_BY.NEWEST,
|
||||
},
|
||||
});
|
||||
},
|
||||
setSavedFilter() {
|
||||
const { inbox_filter_by: filterBy = {} } = this.uiSettings;
|
||||
const { status, type, sort_by: sortBy } = filterBy;
|
||||
this.activeSort = sortBy || wootConstants.INBOX_SORT_BY.NEWEST;
|
||||
this.displayOptions.forEach(option => {
|
||||
option.selected =
|
||||
option.type === wootConstants.INBOX_FILTER_TYPE.STATUS
|
||||
? option.key === status
|
||||
: option.key === type;
|
||||
this.activeDisplayFilter[option.type] = option.selected
|
||||
? option.key
|
||||
: '';
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -132,7 +132,7 @@ export default {
|
||||
.then(() => {
|
||||
this.showAlert(this.$t('INBOX.ALERTS.DELETE'));
|
||||
});
|
||||
this.$router.push({ name: 'inbox-view' });
|
||||
this.$router.push({ name: 'inbox_view' });
|
||||
},
|
||||
onClickNext() {
|
||||
this.$emit('next');
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
v-if="showInboxDisplayMenu"
|
||||
v-on-clickaway="openInboxDisplayMenu"
|
||||
class="absolute top-8"
|
||||
@filter="onFilterChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -109,6 +110,12 @@ export default {
|
||||
this.deleteAllRead();
|
||||
}
|
||||
},
|
||||
onFilterChange(option) {
|
||||
this.$emit('filter', option);
|
||||
this.showInboxDisplayMenu = false;
|
||||
if (this.$route.name === 'inbox_view') return;
|
||||
this.$router.push({ name: 'inbox_view' });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user