diff --git a/app/javascript/dashboard/composables/useAutomation.js b/app/javascript/dashboard/composables/useAutomation.js index dd7c0988a..09828513f 100644 --- a/app/javascript/dashboard/composables/useAutomation.js +++ b/app/javascript/dashboard/composables/useAutomation.js @@ -1,313 +1,143 @@ -import { computed } from 'vue'; -import { useStoreGetters, useMapGetter } from 'dashboard/composables/store'; +import { ref, computed } from 'vue'; +import { useStoreGetters } from 'dashboard/composables/store'; import { useAlert } from 'dashboard/composables'; import { useI18n } from 'vue-i18n'; -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'; +import useAutomationValues from './useAutomationValues'; + +import { + // AUTOMATION_RULE_EVENTS, + // AUTOMATION_ACTION_TYPES, + AUTOMATIONS, +} from 'dashboard/routes/dashboard/settings/automation/constants.js'; /** * Composable for handling automation-related functionality. * @returns {Object} An object containing various automation-related functions and computed properties. */ -export function useAutomation() { +export function useAutomation(startValue = null) { 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, + statusFilterOptions, + getConditionDropdownValues, + getActionDropdownValues, + agents, + campaigns, + contacts, + inboxes, + labels, + teams, + slaPolicies, + } = useAutomationValues(); - const booleanFilterOptions = computed(() => [ - { id: true, name: t('FILTER.ATTRIBUTE_LABELS.TRUE') }, - { id: false, name: t('FILTER.ATTRIBUTE_LABELS.FALSE') }, - ]); + const automation = ref(startValue); + const automationTypes = structuredClone(AUTOMATIONS); + const eventName = computed(() => automation.value?.event_name); - const statusFilterItems = computed(() => { - return { - open: { - TEXT: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.open.TEXT'), - }, - resolved: { - TEXT: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.resolved.TEXT'), - }, - pending: { - TEXT: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.pending.TEXT'), - }, - snoozed: { - TEXT: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.snoozed.TEXT'), - }, - all: { - TEXT: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.all.TEXT'), - }, - }; - }); + /** + * Handles the event change for an automation.value. + */ + const onEventChange = () => { + automation.value.conditions = getDefaultConditions(eventName.value); + automation.value.actions = getDefaultActions(); + }; - const statusFilterOptions = computed(() => { - const statusFilters = statusFilterItems.value; - return [ - ...Object.keys(statusFilters).map(status => ({ - id: status, - name: statusFilters[status].TEXT, - })), - { id: 'all', name: t('CHAT_LIST.FILTER_ALL') }, + /** + * Appends a new condition to the automation.value. + */ + const appendNewCondition = () => { + const defaultCondition = getDefaultConditions(eventName.value); + automation.value.conditions = [ + ...automation.value.conditions, + ...defaultCondition, ]; - }); - - /** - * 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. + * Appends a new action to the automation.value. */ - 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, - }); + const appendNewAction = () => { + const defaultAction = getDefaultActions(); + automation.value.actions = [...automation.value.actions, ...defaultAction]; }; /** - * 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. + * Removes a filter from the automation.value. * @param {number} index - The index of the filter to remove. */ - const removeFilter = (automation, index) => { - if (automation.conditions.length <= 1) { + const removeFilter = index => { + if (automation.value.conditions.length <= 1) { useAlert(t('AUTOMATION.CONDITION.DELETE_MESSAGE')); } else { - automation.conditions.splice(index, 1); + automation.value.conditions = automation.value.conditions.filter( + (_, i) => i !== index + ); } }; /** - * Removes an action from the automation. - * @param {Object} automation - The automation object to update. + * Removes an action from the automation.value. * @param {number} index - The index of the action to remove. */ - const removeAction = (automation, index) => { - if (automation.actions.length <= 1) { + const removeAction = index => { + if (automation.value.actions.length <= 1) { useAlert(t('AUTOMATION.ACTION.DELETE_MESSAGE')); } else { - automation.actions.splice(index, 1); + automation.value.actions = automation.value.actions.filter( + (_, i) => i !== index + ); } }; /** - * Resets a filter in the automation. - * @param {Object} automation - The automation object to update. + * Resets a filter in the automation.value. * @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 = ''; + const resetFilter = (index, currentCondition) => { + const newConditions = [...automation.value.conditions]; + + newConditions[index] = { + ...newConditions[index], + filter_operator: automationTypes[eventName.value].conditions.find( + condition => condition.key === currentCondition.attribute_key + ).filterOperators[0].value, + values: '', + }; + + automation.value.conditions = newConditions; }; /** - * Resets an action in the automation. - * @param {Object} automation - The automation object to update. + * Resets an action in the automation.value. * @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), + const resetAction = index => { + const newActions = [...automation.value.actions]; + newActions[index] = { + ...newActions[index], + action_params: [], }; + + automation.value.actions = newActions; }; /** * 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 manifestCustomAttributes = () => { const conversationCustomAttributesRaw = getters[ 'attributes/getAttributesByModel' ].value('conversation_attribute'); @@ -330,21 +160,22 @@ export function useAutomation() { 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 - ); + [ + 'message_created', + 'conversation_created', + 'conversation_updated', + 'conversation_opened', + ].forEach(eventToUpdate => { + automationTypes[eventToUpdate].conditions = [ + ...automationTypes[eventToUpdate].conditions, + ...manifestedCustomAttributes, + ]; + }); }; return { + automation, + automationTypes, agents, campaigns, contacts, @@ -362,7 +193,6 @@ export function useAutomation() { removeAction, resetFilter, resetAction, - formatAutomation, getActionDropdownValues, manifestCustomAttributes, }; diff --git a/app/javascript/dashboard/composables/useAutomationValues.js b/app/javascript/dashboard/composables/useAutomationValues.js new file mode 100644 index 000000000..88568fee5 --- /dev/null +++ b/app/javascript/dashboard/composables/useAutomationValues.js @@ -0,0 +1,115 @@ +import { computed } from 'vue'; +import { useI18n } from 'vue-i18n'; +import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages'; +import countries from 'shared/constants/countries'; +import { useStoreGetters, useMapGetter } from 'dashboard/composables/store'; + +import { + getActionOptions, + getConditionOptions, +} from 'dashboard/helper/automationHelper'; + +/** + * This is a shared composables that holds utilites used to build dropdown and file options + * @returns {Object} An object containing various automation-related functions and computed properties. + */ +export default function useAutomationValues() { + 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 statusFilterItems = computed(() => { + return { + open: { + TEXT: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.open.TEXT'), + }, + resolved: { + TEXT: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.resolved.TEXT'), + }, + pending: { + TEXT: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.pending.TEXT'), + }, + snoozed: { + TEXT: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.snoozed.TEXT'), + }, + all: { + TEXT: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.all.TEXT'), + }, + }; + }); + + const statusFilterOptions = computed(() => { + const statusFilters = statusFilterItems.value; + return [ + ...Object.keys(statusFilters).map(status => ({ + id: status, + name: statusFilters[status].TEXT, + })), + { id: 'all', name: t('CHAT_LIST.FILTER_ALL') }, + ]; + }); + + /** + * 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, + }); + }; + + /** + * 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, + }); + }; + + return { + booleanFilterOptions, + statusFilterItems, + statusFilterOptions, + getConditionDropdownValues, + getActionDropdownValues, + agents, + campaigns, + contacts, + inboxes, + labels, + teams, + slaPolicies, + }; +} diff --git a/app/javascript/dashboard/composables/useEditableAutomation.js b/app/javascript/dashboard/composables/useEditableAutomation.js new file mode 100644 index 000000000..8b9041a8f --- /dev/null +++ b/app/javascript/dashboard/composables/useEditableAutomation.js @@ -0,0 +1,129 @@ +import useAutomationValues from './useAutomationValues'; + +import { + getCustomAttributeInputType, + filterCustomAttributes, + getStandardAttributeInputType, + isCustomAttribute, +} from 'dashboard/helper/automationHelper'; + +export function useEditableAutomation() { + const { getConditionDropdownValues, getActionDropdownValues } = + useAutomationValues(); + + /** + * 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) + ), + }; + }); + }; + + /** + * 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), + }; + }; + + return { formatAutomation }; +} diff --git a/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue b/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue index b26e2bcd5..ba5252f12 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/automation/AddAutomationRule.vue @@ -12,11 +12,29 @@ import { getCustomAttributeType, showActionInput, } from 'dashboard/helper/automationHelper'; -import { - AUTOMATION_RULE_EVENTS, - AUTOMATION_ACTION_TYPES, - AUTOMATIONS, -} from './constants'; +import { AUTOMATION_RULE_EVENTS, AUTOMATION_ACTION_TYPES } from './constants'; + +const start_value = { + name: null, + description: null, + event_name: 'conversation_created', + conditions: [ + { + attribute_key: 'status', + filter_operator: 'equal_to', + values: '', + query_operator: 'and', + custom_attribute_type: '', + }, + ], + actions: [ + { + action_name: 'assign_agent', + action_params: [], + }, + ], +}; + export default { components: { FilterInputBox, @@ -31,6 +49,8 @@ export default { emits: ['saveAutomation'], setup() { const { + automation, + automationTypes, onEventChange, getConditionDropdownValues, appendNewCondition, @@ -41,8 +61,10 @@ export default { resetAction, getActionDropdownValues, manifestCustomAttributes, - } = useAutomation(); + } = useAutomation(start_value); return { + automation, + automationTypes, onEventChange, getConditionDropdownValues, appendNewCondition, @@ -57,31 +79,10 @@ export default { }, data() { return { - automationTypes: JSON.parse(JSON.stringify(AUTOMATIONS)), automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key, automationRuleEvents: AUTOMATION_RULE_EVENTS, automationMutated: false, show: true, - automation: { - name: null, - description: null, - event_name: 'conversation_created', - conditions: [ - { - attribute_key: 'status', - filter_operator: 'equal_to', - values: '', - query_operator: 'and', - custom_attribute_type: '', - }, - ], - actions: [ - { - action_name: 'assign_agent', - action_params: [], - }, - ], - }, showDeleteConfirmationModal: false, allCustomAttributes: [], mode: 'create', @@ -116,7 +117,7 @@ export default { this.$store.dispatch('labels/get'); this.$store.dispatch('campaigns/get'); this.allCustomAttributes = this.$store.getters['attributes/getAttributes']; - this.manifestCustomAttributes(this.automationTypes); + this.manifestCustomAttributes(); }, methods: { getAttributes, @@ -238,15 +239,8 @@ export default { ? $t(`AUTOMATION.ERRORS.${errors[`condition_${i}`]}`) : '' " - @reset-filter=" - resetFilter( - automation, - automationTypes, - i, - automation.conditions[i] - ) - " - @remove-filter="removeFilter(automation, i)" + @reset-filter="resetFilter(i, automation.conditions[i])" + @remove-filter="removeFilter(i)" />