From 9eb861a3b72266caab51a2871c5489a732005ba2 Mon Sep 17 00:00:00 2001 From: Fayaz Ahmed <15716057+fayazara@users.noreply.github.com> Date: Thu, 10 Nov 2022 10:53:29 +0530 Subject: [PATCH] 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 Co-authored-by: Tejaswini Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Sojan Jose Co-authored-by: Muhsin Keloth --- .vscode/settings.json | 1 + .../widgets/AutomationActionInput.vue | 25 +- .../components/widgets/FilterInput/Index.vue | 5 + .../dashboard/helper/actionQueryGenerator.js | 2 +- .../dashboard/helper/automationHelper.js | 242 ++++++ .../dashboard/helper/filterQueryGenerator.js | 14 - .../helper/specs/filterQueryGenerator.spec.js | 6 +- .../dashboard/i18n/locale/en/automation.json | 4 +- .../i18n/locale/en/generalSettings.json | 3 +- .../mixins/automations/methodsMixin.js | 289 +++++++ .../mixins/automations/validationsMixin.js | 44 + .../settings/automation/AddAutomationRule.vue | 279 +------ .../automation/EditAutomationRule.vue | 378 +-------- .../dashboard/settings/automation/Index.vue | 14 +- .../settings/automation/constants.js | 55 +- .../settings/automation/operators.js | 90 +++ .../shared/mixins/specs/automationFixtures.js | 763 ++++++++++++++++++ .../mixins/specs/automationHelper.spec.js | 323 ++++++++ .../mixins/specs/automationMixin.spec.js | 448 ++++++++++ app/listeners/automation_rule_listener.rb | 10 +- .../concerns/activity_message_handler.rb | 7 +- app/models/conversation.rb | 2 + .../conversations/event_data_presenter.rb | 4 + app/services/action_service.rb | 4 +- .../automation_rules/action_service.rb | 1 + app/services/filter_service.rb | 42 +- spec/listeners/action_cable_listener_spec.rb | 2 +- .../automation_rule_listener_spec.rb | 56 ++ spec/models/conversation_spec.rb | 2 +- .../event_data_presenter_spec.rb | 2 +- 30 files changed, 2413 insertions(+), 704 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 app/javascript/dashboard/helper/automationHelper.js create mode 100644 app/javascript/dashboard/mixins/automations/methodsMixin.js create mode 100644 app/javascript/dashboard/mixins/automations/validationsMixin.js create mode 100644 app/javascript/dashboard/routes/dashboard/settings/automation/operators.js create mode 100644 app/javascript/shared/mixins/specs/automationFixtures.js create mode 100644 app/javascript/shared/mixins/specs/automationHelper.spec.js create mode 100644 app/javascript/shared/mixins/specs/automationMixin.spec.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/app/javascript/dashboard/components/widgets/AutomationActionInput.vue b/app/javascript/dashboard/components/widgets/AutomationActionInput.vue index c79406680..718be4c6f 100644 --- a/app/javascript/dashboard/components/widgets/AutomationActionInput.vue +++ b/app/javascript/dashboard/components/widgets/AutomationActionInput.vue @@ -18,14 +18,32 @@
+
+
+
diff --git a/app/javascript/dashboard/components/widgets/FilterInput/Index.vue b/app/javascript/dashboard/components/widgets/FilterInput/Index.vue index 46c64fc53..b3dd67dc3 100644 --- a/app/javascript/dashboard/components/widgets/FilterInput/Index.vue +++ b/app/javascript/dashboard/components/widgets/FilterInput/Index.vue @@ -32,6 +32,7 @@ v-for="attribute in filterAttributes" :key="attribute.key" :value="attribute.key" + :disabled="attribute.disabled" > {{ attribute.name }} @@ -173,6 +174,10 @@ export default { type: Array, default: () => [], }, + customAttributeType: { + type: String, + default: '', + }, }, computed: { attributeKey: { diff --git a/app/javascript/dashboard/helper/actionQueryGenerator.js b/app/javascript/dashboard/helper/actionQueryGenerator.js index 965c5b238..23cbdee68 100644 --- a/app/javascript/dashboard/helper/actionQueryGenerator.js +++ b/app/javascript/dashboard/helper/actionQueryGenerator.js @@ -22,7 +22,7 @@ const generatePayload = data => { let payload = actions.map(item => { if (Array.isArray(item.action_params)) { item.action_params = formatArray(item.action_params); - } else if (typeof item.values === 'object') { + } else if (typeof item.action_params === 'object') { item.action_params = [item.action_params.id]; } else if (!item.action_params) { item.action_params = []; diff --git a/app/javascript/dashboard/helper/automationHelper.js b/app/javascript/dashboard/helper/automationHelper.js new file mode 100644 index 000000000..715a11294 --- /dev/null +++ b/app/javascript/dashboard/helper/automationHelper.js @@ -0,0 +1,242 @@ +import { + OPERATOR_TYPES_1, + OPERATOR_TYPES_3, + OPERATOR_TYPES_4, +} from 'dashboard/routes/dashboard/settings/automation/operators'; +import filterQueryGenerator from './filterQueryGenerator'; +import actionQueryGenerator from './actionQueryGenerator'; +const MESSAGE_CONDITION_VALUES = [ + { + id: 'incoming', + name: 'Incoming Message', + }, + { + id: 'outgoing', + name: 'Outgoing Message', + }, +]; + +export const getCustomAttributeInputType = key => { + const customAttributeMap = { + date: 'date', + text: 'plain_text', + list: 'search_select', + checkbox: 'search_select', + }; + + return customAttributeMap[key] || 'plain_text'; +}; + +export const isACustomAttribute = (customAttributes, key) => { + return customAttributes.find(attr => { + return attr.attribute_key === key; + }); +}; + +export const getCustomAttributeListDropdownValues = ( + customAttributes, + type +) => { + return customAttributes + .find(attr => attr.attribute_key === type) + .attribute_values.map(item => { + return { + id: item, + name: item, + }; + }); +}; + +export const isCustomAttributeCheckbox = (customAttributes, key) => { + return customAttributes.find(attr => { + return ( + attr.attribute_key === key && attr.attribute_display_type === 'checkbox' + ); + }); +}; + +export const isCustomAttributeList = (customAttributes, type) => { + return customAttributes.find(attr => { + return ( + attr.attribute_key === type && attr.attribute_display_type === 'list' + ); + }); +}; + +export const getOperatorTypes = key => { + const operatorMap = { + list: OPERATOR_TYPES_1, + text: OPERATOR_TYPES_3, + number: OPERATOR_TYPES_1, + link: OPERATOR_TYPES_1, + date: OPERATOR_TYPES_4, + checkbox: OPERATOR_TYPES_1, + }; + + return operatorMap[key] || OPERATOR_TYPES_1; +}; + +export const generateCustomAttributeTypes = (customAttributes, type) => { + return customAttributes.map(attr => { + return { + key: attr.attribute_key, + name: attr.attribute_display_name, + inputType: getCustomAttributeInputType(attr.attribute_display_type), + filterOperators: getOperatorTypes(attr.attribute_display_type), + customAttributeType: type, + }; + }); +}; + +export const generateConditionOptions = (options, key = 'id') => { + return options.map(i => { + return { + id: i[key], + name: i.title, + }; + }); +}; + +export const getActionOptions = ({ teams, labels, type }) => { + const actionsMap = { + assign_team: teams, + send_email_to_team: teams, + add_label: generateConditionOptions(labels, 'title'), + }; + return actionsMap[type]; +}; + +export const getConditionOptions = ({ + agents, + booleanFilterOptions, + campaigns, + contacts, + countries, + customAttributes, + inboxes, + languages, + statusFilterOptions, + teams, + type, +}) => { + if (isCustomAttributeCheckbox(customAttributes, type)) { + return booleanFilterOptions; + } + + if (isCustomAttributeList(customAttributes, type)) { + return getCustomAttributeListDropdownValues(customAttributes, type); + } + + const conditionFilterMaps = { + status: statusFilterOptions, + assignee_id: agents, + contact: contacts, + inbox_id: inboxes, + team_id: teams, + campaigns: generateConditionOptions(campaigns), + browser_language: languages, + country_code: countries, + message_type: MESSAGE_CONDITION_VALUES, + }; + + return conditionFilterMaps[type]; +}; + +export const getFileName = (action, files = []) => { + const blobId = action.action_params[0]; + if (!blobId) return ''; + if (action.action_name === 'send_attachment') { + const file = files.find(item => item.blob_id === blobId); + if (file) return file.filename.toString(); + } + return ''; +}; + +export const getDefaultConditions = eventName => { + if (eventName === 'message_created') { + return [ + { + attribute_key: 'message_type', + filter_operator: 'equal_to', + values: '', + query_operator: 'and', + custom_attribute_type: '', + }, + ]; + } + return [ + { + attribute_key: 'status', + filter_operator: 'equal_to', + values: '', + query_operator: 'and', + custom_attribute_type: '', + }, + ]; +}; + +export const getDefaultActions = () => { + return [ + { + action_name: 'assign_team', + action_params: [], + }, + ]; +}; + +export const filterCustomAttributes = customAttributes => { + return customAttributes.map(attr => { + return { + key: attr.attribute_key, + name: attr.attribute_display_name, + type: attr.attribute_display_type, + }; + }); +}; + +export const getStandardAttributeInputType = (automationTypes, event, key) => { + return automationTypes[event].conditions.find(item => item.key === key) + .inputType; +}; + +export const generateAutomationPayload = payload => { + const automation = JSON.parse(JSON.stringify(payload)); + automation.conditions[automation.conditions.length - 1].query_operator = null; + automation.conditions = filterQueryGenerator(automation.conditions).payload; + automation.actions = actionQueryGenerator(automation.actions); + return automation; +}; + +export const isCustomAttribute = (attrs, key) => { + return attrs.find(attr => attr.key === key); +}; + +export const generateCustomAttributes = ( + conversationAttributes = [], + contactAttribtues = [], + conversationlabel, + contactlabel +) => { + const customAttributes = []; + if (conversationAttributes.length) { + customAttributes.push( + { + key: `conversation_custom_attribute`, + name: conversationlabel, + disabled: true, + }, + ...conversationAttributes + ); + } + if (contactAttribtues.length) { + customAttributes.push( + { + key: `contact_custom_attribute`, + name: contactlabel, + disabled: true, + }, + ...contactAttribtues + ); + } + return customAttributes; +}; diff --git a/app/javascript/dashboard/helper/filterQueryGenerator.js b/app/javascript/dashboard/helper/filterQueryGenerator.js index d549c490c..d519806bf 100644 --- a/app/javascript/dashboard/helper/filterQueryGenerator.js +++ b/app/javascript/dashboard/helper/filterQueryGenerator.js @@ -1,15 +1,3 @@ -const lowerCaseValues = (operator, values) => { - if (operator === 'equal_to' || operator === 'not_equal_to') { - values = values.map(val => { - if (typeof val === 'string') { - return val.toLowerCase(); - } - return val; - }); - } - return values; -}; - const generatePayload = data => { // Make a copy of data to avoid vue data reactivity issues const filters = JSON.parse(JSON.stringify(data)); @@ -23,8 +11,6 @@ const generatePayload = data => { } else { item.values = [item.values]; } - // Convert all values to lowerCase if operator_type is 'equal_to' or 'not_equal_to' - item.values = lowerCaseValues(item.filter_operator, item.values); return item; }); // For every query added, the query_operator is set default to and so the diff --git a/app/javascript/dashboard/helper/specs/filterQueryGenerator.spec.js b/app/javascript/dashboard/helper/specs/filterQueryGenerator.spec.js index 6b38b094a..723ab8ef4 100644 --- a/app/javascript/dashboard/helper/specs/filterQueryGenerator.spec.js +++ b/app/javascript/dashboard/helper/specs/filterQueryGenerator.spec.js @@ -5,7 +5,7 @@ const testData = [ attribute_key: 'status', filter_operator: 'equal_to', values: [ - { id: 'PENDING', name: 'Pending' }, + { id: 'pending', name: 'Pending' }, { id: 'resolved', name: 'Resolved' }, ], query_operator: 'and', @@ -18,7 +18,7 @@ const testData = [ account_id: 1, auto_offline: true, confirmed: true, - email: 'fayazara@gmail.com', + email: 'fayaz@test.com', available_name: 'Fayaz', name: 'Fayaz', role: 'agent', @@ -52,7 +52,7 @@ const finalResult = { { attribute_key: 'id', filter_operator: 'equal_to', - values: ['this is a test'], + values: ['This is a test'], }, ], }; diff --git a/app/javascript/dashboard/i18n/locale/en/automation.json b/app/javascript/dashboard/i18n/locale/en/automation.json index e05ecd9ec..469df1c24 100644 --- a/app/javascript/dashboard/i18n/locale/en/automation.json +++ b/app/javascript/dashboard/i18n/locale/en/automation.json @@ -86,7 +86,9 @@ "RESET_MESSAGE": "Changing event type will reset the conditions and events you have added below" }, "CONDITION": { - "DELETE_MESSAGE": "You need to have atleast one condition to save" + "DELETE_MESSAGE": "You need to have atleast one condition to save", + "CONTACT_CUSTOM_ATTR_LABEL": "Contact Custom Attributes", + "CONVERSATION_CUSTOM_ATTR_LABEL": "Conversation Custom Attributes" }, "ACTION": { "DELETE_MESSAGE": "You need to have atleast one action to save", diff --git a/app/javascript/dashboard/i18n/locale/en/generalSettings.json b/app/javascript/dashboard/i18n/locale/en/generalSettings.json index 164a6b94e..f9e763808 100644 --- a/app/javascript/dashboard/i18n/locale/en/generalSettings.json +++ b/app/javascript/dashboard/i18n/locale/en/generalSettings.json @@ -54,7 +54,8 @@ "MULTISELECT": { "ENTER_TO_SELECT": "Press enter to select", "ENTER_TO_REMOVE": "Press enter to remove", - "SELECT_ONE": "Select one" + "SELECT_ONE": "Select one", + "SELECT": "Select" } }, "NOTIFICATIONS_PAGE": { diff --git a/app/javascript/dashboard/mixins/automations/methodsMixin.js b/app/javascript/dashboard/mixins/automations/methodsMixin.js new file mode 100644 index 000000000..ac9dddd06 --- /dev/null +++ b/app/javascript/dashboard/mixins/automations/methodsMixin.js @@ -0,0 +1,289 @@ +import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages'; +import countries from 'shared/constants/countries'; +import { + generateCustomAttributeTypes, + getActionOptions, + getConditionOptions, + getCustomAttributeInputType, + getOperatorTypes, + isACustomAttribute, + getFileName, + getDefaultConditions, + getDefaultActions, + filterCustomAttributes, + generateAutomationPayload, + getStandardAttributeInputType, + isCustomAttribute, + generateCustomAttributes, +} from 'dashboard/helper/automationHelper'; +import { mapGetters } from 'vuex'; + +export default { + computed: { + ...mapGetters({ + agents: 'agents/getAgents', + campaigns: 'campaigns/getAllCampaigns', + contacts: 'contacts/getContacts', + inboxes: 'inboxes/getInboxes', + labels: 'labels/getLabels', + teams: 'teams/getTeams', + }), + booleanFilterOptions() { + return [ + { + id: true, + name: this.$t('FILTER.ATTRIBUTE_LABELS.TRUE'), + }, + { + id: false, + name: this.$t('FILTER.ATTRIBUTE_LABELS.FALSE'), + }, + ]; + }, + + statusFilterOptions() { + const statusFilters = this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS'); + return [ + ...Object.keys(statusFilters).map(status => { + return { + id: status, + name: statusFilters[status].TEXT, + }; + }), + { + id: 'all', + name: this.$t('CHAT_LIST.FILTER_ALL'), + }, + ]; + }, + }, + methods: { + getFileName, + onEventChange() { + this.automation.conditions = getDefaultConditions( + this.automation.event_name + ); + this.automation.actions = getDefaultActions(); + }, + getAttributes(key) { + return this.automationTypes[key].conditions; + }, + getInputType(key) { + const customAttribute = isACustomAttribute(this.allCustomAttributes, key); + if (customAttribute) { + return getCustomAttributeInputType( + customAttribute.attribute_display_type + ); + } + const type = this.getAutomationType(key); + return type.inputType; + }, + getOperators(key) { + if (this.mode === 'edit') { + const customAttribute = isACustomAttribute( + this.allCustomAttributes, + key + ); + if (customAttribute) { + return getOperatorTypes(customAttribute.attribute_display_type); + } + } + const type = this.getAutomationType(key); + return type.filterOperators; + }, + getAutomationType(key) { + return this.automationTypes[this.automation.event_name].conditions.find( + condition => condition.key === key + ); + }, + getCustomAttributeType(key) { + const type = this.automationTypes[ + this.automation.event_name + ].conditions.find(i => i.key === key).customAttributeType; + return type; + }, + getConditionDropdownValues(type) { + const { + agents, + allCustomAttributes: customAttributes, + booleanFilterOptions, + campaigns, + contacts, + inboxes, + statusFilterOptions, + teams, + } = this; + return getConditionOptions({ + agents, + booleanFilterOptions, + campaigns, + contacts, + customAttributes, + inboxes, + statusFilterOptions, + teams, + languages, + countries, + type, + }); + }, + appendNewCondition() { + this.automation.conditions.push( + ...getDefaultConditions(this.automation.event_name) + ); + }, + appendNewAction() { + this.automation.actions.push(...getDefaultActions()); + }, + 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 = generateAutomationPayload(this.automation); + this.$emit('saveAutomation', automation, this.mode); + }, + 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(type) { + return !(type === 'is_present' || type === 'is_not_present'); + }, + showActionInput(action) { + if (action === 'send_email_to_team' || action === 'send_message') + return false; + const type = this.automationActionTypes.find(i => i.key === action) + .inputType; + return !!type; + }, + resetAction(index) { + this.automation.actions[index].action_params = []; + }, + manifestConditions(automation) { + const customAttributes = filterCustomAttributes(this.allCustomAttributes); + const conditions = automation.conditions.map(condition => { + const customAttr = isCustomAttribute( + customAttributes, + condition.attribute_key + ); + let inputType = 'plain_text'; + if (customAttr) { + inputType = getCustomAttributeInputType(customAttr.type); + } else { + inputType = getStandardAttributeInputType( + this.automationTypes, + automation.event_name, + condition.attribute_key + ); + } + if (inputType === 'plain_text' || inputType === 'date') { + return { + ...condition, + values: condition.values[0], + }; + } + return { + ...condition, + query_operator: condition.query_operator || 'and', + values: [ + ...this.getConditionDropdownValues(condition.attribute_key), + ].filter(item => [...condition.values].includes(item.id)), + }; + }); + return conditions; + }, + generateActionsArray(action) { + const params = action.action_params; + let actionParams = []; + const inputType = this.automationActionTypes.find( + item => item.key === action.action_name + ).inputType; + if (inputType === 'multi_select' || inputType === 'search_select') { + actionParams = [ + ...this.getActionDropdownValues(action.action_name), + ].filter(item => [...params].includes(item.id)); + } else if (inputType === 'team_message') { + actionParams = { + team_ids: [ + ...this.getActionDropdownValues(action.action_name), + ].filter(item => [...params[0].team_ids].includes(item.id)), + message: params[0].message, + }; + } else actionParams = [...params]; + return actionParams; + }, + manifestActions(automation) { + let actionParams = []; + const actions = automation.actions.map(action => { + if (action.action_params.length) { + actionParams = this.generateActionsArray(action); + } + return { + ...action, + action_params: actionParams, + }; + }); + return actions; + }, + formatAutomation(automation) { + this.automation = { + ...automation, + conditions: this.manifestConditions(automation), + actions: this.manifestActions(automation), + }; + }, + getActionDropdownValues(type) { + const { labels, teams } = this; + return getActionOptions({ labels, teams, type }); + }, + manifestCustomAttributes() { + const conversationCustomAttributesRaw = this.$store.getters[ + 'attributes/getAttributesByModel' + ]('conversation_attribute'); + + const contactCustomAttributesRaw = this.$store.getters[ + 'attributes/getAttributesByModel' + ]('contact_attribute'); + const conversationCustomAttributeTypes = generateCustomAttributeTypes( + conversationCustomAttributesRaw, + 'conversation_attribute' + ); + const contactCustomAttributeTypes = generateCustomAttributeTypes( + contactCustomAttributesRaw, + 'contact_attribute' + ); + let manifestedCustomAttributes = generateCustomAttributes( + conversationCustomAttributeTypes, + contactCustomAttributeTypes, + this.$t('AUTOMATION.CONDITION.CONVERSATION_CUSTOM_ATTR_LABEL'), + this.$t('AUTOMATION.CONDITION.CONTACT_CUSTOM_ATTR_LABEL') + ); + this.automationTypes.message_created.conditions.push( + ...manifestedCustomAttributes + ); + this.automationTypes.conversation_created.conditions.push( + ...manifestedCustomAttributes + ); + this.automationTypes.conversation_updated.conditions.push( + ...manifestedCustomAttributes + ); + }, + }, +}; diff --git a/app/javascript/dashboard/mixins/automations/validationsMixin.js b/app/javascript/dashboard/mixins/automations/validationsMixin.js new file mode 100644 index 000000000..68705d2fd --- /dev/null +++ b/app/javascript/dashboard/mixins/automations/validationsMixin.js @@ -0,0 +1,44 @@ +import { required, requiredIf } from 'vuelidate/lib/validators'; + +export 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' + ); + }), + }, + }, + }, + }, + }, +}; diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue b/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue index 66239b623..8b5a13585 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue @@ -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 @@ diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue b/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue index 668b44d51..7230c6a27 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/EditAutomationRule.vue @@ -2,7 +2,7 @@