From 0ab7accd3fe3c99e877882841156bffcc1b00fe1 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:22:05 +0530 Subject: [PATCH] feat: Contact filter preview (#10516) # Pull Request Template ## Description **Screenshots** **Story** --- .../Contacts/ContactsHeader/ContactHeader.vue | 4 +- .../ContactListHeaderWrapper.vue | 1 - .../ContactsActiveFiltersPreview.vue | 29 +++++ .../Contacts/ContactsListLayout.vue | 7 +- .../Contacts/Pages/ContactsList.vue | 2 +- .../HelpCenter/HelpCenterLayout.vue | 1 + .../ArticleEditorControls.vue | 3 + .../ArticleEditorProperties.vue | 1 + .../PortalSwitcher/PortalSwitcher.vue | 2 + .../components-next/button/Button.story.vue | 9 +- .../components-next/button/Button.vue | 7 +- .../filter/ActiveFilterPreview.story.vue | 66 ++++++++++ .../filter/ActiveFilterPreview.vue | 117 ++++++++++++++++++ .../filter/fixtures/filterTypes.js | 45 +++++++ .../filter/helper/filterHelper.js | 8 ++ .../filter/helper/filterHelper.spec.js | 11 ++ .../pagination/PaginationFooter.vue | 4 + .../dashboard/i18n/locale/en/contact.json | 7 +- .../contacts/pages/ContactsIndex.vue | 1 + 19 files changed, 313 insertions(+), 12 deletions(-) create mode 100644 app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactsActiveFiltersPreview.vue create mode 100644 app/javascript/dashboard/components-next/filter/ActiveFilterPreview.story.vue create mode 100644 app/javascript/dashboard/components-next/filter/ActiveFilterPreview.vue diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactHeader.vue b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactHeader.vue index b5511f7e0..6e32b8581 100644 --- a/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactHeader.vue +++ b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactHeader.vue @@ -54,9 +54,9 @@ const emit = defineEmits([ - + {{ headerTitle }} diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactListHeaderWrapper.vue b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactListHeaderWrapper.vue index d9ea3982e..5b27bcf17 100644 --- a/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactListHeaderWrapper.vue +++ b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/ContactListHeaderWrapper.vue @@ -176,7 +176,6 @@ const closeAdvanceFiltersModal = () => { }; const clearFilters = async () => { - await store.dispatch('contacts/clearContactFilters'); emit('clearFilters'); }; diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactsActiveFiltersPreview.vue b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactsActiveFiltersPreview.vue new file mode 100644 index 000000000..b70cfe72d --- /dev/null +++ b/app/javascript/dashboard/components-next/Contacts/ContactsHeader/components/ContactsActiveFiltersPreview.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue b/app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue index d408a314a..1727cf21b 100644 --- a/app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue +++ b/app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue @@ -3,6 +3,7 @@ import { computed } from 'vue'; import { useRoute } from 'vue-router'; import ContactListHeaderWrapper from 'dashboard/components-next/Contacts/ContactsHeader/ContactListHeaderWrapper.vue'; +import ContactsActiveFiltersPreview from 'dashboard/components-next/Contacts/ContactsHeader/components/ContactsActiveFiltersPreview.vue'; import PaginationFooter from 'dashboard/components-next/pagination/PaginationFooter.vue'; defineProps({ @@ -90,8 +91,12 @@ const updateCurrentPage = page => { @apply-filter="emit('applyFilter', $event)" @clear-filters="emit('clearFilters')" /> - + + diff --git a/app/javascript/dashboard/components-next/Contacts/Pages/ContactsList.vue b/app/javascript/dashboard/components-next/Contacts/Pages/ContactsList.vue index 02e86d4de..1ca94ecaf 100644 --- a/app/javascript/dashboard/components-next/Contacts/Pages/ContactsList.vue +++ b/app/javascript/dashboard/components-next/Contacts/Pages/ContactsList.vue @@ -58,7 +58,7 @@ const toggleExpanded = id => { - + { { { " :icon="!selectedCategory?.icon ? 'i-lucide-shapes' : ''" variant="ghost" + color="slate" class="!px-2 font-normal hover:!bg-transparent" @click="openCategoryList = !openCategoryList" > @@ -244,6 +246,7 @@ onMounted(() => { " icon="i-lucide-plus" variant="ghost" + color="slate" :disabled="isNewArticle" class="!px-2 font-normal hover:!bg-transparent hover:!text-n-slate-11" @click="openProperties = !openProperties" diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticleEditorPage/ArticleEditorProperties.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticleEditorPage/ArticleEditorProperties.vue index 0a49be942..e957948b2 100644 --- a/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticleEditorPage/ArticleEditorProperties.vue +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/ArticleEditorPage/ArticleEditorProperties.vue @@ -66,6 +66,7 @@ onMounted(() => { icon="i-lucide-x" size="sm" variant="ghost" + color="slate" class="hover:text-n-slate-11" @click="emit('close')" /> diff --git a/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/PortalSwitcher.vue b/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/PortalSwitcher.vue index a182c78d3..1b4668b91 100644 --- a/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/PortalSwitcher.vue +++ b/app/javascript/dashboard/components-next/HelpCenter/PortalSwitcher/PortalSwitcher.vue @@ -108,6 +108,7 @@ const redirectToPortalHomePage = () => { { :key="index" :label="portal.name" variant="ghost" + color="slate" trailing-icon :icon="isPortalActive(portal) ? 'i-lucide-check' : ''" class="!justify-end !px-2 !py-2 hover:!bg-n-alpha-2 [&>.i-lucide-check]:text-n-teal-10 h-9" diff --git a/app/javascript/dashboard/components-next/button/Button.story.vue b/app/javascript/dashboard/components-next/button/Button.story.vue index fc0904156..404712b34 100644 --- a/app/javascript/dashboard/components-next/button/Button.story.vue +++ b/app/javascript/dashboard/components-next/button/Button.story.vue @@ -114,8 +114,13 @@ const SIZES = ['default', 'sm', 'lg']; - - + + diff --git a/app/javascript/dashboard/components-next/button/Button.vue b/app/javascript/dashboard/components-next/button/Button.vue index b174ca375..fecdbaf9b 100644 --- a/app/javascript/dashboard/components-next/button/Button.vue +++ b/app/javascript/dashboard/components-next/button/Button.vue @@ -88,6 +88,7 @@ const STYLE_CONFIG = { faded: 'bg-n-brand/10 text-n-blue-text hover:bg-n-brand/20 outline-transparent', outline: 'text-n-blue-text outline-n-blue-border', + ghost: 'text-n-blue-text hover:bg-n-alpha-2 outline-transparent', link: 'text-n-blue-text hover:underline outline-transparent', }, ruby: { @@ -95,6 +96,7 @@ const STYLE_CONFIG = { faded: 'bg-n-ruby-9/10 text-n-ruby-11 hover:bg-n-ruby-9/20 outline-transparent', outline: 'text-n-ruby-11 hover:bg-n-ruby-9/10 outline-n-ruby-8', + ghost: 'text-n-ruby-11 hover:bg-n-alpha-2 outline-transparent', link: 'text-n-ruby-9 hover:underline outline-transparent', }, amber: { @@ -103,6 +105,7 @@ const STYLE_CONFIG = { 'bg-n-amber-9/10 text-n-slate-12 hover:bg-n-amber-9/20 outline-transparent', outline: 'text-n-amber-11 hover:bg-n-amber-9/10 outline-n-amber-9', link: 'text-n-amber-9 hover:underline outline-transparent', + ghost: 'text-n-amber-9 hover:bg-n-alpha-2 outline-transparent', }, slate: { solid: @@ -111,6 +114,7 @@ const STYLE_CONFIG = { 'bg-n-slate-9/10 text-n-slate-12 hover:bg-n-slate-9/20 outline-transparent', outline: 'text-n-slate-11 outline-n-strong hover:bg-n-slate-9/10', link: 'text-n-slate-11 hover:text-n-slate-12 hover:underline outline-transparent', + ghost: 'text-n-slate-12 hover:bg-n-alpha-2 outline-transparent', }, teal: { solid: 'bg-n-teal-9 text-white hover:bg-n-teal-10 outline-transparent', @@ -118,6 +122,7 @@ const STYLE_CONFIG = { 'bg-n-teal-9/10 text-n-slate-12 hover:bg-n-teal-9/20 outline-transparent', outline: 'text-n-teal-11 hover:bg-n-teal-9/10 outline-n-teal-9', link: 'text-n-teal-9 hover:underline outline-transparent', + ghost: 'text-n-teal-9 hover:bg-n-alpha-2 outline-transparent', }, }, sizes: { @@ -151,7 +156,7 @@ const STYLE_CONFIG = { const variantClasses = computed(() => { const variantMap = { - ghost: 'text-n-slate-12 hover:bg-n-alpha-2 outline-transparent', + ghost: `${STYLE_CONFIG.colors[computedColor.value].ghost}`, link: `${STYLE_CONFIG.colors[computedColor.value].link} p-0 font-medium underline-offset-4`, outline: STYLE_CONFIG.colors[computedColor.value].outline, faded: STYLE_CONFIG.colors[computedColor.value].faded, diff --git a/app/javascript/dashboard/components-next/filter/ActiveFilterPreview.story.vue b/app/javascript/dashboard/components-next/filter/ActiveFilterPreview.story.vue new file mode 100644 index 000000000..0c79b97da --- /dev/null +++ b/app/javascript/dashboard/components-next/filter/ActiveFilterPreview.story.vue @@ -0,0 +1,66 @@ + + + + + + {}" + /> + + + + {}" + /> + + + + {}" + /> + + + + {}" + /> + + + + {}" + /> + + + diff --git a/app/javascript/dashboard/components-next/filter/ActiveFilterPreview.vue b/app/javascript/dashboard/components-next/filter/ActiveFilterPreview.vue new file mode 100644 index 000000000..b35569a8b --- /dev/null +++ b/app/javascript/dashboard/components-next/filter/ActiveFilterPreview.vue @@ -0,0 +1,117 @@ + + + + + + + + + {{ replaceUnderscoreWithSpace(filter.attributeKey) }} + + + {{ formatOperatorLabel(filter.filterOperator) }} + + + {{ formatFilterValue(filter.values) }} + + + + + {{ filter.queryOperator }} + + + + + + {{ moreFiltersLabel }} + + + + + diff --git a/app/javascript/dashboard/components-next/filter/fixtures/filterTypes.js b/app/javascript/dashboard/components-next/filter/fixtures/filterTypes.js index fcec5eb08..0eb618535 100644 --- a/app/javascript/dashboard/components-next/filter/fixtures/filterTypes.js +++ b/app/javascript/dashboard/components-next/filter/fixtures/filterTypes.js @@ -556,3 +556,48 @@ export const filterTypes = [ attributeModel: 'customAttributes', }, ]; + +export const sampleActiveFilters = [ + { + attributeKey: 'name', + filterOperator: 'contains', + values: 'John', + queryOperator: 'and', + }, + { + attributeKey: 'email', + filterOperator: 'does_not_contain', + values: 'test@chatwoot.com', + queryOperator: 'or', + }, + { + attributeKey: 'phone_number', + filterOperator: 'is_present', + values: '+928383822', + queryOperator: 'and', + }, + { + attributeKey: 'created_at', + filterOperator: 'is_greater_than', + values: '2024-01-01', + queryOperator: 'and', + }, + { + attributeKey: 'last_activity', + filterOperator: 'days_before', + values: '30', + queryOperator: 'and', + }, + { + attributeKey: 'date_of_birth', + filterOperator: 'is_not_present', + values: '', + queryOperator: 'and', + }, + { + attributeKey: 'country', + filterOperator: 'not_equal_to', + values: { id: 1, name: 'India' }, + queryOperator: 'and', + }, +]; diff --git a/app/javascript/dashboard/components-next/filter/helper/filterHelper.js b/app/javascript/dashboard/components-next/filter/helper/filterHelper.js index f1fc0c452..2160365f6 100644 --- a/app/javascript/dashboard/components-next/filter/helper/filterHelper.js +++ b/app/javascript/dashboard/components-next/filter/helper/filterHelper.js @@ -39,3 +39,11 @@ export const buildAttributesFilterTypes = (attributes, getOperatorTypes) => { attributeModel: 'customAttributes', })); }; + +/** + * Replaces underscores with spaces in a string + * @param {string} text - The input string + * @returns {string} The string with underscores replaced by spaces + */ +export const replaceUnderscoreWithSpace = text => + text?.replaceAll('_', ' ') ?? ''; diff --git a/app/javascript/dashboard/components-next/filter/helper/filterHelper.spec.js b/app/javascript/dashboard/components-next/filter/helper/filterHelper.spec.js index 7db5cb2d2..58cf92cf3 100644 --- a/app/javascript/dashboard/components-next/filter/helper/filterHelper.spec.js +++ b/app/javascript/dashboard/components-next/filter/helper/filterHelper.spec.js @@ -1,6 +1,7 @@ import { getCustomAttributeInputType, buildAttributesFilterTypes, + replaceUnderscoreWithSpace, } from './filterHelper'; describe('filterHelper', () => { @@ -123,4 +124,14 @@ describe('filterHelper', () => { expect(result).toEqual([]); }); }); + + describe('replaceUnderscoreWithSpace', () => { + it('replaces underscores with spaces', () => { + expect(replaceUnderscoreWithSpace('test_key')).toBe('test key'); + }); + + it('returns empty string if input is null', () => { + expect(replaceUnderscoreWithSpace(null)).toBe(''); + }); + }); }); diff --git a/app/javascript/dashboard/components-next/pagination/PaginationFooter.vue b/app/javascript/dashboard/components-next/pagination/PaginationFooter.vue index a3324a618..4f6f47c85 100644 --- a/app/javascript/dashboard/components-next/pagination/PaginationFooter.vue +++ b/app/javascript/dashboard/components-next/pagination/PaginationFooter.vue @@ -75,6 +75,7 @@ const pageInfo = computed(() => { icon="i-lucide-chevrons-left" variant="ghost" size="sm" + color="slate" class="!w-8 !h-6" :disabled="isFirstPage" @click="changePage(1)" @@ -82,6 +83,7 @@ const pageInfo = computed(() => { { { ({ }); const fetchContacts = async (page = 1) => { + await store.dispatch('contacts/clearContactFilters'); await store.dispatch('contacts/get', getCommonFetchParams(page)); updatePageParam(page); };