feature: Filtering conversations and contacts with custom attributes (#3851)

This commit is contained in:
Fayaz Ahmed 2022-02-01 13:13:55 +05:30 committed by GitHub
parent e0d24e0a73
commit 52d1821cd3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 451 additions and 284 deletions

View file

@ -22,6 +22,9 @@ checks:
enabled: true
config:
threshold: 300
method-lines:
config:
threshold: 50
exclude_patterns:
- 'spec/'
- '**/specs/'
@ -44,3 +47,4 @@ exclude_patterns:
- 'app/javascript/shared/constants/countries.js'
- 'app/javascript/dashboard/components/widgets/conversation/advancedFilterItems/languages.js'
- 'app/javascript/dashboard/routes/dashboard/contacts/contactFilterItems/index.js'
- 'app/javascript/dashboard/components/widgets/FilterInput/FilterOperatorTypes.js'

View file

@ -129,7 +129,7 @@
>
<conversation-advanced-filter
v-if="showAdvancedFilters"
:filter-types="advancedFilterTypes"
:initial-filter-types="advancedFilterTypes"
:on-close="onToggleAdvanceFiltersModal"
@applyFilter="onApplyFilter"
/>

View file

@ -30,7 +30,7 @@ export default {
},
value: {
type: Date,
default: () => [],
default: [],
},
},

View file

@ -0,0 +1,75 @@
export const OPERATOR_TYPES_1 = [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
];
export const OPERATOR_TYPES_2 = [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
];
export const OPERATOR_TYPES_3 = [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'contains',
label: 'Contains',
},
{
value: 'does_not_contain',
label: 'Does not contain',
},
];
export const OPERATOR_TYPES_4 = [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
{
value: 'is_greater_than',
label: 'Is greater than',
},
{
value: 'is_lesser_than',
label: 'Is lesser than',
},
];

View file

