mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 12:08:01 +00:00
fix: Update inconsistent behaviour of create and edit automation rules (#10197)
When Vue 3 is used with options API, any assignment to `this.<something>` is converted to a Proxy before assignment. This is fine as long as we are in the options context, problem arises when we access this in a `composable` any mutations on the object doesn't behave correctly as expected, this PR fixes that by moving the `automation` object inside the composable and using it in the options. > Another option to fix such an issue is to make the object non-reactive, like done in places where we have `editorView`, but that wasn't viable here --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
@@ -1,313 +1,143 @@
|
|||||||
import { computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useStoreGetters, useMapGetter } from 'dashboard/composables/store';
|
import { useStoreGetters } from 'dashboard/composables/store';
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
|
||||||
import countries from 'shared/constants/countries';
|
|
||||||
import {
|
import {
|
||||||
generateCustomAttributeTypes,
|
generateCustomAttributeTypes,
|
||||||
getActionOptions,
|
|
||||||
getConditionOptions,
|
|
||||||
getCustomAttributeInputType,
|
|
||||||
getDefaultConditions,
|
getDefaultConditions,
|
||||||
getDefaultActions,
|
getDefaultActions,
|
||||||
filterCustomAttributes,
|
|
||||||
getStandardAttributeInputType,
|
|
||||||
isCustomAttribute,
|
|
||||||
generateCustomAttributes,
|
generateCustomAttributes,
|
||||||
} from 'dashboard/helper/automationHelper';
|
} 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.
|
* Composable for handling automation-related functionality.
|
||||||
* @returns {Object} An object containing various automation-related functions and computed properties.
|
* @returns {Object} An object containing various automation-related functions and computed properties.
|
||||||
*/
|
*/
|
||||||
export function useAutomation() {
|
export function useAutomation(startValue = null) {
|
||||||
const getters = useStoreGetters();
|
const getters = useStoreGetters();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const agents = useMapGetter('agents/getAgents');
|
const {
|
||||||
const campaigns = useMapGetter('campaigns/getAllCampaigns');
|
booleanFilterOptions,
|
||||||
const contacts = useMapGetter('contacts/getContacts');
|
statusFilterOptions,
|
||||||
const inboxes = useMapGetter('inboxes/getInboxes');
|
getConditionDropdownValues,
|
||||||
const labels = useMapGetter('labels/getLabels');
|
getActionDropdownValues,
|
||||||
const teams = useMapGetter('teams/getTeams');
|
agents,
|
||||||
const slaPolicies = useMapGetter('sla/getSLA');
|
campaigns,
|
||||||
|
contacts,
|
||||||
|
inboxes,
|
||||||
|
labels,
|
||||||
|
teams,
|
||||||
|
slaPolicies,
|
||||||
|
} = useAutomationValues();
|
||||||
|
|
||||||
const booleanFilterOptions = computed(() => [
|
const automation = ref(startValue);
|
||||||
{ id: true, name: t('FILTER.ATTRIBUTE_LABELS.TRUE') },
|
const automationTypes = structuredClone(AUTOMATIONS);
|
||||||
{ id: false, name: t('FILTER.ATTRIBUTE_LABELS.FALSE') },
|
const eventName = computed(() => automation.value?.event_name);
|
||||||
]);
|
|
||||||
|
|
||||||
const statusFilterItems = computed(() => {
|
/**
|
||||||
return {
|
* Handles the event change for an automation.value.
|
||||||
open: {
|
*/
|
||||||
TEXT: t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.open.TEXT'),
|
const onEventChange = () => {
|
||||||
},
|
automation.value.conditions = getDefaultConditions(eventName.value);
|
||||||
resolved: {
|
automation.value.actions = getDefaultActions();
|
||||||
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;
|
* Appends a new condition to the automation.value.
|
||||||
return [
|
*/
|
||||||
...Object.keys(statusFilters).map(status => ({
|
const appendNewCondition = () => {
|
||||||
id: status,
|
const defaultCondition = getDefaultConditions(eventName.value);
|
||||||
name: statusFilters[status].TEXT,
|
automation.value.conditions = [
|
||||||
})),
|
...automation.value.conditions,
|
||||||
{ id: 'all', name: t('CHAT_LIST.FILTER_ALL') },
|
...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.
|
* Appends a new action to the automation.value.
|
||||||
* @param {string} type - The type of condition.
|
|
||||||
* @returns {Array} An array of condition dropdown values.
|
|
||||||
*/
|
*/
|
||||||
const getConditionDropdownValues = type => {
|
const appendNewAction = () => {
|
||||||
return getConditionOptions({
|
const defaultAction = getDefaultActions();
|
||||||
agents: agents.value,
|
automation.value.actions = [...automation.value.actions, ...defaultAction];
|
||||||
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.
|
* Removes a filter from the automation.value.
|
||||||
* @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.
|
* @param {number} index - The index of the filter to remove.
|
||||||
*/
|
*/
|
||||||
const removeFilter = (automation, index) => {
|
const removeFilter = index => {
|
||||||
if (automation.conditions.length <= 1) {
|
if (automation.value.conditions.length <= 1) {
|
||||||
useAlert(t('AUTOMATION.CONDITION.DELETE_MESSAGE'));
|
useAlert(t('AUTOMATION.CONDITION.DELETE_MESSAGE'));
|
||||||
} else {
|
} else {
|
||||||
automation.conditions.splice(index, 1);
|
automation.value.conditions = automation.value.conditions.filter(
|
||||||
|
(_, i) => i !== index
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an action from the automation.
|
* Removes an action from the automation.value.
|
||||||
* @param {Object} automation - The automation object to update.
|
|
||||||
* @param {number} index - The index of the action to remove.
|
* @param {number} index - The index of the action to remove.
|
||||||
*/
|
*/
|
||||||
const removeAction = (automation, index) => {
|
const removeAction = index => {
|
||||||
if (automation.actions.length <= 1) {
|
if (automation.value.actions.length <= 1) {
|
||||||
useAlert(t('AUTOMATION.ACTION.DELETE_MESSAGE'));
|
useAlert(t('AUTOMATION.ACTION.DELETE_MESSAGE'));
|
||||||
} else {
|
} else {
|
||||||
automation.actions.splice(index, 1);
|
automation.value.actions = automation.value.actions.filter(
|
||||||
|
(_, i) => i !== index
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets a filter in the automation.
|
* Resets a filter in the automation.value.
|
||||||
* @param {Object} automation - The automation object to update.
|
|
||||||
* @param {Object} automationTypes - The automation types object.
|
* @param {Object} automationTypes - The automation types object.
|
||||||
* @param {number} index - The index of the filter to reset.
|
* @param {number} index - The index of the filter to reset.
|
||||||
* @param {Object} currentCondition - The current condition object.
|
* @param {Object} currentCondition - The current condition object.
|
||||||
*/
|
*/
|
||||||
const resetFilter = (
|
const resetFilter = (index, currentCondition) => {
|
||||||
automation,
|
const newConditions = [...automation.value.conditions];
|
||||||
automationTypes,
|
|
||||||
index,
|
newConditions[index] = {
|
||||||
currentCondition
|
...newConditions[index],
|
||||||
) => {
|
filter_operator: automationTypes[eventName.value].conditions.find(
|
||||||
automation.conditions[index].filter_operator = automationTypes[
|
condition => condition.key === currentCondition.attribute_key
|
||||||
automation.event_name
|
).filterOperators[0].value,
|
||||||
].conditions.find(
|
values: '',
|
||||||
condition => condition.key === currentCondition.attribute_key
|
};
|
||||||
).filterOperators[0].value;
|
|
||||||
automation.conditions[index].values = '';
|
automation.value.conditions = newConditions;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets an action in the automation.
|
* Resets an action in the automation.value.
|
||||||
* @param {Object} automation - The automation object to update.
|
|
||||||
* @param {number} index - The index of the action to reset.
|
* @param {number} index - The index of the action to reset.
|
||||||
*/
|
*/
|
||||||
const resetAction = (automation, index) => {
|
const resetAction = index => {
|
||||||
automation.actions[index].action_params = [];
|
const newActions = [...automation.value.actions];
|
||||||
};
|
newActions[index] = {
|
||||||
|
...newActions[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),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
automation.value.actions = newActions;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function formats the custom attributes for automation types.
|
* This function formats the custom attributes for automation types.
|
||||||
* It retrieves custom attributes for conversations and contacts,
|
* It retrieves custom attributes for conversations and contacts,
|
||||||
* generates custom attribute types, and adds them to the relevant automation types.
|
* 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[
|
const conversationCustomAttributesRaw = getters[
|
||||||
'attributes/getAttributesByModel'
|
'attributes/getAttributesByModel'
|
||||||
].value('conversation_attribute');
|
].value('conversation_attribute');
|
||||||
@@ -330,21 +160,22 @@ export function useAutomation() {
|
|||||||
t('AUTOMATION.CONDITION.CONTACT_CUSTOM_ATTR_LABEL')
|
t('AUTOMATION.CONDITION.CONTACT_CUSTOM_ATTR_LABEL')
|
||||||
);
|
);
|
||||||
|
|
||||||
automationTypes.message_created.conditions.push(
|
[
|
||||||
...manifestedCustomAttributes
|
'message_created',
|
||||||
);
|
'conversation_created',
|
||||||
automationTypes.conversation_created.conditions.push(
|
'conversation_updated',
|
||||||
...manifestedCustomAttributes
|
'conversation_opened',
|
||||||
);
|
].forEach(eventToUpdate => {
|
||||||
automationTypes.conversation_updated.conditions.push(
|
automationTypes[eventToUpdate].conditions = [
|
||||||
...manifestedCustomAttributes
|
...automationTypes[eventToUpdate].conditions,
|
||||||
);
|
...manifestedCustomAttributes,
|
||||||
automationTypes.conversation_opened.conditions.push(
|
];
|
||||||
...manifestedCustomAttributes
|
});
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
automation,
|
||||||
|
automationTypes,
|
||||||
agents,
|
agents,
|
||||||
campaigns,
|
campaigns,
|
||||||
contacts,
|
contacts,
|
||||||
@@ -362,7 +193,6 @@ export function useAutomation() {
|
|||||||
removeAction,
|
removeAction,
|
||||||
resetFilter,
|
resetFilter,
|
||||||
resetAction,
|
resetAction,
|
||||||
formatAutomation,
|
|
||||||
getActionDropdownValues,
|
getActionDropdownValues,
|
||||||
manifestCustomAttributes,
|
manifestCustomAttributes,
|
||||||
};
|
};
|
||||||
|
|||||||
115
app/javascript/dashboard/composables/useAutomationValues.js
Normal file
115
app/javascript/dashboard/composables/useAutomationValues.js
Normal file
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
129
app/javascript/dashboard/composables/useEditableAutomation.js
Normal file
129
app/javascript/dashboard/composables/useEditableAutomation.js
Normal file
@@ -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 };
|
||||||
|
}
|
||||||
@@ -12,11 +12,29 @@ import {
|
|||||||
getCustomAttributeType,
|
getCustomAttributeType,
|
||||||
showActionInput,
|
showActionInput,
|
||||||
} from 'dashboard/helper/automationHelper';
|
} from 'dashboard/helper/automationHelper';
|
||||||
import {
|
import { AUTOMATION_RULE_EVENTS, AUTOMATION_ACTION_TYPES } from './constants';
|
||||||
AUTOMATION_RULE_EVENTS,
|
|
||||||
AUTOMATION_ACTION_TYPES,
|
const start_value = {
|
||||||
AUTOMATIONS,
|
name: null,
|
||||||
} from './constants';
|
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 {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
FilterInputBox,
|
FilterInputBox,
|
||||||
@@ -31,6 +49,8 @@ export default {
|
|||||||
emits: ['saveAutomation'],
|
emits: ['saveAutomation'],
|
||||||
setup() {
|
setup() {
|
||||||
const {
|
const {
|
||||||
|
automation,
|
||||||
|
automationTypes,
|
||||||
onEventChange,
|
onEventChange,
|
||||||
getConditionDropdownValues,
|
getConditionDropdownValues,
|
||||||
appendNewCondition,
|
appendNewCondition,
|
||||||
@@ -41,8 +61,10 @@ export default {
|
|||||||
resetAction,
|
resetAction,
|
||||||
getActionDropdownValues,
|
getActionDropdownValues,
|
||||||
manifestCustomAttributes,
|
manifestCustomAttributes,
|
||||||
} = useAutomation();
|
} = useAutomation(start_value);
|
||||||
return {
|
return {
|
||||||
|
automation,
|
||||||
|
automationTypes,
|
||||||
onEventChange,
|
onEventChange,
|
||||||
getConditionDropdownValues,
|
getConditionDropdownValues,
|
||||||
appendNewCondition,
|
appendNewCondition,
|
||||||
@@ -57,31 +79,10 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
automationTypes: JSON.parse(JSON.stringify(AUTOMATIONS)),
|
|
||||||
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
|
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
|
||||||
automationRuleEvents: AUTOMATION_RULE_EVENTS,
|
automationRuleEvents: AUTOMATION_RULE_EVENTS,
|
||||||
automationMutated: false,
|
automationMutated: false,
|
||||||
show: true,
|
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,
|
showDeleteConfirmationModal: false,
|
||||||
allCustomAttributes: [],
|
allCustomAttributes: [],
|
||||||
mode: 'create',
|
mode: 'create',
|
||||||
@@ -116,7 +117,7 @@ export default {
|
|||||||
this.$store.dispatch('labels/get');
|
this.$store.dispatch('labels/get');
|
||||||
this.$store.dispatch('campaigns/get');
|
this.$store.dispatch('campaigns/get');
|
||||||
this.allCustomAttributes = this.$store.getters['attributes/getAttributes'];
|
this.allCustomAttributes = this.$store.getters['attributes/getAttributes'];
|
||||||
this.manifestCustomAttributes(this.automationTypes);
|
this.manifestCustomAttributes();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getAttributes,
|
getAttributes,
|
||||||
@@ -238,15 +239,8 @@ export default {
|
|||||||
? $t(`AUTOMATION.ERRORS.${errors[`condition_${i}`]}`)
|
? $t(`AUTOMATION.ERRORS.${errors[`condition_${i}`]}`)
|
||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
@reset-filter="
|
@reset-filter="resetFilter(i, automation.conditions[i])"
|
||||||
resetFilter(
|
@remove-filter="removeFilter(i)"
|
||||||
automation,
|
|
||||||
automationTypes,
|
|
||||||
i,
|
|
||||||
automation.conditions[i]
|
|
||||||
)
|
|
||||||
"
|
|
||||||
@remove-filter="removeFilter(automation, i)"
|
|
||||||
/>
|
/>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<woot-button
|
<woot-button
|
||||||
@@ -254,7 +248,7 @@ export default {
|
|||||||
color-scheme="success"
|
color-scheme="success"
|
||||||
variant="smooth"
|
variant="smooth"
|
||||||
size="small"
|
size="small"
|
||||||
@click="appendNewCondition(automation)"
|
@click="appendNewCondition"
|
||||||
>
|
>
|
||||||
{{ $t('AUTOMATION.ADD.CONDITION_BUTTON_LABEL') }}
|
{{ $t('AUTOMATION.ADD.CONDITION_BUTTON_LABEL') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
@@ -289,8 +283,8 @@ export default {
|
|||||||
? $t(`AUTOMATION.ERRORS.${errors[`action_${i}`]}`)
|
? $t(`AUTOMATION.ERRORS.${errors[`action_${i}`]}`)
|
||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
@reset-action="resetAction(automation, i)"
|
@reset-action="resetAction(i)"
|
||||||
@remove-action="removeAction(automation, i)"
|
@remove-action="removeAction(i)"
|
||||||
/>
|
/>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<woot-button
|
<woot-button
|
||||||
@@ -298,7 +292,7 @@ export default {
|
|||||||
color-scheme="success"
|
color-scheme="success"
|
||||||
variant="smooth"
|
variant="smooth"
|
||||||
size="small"
|
size="small"
|
||||||
@click="appendNewAction(automation)"
|
@click="appendNewAction"
|
||||||
>
|
>
|
||||||
{{ $t('AUTOMATION.ADD.ACTION_BUTTON_LABEL') }}
|
{{ $t('AUTOMATION.ADD.ACTION_BUTTON_LABEL') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { useAutomation } from 'dashboard/composables/useAutomation';
|
import { useAutomation } from 'dashboard/composables/useAutomation';
|
||||||
|
import { useEditableAutomation } from 'dashboard/composables/useEditableAutomation';
|
||||||
import FilterInputBox from 'dashboard/components/widgets/FilterInput/Index.vue';
|
import FilterInputBox from 'dashboard/components/widgets/FilterInput/Index.vue';
|
||||||
import AutomationActionInput from 'dashboard/components/widgets/AutomationActionInput.vue';
|
import AutomationActionInput from 'dashboard/components/widgets/AutomationActionInput.vue';
|
||||||
import {
|
import {
|
||||||
@@ -14,11 +15,7 @@ import {
|
|||||||
} from 'dashboard/helper/automationHelper';
|
} from 'dashboard/helper/automationHelper';
|
||||||
import { validateAutomation } from 'dashboard/helper/validations';
|
import { validateAutomation } from 'dashboard/helper/validations';
|
||||||
|
|
||||||
import {
|
import { AUTOMATION_RULE_EVENTS, AUTOMATION_ACTION_TYPES } from './constants';
|
||||||
AUTOMATION_RULE_EVENTS,
|
|
||||||
AUTOMATION_ACTION_TYPES,
|
|
||||||
AUTOMATIONS,
|
|
||||||
} from './constants';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -38,6 +35,8 @@ export default {
|
|||||||
emits: ['saveAutomation'],
|
emits: ['saveAutomation'],
|
||||||
setup() {
|
setup() {
|
||||||
const {
|
const {
|
||||||
|
automation,
|
||||||
|
automationTypes,
|
||||||
onEventChange,
|
onEventChange,
|
||||||
getConditionDropdownValues,
|
getConditionDropdownValues,
|
||||||
appendNewCondition,
|
appendNewCondition,
|
||||||
@@ -47,10 +46,12 @@ export default {
|
|||||||
resetFilter,
|
resetFilter,
|
||||||
resetAction,
|
resetAction,
|
||||||
getActionDropdownValues,
|
getActionDropdownValues,
|
||||||
formatAutomation,
|
|
||||||
manifestCustomAttributes,
|
manifestCustomAttributes,
|
||||||
} = useAutomation();
|
} = useAutomation();
|
||||||
|
const { formatAutomation } = useEditableAutomation();
|
||||||
return {
|
return {
|
||||||
|
automation,
|
||||||
|
automationTypes,
|
||||||
onEventChange,
|
onEventChange,
|
||||||
getConditionDropdownValues,
|
getConditionDropdownValues,
|
||||||
appendNewCondition,
|
appendNewCondition,
|
||||||
@@ -66,12 +67,10 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
automationTypes: JSON.parse(JSON.stringify(AUTOMATIONS)),
|
|
||||||
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
|
automationRuleEvent: AUTOMATION_RULE_EVENTS[0].key,
|
||||||
automationRuleEvents: AUTOMATION_RULE_EVENTS,
|
automationRuleEvents: AUTOMATION_RULE_EVENTS,
|
||||||
automationMutated: false,
|
automationMutated: false,
|
||||||
show: true,
|
show: true,
|
||||||
automation: null,
|
|
||||||
showDeleteConfirmationModal: false,
|
showDeleteConfirmationModal: false,
|
||||||
allCustomAttributes: [],
|
allCustomAttributes: [],
|
||||||
mode: 'edit',
|
mode: 'edit',
|
||||||
@@ -99,7 +98,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.manifestCustomAttributes(this.automationTypes);
|
this.manifestCustomAttributes();
|
||||||
this.allCustomAttributes = this.$store.getters['attributes/getAttributes'];
|
this.allCustomAttributes = this.$store.getters['attributes/getAttributes'];
|
||||||
|
|
||||||
this.automation = this.formatAutomation(
|
this.automation = this.formatAutomation(
|
||||||
@@ -223,15 +222,8 @@ export default {
|
|||||||
? $t(`AUTOMATION.ERRORS.${errors[`condition_${i}`]}`)
|
? $t(`AUTOMATION.ERRORS.${errors[`condition_${i}`]}`)
|
||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
@reset-filter="
|
@reset-filter="resetFilter(i, automation.conditions[i])"
|
||||||
resetFilter(
|
@remove-filter="removeFilter(i)"
|
||||||
automation,
|
|
||||||
automationTypes,
|
|
||||||
i,
|
|
||||||
automation.conditions[i]
|
|
||||||
)
|
|
||||||
"
|
|
||||||
@remove-filter="removeFilter(automation, i)"
|
|
||||||
/>
|
/>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<woot-button
|
<woot-button
|
||||||
@@ -239,7 +231,7 @@ export default {
|
|||||||
color-scheme="success"
|
color-scheme="success"
|
||||||
variant="smooth"
|
variant="smooth"
|
||||||
size="small"
|
size="small"
|
||||||
@click="appendNewCondition(automation)"
|
@click="appendNewCondition"
|
||||||
>
|
>
|
||||||
{{ $t('AUTOMATION.ADD.CONDITION_BUTTON_LABEL') }}
|
{{ $t('AUTOMATION.ADD.CONDITION_BUTTON_LABEL') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
@@ -270,8 +262,8 @@ export default {
|
|||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
:initial-file-name="getFileName(action, automation.files)"
|
:initial-file-name="getFileName(action, automation.files)"
|
||||||
@reset-action="resetAction(automation, i)"
|
@reset-action="resetAction(i)"
|
||||||
@remove-action="removeAction(automation, i)"
|
@remove-action="removeAction(i)"
|
||||||
/>
|
/>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<woot-button
|
<woot-button
|
||||||
@@ -279,7 +271,7 @@ export default {
|
|||||||
color-scheme="success"
|
color-scheme="success"
|
||||||
variant="smooth"
|
variant="smooth"
|
||||||
size="small"
|
size="small"
|
||||||
@click="appendNewAction(automation)"
|
@click="appendNewAction"
|
||||||
>
|
>
|
||||||
{{ $t('AUTOMATION.ADD.ACTION_BUTTON_LABEL') }}
|
{{ $t('AUTOMATION.ADD.ACTION_BUTTON_LABEL') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
|
|||||||
Reference in New Issue
Block a user