mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-30 18:47:51 +00:00 
			
		
		
		
	feat: Rewrite automations/methodsMixin to a composable (#9956)
				
					
				
			Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
This commit is contained in:
		
							
								
								
									
										295
									
								
								app/javascript/dashboard/composables/spec/useAutomation.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										295
									
								
								app/javascript/dashboard/composables/spec/useAutomation.spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
|   }); | ||||
| }); | ||||
| @@ -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'); | ||||
|   | ||||
							
								
								
									
										349
									
								
								app/javascript/dashboard/composables/useAutomation.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										349
									
								
								app/javascript/dashboard/composables/useAutomation.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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, | ||||
|   }; | ||||
| } | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										70
									
								
								app/javascript/dashboard/constants/automation.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								app/javascript/dashboard/constants/automation.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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', | ||||
|   }, | ||||
| ]; | ||||
| @@ -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; | ||||
| }; | ||||
|   | ||||
| @@ -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); | ||||
|   }); | ||||
| }); | ||||
| @@ -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 = [ | ||||
|   { | ||||
| @@ -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 | ||||
|       ); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| @@ -1,9 +1,17 @@ | ||||
| <script> | ||||
| import { mapGetters } from 'vuex'; | ||||
| import automationMethodsMixin from 'dashboard/mixins/automations/methodsMixin'; | ||||
| import FilterInputBox from 'dashboard/components/widgets/FilterInput/Index.vue'; | ||||
| import AutomationActionInput from 'dashboard/components/widgets/AutomationActionInput.vue'; | ||||
|  | ||||
| import { useAutomation } from 'dashboard/composables/useAutomation'; | ||||
| import { validateAutomation } from 'dashboard/helper/validations'; | ||||
| import { | ||||
|   generateAutomationPayload, | ||||
|   getAttributes, | ||||
|   getInputType, | ||||
|   getOperators, | ||||
|   getCustomAttributeType, | ||||
|   showActionInput, | ||||
| } from 'dashboard/helper/automationHelper'; | ||||
| import { | ||||
|   AUTOMATION_RULE_EVENTS, | ||||
|   AUTOMATION_ACTION_TYPES, | ||||
| @@ -14,13 +22,38 @@ export default { | ||||
|     FilterInputBox, | ||||
|     AutomationActionInput, | ||||
|   }, | ||||
|   mixins: [automationMethodsMixin], | ||||
|   props: { | ||||
|     onClose: { | ||||
|       type: Function, | ||||
|       default: () => {}, | ||||
|     }, | ||||
|   }, | ||||
|   setup() { | ||||
|     const { | ||||
|       onEventChange, | ||||
|       getConditionDropdownValues, | ||||
|       appendNewCondition, | ||||
|       appendNewAction, | ||||
|       removeFilter, | ||||
|       removeAction, | ||||
|       resetFilter, | ||||
|       resetAction, | ||||
|       getActionDropdownValues, | ||||
|       manifestCustomAttributes, | ||||
|     } = useAutomation(); | ||||
|     return { | ||||
|       onEventChange, | ||||
|       getConditionDropdownValues, | ||||
|       appendNewCondition, | ||||
|       appendNewAction, | ||||
|       removeFilter, | ||||
|       removeAction, | ||||
|       resetFilter, | ||||
|       resetAction, | ||||
|       getActionDropdownValues, | ||||
|       manifestCustomAttributes, | ||||
|     }; | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       automationTypes: JSON.parse(JSON.stringify(AUTOMATIONS)), | ||||
| @@ -82,12 +115,24 @@ export default { | ||||
|     this.$store.dispatch('labels/get'); | ||||
|     this.$store.dispatch('campaigns/get'); | ||||
|     this.allCustomAttributes = this.$store.getters['attributes/getAttributes']; | ||||
|     this.manifestCustomAttributes(); | ||||
|     this.manifestCustomAttributes(this.automationTypes); | ||||
|   }, | ||||
|   methods: { | ||||
|     getAttributes, | ||||
|     getInputType, | ||||
|     getOperators, | ||||
|     getCustomAttributeType, | ||||
|     showActionInput, | ||||
|     isFeatureEnabled(flag) { | ||||
|       return this.isFeatureEnabledonAccount(this.accountId, flag); | ||||
|     }, | ||||
|     emitSaveAutomation() { | ||||
|       this.errors = validateAutomation(this.automation); | ||||
|       if (Object.keys(this.errors).length === 0) { | ||||
|         const automation = generateAutomationPayload(this.automation); | ||||
|         this.$emit('saveAutomation', automation, this.mode); | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| @@ -121,7 +166,7 @@ export default { | ||||
|             <select | ||||
|               v-model="automation.event_name" | ||||
|               class="m-0" | ||||
|               @change="onEventChange()" | ||||
|               @change="onEventChange(automation)" | ||||
|             > | ||||
|               <option | ||||
|                 v-for="event in automationRuleEvents" | ||||
| @@ -154,9 +199,26 @@ export default { | ||||
|               v-for="(condition, i) in automation.conditions" | ||||
|               :key="i" | ||||
|               v-model="automation.conditions[i]" | ||||
|               :filter-attributes="getAttributes(automation.event_name)" | ||||
|               :input-type="getInputType(automation.conditions[i].attribute_key)" | ||||
|               :operators="getOperators(automation.conditions[i].attribute_key)" | ||||
|               :filter-attributes=" | ||||
|                 getAttributes(automationTypes, automation.event_name) | ||||
|               " | ||||
|               :input-type=" | ||||
|                 getInputType( | ||||
|                   allCustomAttributes, | ||||
|                   automationTypes, | ||||
|                   automation, | ||||
|                   automation.conditions[i].attribute_key | ||||
|                 ) | ||||
|               " | ||||
|               :operators=" | ||||
|                 getOperators( | ||||
|                   allCustomAttributes, | ||||
|                   automationTypes, | ||||
|                   automation, | ||||
|                   mode, | ||||
|                   automation.conditions[i].attribute_key | ||||
|                 ) | ||||
|               " | ||||
|               :dropdown-values=" | ||||
|                 getConditionDropdownValues( | ||||
|                   automation.conditions[i].attribute_key | ||||
| @@ -164,15 +226,26 @@ export default { | ||||
|               " | ||||
|               :show-query-operator="i !== automation.conditions.length - 1" | ||||
|               :custom-attribute-type=" | ||||
|                 getCustomAttributeType(automation.conditions[i].attribute_key) | ||||
|                 getCustomAttributeType( | ||||
|                   automationTypes, | ||||
|                   automation, | ||||
|                   automation.conditions[i].attribute_key | ||||
|                 ) | ||||
|               " | ||||
|               :error-message=" | ||||
|                 errors[`condition_${i}`] | ||||
|                   ? $t(`AUTOMATION.ERRORS.${errors[`condition_${i}`]}`) | ||||
|                   : '' | ||||
|               " | ||||
|               @resetFilter="resetFilter(i, automation.conditions[i])" | ||||
|               @removeFilter="removeFilter(i)" | ||||
|               @resetFilter=" | ||||
|                 resetFilter( | ||||
|                   automation, | ||||
|                   automationTypes, | ||||
|                   i, | ||||
|                   automation.conditions[i] | ||||
|                 ) | ||||
|               " | ||||
|               @removeFilter="removeFilter(automation, i)" | ||||
|             /> | ||||
|             <div class="mt-4"> | ||||
|               <woot-button | ||||
| @@ -180,7 +253,7 @@ export default { | ||||
|                 color-scheme="success" | ||||
|                 variant="smooth" | ||||
|                 size="small" | ||||
|                 @click="appendNewCondition" | ||||
|                 @click="appendNewCondition(automation)" | ||||
|               > | ||||
|                 {{ $t('AUTOMATION.ADD.CONDITION_BUTTON_LABEL') }} | ||||
|               </woot-button> | ||||
| @@ -205,15 +278,18 @@ export default { | ||||
|                 getActionDropdownValues(automation.actions[i].action_name) | ||||
|               " | ||||
|               :show-action-input=" | ||||
|                 showActionInput(automation.actions[i].action_name) | ||||
|                 showActionInput( | ||||
|                   automationActionTypes, | ||||
|                   automation.actions[i].action_name | ||||
|                 ) | ||||
|               " | ||||
|               :error-message=" | ||||
|                 errors[`action_${i}`] | ||||
|                   ? $t(`AUTOMATION.ERRORS.${errors[`action_${i}`]}`) | ||||
|                   : '' | ||||
|               " | ||||
|               @resetAction="resetAction(i)" | ||||
|               @removeAction="removeAction(i)" | ||||
|               @resetAction="resetAction(automation, i)" | ||||
|               @removeAction="removeAction(automation, i)" | ||||
|             /> | ||||
|             <div class="mt-4"> | ||||
|               <woot-button | ||||
| @@ -221,7 +297,7 @@ export default { | ||||
|                 color-scheme="success" | ||||
|                 variant="smooth" | ||||
|                 size="small" | ||||
|                 @click="appendNewAction" | ||||
|                 @click="appendNewAction(automation)" | ||||
|               > | ||||
|                 {{ $t('AUTOMATION.ADD.ACTION_BUTTON_LABEL') }} | ||||
|               </woot-button> | ||||
| @@ -234,7 +310,7 @@ export default { | ||||
|             <woot-button class="button clear" @click.prevent="onClose"> | ||||
|               {{ $t('AUTOMATION.ADD.CANCEL_BUTTON_TEXT') }} | ||||
|             </woot-button> | ||||
|             <woot-button @click="submitAutomation"> | ||||
|             <woot-button @click="emitSaveAutomation"> | ||||
|               {{ $t('AUTOMATION.ADD.SUBMIT') }} | ||||
|             </woot-button> | ||||
|           </div> | ||||
|   | ||||
| @@ -1,8 +1,18 @@ | ||||
| <script> | ||||
| import { mapGetters } from 'vuex'; | ||||
| import automationMethodsMixin from 'dashboard/mixins/automations/methodsMixin'; | ||||
| import { useAutomation } from 'dashboard/composables/useAutomation'; | ||||
| import FilterInputBox from 'dashboard/components/widgets/FilterInput/Index.vue'; | ||||
| import AutomationActionInput from 'dashboard/components/widgets/AutomationActionInput.vue'; | ||||
| import { | ||||
|   getFileName, | ||||
|   generateAutomationPayload, | ||||
|   getAttributes, | ||||
|   getInputType, | ||||
|   getOperators, | ||||
|   getCustomAttributeType, | ||||
|   showActionInput, | ||||
| } from 'dashboard/helper/automationHelper'; | ||||
| import { validateAutomation } from 'dashboard/helper/validations'; | ||||
|  | ||||
| import { | ||||
|   AUTOMATION_RULE_EVENTS, | ||||
| @@ -15,7 +25,6 @@ export default { | ||||
|     FilterInputBox, | ||||
|     AutomationActionInput, | ||||
|   }, | ||||
|   mixins: [automationMethodsMixin], | ||||
|   props: { | ||||
|     onClose: { | ||||
|       type: Function, | ||||
| @@ -26,6 +35,34 @@ export default { | ||||
|       default: () => {}, | ||||
|     }, | ||||
|   }, | ||||
|   setup() { | ||||
|     const { | ||||
|       onEventChange, | ||||
|       getConditionDropdownValues, | ||||
|       appendNewCondition, | ||||
|       appendNewAction, | ||||
|       removeFilter, | ||||
|       removeAction, | ||||
|       resetFilter, | ||||
|       resetAction, | ||||
|       getActionDropdownValues, | ||||
|       formatAutomation, | ||||
|       manifestCustomAttributes, | ||||
|     } = useAutomation(); | ||||
|     return { | ||||
|       onEventChange, | ||||
|       getConditionDropdownValues, | ||||
|       appendNewCondition, | ||||
|       appendNewAction, | ||||
|       removeFilter, | ||||
|       removeAction, | ||||
|       resetFilter, | ||||
|       resetAction, | ||||
|       getActionDropdownValues, | ||||
|       formatAutomation, | ||||
|       manifestCustomAttributes, | ||||
|     }; | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       automationTypes: JSON.parse(JSON.stringify(AUTOMATIONS)), | ||||
| @@ -61,14 +98,33 @@ export default { | ||||
|     }, | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.manifestCustomAttributes(); | ||||
|     this.manifestCustomAttributes(this.automationTypes); | ||||
|     this.allCustomAttributes = this.$store.getters['attributes/getAttributes']; | ||||
|     this.formatAutomation(this.selectedResponse); | ||||
|  | ||||
|     this.automation = this.formatAutomation( | ||||
|       this.selectedResponse, | ||||
|       this.allCustomAttributes, | ||||
|       this.automationTypes, | ||||
|       this.automationActionTypes | ||||
|     ); | ||||
|   }, | ||||
|   methods: { | ||||
|     getFileName, | ||||
|     getAttributes, | ||||
|     getInputType, | ||||
|     getOperators, | ||||
|     getCustomAttributeType, | ||||
|     showActionInput, | ||||
|     isFeatureEnabled(flag) { | ||||
|       return this.isFeatureEnabledonAccount(this.accountId, flag); | ||||
|     }, | ||||
|     emitSaveAutomation() { | ||||
|       this.errors = validateAutomation(this.automation); | ||||
|       if (Object.keys(this.errors).length === 0) { | ||||
|         const automation = generateAutomationPayload(this.automation); | ||||
|         this.$emit('saveAutomation', automation, this.mode); | ||||
|       } | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| @@ -99,7 +155,10 @@ export default { | ||||
|         <div class="event_wrapper"> | ||||
|           <label :class="{ error: errors.event_name }"> | ||||
|             {{ $t('AUTOMATION.ADD.FORM.EVENT.LABEL') }} | ||||
|             <select v-model="automation.event_name" @change="onEventChange()"> | ||||
|             <select | ||||
|               v-model="automation.event_name" | ||||
|               @change="onEventChange(automation)" | ||||
|             > | ||||
|               <option | ||||
|                 v-for="event in automationRuleEvents" | ||||
|                 :key="event.key" | ||||
| @@ -125,16 +184,37 @@ export default { | ||||
|               v-for="(condition, i) in automation.conditions" | ||||
|               :key="i" | ||||
|               v-model="automation.conditions[i]" | ||||
|               :filter-attributes="getAttributes(automation.event_name)" | ||||
|               :input-type="getInputType(automation.conditions[i].attribute_key)" | ||||
|               :operators="getOperators(automation.conditions[i].attribute_key)" | ||||
|               :filter-attributes=" | ||||
|                 getAttributes(automationTypes, automation.event_name) | ||||
|               " | ||||
|               :input-type=" | ||||
|                 getInputType( | ||||
|                   allCustomAttributes, | ||||
|                   automationTypes, | ||||
|                   automation, | ||||
|                   automation.conditions[i].attribute_key | ||||
|                 ) | ||||
|               " | ||||
|               :operators=" | ||||
|                 getOperators( | ||||
|                   allCustomAttributes, | ||||
|                   automationTypes, | ||||
|                   automation, | ||||
|                   mode, | ||||
|                   automation.conditions[i].attribute_key | ||||
|                 ) | ||||
|               " | ||||
|               :dropdown-values=" | ||||
|                 getConditionDropdownValues( | ||||
|                   automation.conditions[i].attribute_key | ||||
|                 ) | ||||
|               " | ||||
|               :custom-attribute-type=" | ||||
|                 getCustomAttributeType(automation.conditions[i].attribute_key) | ||||
|                 getCustomAttributeType( | ||||
|                   automationTypes, | ||||
|                   automation, | ||||
|                   automation.conditions[i].attribute_key | ||||
|                 ) | ||||
|               " | ||||
|               :show-query-operator="i !== automation.conditions.length - 1" | ||||
|               :error-message=" | ||||
| @@ -142,8 +222,15 @@ export default { | ||||
|                   ? $t(`AUTOMATION.ERRORS.${errors[`condition_${i}`]}`) | ||||
|                   : '' | ||||
|               " | ||||
|               @resetFilter="resetFilter(i, automation.conditions[i])" | ||||
|               @removeFilter="removeFilter(i)" | ||||
|               @resetFilter=" | ||||
|                 resetFilter( | ||||
|                   automation, | ||||
|                   automationTypes, | ||||
|                   i, | ||||
|                   automation.conditions[i] | ||||
|                 ) | ||||
|               " | ||||
|               @removeFilter="removeFilter(automation, i)" | ||||
|             /> | ||||
|             <div class="mt-4"> | ||||
|               <woot-button | ||||
| @@ -151,7 +238,7 @@ export default { | ||||
|                 color-scheme="success" | ||||
|                 variant="smooth" | ||||
|                 size="small" | ||||
|                 @click="appendNewCondition" | ||||
|                 @click="appendNewCondition(automation)" | ||||
|               > | ||||
|                 {{ $t('AUTOMATION.ADD.CONDITION_BUTTON_LABEL') }} | ||||
|               </woot-button> | ||||
| @@ -173,15 +260,17 @@ export default { | ||||
|               v-model="automation.actions[i]" | ||||
|               :action-types="automationActionTypes" | ||||
|               :dropdown-values="getActionDropdownValues(action.action_name)" | ||||
|               :show-action-input="showActionInput(action.action_name)" | ||||
|               :show-action-input=" | ||||
|                 showActionInput(automationActionTypes, action.action_name) | ||||
|               " | ||||
|               :error-message=" | ||||
|                 errors[`action_${i}`] | ||||
|                   ? $t(`AUTOMATION.ERRORS.${errors[`action_${i}`]}`) | ||||
|                   : '' | ||||
|               " | ||||
|               :initial-file-name="getFileName(action, automation.files)" | ||||
|               @resetAction="resetAction(i)" | ||||
|               @removeAction="removeAction(i)" | ||||
|               @resetAction="resetAction(automation, i)" | ||||
|               @removeAction="removeAction(automation, i)" | ||||
|             /> | ||||
|             <div class="mt-4"> | ||||
|               <woot-button | ||||
| @@ -189,7 +278,7 @@ export default { | ||||
|                 color-scheme="success" | ||||
|                 variant="smooth" | ||||
|                 size="small" | ||||
|                 @click="appendNewAction" | ||||
|                 @click="appendNewAction(automation)" | ||||
|               > | ||||
|                 {{ $t('AUTOMATION.ADD.ACTION_BUTTON_LABEL') }} | ||||
|               </woot-button> | ||||
| @@ -206,7 +295,7 @@ export default { | ||||
|             > | ||||
|               {{ $t('AUTOMATION.EDIT.CANCEL_BUTTON_TEXT') }} | ||||
|             </woot-button> | ||||
|             <woot-button @click="submitAutomation"> | ||||
|             <woot-button @click="emitSaveAutomation"> | ||||
|               {{ $t('AUTOMATION.EDIT.SUBMIT') }} | ||||
|             </woot-button> | ||||
|           </div> | ||||
|   | ||||
| @@ -1,450 +0,0 @@ | ||||
| import methodsMixin from '../../../dashboard/mixins/automations/methodsMixin'; | ||||
| import { | ||||
|   automation, | ||||
|   customAttributes, | ||||
|   agents, | ||||
|   booleanFilterOptions, | ||||
|   teams, | ||||
|   labels, | ||||
|   statusFilterOptions, | ||||
|   campaigns, | ||||
|   contacts, | ||||
|   inboxes, | ||||
|   languages, | ||||
|   countries, | ||||
|   slaPolicies, | ||||
|   MESSAGE_CONDITION_VALUES, | ||||
|   automationToSubmit, | ||||
|   savedAutomation, | ||||
| } from './automationFixtures'; | ||||
| import { | ||||
|   AUTOMATIONS, | ||||
|   AUTOMATION_ACTION_TYPES, | ||||
| } from '../../../dashboard/routes/dashboard/settings/automation/constants.js'; | ||||
|  | ||||
| import { createWrapper, createLocalVue } from '@vue/test-utils'; | ||||
| import Vue from 'vue'; | ||||
| import Vuex from 'vuex'; | ||||
| const localVue = createLocalVue(); | ||||
| localVue.use(Vuex); | ||||
|  | ||||
| // Vuelidate required to test submit method | ||||
|  | ||||
| const createComponent = ( | ||||
|   mixins, | ||||
|   data, | ||||
|   // eslint-disable-next-line default-param-last | ||||
|   computed = {}, | ||||
|   // eslint-disable-next-line default-param-last | ||||
|   methods = {}, | ||||
|   validations | ||||
| ) => { | ||||
|   const Component = { | ||||
|     render() {}, | ||||
|     mixins, | ||||
|     data, | ||||
|     computed, | ||||
|     methods, | ||||
|     validations, | ||||
|   }; | ||||
|   const Constructor = Vue.extend(Component); | ||||
|   const vm = new Constructor().$mount(); | ||||
|   return createWrapper(vm); | ||||
| }; | ||||
|  | ||||
| const generateComputedProperties = () => { | ||||
|   return { | ||||
|     statusFilterOptions() { | ||||
|       return statusFilterOptions; | ||||
|     }, | ||||
|     agents() { | ||||
|       return agents; | ||||
|     }, | ||||
|     customAttributes() { | ||||
|       return customAttributes; | ||||
|     }, | ||||
|     labels() { | ||||
|       return labels; | ||||
|     }, | ||||
|     teams() { | ||||
|       return teams; | ||||
|     }, | ||||
|     booleanFilterOptions() { | ||||
|       return booleanFilterOptions; | ||||
|     }, | ||||
|     campaigns() { | ||||
|       return campaigns; | ||||
|     }, | ||||
|     contacts() { | ||||
|       return contacts; | ||||
|     }, | ||||
|     inboxes() { | ||||
|       return inboxes; | ||||
|     }, | ||||
|     languages() { | ||||
|       return languages; | ||||
|     }, | ||||
|     countries() { | ||||
|       return countries; | ||||
|     }, | ||||
|     slaPolicies() { | ||||
|       return slaPolicies; | ||||
|     }, | ||||
|     MESSAGE_CONDITION_VALUES() { | ||||
|       return MESSAGE_CONDITION_VALUES; | ||||
|     }, | ||||
|   }; | ||||
| }; | ||||
|  | ||||
| describe('automationMethodsMixin', () => { | ||||
|   it('getFileName returns the correct file name', () => { | ||||
|     const data = () => { | ||||
|       return {}; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     expect( | ||||
|       wrapper.vm.getFileName(automation.actions[0], automation.files) | ||||
|     ).toEqual(automation.files[0].filename); | ||||
|   }); | ||||
|  | ||||
|   it('getAttributes returns all attributes', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automationTypes: AUTOMATIONS, | ||||
|       }; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     expect(wrapper.vm.getAttributes('conversation_created')).toEqual( | ||||
|       AUTOMATIONS.conversation_created.conditions | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('getAttributes returns all respective attributes', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         allCustomAttributes: customAttributes, | ||||
|         automationTypes: AUTOMATIONS, | ||||
|         automation, | ||||
|       }; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     expect(wrapper.vm.getInputType('status')).toEqual('multi_select'); | ||||
|     expect(wrapper.vm.getInputType('my_list')).toEqual('search_select'); | ||||
|   }); | ||||
|  | ||||
|   it('getOperators returns all respective operators', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         allCustomAttributes: customAttributes, | ||||
|         automationTypes: AUTOMATIONS, | ||||
|         automation, | ||||
|       }; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     expect(wrapper.vm.getOperators('status')).toEqual( | ||||
|       AUTOMATIONS.conversation_created.conditions[0].filterOperators | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('getAutomationType returns the correct automationType', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automationTypes: AUTOMATIONS, | ||||
|         automation, | ||||
|       }; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     expect(wrapper.vm.getAutomationType('status')).toEqual( | ||||
|       AUTOMATIONS[automation.event_name].conditions[0] | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('getConditionDropdownValues returns respective condition dropdown values', () => { | ||||
|     const computed = generateComputedProperties(); | ||||
|     const data = () => { | ||||
|       return { | ||||
|         allCustomAttributes: customAttributes, | ||||
|       }; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data, computed); | ||||
|     expect(wrapper.vm.getConditionDropdownValues('status')).toEqual( | ||||
|       statusFilterOptions | ||||
|     ); | ||||
|     expect(wrapper.vm.getConditionDropdownValues('team_id')).toEqual(teams); | ||||
|     expect(wrapper.vm.getConditionDropdownValues('assignee_id')).toEqual( | ||||
|       agents | ||||
|     ); | ||||
|     expect(wrapper.vm.getConditionDropdownValues('contact')).toEqual(contacts); | ||||
|     expect(wrapper.vm.getConditionDropdownValues('inbox_id')).toEqual(inboxes); | ||||
|     expect(wrapper.vm.getConditionDropdownValues('campaigns')).toEqual( | ||||
|       campaigns | ||||
|     ); | ||||
|     expect(wrapper.vm.getConditionDropdownValues('browser_language')).toEqual( | ||||
|       languages | ||||
|     ); | ||||
|     expect(wrapper.vm.getConditionDropdownValues('country_code')).toEqual( | ||||
|       countries | ||||
|     ); | ||||
|     expect(wrapper.vm.getConditionDropdownValues('message_type')).toEqual( | ||||
|       MESSAGE_CONDITION_VALUES | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('appendNewCondition appends a new condition to the automation data property', () => { | ||||
|     const condition = { | ||||
|       attribute_key: 'status', | ||||
|       filter_operator: 'equal_to', | ||||
|       values: '', | ||||
|       query_operator: 'and', | ||||
|       custom_attribute_type: '', | ||||
|     }; | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automation, | ||||
|       }; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     wrapper.vm.appendNewCondition(); | ||||
|     expect(automation.conditions[automation.conditions.length - 1]).toEqual( | ||||
|       condition | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('appendNewAction appends a new condition to the automation data property', () => { | ||||
|     const action = { | ||||
|       action_name: 'assign_agent', | ||||
|       action_params: [], | ||||
|     }; | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automation, | ||||
|       }; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     wrapper.vm.appendNewAction(); | ||||
|     expect(automation.actions[automation.actions.length - 1]).toEqual(action); | ||||
|   }); | ||||
|  | ||||
|   it('removeFilter removes the given condition in the automation', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automation, | ||||
|       }; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     wrapper.vm.removeFilter(0); | ||||
|     expect(automation.conditions.length).toEqual(1); | ||||
|   }); | ||||
|  | ||||
|   it('removeAction removes the given action in the automation', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automation, | ||||
|       }; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     wrapper.vm.removeAction(0); | ||||
|     expect(automation.actions.length).toEqual(1); | ||||
|   }); | ||||
|  | ||||
|   it('resetFilter resets the current automation conditions', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automation: automationToSubmit, | ||||
|         automationTypes: AUTOMATIONS, | ||||
|       }; | ||||
|     }; | ||||
|     const conditionAfterReset = { | ||||
|       attribute_key: 'status', | ||||
|       filter_operator: 'equal_to', | ||||
|       values: '', | ||||
|       query_operator: 'and', | ||||
|       custom_attribute_type: '', | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     wrapper.vm.resetFilter(0, automationToSubmit.conditions[0]); | ||||
|     expect(automation.conditions[0]).toEqual(conditionAfterReset); | ||||
|   }); | ||||
|  | ||||
|   it('showUserInput returns boolean value based on the operator type', () => { | ||||
|     const data = () => { | ||||
|       return {}; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     expect(wrapper.vm.showUserInput('is_present')).toBeFalsy(); | ||||
|     expect(wrapper.vm.showUserInput('is_not_present')).toBeFalsy(); | ||||
|     expect(wrapper.vm.showUserInput('equal_to')).toBeTruthy(); | ||||
|     expect(wrapper.vm.showUserInput('not_equal_to')).toBeTruthy(); | ||||
|   }); | ||||
|  | ||||
|   it('showActionInput returns boolean value based on the action type', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automationActionTypes: AUTOMATION_ACTION_TYPES, | ||||
|       }; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     expect(wrapper.vm.showActionInput('send_email_to_team')).toBeFalsy(); | ||||
|     expect(wrapper.vm.showActionInput('send_message')).toBeFalsy(); | ||||
|     expect(wrapper.vm.showActionInput('send_webhook_event')).toBeTruthy(); | ||||
|     expect(wrapper.vm.showActionInput('resolve_conversation')).toBeFalsy(); | ||||
|     expect(wrapper.vm.showActionInput('add_label')).toBeTruthy(); | ||||
|   }); | ||||
|  | ||||
|   it('resetAction resets the action to default state', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automation, | ||||
|       }; | ||||
|     }; | ||||
|     const wrapper = createComponent([methodsMixin], data); | ||||
|     wrapper.vm.resetAction(0); | ||||
|     expect(automation.actions[0].action_params).toEqual([]); | ||||
|   }); | ||||
|  | ||||
|   it('manifestConditions resets the action to default state', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automation: {}, | ||||
|         allCustomAttributes: customAttributes, | ||||
|         automationTypes: AUTOMATIONS, | ||||
|       }; | ||||
|     }; | ||||
|     const methods = { | ||||
|       getConditionDropdownValues() { | ||||
|         return statusFilterOptions; | ||||
|       }, | ||||
|     }; | ||||
|  | ||||
|     const manifestedConditions = [ | ||||
|       { | ||||
|         values: [ | ||||
|           { | ||||
|             id: 'open', | ||||
|             name: 'Open', | ||||
|           }, | ||||
|         ], | ||||
|         attribute_key: 'status', | ||||
|         filter_operator: 'equal_to', | ||||
|         query_operator: 'and', | ||||
|       }, | ||||
|     ]; | ||||
|     const wrapper = createComponent([methodsMixin], data, {}, methods); | ||||
|     expect(wrapper.vm.manifestConditions(savedAutomation)).toEqual( | ||||
|       manifestedConditions | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('generateActionsArray return the manifested actions array', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automationActionTypes: AUTOMATION_ACTION_TYPES, | ||||
|       }; | ||||
|     }; | ||||
|     const computed = { | ||||
|       agents() { | ||||
|         return agents; | ||||
|       }, | ||||
|       labels() { | ||||
|         return labels; | ||||
|       }, | ||||
|       teams() { | ||||
|         return teams; | ||||
|       }, | ||||
|     }; | ||||
|  | ||||
|     const methods = { | ||||
|       getActionDropdownValues() { | ||||
|         return [ | ||||
|           { | ||||
|             id: 2, | ||||
|             name: 'testlabel', | ||||
|           }, | ||||
|           { | ||||
|             id: 1, | ||||
|             name: 'snoozes', | ||||
|           }, | ||||
|         ]; | ||||
|       }, | ||||
|     }; | ||||
|     const testAction = { | ||||
|       action_name: 'add_label', | ||||
|       action_params: [2], | ||||
|     }; | ||||
|  | ||||
|     const expectedActionArray = [ | ||||
|       { | ||||
|         id: 2, | ||||
|         name: 'testlabel', | ||||
|       }, | ||||
|     ]; | ||||
|  | ||||
|     const wrapper = createComponent([methodsMixin], data, computed, methods); | ||||
|     expect(wrapper.vm.generateActionsArray(testAction)).toEqual( | ||||
|       expectedActionArray | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('manifestActions manifest the received action and generate the correct array', () => { | ||||
|     const data = () => { | ||||
|       return { | ||||
|         automation: {}, | ||||
|         allCustomAttributes: customAttributes, | ||||
|         automationTypes: AUTOMATIONS, | ||||
|       }; | ||||
|     }; | ||||
|     const methods = { | ||||
|       generateActionsArray() { | ||||
|         return [ | ||||
|           { | ||||
|             id: 2, | ||||
|             name: 'testlabel', | ||||
|           }, | ||||
|         ]; | ||||
|       }, | ||||
|     }; | ||||
|     const expectedActions = [ | ||||
|       { | ||||
|         action_name: 'add_label', | ||||
|         action_params: [ | ||||
|           { | ||||
|             id: 2, | ||||
|             name: 'testlabel', | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|     ]; | ||||
|     const wrapper = createComponent([methodsMixin], data, {}, methods); | ||||
|     expect(wrapper.vm.manifestActions(savedAutomation)).toEqual( | ||||
|       expectedActions | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
|   it('getActionDropdownValues returns Action dropdown Values', () => { | ||||
|     const data = () => { | ||||
|       return {}; | ||||
|     }; | ||||
|     const computed = { | ||||
|       agents() { | ||||
|         return agents; | ||||
|       }, | ||||
|       labels() { | ||||
|         return labels; | ||||
|       }, | ||||
|       teams() { | ||||
|         return teams; | ||||
|       }, | ||||
|       slaPolicies() { | ||||
|         return slaPolicies; | ||||
|       }, | ||||
|     }; | ||||
|     const expectedActionDropdownValues = [ | ||||
|       { id: 'testlabel', name: 'testlabel' }, | ||||
|       { id: 'snoozes', name: 'snoozes' }, | ||||
|     ]; | ||||
|     const wrapper = createComponent([methodsMixin], data, computed); | ||||
|     expect(wrapper.vm.getActionDropdownValues('add_label')).toEqual( | ||||
|       expectedActionDropdownValues | ||||
|     ); | ||||
|   }); | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user
	 Sivin Varghese
					Sivin Varghese