@ -3,6 +3,27 @@
<div class="filter">
<div class="filter-inputs">
<select
v-if="groupedFilters"
v-model="attributeKey"
class="filter__question"
@change="resetFilter()"
>
<optgroup
v-for="(group, i) in filterGroups"
:key="i"
:label="group.name"
>
<option
v-for="attribute in group.attributes"
:key="attribute.key"
:value="attribute.key"
>
{{ attribute.name }}
</option>
</optgroup>
</select>
<select
v-else
v-model="attributeKey"
class="filter__question"
@change="resetFilter()"
@ -63,6 +84,14 @@
:option-height="104"
/>
</div>
<div v-else-if="inputType === 'date'" class="multiselect-wrap--small">
<input
v-model="values"
type="date"
:editable="false"
class="answer--text-input datepicker"
/>
</div>
<input
v-else
v-model="values"
@ -132,6 +161,14 @@ export default {
type: Boolean,
default: true,
},
groupedFilters: {
type: Boolean,
default: true,
},
filterGroups: {
type: Array,
default: () => [],
},
},
computed: {
attributeKey: {

View file

@ -9,12 +9,13 @@
v-for="(filter, i) in appliedFilters"
:key="i"
v-model="appliedFilters[i]"
:filter-attributes="filterAttributes"
:filter-groups="filterGroups"
:input-type="getInputType(appliedFilters[i].attribute_key)"
:operators="getOperators(appliedFilters[i].attribute_key)"
:dropdown-values="getDropdownValues(appliedFilters[i].attribute_key)"
:show-query-operator="i !== appliedFilters.length - 1"
:show-user-input="showUserInput(appliedFilters[i].filter_operator)"
:grouped-filters="true"
:v="$v.appliedFilters.$each[i]"
@resetFilter="resetFilter(i, appliedFilters[i])"
@removeFilter="removeFilter(i)"
@ -48,22 +49,24 @@
<script>
import alertMixin from 'shared/mixins/alertMixin';
import { required, requiredIf } from 'vuelidate/lib/validators';
import FilterInputBox from '../FilterInput.vue';
import FilterInputBox from '../FilterInput/Index.vue';
import languages from './advancedFilterItems/languages';
import countries from '/app/javascript/shared/constants/countries.js';
import countries from 'shared/constants/countries.js';
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,
},
mixins: [alertMixin],
mixins: [alertMixin, filterMixin],
props: {
onClose: {
type: Function,
default: () => {},
},
filterTypes: {
initialFilterTypes: {
type: Array,
default: () => [],
},
@ -87,22 +90,21 @@ export default {
return {
show: true,
appliedFilters: [],
filterTypes: this.initialFilterTypes,
filterAttributeGroups,
filterGroups: [],
allCustomAttributes: [],
attributeModel: 'conversation_attribute',
filtersFori18n: 'FILTER',
};
},
computed: {
filterAttributes() {
return this.filterTypes.map(type => {
return {
key: type.attributeKey,
name: this.$t(`FILTER.ATTRIBUTES.${type.attributeI18nKey}`),
};
});
},
...mapGetters({
getAppliedConversationFilters: 'getAppliedConversationFilters',
}),
},
mounted() {
this.setFilterAttributes();
this.$store.dispatch('campaigns/get');
if (this.getAppliedConversationFilters.length) {
this.appliedFilters = [...this.getAppliedConversationFilters];
@ -112,10 +114,41 @@ export default {
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
attribute_model: 'standard',
});
}
},
methods: {
getOperatorTypes(key) {
switch (key) {
case 'list':
return OPERATORS.OPERATOR_TYPES_1;
case 'text':
return OPERATORS.OPERATOR_TYPES_3;
case 'number':
return OPERATORS.OPERATOR_TYPES_1;
case 'link':
return OPERATORS.OPERATOR_TYPES_1;
case 'date':
return OPERATORS.OPERATOR_TYPES_4;
case 'checkbox':
return OPERATORS.OPERATOR_TYPES_1;
default:
return OPERATORS.OPERATOR_TYPES_1;
}
},
customAttributeInputType(key) {
switch (key) {
case 'date':
return 'date';
default:
return 'plain_text';
}
},
getAttributeModel(key) {
const type = this.filterTypes.find(filter => filter.attributeKey === key);
return type.attributeModel;
},
getInputType(key) {
const type = this.filterTypes.find(filter => filter.attributeKey === key);
return type.inputType;

View file

@ -1,229 +1,144 @@
import {
OPERATOR_TYPES_1,
OPERATOR_TYPES_2,
OPERATOR_TYPES_3,
} from '../../FilterInput/FilterOperatorTypes';
const filterTypes = [
{
attributeKey: 'status',
attributeI18nKey: 'STATUS',
inputType: 'multi_select',
dataType: 'text',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
],
attribute_type: 'standard',
filterOperators: OPERATOR_TYPES_1,
attributeModel: 'standard',
},
{
attributeKey: 'assignee_id',
attributeI18nKey: 'ASSIGNEE_NAME',
inputType: 'search_select',
dataType: 'text',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
],
attribute_type: 'standard',
filterOperators: OPERATOR_TYPES_2,
attributeModel: 'standard',
},
{
attributeKey: 'inbox_id',
attributeI18nKey: 'INBOX_NAME',
inputType: 'search_select',
dataType: 'text',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
],
attribute_type: 'standard',
filterOperators: OPERATOR_TYPES_2,
attributeModel: 'standard',
},
{
attributeKey: 'team_id',
attributeI18nKey: 'TEAM_NAME',
inputType: 'search_select',
dataType: 'number',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
],
attribute_type: 'standard',
filterOperators: OPERATOR_TYPES_2,
attributeModel: 'standard',
},
{
attributeKey: 'display_id',
attributeI18nKey: 'CONVERSATION_IDENTIFIER',
inputType: 'plain_text',
dataType: 'Number',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'contains',
label: 'Contains',
},
{
value: 'does_not_contain',
label: 'Does not contain',
},
],
attribute_type: 'standard',
filterOperators: OPERATOR_TYPES_3,
attributeModel: 'standard',
},
{
attributeKey: 'campaign_id',
attributeI18nKey: 'CAMPAIGN_NAME',
inputType: 'search_select',
dataType: 'Number',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
],
attribute_type: 'standard',
filterOperators: OPERATOR_TYPES_2,
attributeModel: 'standard',
},
{
attributeKey: 'labels',
attributeI18nKey: 'LABELS',
inputType: 'multi_select',
dataType: 'text',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'is_present',
label: 'Is present',
},
{
value: 'is_not_present',
label: 'Is not present',
},
],
attribute_type: 'standard',
filterOperators: OPERATOR_TYPES_2,
attributeModel: 'standard',
},
{
attributeKey: 'browser_language',
attributeI18nKey: 'BROWSER_LANGUAGE',
inputType: 'search_select',
dataType: 'text',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
],
attribute_type: 'additional_attributes',
filterOperators: OPERATOR_TYPES_1,
attributeModel: 'additional',
},
{
attributeKey: 'country_code',
attributeI18nKey: 'COUNTRY_NAME',
inputType: 'search_select',
dataType: 'text',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
],
attribute_type: 'additional_attributes',
filterOperators: OPERATOR_TYPES_1,
attributeModel: 'additional',
},
{
attributeKey: 'referer',
attributeI18nKey: 'REFERER_LINK',
inputType: 'plain_text',
dataType: 'text',
filterOperators: [
filterOperators: OPERATOR_TYPES_3,
attributeModel: 'additional',
},
];
export const filterAttributeGroups = [
{
value: 'equal_to',
label: 'Equal to',
name: 'Standard Filters',
i18nGroup: 'STANDARD_FILTERS',
attributes: [
{
key: 'status',
i18nKey: 'STATUS',
},
{
value: 'not_equal_to',
label: 'Not equal to',
key: 'assignee_id',
i18nKey: 'ASSIGNEE_NAME',
},
{
value: 'contains',
label: 'Contains',
key: 'inbox_id',
i18nKey: 'INBOX_NAME',
},
{
value: 'does_not_contain',
label: 'Does not contain',
key: 'team_id',
i18nKey: 'TEAM_NAME',
},
{
key: 'display_id',
i18nKey: 'CONVERSATION_IDENTIFIER',
},
{
key: 'campaign_id',
i18nKey: 'CAMPAIGN_NAME',
},
{
key: 'labels',
i18nKey: 'LABELS',
},
],
},
{
name: 'Additional Filters',
i18nGroup: 'ADDITIONAL_FILTERS',
attributes: [
{
key: 'browser_language',
i18nKey: 'BROWSER_LANGUAGE',
},
{
key: 'country_code',
i18nKey: 'COUNTRY_NAME',
},
{
key: 'referer',
i18nKey: 'REFERER_LINK',
},
],
attribute_type: 'additional_attributes',
},
];

