feat: Update conversation basic filter (#11415)
# Pull Request Template ## Description This PR updates the basic filter UI for conversations. ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? ### Loom video https://www.loom.com/share/df69a023a39c4dfca2c12b1ee42a0b2e?sid=977e802e-2865-46f1-ae8e-f89ab5eabc2a ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Pranav <pranav@chatwoot.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
@@ -15,6 +15,13 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
subMenuPosition: {
|
||||||
|
type: String,
|
||||||
|
default: 'right',
|
||||||
|
validator: value => {
|
||||||
|
return ['right', 'left', 'bottom'].includes(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
@@ -44,14 +51,21 @@ const handleSelect = value => {
|
|||||||
trailing-icon
|
trailing-icon
|
||||||
color="slate"
|
color="slate"
|
||||||
variant="faded"
|
variant="faded"
|
||||||
class="!w-fit"
|
class="!w-fit max-w-40"
|
||||||
:class="{ 'dark:!bg-n-alpha-2 !bg-n-slate-9/20': isOpen }"
|
:class="{ 'dark:!bg-n-alpha-2 !bg-n-slate-9/20': isOpen }"
|
||||||
:label="labelValue"
|
:label="labelValue"
|
||||||
@click="toggleMenu"
|
@click="toggleMenu"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="isOpen"
|
v-if="isOpen"
|
||||||
class="absolute ltr:left-full rtl:right-full select-none max-w-48 ltr:ml-1 rtl:mr-1 flex flex-col gap-1 bg-n-alpha-3 backdrop-blur-[100px] p-1 top-0 shadow-lg rounded-lg border border-n-weak"
|
class="absolute select-none max-w-64 flex flex-col gap-1 bg-n-alpha-3 backdrop-blur-[100px] p-1 top-0 shadow-lg z-40 rounded-lg border border-n-weak dark:border-n-strong/50"
|
||||||
|
:class="{
|
||||||
|
'ltr:left-full rtl:right-full ltr:ml-1 rtl:mr-1':
|
||||||
|
subMenuPosition === 'right',
|
||||||
|
'ltr:right-full rtl:left-full ltr:mr-1 rtl:ml-1':
|
||||||
|
subMenuPosition === 'left',
|
||||||
|
'top-full mt-1 ltr:right-0 rtl:left-0': subMenuPosition === 'bottom',
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
v-for="option in options"
|
v-for="option in options"
|
||||||
|
|||||||
@@ -1,93 +1,129 @@
|
|||||||
<script>
|
<script setup>
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import { computed } from 'vue';
|
||||||
import { mapGetters } from 'vuex';
|
import { useI18n } from 'vue-i18n';
|
||||||
import FilterItem from './FilterItem.vue';
|
import { useToggle } from '@vueuse/core';
|
||||||
|
import { vOnClickOutside } from '@vueuse/components';
|
||||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||||
|
import { useMapGetter, useStore } from 'dashboard/composables/store.js';
|
||||||
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
|
import SelectMenu from 'dashboard/components-next/selectmenu/SelectMenu.vue';
|
||||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||||
|
|
||||||
const CHAT_STATUS_FILTER_ITEMS = Object.freeze([
|
defineProps({
|
||||||
'open',
|
|
||||||
'resolved',
|
|
||||||
'pending',
|
|
||||||
'snoozed',
|
|
||||||
'all',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const SORT_ORDER_ITEMS = Object.freeze([
|
|
||||||
'last_activity_at_asc',
|
|
||||||
'last_activity_at_desc',
|
|
||||||
'created_at_desc',
|
|
||||||
'created_at_asc',
|
|
||||||
'priority_desc',
|
|
||||||
'priority_asc',
|
|
||||||
'waiting_since_asc',
|
|
||||||
'waiting_since_desc',
|
|
||||||
]);
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
FilterItem,
|
|
||||||
NextButton,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
isOnExpandedLayout: {
|
isOnExpandedLayout: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
emits: ['changeFilter'],
|
|
||||||
setup() {
|
const emit = defineEmits(['changeFilter']);
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { updateUISettings } = useUISettings();
|
const { updateUISettings } = useUISettings();
|
||||||
|
|
||||||
return {
|
const chatStatusFilter = useMapGetter('getChatStatusFilter');
|
||||||
updateUISettings,
|
const chatSortFilter = useMapGetter('getChatSortFilter');
|
||||||
};
|
|
||||||
},
|
const [showActionsDropdown, toggleDropdown] = useToggle();
|
||||||
data() {
|
|
||||||
return {
|
const currentStatusFilter = computed(() => {
|
||||||
showActionsDropdown: false,
|
return chatStatusFilter.value || wootConstants.STATUS_TYPE.OPEN;
|
||||||
chatStatusItems: CHAT_STATUS_FILTER_ITEMS,
|
});
|
||||||
chatSortItems: SORT_ORDER_ITEMS,
|
|
||||||
};
|
const currentSortBy = computed(() => {
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters({
|
|
||||||
chatStatusFilter: 'getChatStatusFilter',
|
|
||||||
chatSortFilter: 'getChatSortFilter',
|
|
||||||
}),
|
|
||||||
chatStatus() {
|
|
||||||
return this.chatStatusFilter || wootConstants.STATUS_TYPE.OPEN;
|
|
||||||
},
|
|
||||||
sortFilter() {
|
|
||||||
return (
|
return (
|
||||||
this.chatSortFilter || wootConstants.SORT_BY_TYPE.LAST_ACTIVITY_AT_DESC
|
chatSortFilter.value || wootConstants.SORT_BY_TYPE.LAST_ACTIVITY_AT_DESC
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const chatStatusOptions = [
|
||||||
|
{
|
||||||
|
label: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.open.TEXT'),
|
||||||
|
value: 'open',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.resolved.TEXT'),
|
||||||
|
value: 'resolved',
|
||||||
},
|
},
|
||||||
methods: {
|
{
|
||||||
onTabChange(value) {
|
label: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.pending.TEXT'),
|
||||||
this.$emit('changeFilter', value);
|
value: 'pending',
|
||||||
this.closeDropdown();
|
|
||||||
},
|
},
|
||||||
toggleDropdown() {
|
{
|
||||||
this.showActionsDropdown = !this.showActionsDropdown;
|
label: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.snoozed.TEXT'),
|
||||||
|
value: 'snoozed',
|
||||||
},
|
},
|
||||||
closeDropdown() {
|
{
|
||||||
this.showActionsDropdown = false;
|
label: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.all.TEXT'),
|
||||||
|
value: 'all',
|
||||||
},
|
},
|
||||||
onChangeFilter(value, type) {
|
];
|
||||||
this.$emit('changeFilter', value, type);
|
|
||||||
this.saveSelectedFilter(type, value);
|
const chatSortOptions = [
|
||||||
|
{
|
||||||
|
label: t('CHAT_LIST.SORT_ORDER_ITEMS.last_activity_at_asc.TEXT'),
|
||||||
|
value: 'last_activity_at_asc',
|
||||||
},
|
},
|
||||||
saveSelectedFilter(type, value) {
|
{
|
||||||
this.updateUISettings({
|
label: t('CHAT_LIST.SORT_ORDER_ITEMS.last_activity_at_desc.TEXT'),
|
||||||
|
value: 'last_activity_at_desc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('CHAT_LIST.SORT_ORDER_ITEMS.created_at_desc.TEXT'),
|
||||||
|
value: 'created_at_desc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('CHAT_LIST.SORT_ORDER_ITEMS.created_at_asc.TEXT'),
|
||||||
|
value: 'created_at_asc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('CHAT_LIST.SORT_ORDER_ITEMS.priority_desc.TEXT'),
|
||||||
|
value: 'priority_desc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('CHAT_LIST.SORT_ORDER_ITEMS.priority_asc.TEXT'),
|
||||||
|
value: 'priority_asc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('CHAT_LIST.SORT_ORDER_ITEMS.waiting_since_asc.TEXT'),
|
||||||
|
value: 'waiting_since_asc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('CHAT_LIST.SORT_ORDER_ITEMS.waiting_since_desc.TEXT'),
|
||||||
|
value: 'waiting_since_desc',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const activeChatStatusLabel = computed(
|
||||||
|
() =>
|
||||||
|
chatStatusOptions.find(m => m.value === chatStatusFilter.value)?.label || ''
|
||||||
|
);
|
||||||
|
|
||||||
|
const activeChatSortLabel = computed(
|
||||||
|
() => chatSortOptions.find(m => m.value === chatSortFilter.value)?.label || ''
|
||||||
|
);
|
||||||
|
|
||||||
|
const saveSelectedFilter = (type, value) => {
|
||||||
|
updateUISettings({
|
||||||
conversations_filter_by: {
|
conversations_filter_by: {
|
||||||
status: type === 'status' ? value : this.chatStatus,
|
status: type === 'status' ? value : currentStatusFilter.value,
|
||||||
order_by: type === 'sort' ? value : this.sortFilter,
|
order_by: type === 'sort' ? value : currentSortBy.value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
},
|
|
||||||
|
const handleStatusChange = value => {
|
||||||
|
emit('changeFilter', value, 'status');
|
||||||
|
store.dispatch('setChatStatusFilter', value);
|
||||||
|
saveSelectedFilter('status', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSortChange = value => {
|
||||||
|
emit('changeFilter', value, 'sort');
|
||||||
|
store.dispatch('setChatSortFilter', value);
|
||||||
|
saveSelectedFilter('sort', value);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -99,39 +135,39 @@ export default {
|
|||||||
slate
|
slate
|
||||||
faded
|
faded
|
||||||
xs
|
xs
|
||||||
@click="toggleDropdown"
|
@click="toggleDropdown()"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="showActionsDropdown"
|
v-if="showActionsDropdown"
|
||||||
v-on-clickaway="closeDropdown"
|
v-on-click-outside="() => toggleDropdown()"
|
||||||
class="mt-1 dropdown-pane dropdown-pane--open !w-52 !p-4 top-6 border !border-n-weak dark:!border-n-weak !bg-n-alpha-3 dark:!bg-n-alpha-3 backdrop-blur-[100px]"
|
class="mt-1 bg-n-alpha-3 backdrop-blur-[100px] border border-n-weak w-72 rounded-xl p-4 absolute z-40 top-full"
|
||||||
:class="{
|
:class="{
|
||||||
'ltr:left-0 rtl:right-0': !isOnExpandedLayout,
|
'ltr:left-0 rtl:right-0': !isOnExpandedLayout,
|
||||||
'ltr:right-0 rtl:left-0': isOnExpandedLayout,
|
'ltr:right-0 rtl:left-0': isOnExpandedLayout,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between last:mt-4">
|
<div class="flex items-center justify-between last:mt-4 gap-2">
|
||||||
<span class="text-xs font-medium text-n-slate-12">{{
|
<span class="text-sm truncate text-n-slate-12">
|
||||||
$t('CHAT_LIST.CHAT_SORT.STATUS')
|
{{ $t('CHAT_LIST.CHAT_SORT.STATUS') }}
|
||||||
}}</span>
|
</span>
|
||||||
<FilterItem
|
<SelectMenu
|
||||||
type="status"
|
:model-value="chatStatusFilter"
|
||||||
:selected-value="chatStatus"
|
:options="chatStatusOptions"
|
||||||
:items="chatStatusItems"
|
:label="activeChatStatusLabel"
|
||||||
path-prefix="CHAT_LIST.CHAT_STATUS_FILTER_ITEMS"
|
:sub-menu-position="isOnExpandedLayout ? 'left' : 'right'"
|
||||||
@on-change-filter="onChangeFilter"
|
@update:model-value="handleStatusChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-between last:mt-4">
|
<div class="flex items-center justify-between last:mt-4 gap-2">
|
||||||
<span class="text-xs font-medium text-n-slate-12">{{
|
<span class="text-sm truncate text-n-slate-12">
|
||||||
$t('CHAT_LIST.CHAT_SORT.ORDER_BY')
|
{{ $t('CHAT_LIST.CHAT_SORT.ORDER_BY') }}
|
||||||
}}</span>
|
</span>
|
||||||
<FilterItem
|
<SelectMenu
|
||||||
type="sort"
|
:model-value="chatSortFilter"
|
||||||
:selected-value="sortFilter"
|
:options="chatSortOptions"
|
||||||
:items="chatSortItems"
|
:label="activeChatSortLabel"
|
||||||
path-prefix="CHAT_LIST.SORT_ORDER_ITEMS"
|
:sub-menu-position="isOnExpandedLayout ? 'left' : 'right'"
|
||||||
@on-change-filter="onChangeFilter"
|
@update:model-value="handleSortChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user