feat: Custom attributes in automations and refactor (#4548)

* Custom attributes

* Custom Attrs Manifest

* Fix dropdown values for custom attributes

* Handle edit mode for custom attributes

* Ported duplicate logic to a mixin

* fix Code climate issue

* Fix Codeclimate complexity warning

* Bug fix - Custom attributes getting duplicated

* Bug fixes and Code Climate issue fix

* Code Climate Issues Breakdown

* Fix test spec

* Add labels for Custom attributes in dropdown

* Refactor

* Refactor Automion mixin

* Refactor Mixin

* Refactor getOperator

* Fix getOperatorType

* File name method refactor

* Refactor appendNewCondition

* spec update

* Refactor methods

* Mixin Spec update

* Automation Mixins Test Specs

* Mixin Spec Rerun

* Automation validations mixin spec

* Automation helper test spec

* Send custom_attr key

* Fix spec fixtures

* fix: Changes for custom attribute type and lower case search

* fix: Specs

* fix: Specs

* fix: Ruby version change

* fix: Ruby version change

* Removes Lowercased values and fix label value in api payload

* Fix specs

* Fixed Query Spec

* Removed disabled labels if no attributes are present

* Code Climate Fixes

* fix: custom attribute with indifferent access

* fix: custom attribute with indifferent access

* Fix specs

* Minor label fix

* REtrigger circle ci build

* Update app/javascript/shared/mixins/specs/automationMixin.spec.js

* Update app/javascript/shared/mixins/specs/automationMixin.spec.js

* fix: Custom attribute case insensitivity search

* Add missing reset action method to input

* Set team_input to single select instead of multiple

* fix: remove value case check for date,boolean and number data type

* fix: cognitive complexity

* fix: cognitive complexity

* fix: Fixed activity message for automation system

* fix: Fixed activity message for automation system

* fix: Fixed activity message for automation system

* fix: codeclimate

* fix: codeclimate

* fix: action cable events for label update

* fix: codeclimate, conversation modela number of methods

* fix: codeclimate, conversation modela number of methods

* fix: codeclimate, conversation modela number of methods

* fix: codeclimate, conversation modela number of methods

* Fix margin bottom for attachment button

* Remove margin bottom to avoid conflict from macros

* Fix automation action query generator using the right key

* fix: not running message created event for activity message

* fix: not running message created event for activity message

* codeclimate fix

* codeclimate fix

* codeclimate fix

* Update app/javascript/dashboard/mixins/automations/methodsMixin.js

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>

* Update app/javascript/shared/mixins/specs/automationHelper.spec.js

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>

* Update app/javascript/dashboard/helper/automationHelper.js

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>

* Update app/javascript/dashboard/mixins/automations/methodsMixin.js

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
Co-authored-by: Tejaswini <tejaswini@chatwoot.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Fayaz Ahmed
2022-11-10 10:53:29 +05:30
committed by GitHub
parent 3184c8964d
commit 9eb861a3b7
30 changed files with 2413 additions and 704 deletions

View File

@@ -68,6 +68,9 @@
)
"
:show-query-operator="i !== automation.conditions.length - 1"
:custom-attribute-type="
getCustomAttributeType(automation.conditions[i].attribute_key)
"
:v="$v.automation.conditions.$each[i]"
@resetFilter="resetFilter(i, automation.conditions[i])"
@removeFilter="removeFilter(i)"
@@ -138,75 +141,32 @@
<script>
import alertMixin from 'shared/mixins/alertMixin';
import { required, requiredIf } from 'vuelidate/lib/validators';
import automationMethodsMixin from 'dashboard/mixins/automations/methodsMixin';
import automationValidationsMixin from 'dashboard/mixins/automations/validationsMixin';
import filterInputBox from 'dashboard/components/widgets/FilterInput/Index.vue';
import automationActionInput from 'dashboard/components/widgets/AutomationActionInput.vue';
import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
import countries from '/app/javascript/shared/constants/countries.js';
import {
AUTOMATION_RULE_EVENTS,
AUTOMATION_ACTION_TYPES,
AUTOMATIONS,
} from './constants';
import filterQueryGenerator from 'dashboard/helper/filterQueryGenerator.js';
import actionQueryGenerator from 'dashboard/helper/actionQueryGenerator.js';
export default {
components: {
filterInputBox,
automationActionInput,
},
mixins: [alertMixin],
mixins: [alertMixin, automationMethodsMixin, automationValidationsMixin],
props: {
onClose: {
type: Function,
default: () => {},
},
},
validations: {
automation: {
name: {
required,
},
description: {
required,
},
event_name: {
required,
},
conditions: {
required,
$each: {
values: {
required: requiredIf(prop => {
return !(
prop.filter_operator === 'is_present' ||
prop.filter_operator === 'is_not_present'
);
}),
},
},
},
actions: {
required,
$each: {
action_params: {
required: requiredIf(prop => {
if (prop.action_name === 'send_email_to_team') return true;
return !(
prop.action_name === 'mute_conversation' ||
prop.action_name === 'snooze_conversation' ||
prop.action_name === 'resolve_conversation'
);
}),
},
},
},
},
},
data() {
return {
automationTypes: AUTOMATIONS,
automationTypes: JSON.parse(JSON.stringify(AUTOMATIONS)),
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
automationRuleEvents: AUTOMATION_RULE_EVENTS,
automationActionTypes: AUTOMATION_ACTION_TYPES,
@@ -222,6 +182,7 @@ export default {
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
custom_attribute_type: '',
},
],
actions: [
@@ -232,24 +193,11 @@ export default {
],
},
showDeleteConfirmationModal: false,
allCustomAttributes: [],
mode: 'create',
};
},
computed: {
conditions() {
return this.automationTypes[this.automation.event_name].conditions;
},
actions() {
return this.automationTypes[this.automation.event_name].actions;
},
filterAttributes() {
return this.filterTypes.map(type => {
return {
key: type.attributeKey,
name: type.attributeName,
attributeI18nKey: type.attributeI18nKey,
};
});
},
hasAutomationMutated() {
if (
this.automation.conditions[0].values ||
@@ -259,200 +207,15 @@ export default {
return false;
},
},
methods: {
onEventChange() {
if (this.automation.event_name === 'message_created') {
this.automation.conditions = [
{
attribute_key: 'message_type',
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
},
];
} else {
this.automation.conditions = [
{
attribute_key: 'status',
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
},
];
}
this.automation.actions = [
{
action_name: 'assign_team',
action_params: [],
},
];
},
getAttributes(key) {
return this.automationTypes[key].conditions;
},
getInputType(key) {
const type = this.automationTypes[
this.automation.event_name
].conditions.find(condition => condition.key === key);
return type.inputType;
},
getOperators(key) {
const type = this.automationTypes[
this.automation.event_name
].conditions.find(condition => condition.key === key);
return type.filterOperators;
},
getConditionDropdownValues(type) {
const statusFilters = this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS');
switch (type) {
case 'status':
return [
...Object.keys(statusFilters).map(status => {
return {
id: status,
name: statusFilters[status].TEXT,
};
}),
{
id: 'all',
name: this.$t('CHAT_LIST.FILTER_ALL'),
},
];
case 'assignee_id':
return this.$store.getters['agents/getAgents'];
case 'contact':
return this.$store.getters['contacts/getContacts'];
case 'inbox_id':
return this.$store.getters['inboxes/getInboxes'];
case 'team_id':
return this.$store.getters['teams/getTeams'];
case 'campaign_id':
return this.$store.getters['campaigns/getAllCampaigns'].map(i => {
return {
id: i.id,
name: i.title,
};
});
case 'labels':
return this.$store.getters['labels/getLabels'].map(i => {
return {
id: i.id,
name: i.title,
};
});
case 'browser_language':
return languages;
case 'country_code':
return countries;
case 'message_type':
return [
{
id: 'incoming',
name: 'Incoming Message',
},
{
id: 'outgoing',
name: 'Outgoing Message',
},
];
default:
return undefined;
}
},
getActionDropdownValues(type) {
switch (type) {
case 'assign_team':
case 'send_email_to_team':
return this.$store.getters['teams/getTeams'];
case 'add_label':
return this.$store.getters['labels/getLabels'].map(i => {
return {
id: i.title,
name: i.title,
};
});
default:
return undefined;
}
},
appendNewCondition() {
switch (this.automation.event_name) {
case 'message_created':
this.automation.conditions.push({
attribute_key: 'message_type',
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
});
break;
default:
this.automation.conditions.push({
attribute_key: 'status',
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
});
break;
}
},
appendNewAction() {
this.automation.actions.push({
action_name: 'assign_team',
action_params: [],
});
},
removeFilter(index) {
if (this.automation.conditions.length <= 1) {
this.showAlert(this.$t('FILTER.FILTER_DELETE_ERROR'));
} else {
this.automation.conditions.splice(index, 1);
}
},
removeAction(index) {
if (this.automation.actions.length <= 1) {
this.showAlert(this.$t('FILTER.FILTER_DELETE_ERROR'));
} else {
this.automation.actions.splice(index, 1);
}
},
submitAutomation() {
this.$v.$touch();
if (this.$v.$invalid) return;
const automation = JSON.parse(JSON.stringify(this.automation));
automation.conditions[
automation.conditions.length - 1
].query_operator = null;
automation.conditions = filterQueryGenerator(
automation.conditions
).payload;
automation.actions = actionQueryGenerator(automation.actions);
this.$emit('saveAutomation', automation);
},
resetFilter(index, currentCondition) {
this.automation.conditions[index].filter_operator = this.automationTypes[
this.automation.event_name
].conditions.find(
condition => condition.key === currentCondition.attribute_key
).filterOperators[0].value;
this.automation.conditions[index].values = '';
},
resetAction(index) {
this.automation.actions[index].action_params = [];
},
showUserInput(operatorType) {
if (operatorType === 'is_present' || operatorType === 'is_not_present')
return false;
return true;
},
showActionInput(actionName) {
if (actionName === 'send_email_to_team' || actionName === 'send_message')
return false;
const type = AUTOMATION_ACTION_TYPES.find(
action => action.key === actionName
).inputType;
if (type === null) return false;
return true;
},
mounted() {
this.$store.dispatch('inboxes/get');
this.$store.dispatch('agents/get');
this.$store.dispatch('contacts/get');
this.$store.dispatch('teams/get');
this.$store.dispatch('labels/get');
this.$store.dispatch('campaigns/get');
this.allCustomAttributes = this.$store.getters['attributes/getAttributes'];
this.manifestCustomAttributes();
},
};
</script>

View File

@@ -2,7 +2,7 @@
<div class="column">
<woot-modal-header :header-title="$t('AUTOMATION.EDIT.TITLE')" />
<div class="row modal-content">
<div class="medium-12 columns">
<div v-if="automation" class="medium-12 columns">
<woot-input
v-model="automation.name"
:label="$t('AUTOMATION.ADD.FORM.NAME.LABEL')"
@@ -64,6 +64,9 @@
automation.conditions[i].attribute_key
)
"
:custom-attribute-type="
getCustomAttributeType(automation.conditions[i].attribute_key)
"
:show-query-operator="i !== automation.conditions.length - 1"
:v="$v.automation.conditions.$each[i]"
@resetFilter="resetFilter(i, automation.conditions[i])"
@@ -94,19 +97,11 @@
:key="i"
v-model="automation.actions[i]"
:action-types="automationActionTypes"
:dropdown-values="
getActionDropdownValues(automation.actions[i].action_name)
"
:show-action-input="
showActionInput(automation.actions[i].action_name)
"
:dropdown-values="getActionDropdownValues(action.action_name)"
:show-action-input="showActionInput(action.action_name)"
:v="$v.automation.actions.$each[i]"
:initial-file-name="
getFileName(
automation.actions[i].action_params[0],
automation.actions[i].action_name
)
"
:initial-file-name="getFileName(action, automation.files)"
@resetAction="resetAction(i)"
@removeAction="removeAction(i)"
/>
<div class="filter-actions">
@@ -144,25 +139,23 @@
<script>
import alertMixin from 'shared/mixins/alertMixin';
import { required, requiredIf } from 'vuelidate/lib/validators';
import automationMethodsMixin from 'dashboard/mixins/automations/methodsMixin';
import automationValidationsMixin from 'dashboard/mixins/automations/validationsMixin';
import filterInputBox from 'dashboard/components/widgets/FilterInput/Index.vue';
import automationActionInput from 'dashboard/components/widgets/AutomationActionInput.vue';
import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
import countries from 'shared/constants/countries.js';
import {
AUTOMATION_RULE_EVENTS,
AUTOMATION_ACTION_TYPES,
AUTOMATIONS,
} from './constants';
import filterQueryGenerator from 'dashboard/helper/filterQueryGenerator.js';
import actionQueryGenerator from 'dashboard/helper/actionQueryGenerator.js';
export default {
components: {
filterInputBox,
automationActionInput,
},
mixins: [alertMixin],
mixins: [alertMixin, automationMethodsMixin, automationValidationsMixin],
props: {
onClose: {
type: Function,
@@ -173,356 +166,35 @@ export default {
default: () => {},
},
},
validations: {
automation: {
name: {
required,
},
description: {
required,
},
event_name: {
required,
},
conditions: {
required,
$each: {
values: {
required: requiredIf(prop => {
return !(
prop.filter_operator === 'is_present' ||
prop.filter_operator === 'is_not_present'
);
}),
},
},
},
actions: {
required,
$each: {
action_params: {
required: requiredIf(prop => {
return !(
prop.action_name === 'mute_conversation' ||
prop.action_name === 'snooze_conversation' ||
prop.action_name === 'resolve_conversation'
);
}),
},
},
},
},
},
data() {
return {
automationTypes: AUTOMATIONS,
automationTypes: JSON.parse(JSON.stringify(AUTOMATIONS)),
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
automationRuleEvents: AUTOMATION_RULE_EVENTS,
automationActionTypes: AUTOMATION_ACTION_TYPES,
automationMutated: false,
show: true,
automation: {
name: null,
description: null,
event_name: 'conversation_created',
conditions: [
{
attribute_key: 'status',
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
},
],
actions: [
{
action_name: 'assign_team',
action_params: [],
},
],
},
automation: null,
showDeleteConfirmationModal: false,
allCustomAttributes: [],
mode: 'edit',
};
},
computed: {
conditions() {
return this.automationTypes[this.automation.event_name].conditions;
},
actions() {
return this.automationTypes[this.automation.event_name].actions;
},
filterAttributes() {
return this.filterTypes.map(type => {
return {
key: type.attributeKey,
name: type.attributeName,
attributeI18nKey: type.attributeI18nKey,
};
});
hasAutomationMutated() {
if (
this.automation.conditions[0].values ||
this.automation.actions[0].action_params.length
)
return true;
return false;
},
},
mounted() {
this.manifestCustomAttributes();
this.allCustomAttributes = this.$store.getters['attributes/getAttributes'];
this.formatAutomation(this.selectedResponse);
},
methods: {
onEventChange() {
if (this.automation.event_name === 'message_created') {
this.automation.conditions = [
{
attribute_key: 'message_type',
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
},
];
} else {
this.automation.conditions = [
{
attribute_key: 'status',
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
},
];
}
this.automation.actions = [
{
action_name: 'assign_team',
action_params: [],
},
];
},
getAttributes(key) {
return this.automationTypes[key].conditions;
},
getInputType(key) {
const type = this.automationTypes[
this.automation.event_name
].conditions.find(condition => condition.key === key);
return type.inputType;
},
getOperators(key) {
const type = this.automationTypes[
this.automation.event_name
].conditions.find(condition => condition.key === key);
return type.filterOperators;
},
getConditionDropdownValues(type) {
const statusFilters = this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS');
switch (type) {
case 'status':
return [
...Object.keys(statusFilters).map(status => {
return {
id: status,
name: statusFilters[status].TEXT,
};
}),
{
id: 'all',
name: this.$t('CHAT_LIST.FILTER_ALL'),
},
];
case 'assignee_id':
return this.$store.getters['agents/getAgents'];
case 'contact':
return this.$store.getters['contacts/getContacts'];
case 'inbox_id':
return this.$store.getters['inboxes/getInboxes'];
case 'team_id':
return this.$store.getters['teams/getTeams'];
case 'campaign_id':
return this.$store.getters['campaigns/getAllCampaigns'].map(i => {
return {
id: i.id,
name: i.title,
};
});
case 'labels':
return this.$store.getters['labels/getLabels'].map(i => {
return {
id: i.id,
name: i.title,
};
});
case 'browser_language':
return languages;
case 'country_code':
return countries;
case 'message_type':
return [
{
id: 'incoming',
name: 'Incoming Message',
},
{
id: 'outgoing',
name: 'Outgoing Message',
},
];
default:
return undefined;
}
},
getActionDropdownValues(type) {
switch (type) {
case 'assign_team':
case 'send_email_to_team':
return this.$store.getters['teams/getTeams'];
case 'add_label':
return this.$store.getters['labels/getLabels'].map(i => {
return {
id: i.title,
name: i.title,
};
});
default:
return undefined;
}
},
appendNewCondition() {
if (
!this.automation.conditions[this.automation.conditions.length - 1]
.query_operator
) {
this.automation.conditions[
this.automation.conditions.length - 1
].query_operator = 'and';
}
switch (this.automation.event_name) {
case 'message_created':
this.automation.conditions.push({
attribute_key: 'message_type',
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
});
break;
default:
this.automation.conditions.push({
attribute_key: 'status',
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
});
break;
}
},
appendNewAction() {
this.automation.actions.push({
action_name: 'assign_team',
action_params: [],
});
},
removeFilter(index) {
if (this.automation.conditions.length <= 1) {
this.showAlert(this.$t('AUTOMATION.CONDITION.DELETE_MESSAGE'));
} else {
this.automation.conditions.splice(index, 1);
}
},
removeAction(index) {
if (this.automation.actions.length <= 1) {
this.showAlert(this.$t('AUTOMATION.ACTION.DELETE_MESSAGE'));
} else {
this.automation.actions.splice(index, 1);
}
},
submitAutomation() {
this.$v.$touch();
if (this.$v.$invalid) return;
const automation = JSON.parse(JSON.stringify(this.automation));
automation.conditions[
automation.conditions.length - 1
].query_operator = null;
automation.conditions = filterQueryGenerator(
automation.conditions
).payload;
automation.actions = actionQueryGenerator(automation.actions);
this.$emit('saveAutomation', automation, 'EDIT');
},
resetFilter(index, currentCondition) {
this.automation.conditions[index].filter_operator = this.automationTypes[
this.automation.event_name
].conditions.find(
condition => condition.key === currentCondition.attribute_key
).filterOperators[0].value;
this.automation.conditions[index].values = '';
},
showUserInput(operatorType) {
if (operatorType === 'is_present' || operatorType === 'is_not_present')
return false;
return true;
},
formatAutomation(automation) {
const formattedConditions = automation.conditions.map(condition => {
const inputType = this.automationTypes[
automation.event_name
].conditions.find(item => item.key === condition.attribute_key)
.inputType;
if (inputType === 'plain_text') {
return {
...condition,
values: condition.values[0],
};
}
return {
...condition,
values: [
...this.getConditionDropdownValues(condition.attribute_key),
].filter(item => [...condition.values].includes(item.id)),
};
});
const formattedActions = automation.actions.map(action => {
let actionParams = [];
if (action.action_params.length) {
const inputType = AUTOMATION_ACTION_TYPES.find(
item => item.key === action.action_name
).inputType;
if (inputType === 'multi_select') {
actionParams = [
...this.getActionDropdownValues(action.action_name),
].filter(item => [...action.action_params].includes(item.id));
} else if (inputType === 'team_message') {
actionParams = {
team_ids: [
...this.getActionDropdownValues(action.action_name),
].filter(item =>
[...action.action_params[0].team_ids].includes(item.id)
),
message: action.action_params[0].message,
};
} else actionParams = [...action.action_params];
}
return {
...action,
action_params: actionParams,
};
});
this.automation = {
...automation,
conditions: formattedConditions,
actions: formattedActions,
};
},
showActionInput(actionName) {
if (actionName === 'send_email_to_team' || actionName === 'send_message')
return false;
const type = AUTOMATION_ACTION_TYPES.find(
action => action.key === actionName
).inputType;
if (type === null) return false;
return true;
},
getFileName(id, actionType) {
if (!id) return '';
if (actionType === 'send_attachment') {
const file = this.automation.files.find(item => item.blob_id === id);
// replace `blob_id.toString()` with file name once api is fixed.
if (file) return file.filename.toString();
}
return '';
},
},
};
</script>
<style lang="scss" scoped>

View File

@@ -170,6 +170,12 @@ export default {
},
},
mounted() {
this.$store.dispatch('inboxes/get');
this.$store.dispatch('agents/get');
this.$store.dispatch('contacts/get');
this.$store.dispatch('teams/get');
this.$store.dispatch('labels/get');
this.$store.dispatch('campaigns/get');
this.$store.dispatch('automations/get');
},
methods: {
@@ -220,18 +226,18 @@ export default {
async submitAutomation(payload, mode) {
try {
const action =
mode === 'EDIT' ? 'automations/update' : 'automations/create';
mode === 'edit' ? 'automations/update' : 'automations/create';
const successMessage =
mode === 'EDIT'
mode === 'edit'
? this.$t('AUTOMATION.EDIT.API.SUCCESS_MESSAGE')
: this.$t('AUTOMATION.ADD.API.SUCCESS_MESSAGE');
await this.$store.dispatch(action, payload);
this.showAlert(this.$t(successMessage));
this.showAlert(successMessage);
this.hideAddPopup();
this.hideEditPopup();
} catch (error) {
const errorMessage =
mode === 'EDIT'
mode === 'edit'
? this.$t('AUTOMATION.EDIT.API.ERROR_MESSAGE')
: this.$t('AUTOMATION.ADD.API.ERROR_MESSAGE');
this.showAlert(errorMessage);

View File

@@ -1,51 +1,8 @@
const OPERATOR_TYPES_1 = [
{
value: 'equal_to',
label: 'Equal to',
},
{
value: 'not_equal_to',
label: 'Not equal to',
},
];
const OPERATOR_TYPES_2 = [
{
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',
},
];
const OPERATOR_TYPES_3 = [
{
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',
},
];
import {
OPERATOR_TYPES_1,
OPERATOR_TYPES_2,
OPERATOR_TYPES_3,
} from './operators';
export const AUTOMATIONS = {
message_created: {
@@ -343,7 +300,7 @@ export const AUTOMATION_ACTION_TYPES = [
{
key: 'assign_team',
label: 'Assign a team',
inputType: 'multi_select',
inputType: 'search_select',
},
{
key: 'add_label',

View File

@@ -0,0 +1,90 @@
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: 'contains',
label: 'Contains',
},
{
value: 'does_not_contain',
label: 'Does not contain',
},
];
export const OPERATOR_TYPES_3 = [
{
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_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_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',
},
];