View file

@ -19,7 +19,9 @@
"contains": "Contains",
"does_not_contain": "Does not contain",
"is_present": "Is present",
"is_not_present": "Is not present"
"is_not_present": "Is not present",
"is_greater_than": "Is greater than",
"is_lesser_than": "Is lesser than"
},
"ATTRIBUTES": {
"STATUS": "Status",
@ -31,7 +33,17 @@
"LABELS": "Labels",
"BROWSER_LANGUAGE": "Browser Language",
"COUNTRY_NAME": "Country Name",
"REFERER_LINK": "Referer link"
"REFERER_LINK": "Referer link",
"CUSTOM_ATTRIBUTE_LIST": "List",
"CUSTOM_ATTRIBUTE_TEXT": "Text",
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
"CUSTOM_ATTRIBUTE_LINK": "Link",
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
"ADDITIONAL_FILTERS": "Additional Filters",
"CUSTOM_ATTRIBUTES": "Custom Attributes"
},
"CUSTOM_VIEWS": {
"ADD": {

View file

@ -20,7 +20,9 @@
"contains": "Contains",
"does_not_contain": "Does not contain",
"is_present": "Is present",
"is_not_present": "Is not present"
"is_not_present": "Is not present",
"is_greater_than": "Is greater than",
"is_lesser_than": "Is lesser than"
},
"ATTRIBUTES": {
"NAME": "Name",
@ -28,7 +30,17 @@
"PHONE_NUMBER": "Phone number",
"IDENTIFIER": "Identifier",
"CITY": "City",
"COUNTRY": "Country"
"COUNTRY": "Country",
"CUSTOM_ATTRIBUTE_LIST": "List",
"CUSTOM_ATTRIBUTE_TEXT": "Text",
"CUSTOM_ATTRIBUTE_NUMBER": "Number",
"CUSTOM_ATTRIBUTE_LINK": "Link",
"CUSTOM_ATTRIBUTE_CHECKBOX": "Checkbox"
},
"GROUPS": {
"STANDARD_FILTERS": "Standard Filters",
"ADDITIONAL_FILTERS": "Additional Filters",
"CUSTOM_ATTRIBUTES": "Custom Attributes"
}
}
}

View file

