feature: Filtering conversations and contacts with custom attributes (#3851)
This commit is contained in:
parent
e0d24e0a73
commit
52d1821cd3
16 changed files with 451 additions and 284 deletions
|
@ -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'
|
||||
|
|
|
@ -129,7 +129,7 @@
|
|||
>
|
||||
<conversation-advanced-filter
|
||||
v-if="showAdvancedFilters"
|
||||
:filter-types="advancedFilterTypes"
|
||||
:initial-filter-types="advancedFilterTypes"
|
||||
:on-close="onToggleAdvanceFiltersModal"
|
||||
@applyFilter="onApplyFilter"
|
||||
/>
|
||||
|
|
|
@ -30,7 +30,7 @@ export default {
|
|||
},
|
||||
value: {
|
||||
type: Date,
|
||||
default: () => [],
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
];
|
|
@ -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: {
|
|
@ -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;
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
<contacts-advanced-filters
|
||||
v-if="showFiltersModal"
|
||||
:on-close="onToggleFilters"
|
||||
:filter-types="contactFilterItems"
|
||||
:initial-filter-types="contactFilterItems"
|
||||
@applyFilter="onApplyFilter"
|
||||
@clearFilters="clearFilters"
|
||||
/>
|
||||
|
|
|
@ -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;
|
||||
|
|
44
app/javascript/shared/mixins/filterMixin.js
Normal file
44
app/javascript/shared/mixins/filterMixin.js
Normal 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];
|
||||
},
|
||||
},
|
||||
};
|
9
app/javascript/shared/mixins/specs/MockComponent.vue
Normal file
9
app/javascript/shared/mixins/specs/MockComponent.vue
Normal file
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<div class="test"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'MockComponent',
|
||||
};
|
||||
</script>
|
29
app/javascript/shared/mixins/specs/filterFixtures.js
Normal file
29
app/javascript/shared/mixins/specs/filterFixtures.js
Normal 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' },
|
||||
],
|
||||
},
|
||||
];
|
13
app/javascript/shared/mixins/specs/filterMixin.spec.js
Normal file
13
app/javascript/shared/mixins/specs/filterMixin.spec.js
Normal 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();
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue