From bc6420019fce6902f4515e3ce69d0a8b4a8d846c Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Tue, 27 Aug 2024 12:30:08 +0530 Subject: [PATCH] feat: Rewrite `automations/methodsMixin` to a composable (#9956) Co-authored-by: Shivam Mishra --- .../composables/spec/useAutomation.spec.js | 295 ++++++++++++ .../composables/spec/useMacros.spec.js | 2 +- .../dashboard/composables/useAutomation.js | 349 ++++++++++++++ .../dashboard/composables/useMacros.js | 2 +- .../dashboard/constants/automation.js | 70 +++ .../dashboard/helper/automationHelper.js | 175 ++++--- .../helper}/specs/automationHelper.spec.js | 296 ++++++++---- .../specs/fixtures}/automationFixtures.js | 4 +- .../mixins/automations/methodsMixin.js | 314 ------------ .../settings/automation/AddAutomationRule.vue | 110 ++++- .../automation/EditAutomationRule.vue | 123 ++++- .../mixins/specs/automationMixin.spec.js | 450 ------------------ 12 files changed, 1220 insertions(+), 970 deletions(-) create mode 100644 app/javascript/dashboard/composables/spec/useAutomation.spec.js create mode 100644 app/javascript/dashboard/composables/useAutomation.js create mode 100644 app/javascript/dashboard/constants/automation.js rename app/javascript/{shared/mixins => dashboard/helper}/specs/automationHelper.spec.js (53%) rename app/javascript/{shared/mixins/specs => dashboard/helper/specs/fixtures}/automationFixtures.js (99%) delete mode 100644 app/javascript/dashboard/mixins/automations/methodsMixin.js delete mode 100644 app/javascript/shared/mixins/specs/automationMixin.spec.js diff --git a/app/javascript/dashboard/composables/spec/useAutomation.spec.js b/app/javascript/dashboard/composables/spec/useAutomation.spec.js new file mode 100644 index 000000000..c0881730b --- /dev/null +++ b/app/javascript/dashboard/composables/spec/useAutomation.spec.js @@ -0,0 +1,295 @@ +import { useAutomation } from '../useAutomation'; +import { useStoreGetters, useMapGetter } from 'dashboard/composables/store'; +import { useAlert } from 'dashboard/composables'; +import { useI18n } from '../useI18n'; +import * as automationHelper from 'dashboard/helper/automationHelper'; +import { + customAttributes, + agents, + teams, + labels, + statusFilterOptions, + campaigns, + contacts, + inboxes, + languages, + countries, + slaPolicies, +} from 'dashboard/helper/specs/fixtures/automationFixtures.js'; +import { MESSAGE_CONDITION_VALUES } from 'dashboard/constants/automation'; + +vi.mock('dashboard/composables/store'); +vi.mock('dashboard/composables'); +vi.mock('../useI18n'); +vi.mock('dashboard/helper/automationHelper'); + +describe('useAutomation', () => { + beforeEach(() => { + useStoreGetters.mockReturnValue({ + 'attributes/getAttributes': { value: customAttributes }, + 'attributes/getAttributesByModel': { + value: model => { + return model === 'conversation_attribute' + ? [{ id: 1, name: 'Conversation Attribute' }] + : [{ id: 2, name: 'Contact Attribute' }]; + }, + }, + }); + useMapGetter.mockImplementation(getter => { + const getterMap = { + 'agents/getAgents': agents, + 'campaigns/getAllCampaigns': campaigns, + 'contacts/getContacts': contacts, + 'inboxes/getInboxes': inboxes, + 'labels/getLabels': labels, + 'teams/getTeams': teams, + 'sla/getSLA': slaPolicies, + }; + return { value: getterMap[getter] }; + }); + useI18n.mockReturnValue({ t: key => key }); + useAlert.mockReturnValue(vi.fn()); + + // Mock getConditionOptions for different types + automationHelper.getConditionOptions.mockImplementation(options => { + const { type } = options; + switch (type) { + case 'status': + return statusFilterOptions; + case 'team_id': + return teams; + case 'assignee_id': + return agents; + case 'contact': + return contacts; + case 'inbox_id': + return inboxes; + case 'campaigns': + return campaigns; + case 'browser_language': + return languages; + case 'country_code': + return countries; + case 'message_type': + return MESSAGE_CONDITION_VALUES; + default: + return []; + } + }); + + // Mock getActionOptions for different types + automationHelper.getActionOptions.mockImplementation(options => { + const { type } = options; + switch (type) { + case 'add_label': + return labels; + case 'assign_team': + return teams; + case 'assign_agent': + return agents; + case 'send_email_to_team': + return teams; + case 'send_message': + return []; + case 'add_sla': + return slaPolicies; + default: + return []; + } + }); + }); + + it('initializes computed properties correctly', () => { + const { + agents: computedAgents, + campaigns: computedCampaigns, + contacts: computedContacts, + inboxes: computedInboxes, + labels: computedLabels, + teams: computedTeams, + slaPolicies: computedSlaPolicies, + } = useAutomation(); + + expect(computedAgents.value).toEqual(agents); + expect(computedCampaigns.value).toEqual(campaigns); + expect(computedContacts.value).toEqual(contacts); + expect(computedInboxes.value).toEqual(inboxes); + expect(computedLabels.value).toEqual(labels); + expect(computedTeams.value).toEqual(teams); + expect(computedSlaPolicies.value).toEqual(slaPolicies); + }); + + it('appends new condition and action correctly', () => { + const { appendNewCondition, appendNewAction } = useAutomation(); + const mockAutomation = { + event_name: 'message_created', + conditions: [], + actions: [], + }; + + automationHelper.getDefaultConditions.mockReturnValue([{}]); + automationHelper.getDefaultActions.mockReturnValue([{}]); + + appendNewCondition(mockAutomation); + appendNewAction(mockAutomation); + + expect(automationHelper.getDefaultConditions).toHaveBeenCalledWith( + 'message_created' + ); + expect(automationHelper.getDefaultActions).toHaveBeenCalled(); + expect(mockAutomation.conditions).toHaveLength(1); + expect(mockAutomation.actions).toHaveLength(1); + }); + + it('removes filter and action correctly', () => { + const { removeFilter, removeAction } = useAutomation(); + const mockAutomation = { + conditions: [{ id: 1 }, { id: 2 }], + actions: [{ id: 1 }, { id: 2 }], + }; + + removeFilter(mockAutomation, 0); + removeAction(mockAutomation, 0); + + expect(mockAutomation.conditions).toHaveLength(1); + expect(mockAutomation.actions).toHaveLength(1); + expect(mockAutomation.conditions[0].id).toBe(2); + expect(mockAutomation.actions[0].id).toBe(2); + }); + + it('resets filter and action correctly', () => { + const { resetFilter, resetAction } = useAutomation(); + const mockAutomation = { + event_name: 'message_created', + conditions: [ + { + attribute_key: 'status', + filter_operator: 'equal_to', + values: 'open', + }, + ], + actions: [{ action_name: 'assign_agent', action_params: [1] }], + }; + const mockAutomationTypes = { + message_created: { + conditions: [ + { key: 'status', filterOperators: [{ value: 'not_equal_to' }] }, + ], + }, + }; + + resetFilter( + mockAutomation, + mockAutomationTypes, + 0, + mockAutomation.conditions[0] + ); + resetAction(mockAutomation, 0); + + expect(mockAutomation.conditions[0].filter_operator).toBe('not_equal_to'); + expect(mockAutomation.conditions[0].values).toBe(''); + expect(mockAutomation.actions[0].action_params).toEqual([]); + }); + + it('formats automation correctly', () => { + const { formatAutomation } = useAutomation(); + const mockAutomation = { + conditions: [{ attribute_key: 'status', values: ['open'] }], + actions: [{ action_name: 'assign_agent', action_params: [1] }], + }; + const mockAutomationTypes = {}; + const mockAutomationActionTypes = [ + { key: 'assign_agent', inputType: 'search_select' }, + ]; + + automationHelper.getConditionOptions.mockReturnValue([ + { id: 'open', name: 'open' }, + ]); + automationHelper.getActionOptions.mockReturnValue([ + { id: 1, name: 'Agent 1' }, + ]); + + const result = formatAutomation( + mockAutomation, + customAttributes, + mockAutomationTypes, + mockAutomationActionTypes + ); + + expect(result.conditions[0].values).toEqual([{ id: 'open', name: 'open' }]); + expect(result.actions[0].action_params).toEqual([ + { id: 1, name: 'Agent 1' }, + ]); + }); + + it('manifests custom attributes correctly', () => { + const { manifestCustomAttributes } = useAutomation(); + const mockAutomationTypes = { + message_created: { conditions: [] }, + conversation_created: { conditions: [] }, + conversation_updated: { conditions: [] }, + conversation_opened: { conditions: [] }, + }; + + automationHelper.generateCustomAttributeTypes.mockReturnValue([]); + automationHelper.generateCustomAttributes.mockReturnValue([]); + + manifestCustomAttributes(mockAutomationTypes); + + expect(automationHelper.generateCustomAttributeTypes).toHaveBeenCalledTimes( + 2 + ); + expect(automationHelper.generateCustomAttributes).toHaveBeenCalledTimes(1); + Object.values(mockAutomationTypes).forEach(type => { + expect(type.conditions).toHaveLength(0); + }); + }); + + it('gets condition dropdown values correctly', () => { + const { getConditionDropdownValues } = useAutomation(); + + expect(getConditionDropdownValues('status')).toEqual(statusFilterOptions); + expect(getConditionDropdownValues('team_id')).toEqual(teams); + expect(getConditionDropdownValues('assignee_id')).toEqual(agents); + expect(getConditionDropdownValues('contact')).toEqual(contacts); + expect(getConditionDropdownValues('inbox_id')).toEqual(inboxes); + expect(getConditionDropdownValues('campaigns')).toEqual(campaigns); + expect(getConditionDropdownValues('browser_language')).toEqual(languages); + expect(getConditionDropdownValues('country_code')).toEqual(countries); + expect(getConditionDropdownValues('message_type')).toEqual( + MESSAGE_CONDITION_VALUES + ); + }); + + it('gets action dropdown values correctly', () => { + const { getActionDropdownValues } = useAutomation(); + + expect(getActionDropdownValues('add_label')).toEqual(labels); + expect(getActionDropdownValues('assign_team')).toEqual(teams); + expect(getActionDropdownValues('assign_agent')).toEqual(agents); + expect(getActionDropdownValues('send_email_to_team')).toEqual(teams); + expect(getActionDropdownValues('send_message')).toEqual([]); + expect(getActionDropdownValues('add_sla')).toEqual(slaPolicies); + }); + + it('handles event change correctly', () => { + const { onEventChange } = useAutomation(); + const mockAutomation = { + event_name: 'message_created', + conditions: [], + actions: [], + }; + + automationHelper.getDefaultConditions.mockReturnValue([{}]); + automationHelper.getDefaultActions.mockReturnValue([{}]); + + onEventChange(mockAutomation); + + expect(automationHelper.getDefaultConditions).toHaveBeenCalledWith( + 'message_created' + ); + expect(automationHelper.getDefaultActions).toHaveBeenCalled(); + expect(mockAutomation.conditions).toHaveLength(1); + expect(mockAutomation.actions).toHaveLength(1); + }); +}); diff --git a/app/javascript/dashboard/composables/spec/useMacros.spec.js b/app/javascript/dashboard/composables/spec/useMacros.spec.js index d268547a5..a72d2ddfe 100644 --- a/app/javascript/dashboard/composables/spec/useMacros.spec.js +++ b/app/javascript/dashboard/composables/spec/useMacros.spec.js @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { useMacros } from '../useMacros'; import { useStoreGetters } from 'dashboard/composables/store'; -import { PRIORITY_CONDITION_VALUES } from 'dashboard/helper/automationHelper.js'; +import { PRIORITY_CONDITION_VALUES } from 'dashboard/constants/automation'; vi.mock('dashboard/composables/store'); vi.mock('dashboard/helper/automationHelper.js'); diff --git a/app/javascript/dashboard/composables/useAutomation.js b/app/javascript/dashboard/composables/useAutomation.js new file mode 100644 index 000000000..986a47ec7 --- /dev/null +++ b/app/javascript/dashboard/composables/useAutomation.js @@ -0,0 +1,349 @@ +import { computed } from 'vue'; +import { useStoreGetters, useMapGetter } from 'dashboard/composables/store'; +import { useAlert } from 'dashboard/composables'; +import { useI18n } from './useI18n'; +import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages'; +import countries from 'shared/constants/countries'; +import { + generateCustomAttributeTypes, + getActionOptions, + getConditionOptions, + getCustomAttributeInputType, + getDefaultConditions, + getDefaultActions, + filterCustomAttributes, + getStandardAttributeInputType, + isCustomAttribute, + generateCustomAttributes, +} from 'dashboard/helper/automationHelper'; + +/** + * Composable for handling automation-related functionality. + * @returns {Object} An object containing various automation-related functions and computed properties. + */ +export function useAutomation() { + const getters = useStoreGetters(); + const { t } = useI18n(); + + const agents = useMapGetter('agents/getAgents'); + const campaigns = useMapGetter('campaigns/getAllCampaigns'); + const contacts = useMapGetter('contacts/getContacts'); + const inboxes = useMapGetter('inboxes/getInboxes'); + const labels = useMapGetter('labels/getLabels'); + const teams = useMapGetter('teams/getTeams'); + const slaPolicies = useMapGetter('sla/getSLA'); + + const booleanFilterOptions = computed(() => [ + { id: true, name: t('FILTER.ATTRIBUTE_LABELS.TRUE') }, + { id: false, name: t('FILTER.ATTRIBUTE_LABELS.FALSE') }, + ]); + + const statusFilterOptions = computed(() => { + const statusFilters = t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS'); + return [ + ...Object.keys(statusFilters).map(status => ({ + id: status, + name: statusFilters[status].TEXT, + })), + { id: 'all', name: t('CHAT_LIST.FILTER_ALL') }, + ]; + }); + + /** + * Handles the event change for an automation. + * @param {Object} automation - The automation object to update. + */ + const onEventChange = automation => { + automation.conditions = getDefaultConditions(automation.event_name); + automation.actions = getDefaultActions(); + }; + + /** + * Gets the condition dropdown values for a given type. + * @param {string} type - The type of condition. + * @returns {Array} An array of condition dropdown values. + */ + const getConditionDropdownValues = type => { + return getConditionOptions({ + agents: agents.value, + booleanFilterOptions: booleanFilterOptions.value, + campaigns: campaigns.value, + contacts: contacts.value, + customAttributes: getters['attributes/getAttributes'].value, + inboxes: inboxes.value, + statusFilterOptions: statusFilterOptions.value, + teams: teams.value, + languages, + countries, + type, + }); + }; + + /** + * Appends a new condition to the automation. + * @param {Object} automation - The automation object to update. + */ + const appendNewCondition = automation => { + automation.conditions.push(...getDefaultConditions(automation.event_name)); + }; + + /** + * Appends a new action to the automation. + * @param {Object} automation - The automation object to update. + */ + const appendNewAction = automation => { + automation.actions.push(...getDefaultActions()); + }; + + /** + * Removes a filter from the automation. + * @param {Object} automation - The automation object to update. + * @param {number} index - The index of the filter to remove. + */ + const removeFilter = (automation, index) => { + if (automation.conditions.length <= 1) { + useAlert(t('AUTOMATION.CONDITION.DELETE_MESSAGE')); + } else { + automation.conditions.splice(index, 1); + } + }; + + /** + * Removes an action from the automation. + * @param {Object} automation - The automation object to update. + * @param {number} index - The index of the action to remove. + */ + const removeAction = (automation, index) => { + if (automation.actions.length <= 1) { + useAlert(t('AUTOMATION.ACTION.DELETE_MESSAGE')); + } else { + automation.actions.splice(index, 1); + } + }; + + /** + * Resets a filter in the automation. + * @param {Object} automation - The automation object to update. + * @param {Object} automationTypes - The automation types object. + * @param {number} index - The index of the filter to reset. + * @param {Object} currentCondition - The current condition object. + */ + const resetFilter = ( + automation, + automationTypes, + index, + currentCondition + ) => { + automation.conditions[index].filter_operator = automationTypes[ + automation.event_name + ].conditions.find( + condition => condition.key === currentCondition.attribute_key + ).filterOperators[0].value; + automation.conditions[index].values = ''; + }; + + /** + * Resets an action in the automation. + * @param {Object} automation - The automation object to update. + * @param {number} index - The index of the action to reset. + */ + const resetAction = (automation, index) => { + automation.actions[index].action_params = []; + }; + + /** + * This function sets the conditions for automation. + * It help to format the conditions for the automation when we open the edit automation modal. + * @param {Object} automation - The automation object containing conditions to manifest. + * @param {Array} allCustomAttributes - List of all custom attributes. + * @param {Object} automationTypes - Object containing automation type definitions. + * @returns {Array} An array of manifested conditions. + */ + const manifestConditions = ( + automation, + allCustomAttributes, + automationTypes + ) => { + const customAttributes = filterCustomAttributes(allCustomAttributes); + return automation.conditions.map(condition => { + const customAttr = isCustomAttribute( + customAttributes, + condition.attribute_key + ); + let inputType = 'plain_text'; + if (customAttr) { + inputType = getCustomAttributeInputType(customAttr.type); + } else { + inputType = getStandardAttributeInputType( + automationTypes, + automation.event_name, + condition.attribute_key + ); + } + if (inputType === 'plain_text' || inputType === 'date') { + return { ...condition, values: condition.values[0] }; + } + if (inputType === 'comma_separated_plain_text') { + return { ...condition, values: condition.values.join(',') }; + } + return { + ...condition, + query_operator: condition.query_operator || 'and', + values: [...getConditionDropdownValues(condition.attribute_key)].filter( + item => [...condition.values].includes(item.id) + ), + }; + }); + }; + + /** + * Gets the action dropdown values for a given type. + * @param {string} type - The type of action. + * @returns {Array} An array of action dropdown values. + */ + const getActionDropdownValues = type => { + return getActionOptions({ + agents: agents.value, + labels: labels.value, + teams: teams.value, + slaPolicies: slaPolicies.value, + languages, + type, + }); + }; + + /** + * Generates an array of actions for the automation. + * @param {Object} action - The action object. + * @param {Array} automationActionTypes - List of available automation action types. + * @returns {Array|Object} Generated actions array or object based on input type. + */ + const generateActionsArray = (action, automationActionTypes) => { + const params = action.action_params; + const inputType = automationActionTypes.find( + item => item.key === action.action_name + ).inputType; + if (inputType === 'multi_select' || inputType === 'search_select') { + return [...getActionDropdownValues(action.action_name)].filter(item => + [...params].includes(item.id) + ); + } + if (inputType === 'team_message') { + return { + team_ids: [...getActionDropdownValues(action.action_name)].filter( + item => [...params[0].team_ids].includes(item.id) + ), + message: params[0].message, + }; + } + return [...params]; + }; + + /** + * This function sets the actions for automation. + * It help to format the actions for the automation when we open the edit automation modal. + * @param {Object} automation - The automation object containing actions. + * @param {Array} automationActionTypes - List of available automation action types. + * @returns {Array} An array of manifested actions. + */ + const manifestActions = (automation, automationActionTypes) => { + return automation.actions.map(action => ({ + ...action, + action_params: action.action_params.length + ? generateActionsArray(action, automationActionTypes) + : [], + })); + }; + + /** + * Formats the automation object for use when we edit the automation. + * It help to format the conditions and actions for the automation when we open the edit automation modal. + * @param {Object} automation - The automation object to format. + * @param {Array} allCustomAttributes - List of all custom attributes. + * @param {Object} automationTypes - Object containing automation type definitions. + * @param {Array} automationActionTypes - List of available automation action types. + * @returns {Object} A new object with formatted automation data, including automation conditions and actions. + */ + const formatAutomation = ( + automation, + allCustomAttributes, + automationTypes, + automationActionTypes + ) => { + return { + ...automation, + conditions: manifestConditions( + automation, + allCustomAttributes, + automationTypes + ), + actions: manifestActions(automation, automationActionTypes), + }; + }; + + /** + * This function formats the custom attributes for automation types. + * It retrieves custom attributes for conversations and contacts, + * generates custom attribute types, and adds them to the relevant automation types. + * @param {Object} automationTypes - The automation types object to update with custom attributes. + */ + const manifestCustomAttributes = automationTypes => { + const conversationCustomAttributesRaw = getters[ + 'attributes/getAttributesByModel' + ].value('conversation_attribute'); + const contactCustomAttributesRaw = + getters['attributes/getAttributesByModel'].value('contact_attribute'); + + const conversationCustomAttributeTypes = generateCustomAttributeTypes( + conversationCustomAttributesRaw, + 'conversation_attribute' + ); + const contactCustomAttributeTypes = generateCustomAttributeTypes( + contactCustomAttributesRaw, + 'contact_attribute' + ); + + const manifestedCustomAttributes = generateCustomAttributes( + conversationCustomAttributeTypes, + contactCustomAttributeTypes, + t('AUTOMATION.CONDITION.CONVERSATION_CUSTOM_ATTR_LABEL'), + t('AUTOMATION.CONDITION.CONTACT_CUSTOM_ATTR_LABEL') + ); + + automationTypes.message_created.conditions.push( + ...manifestedCustomAttributes + ); + automationTypes.conversation_created.conditions.push( + ...manifestedCustomAttributes + ); + automationTypes.conversation_updated.conditions.push( + ...manifestedCustomAttributes + ); + automationTypes.conversation_opened.conditions.push( + ...manifestedCustomAttributes + ); + }; + + return { + agents, + campaigns, + contacts, + inboxes, + labels, + teams, + slaPolicies, + booleanFilterOptions, + statusFilterOptions, + onEventChange, + getConditionDropdownValues, + appendNewCondition, + appendNewAction, + removeFilter, + removeAction, + resetFilter, + resetAction, + formatAutomation, + getActionDropdownValues, + manifestCustomAttributes, + }; +} diff --git a/app/javascript/dashboard/composables/useMacros.js b/app/javascript/dashboard/composables/useMacros.js index 6967331ea..0489e2d1f 100644 --- a/app/javascript/dashboard/composables/useMacros.js +++ b/app/javascript/dashboard/composables/useMacros.js @@ -1,6 +1,6 @@ import { computed } from 'vue'; import { useStoreGetters } from 'dashboard/composables/store'; -import { PRIORITY_CONDITION_VALUES } from 'dashboard/helper/automationHelper.js'; +import { PRIORITY_CONDITION_VALUES } from 'dashboard/constants/automation'; /** * Composable for handling macro-related functionality diff --git a/app/javascript/dashboard/constants/automation.js b/app/javascript/dashboard/constants/automation.js new file mode 100644 index 000000000..3aa9754d5 --- /dev/null +++ b/app/javascript/dashboard/constants/automation.js @@ -0,0 +1,70 @@ +export const DEFAULT_MESSAGE_CREATED_CONDITION = [ + { + attribute_key: 'message_type', + filter_operator: 'equal_to', + values: '', + query_operator: 'and', + custom_attribute_type: '', + }, +]; + +export const DEFAULT_CONVERSATION_OPENED_CONDITION = [ + { + attribute_key: 'browser_language', + filter_operator: 'equal_to', + values: '', + query_operator: 'and', + custom_attribute_type: '', + }, +]; + +export const DEFAULT_OTHER_CONDITION = [ + { + attribute_key: 'status', + filter_operator: 'equal_to', + values: '', + query_operator: 'and', + custom_attribute_type: '', + }, +]; + +export const DEFAULT_ACTIONS = [ + { + action_name: 'assign_agent', + action_params: [], + }, +]; + +export const MESSAGE_CONDITION_VALUES = [ + { + id: 'incoming', + name: 'Incoming Message', + }, + { + id: 'outgoing', + name: 'Outgoing Message', + }, +]; + +export const PRIORITY_CONDITION_VALUES = [ + { + id: 'nil', + name: 'None', + }, + { + id: 'low', + name: 'Low', + }, + { + id: 'medium', + name: 'Medium', + }, + { + id: 'high', + name: 'High', + }, + { + id: 'urgent', + name: 'Urgent', + }, +]; diff --git a/app/javascript/dashboard/helper/automationHelper.js b/app/javascript/dashboard/helper/automationHelper.js index 33e920edc..6ff941822 100644 --- a/app/javascript/dashboard/helper/automationHelper.js +++ b/app/javascript/dashboard/helper/automationHelper.js @@ -3,41 +3,16 @@ import { OPERATOR_TYPES_3, OPERATOR_TYPES_4, } from 'dashboard/routes/dashboard/settings/automation/operators'; +import { + DEFAULT_MESSAGE_CREATED_CONDITION, + DEFAULT_CONVERSATION_OPENED_CONDITION, + DEFAULT_OTHER_CONDITION, + DEFAULT_ACTIONS, + MESSAGE_CONDITION_VALUES, + PRIORITY_CONDITION_VALUES, +} from 'dashboard/constants/automation'; import filterQueryGenerator from './filterQueryGenerator'; import actionQueryGenerator from './actionQueryGenerator'; -const MESSAGE_CONDITION_VALUES = [ - { - id: 'incoming', - name: 'Incoming Message', - }, - { - id: 'outgoing', - name: 'Outgoing Message', - }, -]; - -export const PRIORITY_CONDITION_VALUES = [ - { - id: 'nil', - name: 'None', - }, - { - id: 'low', - name: 'Low', - }, - { - id: 'medium', - name: 'Medium', - }, - { - id: 'high', - name: 'High', - }, - { - id: 'urgent', - name: 'Urgent', - }, -]; export const getCustomAttributeInputType = key => { const customAttributeMap = { @@ -198,45 +173,16 @@ export const getFileName = (action, files = []) => { 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 DEFAULT_MESSAGE_CREATED_CONDITION; } if (eventName === 'conversation_opened') { - return [ - { - attribute_key: 'browser_language', - filter_operator: 'equal_to', - values: '', - query_operator: 'and', - custom_attribute_type: '', - }, - ]; + return DEFAULT_CONVERSATION_OPENED_CONDITION; } - return [ - { - attribute_key: 'status', - filter_operator: 'equal_to', - values: '', - query_operator: 'and', - custom_attribute_type: '', - }, - ]; + return DEFAULT_OTHER_CONDITION; }; export const getDefaultActions = () => { - return [ - { - action_name: 'assign_agent', - action_params: [], - }, - ]; + return DEFAULT_ACTIONS; }; export const filterCustomAttributes = customAttributes => { @@ -297,3 +243,100 @@ export const generateCustomAttributes = ( } return customAttributes; }; + +/** + * Get attributes for a given key from automation types. + * @param {Object} automationTypes - Object containing automation types. + * @param {string} key - The key to get attributes for. + * @returns {Array} Array of condition objects for the given key. + */ +export const getAttributes = (automationTypes, key) => { + return automationTypes[key].conditions; +}; + +/** + * Get the automation type for a given key. + * @param {Object} automationTypes - Object containing automation types. + * @param {Object} automation - The automation object. + * @param {string} key - The key to get the automation type for. + * @returns {Object} The automation type object. + */ +export const getAutomationType = (automationTypes, automation, key) => { + return automationTypes[automation.event_name].conditions.find( + condition => condition.key === key + ); +}; + +/** + * Get the input type for a given key. + * @param {Array} allCustomAttributes - Array of all custom attributes. + * @param {Object} automationTypes - Object containing automation types. + * @param {Object} automation - The automation object. + * @param {string} key - The key to get the input type for. + * @returns {string} The input type. + */ +export const getInputType = ( + allCustomAttributes, + automationTypes, + automation, + key +) => { + const customAttribute = isACustomAttribute(allCustomAttributes, key); + if (customAttribute) { + return getCustomAttributeInputType(customAttribute.attribute_display_type); + } + const type = getAutomationType(automationTypes, automation, key); + return type.inputType; +}; + +/** + * Get operators for a given key. + * @param {Array} allCustomAttributes - Array of all custom attributes. + * @param {Object} automationTypes - Object containing automation types. + * @param {Object} automation - The automation object. + * @param {string} mode - The mode ('edit' or other). + * @param {string} key - The key to get operators for. + * @returns {Array} Array of operators. + */ +export const getOperators = ( + allCustomAttributes, + automationTypes, + automation, + mode, + key +) => { + if (mode === 'edit') { + const customAttribute = isACustomAttribute(allCustomAttributes, key); + if (customAttribute) { + return getOperatorTypes(customAttribute.attribute_display_type); + } + } + const type = getAutomationType(automationTypes, automation, key); + return type.filterOperators; +}; + +/** + * Get the custom attribute type for a given key. + * @param {Object} automationTypes - Object containing automation types. + * @param {Object} automation - The automation object. + * @param {string} key - The key to get the custom attribute type for. + * @returns {string} The custom attribute type. + */ +export const getCustomAttributeType = (automationTypes, automation, key) => { + return automationTypes[automation.event_name].conditions.find( + i => i.key === key + ).customAttributeType; +}; + +/** + * Determine if an action input should be shown. + * @param {Array} automationActionTypes - Array of automation action type objects. + * @param {string} action - The action to check. + * @returns {boolean} True if the action input should be shown, false otherwise. + */ +export const showActionInput = (automationActionTypes, action) => { + if (action === 'send_email_to_team' || action === 'send_message') + return false; + const type = automationActionTypes.find(i => i.key === action).inputType; + return !!type; +}; diff --git a/app/javascript/shared/mixins/specs/automationHelper.spec.js b/app/javascript/dashboard/helper/specs/automationHelper.spec.js similarity index 53% rename from app/javascript/shared/mixins/specs/automationHelper.spec.js rename to app/javascript/dashboard/helper/specs/automationHelper.spec.js index 481613323..b55da1bad 100644 --- a/app/javascript/shared/mixins/specs/automationHelper.spec.js +++ b/app/javascript/dashboard/helper/specs/automationHelper.spec.js @@ -11,11 +11,11 @@ import { contactAttrs, conversationAttrs, expectedOutputForCustomAttributeGenerator, -} from './automationFixtures'; +} from './fixtures/automationFixtures'; import { AUTOMATIONS } from 'dashboard/routes/dashboard/settings/automation/constants'; -describe('automationMethodsMixin', () => { - it('getCustomAttributeInputType returns the attribute input type', () => { +describe('getCustomAttributeInputType', () => { + it('returns the attribute input type', () => { expect(helpers.getCustomAttributeInputType('date')).toEqual('date'); expect(helpers.getCustomAttributeInputType('date')).not.toEqual( 'some_random_value' @@ -31,33 +31,32 @@ describe('automationMethodsMixin', () => { 'plain_text' ); }); - it('isACustomAttribute returns the custom attribute value if true', () => { +}); + +describe('isACustomAttribute', () => { + it('returns the custom attribute value if true', () => { expect( helpers.isACustomAttribute(customAttributes, 'signed_up_at') ).toBeTruthy(); expect(helpers.isACustomAttribute(customAttributes, 'status')).toBeFalsy(); }); - it('getCustomAttributeListDropdownValues returns the attribute dropdown values', () => { +}); + +describe('getCustomAttributeListDropdownValues', () => { + it('returns the attribute dropdown values', () => { const myListValues = [ - { - id: 'item1', - name: 'item1', - }, - { - id: 'item2', - name: 'item2', - }, - { - id: 'item3', - name: 'item3', - }, + { id: 'item1', name: 'item1' }, + { id: 'item2', name: 'item2' }, + { id: 'item3', name: 'item3' }, ]; expect( helpers.getCustomAttributeListDropdownValues(customAttributes, 'my_list') ).toEqual(myListValues); }); +}); - it('isCustomAttributeCheckbox checks if attribute is a checkbox', () => { +describe('isCustomAttributeCheckbox', () => { + it('checks if attribute is a checkbox', () => { expect( helpers.isCustomAttributeCheckbox(customAttributes, 'prime_user') .attribute_display_type @@ -70,13 +69,19 @@ describe('automationMethodsMixin', () => { helpers.isCustomAttributeCheckbox(customAttributes, 'my_list') ).not.toEqual('checkbox'); }); - it('isCustomAttributeList checks if attribute is a list', () => { +}); + +describe('isCustomAttributeList', () => { + it('checks if attribute is a list', () => { expect( helpers.isCustomAttributeList(customAttributes, 'my_list') .attribute_display_type ).toEqual('list'); }); - it('getOperatorTypes returns the correct custom attribute operators', () => { +}); + +describe('getOperatorTypes', () => { + it('returns the correct custom attribute operators', () => { expect(helpers.getOperatorTypes('list')).toEqual(OPERATOR_TYPES_1); expect(helpers.getOperatorTypes('text')).toEqual(OPERATOR_TYPES_3); expect(helpers.getOperatorTypes('number')).toEqual(OPERATOR_TYPES_1); @@ -85,93 +90,44 @@ describe('automationMethodsMixin', () => { expect(helpers.getOperatorTypes('checkbox')).toEqual(OPERATOR_TYPES_1); expect(helpers.getOperatorTypes('some_random')).toEqual(OPERATOR_TYPES_1); }); - it('generateConditionOptions returns expected conditions options array', () => { - const testConditions = [ - { - id: 123, - title: 'Fayaz', - email: 'test@test.com', - }, - { - title: 'John', - id: 324, - email: 'test@john.com', - }, - ]; +}); +describe('generateConditionOptions', () => { + it('returns expected conditions options array', () => { + const testConditions = [ + { id: 123, title: 'Fayaz', email: 'test@test.com' }, + { title: 'John', id: 324, email: 'test@john.com' }, + ]; const expectedConditions = [ - { - id: 123, - name: 'Fayaz', - }, - { - id: 324, - name: 'John', - }, + { id: 123, name: 'Fayaz' }, + { id: 324, name: 'John' }, ]; expect(helpers.generateConditionOptions(testConditions)).toEqual( expectedConditions ); }); - it('getActionOptions returns expected actions options array', () => { +}); + +describe('getActionOptions', () => { + it('returns expected actions options array', () => { const expectedOptions = [ - { - id: 'testlabel', - name: 'testlabel', - }, - { - id: 'snoozes', - name: 'snoozes', - }, + { id: 'testlabel', name: 'testlabel' }, + { id: 'snoozes', name: 'snoozes' }, ]; expect(helpers.getActionOptions({ labels, type: 'add_label' })).toEqual( expectedOptions ); }); - it('getConditionOptions returns expected conditions options', () => { +}); + +describe('getConditionOptions', () => { + it('returns expected conditions options', () => { const testOptions = [ - { - id: 'open', - name: 'Open', - }, - { - id: 'resolved', - name: 'Resolved', - }, - { - id: 'pending', - name: 'Pending', - }, - { - id: 'snoozed', - name: 'Snoozed', - }, - { - id: 'all', - name: 'All', - }, - ]; - const expectedOptions = [ - { - id: 'open', - name: 'Open', - }, - { - id: 'resolved', - name: 'Resolved', - }, - { - id: 'pending', - name: 'Pending', - }, - { - id: 'snoozed', - name: 'Snoozed', - }, - { - id: 'all', - name: 'All', - }, + { id: 'open', name: 'Open' }, + { id: 'resolved', name: 'Resolved' }, + { id: 'pending', name: 'Pending' }, + { id: 'snoozed', name: 'Snoozed' }, + { id: 'all', name: 'All' }, ]; expect( helpers.getConditionOptions({ @@ -180,14 +136,20 @@ describe('automationMethodsMixin', () => { statusFilterOptions: testOptions, type: 'status', }) - ).toEqual(expectedOptions); + ).toEqual(testOptions); }); - it('getFileName returns the correct file name', () => { +}); + +describe('getFileName', () => { + it('returns the correct file name', () => { expect( helpers.getFileName(automation.actions[0], automation.files) ).toEqual('pfp.jpeg'); }); - it('getDefaultConditions returns the resp default condition model', () => { +}); + +describe('getDefaultConditions', () => { + it('returns the resp default condition model', () => { const messageCreatedModel = [ { attribute_key: 'message_type', @@ -211,7 +173,10 @@ describe('automationMethodsMixin', () => { ); expect(helpers.getDefaultConditions()).toEqual(genericConditionModel); }); - it('getDefaultActions returns the resp default action model', () => { +}); + +describe('getDefaultActions', () => { + it('returns the resp default action model', () => { const genericActionModel = [ { action_name: 'assign_agent', @@ -220,7 +185,10 @@ describe('automationMethodsMixin', () => { ]; expect(helpers.getDefaultActions()).toEqual(genericActionModel); }); - it('filterCustomAttributes filters the raw custom attributes', () => { +}); + +describe('filterCustomAttributes', () => { + it('filters the raw custom attributes', () => { const filteredAttributes = [ { key: 'signed_up_at', name: 'Signed Up At', type: 'date' }, { key: 'prime_user', name: 'Prime User', type: 'checkbox' }, @@ -235,7 +203,10 @@ describe('automationMethodsMixin', () => { filteredAttributes ); }); - it('getStandardAttributeInputType returns the resp default action model', () => { +}); + +describe('getStandardAttributeInputType', () => { + it('returns the resp default action model', () => { expect( helpers.getStandardAttributeInputType( AUTOMATIONS, @@ -258,7 +229,10 @@ describe('automationMethodsMixin', () => { ) ).toEqual('plain_text'); }); - it('generateAutomationPayload returns the resp default action model', () => { +}); + +describe('generateAutomationPayload', () => { + it('returns the resp default action model', () => { const testPayload = { name: 'Test', description: 'This is a test', @@ -300,7 +274,10 @@ describe('automationMethodsMixin', () => { expectedPayload ); }); - it('isCustomAttribute returns the resp default action model', () => { +}); + +describe('isCustomAttribute', () => { + it('returns the resp default action model', () => { const attrs = helpers.filterCustomAttributes(customAttributes); expect(helpers.isCustomAttribute(attrs, 'my_list')).toBeTruthy(); expect(helpers.isCustomAttribute(attrs, 'my_check')).toBeTruthy(); @@ -309,8 +286,10 @@ describe('automationMethodsMixin', () => { expect(helpers.isCustomAttribute(attrs, 'prime_user')).toBeTruthy(); expect(helpers.isCustomAttribute(attrs, 'hello')).toBeFalsy(); }); +}); - it('generateCustomAttributes generates and returns correct condition attribute', () => { +describe('generateCustomAttributes', () => { + it('generates and returns correct condition attribute', () => { expect( helpers.generateCustomAttributes( conversationAttrs, @@ -321,3 +300,116 @@ describe('automationMethodsMixin', () => { ).toEqual(expectedOutputForCustomAttributeGenerator); }); }); + +describe('getAttributes', () => { + it('returns the conditions for the given automation type', () => { + const result = helpers.getAttributes(AUTOMATIONS, 'message_created'); + expect(result).toEqual(AUTOMATIONS.message_created.conditions); + }); +}); + +describe('getAttributes', () => { + it('returns the conditions for the given automation type', () => { + const result = helpers.getAttributes(AUTOMATIONS, 'message_created'); + expect(result).toEqual(AUTOMATIONS.message_created.conditions); + }); +}); + +describe('getAutomationType', () => { + it('returns the automation type for the given key', () => { + const mockAutomation = { event_name: 'message_created' }; + const result = helpers.getAutomationType( + AUTOMATIONS, + mockAutomation, + 'message_type' + ); + expect(result).toEqual( + AUTOMATIONS.message_created.conditions.find(c => c.key === 'message_type') + ); + }); +}); + +describe('getInputType', () => { + it('returns the input type for a custom attribute', () => { + const mockAutomation = { event_name: 'message_created' }; + const result = helpers.getInputType( + customAttributes, + AUTOMATIONS, + mockAutomation, + 'signed_up_at' + ); + expect(result).toEqual('date'); + }); + + it('returns the input type for a standard attribute', () => { + const mockAutomation = { event_name: 'message_created' }; + const result = helpers.getInputType( + customAttributes, + AUTOMATIONS, + mockAutomation, + 'message_type' + ); + expect(result).toEqual('search_select'); + }); +}); + +describe('getOperators', () => { + it('returns operators for a custom attribute in edit mode', () => { + const mockAutomation = { event_name: 'message_created' }; + const result = helpers.getOperators( + customAttributes, + AUTOMATIONS, + mockAutomation, + 'edit', + 'signed_up_at' + ); + expect(result).toEqual(OPERATOR_TYPES_4); + }); + + it('returns operators for a standard attribute', () => { + const mockAutomation = { event_name: 'message_created' }; + const result = helpers.getOperators( + customAttributes, + AUTOMATIONS, + mockAutomation, + 'create', + 'message_type' + ); + expect(result).toEqual( + AUTOMATIONS.message_created.conditions.find(c => c.key === 'message_type') + .filterOperators + ); + }); +}); + +describe('getCustomAttributeType', () => { + it('returns the custom attribute type for the given key', () => { + const mockAutomation = { event_name: 'message_created' }; + const result = helpers.getCustomAttributeType( + AUTOMATIONS, + mockAutomation, + 'message_type' + ); + expect(result).toEqual( + AUTOMATIONS.message_created.conditions.find(c => c.key === 'message_type') + .customAttributeType + ); + }); +}); + +describe('showActionInput', () => { + it('returns false for send_email_to_team and send_message actions', () => { + expect(helpers.showActionInput([], 'send_email_to_team')).toBe(false); + expect(helpers.showActionInput([], 'send_message')).toBe(false); + }); + + it('returns true if the action has an input type', () => { + const mockActionTypes = [{ key: 'add_label', inputType: 'select' }]; + expect(helpers.showActionInput(mockActionTypes, 'add_label')).toBe(true); + }); + + it('returns false if the action does not have an input type', () => { + const mockActionTypes = [{ key: 'some_action', inputType: null }]; + expect(helpers.showActionInput(mockActionTypes, 'some_action')).toBe(false); + }); +}); diff --git a/app/javascript/shared/mixins/specs/automationFixtures.js b/app/javascript/dashboard/helper/specs/fixtures/automationFixtures.js similarity index 99% rename from app/javascript/shared/mixins/specs/automationFixtures.js rename to app/javascript/dashboard/helper/specs/fixtures/automationFixtures.js index 1647f9004..1f642587b 100644 --- a/app/javascript/shared/mixins/specs/automationFixtures.js +++ b/app/javascript/dashboard/helper/specs/fixtures/automationFixtures.js @@ -1,6 +1,6 @@ -import allLanguages from '../../../dashboard/components/widgets/conversation/advancedFilterItems/languages.js'; +import allLanguages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages'; -import allCountries from '../../../shared/constants/countries.js'; +import allCountries from 'shared/constants/countries.js'; export const customAttributes = [ { diff --git a/app/javascript/dashboard/mixins/automations/methodsMixin.js b/app/javascript/dashboard/mixins/automations/methodsMixin.js deleted file mode 100644 index 1e9863283..000000000 --- a/app/javascript/dashboard/mixins/automations/methodsMixin.js +++ /dev/null @@ -1,314 +0,0 @@ -import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages'; -import countries from 'shared/constants/countries'; -import { validateAutomation } from 'dashboard/helper/validations'; - -import { - generateCustomAttributeTypes, - getActionOptions, - getConditionOptions, - getCustomAttributeInputType, - getOperatorTypes, - isACustomAttribute, - getFileName, - getDefaultConditions, - getDefaultActions, - filterCustomAttributes, - generateAutomationPayload, - getStandardAttributeInputType, - isCustomAttribute, - generateCustomAttributes, -} from 'dashboard/helper/automationHelper'; -import { mapGetters } from 'vuex'; -import { useAlert } from 'dashboard/composables'; - -export default { - computed: { - ...mapGetters({ - agents: 'agents/getAgents', - campaigns: 'campaigns/getAllCampaigns', - contacts: 'contacts/getContacts', - inboxes: 'inboxes/getInboxes', - labels: 'labels/getLabels', - teams: 'teams/getTeams', - slaPolicies: 'sla/getSLA', - }), - 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) { - useAlert(this.$t('AUTOMATION.CONDITION.DELETE_MESSAGE')); - } else { - this.automation.conditions.splice(index, 1); - } - }, - removeAction(index) { - if (this.automation.actions.length <= 1) { - useAlert(this.$t('AUTOMATION.ACTION.DELETE_MESSAGE')); - } else { - this.automation.actions.splice(index, 1); - } - }, - submitAutomation() { - // we assign it to this.errors so that it can be accessed in the template - // it is supposed to be declared in the data function - this.errors = validateAutomation(this.automation); - if (Object.keys(this.errors).length === 0) { - 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], - }; - } - if (inputType === 'comma_separated_plain_text') { - return { - ...condition, - values: condition.values.join(','), - }; - } - 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 { agents, labels, teams, slaPolicies } = this; - return getActionOptions({ - agents, - labels, - teams, - slaPolicies, - languages, - 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 - ); - this.automationTypes.conversation_opened.conditions.push( - ...manifestedCustomAttributes - ); - }, - }, -}; diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue b/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue index 8cb143a7e..63c352dbb 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue @@ -1,9 +1,17 @@ @@ -121,7 +166,7 @@ export default { +