@ -9,7 +9,8 @@
v-for="(filter, i) in appliedFilters"
:key="i"
v-model="appliedFilters[i]"
:filter-attributes="filterAttributes"
:filter-groups="filterGroups"
:grouped-filters="true"
:input-type="getInputType(appliedFilters[i].attribute_key)"
:operators="getOperators(appliedFilters[i].attribute_key)"
:dropdown-values="getDropdownValues(appliedFilters[i].attribute_key)"
@ -58,21 +59,23 @@
<script>
import alertMixin from 'shared/mixins/alertMixin';
import { required } from 'vuelidate/lib/validators';
import FilterInputBox from '../../../../components/widgets/FilterInput.vue';
import countries from '/app/javascript/shared/constants/countries.js';
import FilterInputBox from '../../../../components/widgets/FilterInput/Index.vue';
import countries from 'shared/constants/countries.js';
import { mapGetters } from 'vuex';
import { filterAttributeGroups } from '../contactFilterItems';
import filterMixin from 'shared/mixins/filterMixin';
import * as OPERATORS from 'dashboard/components/widgets/FilterInput/FilterOperatorTypes.js';
export default {
components: {
FilterInputBox,
},
mixins: [alertMixin],
mixins: [alertMixin, filterMixin],
props: {
onClose: {
type: Function,
default: () => {},
},
filterTypes: {
initialFilterTypes: {
type: Array,
default: () => [],
},
@ -91,17 +94,15 @@ export default {
return {
show: true,
appliedFilters: [],
filterTypes: this.initialFilterTypes,
filterGroups: [],
allCustomAttributes: [],
filterAttributeGroups,
attributeModel: 'contact_attribute',
filtersFori18n: 'CONTACTS_FILTER',
};
},
computed: {
filterAttributes() {
return this.filterTypes.map(type => {
return {
key: type.attributeKey,
name: this.$t(`CONTACTS_FILTER.ATTRIBUTES.${type.attributeI18nKey}`),
};
});
},
...mapGetters({
getAppliedContactFilters: 'contacts/getAppliedContactFilters',
}),
@ -110,6 +111,7 @@ export default {
},
},
mounted() {
this.setFilterAttributes();
if (this.getAppliedContactFilters.length) {
this.appliedFilters = [...this.getAppliedContactFilters];
} else {
@ -118,10 +120,41 @@ export default {
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
attribute_model: 'standard',
});
}
},
methods: {
getOperatorTypes(key) {
switch (key) {
case 'list':
return OPERATORS.OPERATOR_TYPES_1;
case 'text':
return OPERATORS.OPERATOR_TYPES_3;
case 'number':
return OPERATORS.OPERATOR_TYPES_1;
case 'link':
return OPERATORS.OPERATOR_TYPES_1;
case 'date':
return OPERATORS.OPERATOR_TYPES_4;
case 'checkbox':
return OPERATORS.OPERATOR_TYPES_1;
default:
return OPERATORS.OPERATOR_TYPES_1;
}
},
customAttributeInputType(key) {
switch (key) {
case 'date':
return 'date';
default:
return 'plain_text';
}
},
getAttributeModel(key) {
const type = this.filterTypes.find(filter => filter.attributeKey === key);
return type.attributeModel;
},
getInputType(key) {
const type = this.filterTypes.find(filter => filter.attributeKey === key);
return type.inputType;

View file

@ -64,7 +64,7 @@
<contacts-advanced-filters
v-if="showFiltersModal"
:on-close="onToggleFilters"
:filter-types="contactFilterItems"
:initial-filter-types="contactFilterItems"
@applyFilter="onApplyFilter"
@clearFilters="clearFilters"
/>

View file

@ -1,27 +1,14 @@
import {
OPERATOR_TYPES_1,
OPERATOR_TYPES_3,
} from 'dashboard/components/widgets/FilterInput/FilterOperatorTypes.js';
const filterTypes = [
{
attributeKey: 'name',
attributeI18nKey: 'NAME',
inputType: 'plain_text',
dataType: 'text',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'contains',
label: 'Contains',
},
{
value: 'does_not_contain',
label: 'Does not contain',
},
],
filterOperators: OPERATOR_TYPES_1,
attribute_type: 'standard',
},
{
@ -29,24 +16,7 @@ const filterTypes = [
attributeI18nKey: 'EMAIL',
inputType: 'plain_text',
dataType: 'text',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'contains',
label: 'Contains',
},
{
value: 'does_not_contain',
label: 'Does not contain',
},
],
filterOperators: OPERATOR_TYPES_3,
attribute_type: 'standard',
},
{
@ -54,24 +24,7 @@ const filterTypes = [
attributeI18nKey: 'PHONE_NUMBER',
inputType: 'plain_text',
dataType: 'text',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'contains',
label: 'Contains',
},
{
value: 'does_not_contain',
label: 'Does not contain',
},
],
filterOperators: OPERATOR_TYPES_3,
attribute_type: 'standard',
},
{
@ -79,16 +32,7 @@ const filterTypes = [
attributeI18nKey: 'IDENTIFIER',
inputType: 'plain_text',
dataType: 'number',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
],
filterOperators: OPERATOR_TYPES_1,
attribute_type: 'standard',
},
{
@ -96,16 +40,7 @@ const filterTypes = [
attributeI18nKey: 'COUNTRY',
inputType: 'search_select',
dataType: 'number',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
],
filterOperators: OPERATOR_TYPES_1,
attribute_type: 'standard',
},
{
@ -113,26 +48,42 @@ const filterTypes = [
attributeI18nKey: 'CITY',
inputType: 'plain_text',
dataType: 'Number',
filterOperators: [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
{
value: 'contains',
label: 'Contains',
},
{
value: 'does_not_contain',
label: 'Does not contain',
},
],
filterOperators: OPERATOR_TYPES_3,
attribute_type: 'standard',
},
];
export const filterAttributeGroups = [
{
name: 'Standard Filters',
i18nGroup: 'STANDARD_FILTERS',
attributes: [
{
key: 'name',
i18nKey: 'NAME',
},
{
key: 'email',
i18nKey: 'EMAIL',
},
{
key: 'phone_number',
i18nKey: 'PHONE_NUMBER',
},
{
key: 'identifier',
i18nKey: 'IDENTIFIER',
},
{
key: 'country_code',
i18nKey: 'COUNTRY',
},
{
key: 'city',
i18nKey: 'CITY',
},
],
},
];
export default filterTypes;

View file

@ -0,0 +1,44 @@
// import groupBy from 'lodash.groupby';
export default {
methods: {
setFilterAttributes() {
const allCustomAttributes = this.$store.getters[
'attributes/getAttributesByModel'
](this.attributeModel);
const customAttributesFormatted = {
name: this.$t(`${this.filtersFori18n}.GROUPS.CUSTOM_ATTRIBUTES`),
attributes: allCustomAttributes.map(attr => {
return {
key: attr.attribute_key,
name: attr.attribute_display_name,
};
}),
};
const allFilterGroups = this.filterAttributeGroups.map(group => {
return {
name: this.$t(`${this.filtersFori18n}.GROUPS.${group.i18nGroup}`),
attributes: group.attributes.map(attribute => {
return {
key: attribute.key,
name: this.$t(
`${this.filtersFori18n}.ATTRIBUTES.${attribute.i18nKey}`
),
};
}),
};
});
const customAttributeTypes = allCustomAttributes.map(attr => {
return {
attributeKey: attr.attribute_key,
attributeI18nKey: `CUSTOM_ATTRIBUTE_${attr.attribute_display_type.toUpperCase()}`,
inputType: this.customAttributeInputType(attr.attribute_display_type),
filterOperators: this.getOperatorTypes(attr.attribute_display_type),
attributeModel: 'custom_attributes',
};
});
this.filterTypes = [...this.filterTypes, ...customAttributeTypes];
this.filterGroups = [...allFilterGroups, customAttributesFormatted];
},
},
};

View file

@ -0,0 +1,9 @@
<template>
<div class="test"></div>
</template>
<script>
export default {
name: 'MockComponent',
};
</script>

View file

@ -0,0 +1,29 @@
export const filterGroups = [
{
name: 'Standard Filters',
attributes: [
{ key: 'status', name: 'Status' },
{ key: 'assignee_id', name: 'Assignee Name' },
{ key: 'inbox_id', name: 'Inbox Name' },
{ key: 'team_id', name: 'Team Name' },
{ key: 'display_id', name: 'Conversation Identifier' },
{ key: 'campaign_id', name: 'Campaign Name' },
{ key: 'labels', name: 'Labels' },
],
},
{
name: 'Additional Filters',
attributes: [
{ key: 'browser_language', name: 'Browser Language' },
{ key: 'country_code', name: 'Country Name' },
{ key: 'referer', name: 'Referer link' },
],
},
{
name: 'Custom Attributes',
attributes: [
{ key: 'signed_up_at', name: 'Signed Up At' },
{ key: 'test', name: 'Test' },
],
},
];

View file

@ -0,0 +1,13 @@
import filterMixin from '../filterMixin';
import { shallowMount } from '@vue/test-utils';
import MockComponent from './MockComponent.vue';
describe('Test mixin function', () => {
const wrapper = shallowMount(MockComponent, {
mixins: [filterMixin],
});
it('should return proper value from bool', () => {
expect(wrapper.vm.setFilterAttributes).toBeTruthy();
});
});