From 4a2452173e6f2b98451d23651b0ee3bc5f67defb Mon Sep 17 00:00:00 2001 From: Fayaz Ahmed <15716057+fayazara@users.noreply.github.com> Date: Mon, 14 Mar 2022 20:24:53 +0530 Subject: [PATCH] feat: Added support for Created At, Last Activity At filters in the UI (#4031) --- .../FilterInput/FilterOperatorTypes.js | 19 +++++++++-- .../FilterInput/FilterOperatorTypes.spec.js | 15 +++++++++ .../components/widgets/FilterInput/Index.vue | 4 +++ .../ConversationAdvancedFilter.vue | 18 ++++++++-- .../advancedFilterItems.spec.js | 9 +++++ .../conversation/advancedFilterItems/index.js | 33 +++++++++++++++++++ .../i18n/locale/en/advancedFilters.json | 7 ++-- .../i18n/locale/en/contactFilters.json | 7 ++-- .../components/ContactsAdvancedFilters.vue | 17 ++++++++-- .../contacts/contactFilterItems/index.js | 33 +++++++++++++++++++ 10 files changed, 152 insertions(+), 10 deletions(-) create mode 100644 app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.spec.js create mode 100644 app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/advancedFilterItems.spec.js diff --git a/app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js b/app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js index 9e9e3a844..07792c5d5 100644 --- a/app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js +++ b/app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js @@ -69,7 +69,22 @@ export const OPERATOR_TYPES_4 = [ label: 'Is greater than', }, { - value: 'is_lesser_than', - label: 'Is lesser than', + value: 'is_less_than', + label: 'Is less than', + }, +]; + +export const OPERATOR_TYPES_5 = [ + { + value: 'is_greater_than', + label: 'Is greater than', + }, + { + value: 'is_less_than', + label: 'Is less than', + }, + { + value: 'days_before', + label: 'Is x days before', }, ]; diff --git a/app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.spec.js b/app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.spec.js new file mode 100644 index 000000000..4e8e57b16 --- /dev/null +++ b/app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.spec.js @@ -0,0 +1,15 @@ +import { + OPERATOR_TYPES_1, + OPERATOR_TYPES_2, + OPERATOR_TYPES_3, + OPERATOR_TYPES_4, +} from './FilterOperatorTypes'; + +describe('#filterOperators', () => { + it('Matches the correct Operators', () => { + expect(OPERATOR_TYPES_1).toMatchObject(OPERATOR_TYPES_1); + expect(OPERATOR_TYPES_2).toMatchObject(OPERATOR_TYPES_2); + expect(OPERATOR_TYPES_3).toMatchObject(OPERATOR_TYPES_3); + expect(OPERATOR_TYPES_4).toMatchObject(OPERATOR_TYPES_4); + }); +}); diff --git a/app/javascript/dashboard/components/widgets/FilterInput/Index.vue b/app/javascript/dashboard/components/widgets/FilterInput/Index.vue index d26467ab6..25203642a 100644 --- a/app/javascript/dashboard/components/widgets/FilterInput/Index.vue +++ b/app/javascript/dashboard/components/widgets/FilterInput/Index.vue @@ -141,6 +141,10 @@ export default { type: String, default: 'plain_text', }, + dataType: { + type: String, + default: 'plain_text', + }, operators: { type: Array, default: () => [], diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationAdvancedFilter.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationAdvancedFilter.vue index 916df9a31..34a349fff 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationAdvancedFilter.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationAdvancedFilter.vue @@ -10,7 +10,12 @@ :key="i" v-model="appliedFilters[i]" :filter-groups="filterGroups" - :input-type="getInputType(appliedFilters[i].attribute_key)" + :input-type=" + getInputType( + appliedFilters[i].attribute_key, + appliedFilters[i].filter_operator + ) + " :operators="getOperators(appliedFilters[i].attribute_key)" :dropdown-values="getDropdownValues(appliedFilters[i].attribute_key)" :show-query-operator="i !== appliedFilters.length - 1" @@ -56,6 +61,7 @@ import { mapGetters } from 'vuex'; import { filterAttributeGroups } from './advancedFilterItems'; import filterMixin from 'shared/mixins/filterMixin'; import * as OPERATORS from 'dashboard/components/widgets/FilterInput/FilterOperatorTypes.js'; + export default { components: { FilterInputBox, @@ -76,6 +82,12 @@ export default { required, $each: { values: { + ensureBetween0to999(value, prop) { + if (prop.filter_operator === 'days_before') { + return parseInt(value, 10) > 0 && parseInt(value, 10) < 999; + } + return true; + }, required: requiredIf(prop => { return !( prop.filter_operator === 'is_present' || @@ -155,7 +167,9 @@ export default { const type = this.filterTypes.find(filter => filter.attributeKey === key); return type.attributeModel; }, - getInputType(key) { + getInputType(key, operator) { + if (key === 'created_at' || key === 'last_activity_at') + if (operator === 'days_before') return 'plain_text'; const type = this.filterTypes.find(filter => filter.attributeKey === key); return type.inputType; }, diff --git a/app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/advancedFilterItems.spec.js b/app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/advancedFilterItems.spec.js new file mode 100644 index 000000000..69eafa994 --- /dev/null +++ b/app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/advancedFilterItems.spec.js @@ -0,0 +1,9 @@ +import defaultFilters from './index'; +import { filterAttributeGroups } from './index'; + +describe('#filterItems', () => { + it('Matches the correct filterItems', () => { + expect(defaultFilters).toMatchObject(defaultFilters); + expect(filterAttributeGroups).toMatchObject(filterAttributeGroups); + }); +}); diff --git a/app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/index.js b/app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/index.js index 98e27c5bb..a3e8e2c10 100644 --- a/app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/index.js +++ b/app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/index.js @@ -2,6 +2,7 @@ import { OPERATOR_TYPES_1, OPERATOR_TYPES_2, OPERATOR_TYPES_3, + OPERATOR_TYPES_5, } from '../../FilterInput/FilterOperatorTypes'; const filterTypes = [ @@ -85,6 +86,30 @@ const filterTypes = [ filterOperators: OPERATOR_TYPES_3, attributeModel: 'additional', }, + { + attributeKey: 'created_at', + attributeI18nKey: 'CREATED_AT', + inputType: 'date', + dataType: 'text', + filterOperators: OPERATOR_TYPES_5, + attributeModel: 'standard', + }, + { + attributeKey: 'last_activity_at', + attributeI18nKey: 'LAST_ACTIVITY', + inputType: 'date', + dataType: 'text', + filterOperators: OPERATOR_TYPES_5, + attributeModel: 'standard', + }, + { + attributeKey: 'referer', + attributeI18nKey: 'REFERER_LINK', + inputType: 'plain_text', + dataType: 'text', + filterOperators: OPERATOR_TYPES_5, + attributeModel: 'standard', + }, ]; export const filterAttributeGroups = [ @@ -120,6 +145,14 @@ export const filterAttributeGroups = [ key: 'labels', i18nKey: 'LABELS', }, + { + key: 'created_at', + i18nKey: 'CREATED_AT', + }, + { + key: 'last_activity_at', + i18nKey: 'LAST_ACTIVITY', + }, ], }, { diff --git a/app/javascript/dashboard/i18n/locale/en/advancedFilters.json b/app/javascript/dashboard/i18n/locale/en/advancedFilters.json index bdbe2c7cd..46a573c42 100644 --- a/app/javascript/dashboard/i18n/locale/en/advancedFilters.json +++ b/app/javascript/dashboard/i18n/locale/en/advancedFilters.json @@ -21,7 +21,8 @@ "is_present": "Is present", "is_not_present": "Is not present", "is_greater_than": "Is greater than", - "is_lesser_than": "Is lesser than" + "is_less_than": "Is lesser than", + "days_before": "Is x days before" }, "ATTRIBUTE_LABELS": { "TRUE": "True", @@ -42,7 +43,9 @@ "CUSTOM_ATTRIBUTE_TEXT": "Text", "CUSTOM_ATTRIBUTE_NUMBER": "Number", "CUSTOM_ATTRIBUTE_LINK": "Link", - "CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox" + "CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox", + "CREATED_AT": "Created At", + "LAST_ACTIVITY": "Last Activity" }, "GROUPS": { "STANDARD_FILTERS": "Standard Filters", diff --git a/app/javascript/dashboard/i18n/locale/en/contactFilters.json b/app/javascript/dashboard/i18n/locale/en/contactFilters.json index a752d537c..bd58ccf3e 100644 --- a/app/javascript/dashboard/i18n/locale/en/contactFilters.json +++ b/app/javascript/dashboard/i18n/locale/en/contactFilters.json @@ -22,7 +22,8 @@ "is_present": "Is present", "is_not_present": "Is not present", "is_greater_than": "Is greater than", - "is_lesser_than": "Is lesser than" + "is_lesser_than": "Is lesser than", + "days_before": "Is x days before" }, "ATTRIBUTES": { "NAME": "Name", @@ -35,7 +36,9 @@ "CUSTOM_ATTRIBUTE_TEXT": "Text", "CUSTOM_ATTRIBUTE_NUMBER": "Number", "CUSTOM_ATTRIBUTE_LINK": "Link", - "CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox" + "CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox", + "CREATED_AT": "Created At", + "LAST_ACTIVITY": "Last Activity" }, "GROUPS": { "STANDARD_FILTERS": "Standard Filters", diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsAdvancedFilters.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsAdvancedFilters.vue index 3f5162292..95c9ae61c 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsAdvancedFilters.vue +++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsAdvancedFilters.vue @@ -11,7 +11,12 @@ v-model="appliedFilters[i]" :filter-groups="filterGroups" :grouped-filters="true" - :input-type="getInputType(appliedFilters[i].attribute_key)" + :input-type=" + getInputType( + appliedFilters[i].attribute_key, + appliedFilters[i].filter_operator + ) + " :operators="getOperators(appliedFilters[i].attribute_key)" :dropdown-values="getDropdownValues(appliedFilters[i].attribute_key)" :show-query-operator="i !== appliedFilters.length - 1" @@ -86,6 +91,12 @@ export default { $each: { values: { required, + ensureBetween0to999(value, prop) { + if (prop.filter_operator === 'days_before') { + return parseInt(value, 10) > 0 && parseInt(value, 10) < 999; + } + return true; + }, }, }, }, @@ -161,7 +172,9 @@ export default { const type = this.filterTypes.find(filter => filter.attributeKey === key); return type.attributeModel; }, - getInputType(key) { + getInputType(key, operator) { + if (key === 'created_at' || key === 'last_activity_at') + if (operator === 'days_before') return 'plain_text'; const type = this.filterTypes.find(filter => filter.attributeKey === key); return type.inputType; }, diff --git a/app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js b/app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js index dc12d2640..2d54c37bc 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js +++ b/app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js @@ -1,6 +1,7 @@ import { OPERATOR_TYPES_1, OPERATOR_TYPES_3, + OPERATOR_TYPES_5, } from 'dashboard/components/widgets/FilterInput/FilterOperatorTypes.js'; const filterTypes = [ { @@ -51,6 +52,30 @@ const filterTypes = [ filterOperators: OPERATOR_TYPES_3, attribute_type: 'standard', }, + { + attributeKey: 'created_at', + attributeI18nKey: 'CREATED_AT', + inputType: 'date', + dataType: 'text', + filterOperators: OPERATOR_TYPES_5, + attributeModel: 'standard', + }, + { + attributeKey: 'last_activity_at', + attributeI18nKey: 'LAST_ACTIVITY', + inputType: 'date', + dataType: 'text', + filterOperators: OPERATOR_TYPES_5, + attributeModel: 'standard', + }, + { + attributeKey: 'referer', + attributeI18nKey: 'REFERER_LINK', + inputType: 'plain_text', + dataType: 'text', + filterOperators: OPERATOR_TYPES_5, + attributeModel: 'standard', + }, ]; export const filterAttributeGroups = [ @@ -82,6 +107,14 @@ export const filterAttributeGroups = [ key: 'city', i18nKey: 'CITY', }, + { + key: 'created_at', + i18nKey: 'CREATED_AT', + }, + { + key: 'last_activity_at', + i18nKey: 'LAST_ACTIVITY', + }, ], }, ];