feat: Custom attributes in automations and refactor (#4548)
* Custom attributes * Custom Attrs Manifest * Fix dropdown values for custom attributes * Handle edit mode for custom attributes * Ported duplicate logic to a mixin * fix Code climate issue * Fix Codeclimate complexity warning * Bug fix - Custom attributes getting duplicated * Bug fixes and Code Climate issue fix * Code Climate Issues Breakdown * Fix test spec * Add labels for Custom attributes in dropdown * Refactor * Refactor Automion mixin * Refactor Mixin * Refactor getOperator * Fix getOperatorType * File name method refactor * Refactor appendNewCondition * spec update * Refactor methods * Mixin Spec update * Automation Mixins Test Specs * Mixin Spec Rerun * Automation validations mixin spec * Automation helper test spec * Send custom_attr key * Fix spec fixtures * fix: Changes for custom attribute type and lower case search * fix: Specs * fix: Specs * fix: Ruby version change * fix: Ruby version change * Removes Lowercased values and fix label value in api payload * Fix specs * Fixed Query Spec * Removed disabled labels if no attributes are present * Code Climate Fixes * fix: custom attribute with indifferent access * fix: custom attribute with indifferent access * Fix specs * Minor label fix * REtrigger circle ci build * Update app/javascript/shared/mixins/specs/automationMixin.spec.js * Update app/javascript/shared/mixins/specs/automationMixin.spec.js * fix: Custom attribute case insensitivity search * Add missing reset action method to input * Set team_input to single select instead of multiple * fix: remove value case check for date,boolean and number data type * fix: cognitive complexity * fix: cognitive complexity * fix: Fixed activity message for automation system * fix: Fixed activity message for automation system * fix: Fixed activity message for automation system * fix: codeclimate * fix: codeclimate * fix: action cable events for label update * fix: codeclimate, conversation modela number of methods * fix: codeclimate, conversation modela number of methods * fix: codeclimate, conversation modela number of methods * fix: codeclimate, conversation modela number of methods * Fix margin bottom for attachment button * Remove margin bottom to avoid conflict from macros * Fix automation action query generator using the right key * fix: not running message created event for activity message * fix: not running message created event for activity message * codeclimate fix * codeclimate fix * codeclimate fix * Update app/javascript/dashboard/mixins/automations/methodsMixin.js Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> * Update app/javascript/shared/mixins/specs/automationHelper.spec.js Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> * Update app/javascript/dashboard/helper/automationHelper.js Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> * Update app/javascript/dashboard/mixins/automations/methodsMixin.js Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Pranav Raj S <pranav@chatwoot.com> Co-authored-by: Tejaswini <tejaswini@chatwoot.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Sojan Jose <sojan@pepalo.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
@@ -22,7 +22,7 @@ const generatePayload = data => {
|
||||
let payload = actions.map(item => {
|
||||
if (Array.isArray(item.action_params)) {
|
||||
item.action_params = formatArray(item.action_params);
|
||||
} else if (typeof item.values === 'object') {
|
||||
} else if (typeof item.action_params === 'object') {
|
||||
item.action_params = [item.action_params.id];
|
||||
} else if (!item.action_params) {
|
||||
item.action_params = [];
|
||||
|
||||
242
app/javascript/dashboard/helper/automationHelper.js
Normal file
242
app/javascript/dashboard/helper/automationHelper.js
Normal file
@@ -0,0 +1,242 @@
|
||||
import {
|
||||
OPERATOR_TYPES_1,
|
||||
OPERATOR_TYPES_3,
|
||||
OPERATOR_TYPES_4,
|
||||
} from 'dashboard/routes/dashboard/settings/automation/operators';
|
||||
import filterQueryGenerator from './filterQueryGenerator';
|
||||
import actionQueryGenerator from './actionQueryGenerator';
|
||||
const MESSAGE_CONDITION_VALUES = [
|
||||
{
|
||||
id: 'incoming',
|
||||
name: 'Incoming Message',
|
||||
},
|
||||
{
|
||||
id: 'outgoing',
|
||||
name: 'Outgoing Message',
|
||||
},
|
||||
];
|
||||
|
||||
export const getCustomAttributeInputType = key => {
|
||||
const customAttributeMap = {
|
||||
date: 'date',
|
||||
text: 'plain_text',
|
||||
list: 'search_select',
|
||||
checkbox: 'search_select',
|
||||
};
|
||||
|
||||
return customAttributeMap[key] || 'plain_text';
|
||||
};
|
||||
|
||||
export const isACustomAttribute = (customAttributes, key) => {
|
||||
return customAttributes.find(attr => {
|
||||
return attr.attribute_key === key;
|
||||
});
|
||||
};
|
||||
|
||||
export const getCustomAttributeListDropdownValues = (
|
||||
customAttributes,
|
||||
type
|
||||
) => {
|
||||
return customAttributes
|
||||
.find(attr => attr.attribute_key === type)
|
||||
.attribute_values.map(item => {
|
||||
return {
|
||||
id: item,
|
||||
name: item,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const isCustomAttributeCheckbox = (customAttributes, key) => {
|
||||
return customAttributes.find(attr => {
|
||||
return (
|
||||
attr.attribute_key === key && attr.attribute_display_type === 'checkbox'
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const isCustomAttributeList = (customAttributes, type) => {
|
||||
return customAttributes.find(attr => {
|
||||
return (
|
||||
attr.attribute_key === type && attr.attribute_display_type === 'list'
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const getOperatorTypes = key => {
|
||||
const operatorMap = {
|
||||
list: OPERATOR_TYPES_1,
|
||||
text: OPERATOR_TYPES_3,
|
||||
number: OPERATOR_TYPES_1,
|
||||
link: OPERATOR_TYPES_1,
|
||||
date: OPERATOR_TYPES_4,
|
||||
checkbox: OPERATOR_TYPES_1,
|
||||
};
|
||||
|
||||
return operatorMap[key] || OPERATOR_TYPES_1;
|
||||
};
|
||||
|
||||
export const generateCustomAttributeTypes = (customAttributes, type) => {
|
||||
return customAttributes.map(attr => {
|
||||
return {
|
||||
key: attr.attribute_key,
|
||||
name: attr.attribute_display_name,
|
||||
inputType: getCustomAttributeInputType(attr.attribute_display_type),
|
||||
filterOperators: getOperatorTypes(attr.attribute_display_type),
|
||||
customAttributeType: type,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const generateConditionOptions = (options, key = 'id') => {
|
||||
return options.map(i => {
|
||||
return {
|
||||
id: i[key],
|
||||
name: i.title,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const getActionOptions = ({ teams, labels, type }) => {
|
||||
const actionsMap = {
|
||||
assign_team: teams,
|
||||
send_email_to_team: teams,
|
||||
add_label: generateConditionOptions(labels, 'title'),
|
||||
};
|
||||
return actionsMap[type];
|
||||
};
|
||||
|
||||
export const getConditionOptions = ({
|
||||
agents,
|
||||
booleanFilterOptions,
|
||||
campaigns,
|
||||
contacts,
|
||||
countries,
|
||||
customAttributes,
|
||||
inboxes,
|
||||
languages,
|
||||
statusFilterOptions,
|
||||
teams,
|
||||
type,
|
||||
}) => {
|
||||
if (isCustomAttributeCheckbox(customAttributes, type)) {
|
||||
return booleanFilterOptions;
|
||||
}
|
||||
|
||||
if (isCustomAttributeList(customAttributes, type)) {
|
||||
return getCustomAttributeListDropdownValues(customAttributes, type);
|
||||
}
|
||||
|
||||
const conditionFilterMaps = {
|
||||
status: statusFilterOptions,
|
||||
assignee_id: agents,
|
||||
contact: contacts,
|
||||
inbox_id: inboxes,
|
||||
team_id: teams,
|
||||
campaigns: generateConditionOptions(campaigns),
|
||||
browser_language: languages,
|
||||
country_code: countries,
|
||||
message_type: MESSAGE_CONDITION_VALUES,
|
||||
};
|
||||
|
||||
return conditionFilterMaps[type];
|
||||
};
|
||||
|
||||
export const getFileName = (action, files = []) => {
|
||||
const blobId = action.action_params[0];
|
||||
if (!blobId) return '';
|
||||
if (action.action_name === 'send_attachment') {
|
||||
const file = files.find(item => item.blob_id === blobId);
|
||||
if (file) return file.filename.toString();
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export const getDefaultConditions = eventName => {
|
||||
if (eventName === 'message_created') {
|
||||
return [
|
||||
{
|
||||
attribute_key: 'message_type',
|
||||
filter_operator: 'equal_to',
|
||||
values: '',
|
||||
query_operator: 'and',
|
||||
custom_attribute_type: '',
|
||||
},
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
attribute_key: 'status',
|
||||
filter_operator: 'equal_to',
|
||||
values: '',
|
||||
query_operator: 'and',
|
||||
custom_attribute_type: '',
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export const getDefaultActions = () => {
|
||||
return [
|
||||
{
|
||||
action_name: 'assign_team',
|
||||
action_params: [],
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export const filterCustomAttributes = customAttributes => {
|
||||
return customAttributes.map(attr => {
|
||||
return {
|
||||
key: attr.attribute_key,
|
||||
name: attr.attribute_display_name,
|
||||
type: attr.attribute_display_type,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const getStandardAttributeInputType = (automationTypes, event, key) => {
|
||||
return automationTypes[event].conditions.find(item => item.key === key)
|
||||
.inputType;
|
||||
};
|
||||
|
||||
export const generateAutomationPayload = payload => {
|
||||
const automation = JSON.parse(JSON.stringify(payload));
|
||||
automation.conditions[automation.conditions.length - 1].query_operator = null;
|
||||
automation.conditions = filterQueryGenerator(automation.conditions).payload;
|
||||
automation.actions = actionQueryGenerator(automation.actions);
|
||||
return automation;
|
||||
};
|
||||
|
||||
export const isCustomAttribute = (attrs, key) => {
|
||||
return attrs.find(attr => attr.key === key);
|
||||
};
|
||||
|
||||
export const generateCustomAttributes = (
|
||||
conversationAttributes = [],
|
||||
contactAttribtues = [],
|
||||
conversationlabel,
|
||||
contactlabel
|
||||
) => {
|
||||
const customAttributes = [];
|
||||
if (conversationAttributes.length) {
|
||||
customAttributes.push(
|
||||
{
|
||||
key: `conversation_custom_attribute`,
|
||||
name: conversationlabel,
|
||||
disabled: true,
|
||||
},
|
||||
...conversationAttributes
|
||||
);
|
||||
}
|
||||
if (contactAttribtues.length) {
|
||||
customAttributes.push(
|
||||
{
|
||||
key: `contact_custom_attribute`,
|
||||
name: contactlabel,
|
||||
disabled: true,
|
||||
},
|
||||
...contactAttribtues
|
||||
);
|
||||
}
|
||||
return customAttributes;
|
||||
};
|
||||
@@ -1,15 +1,3 @@
|
||||
const lowerCaseValues = (operator, values) => {
|
||||
if (operator === 'equal_to' || operator === 'not_equal_to') {
|
||||
values = values.map(val => {
|
||||
if (typeof val === 'string') {
|
||||
return val.toLowerCase();
|
||||
}
|
||||
return val;
|
||||
});
|
||||
}
|
||||
return values;
|
||||
};
|
||||
|
||||
const generatePayload = data => {
|
||||
// Make a copy of data to avoid vue data reactivity issues
|
||||
const filters = JSON.parse(JSON.stringify(data));
|
||||
@@ -23,8 +11,6 @@ const generatePayload = data => {
|
||||
} else {
|
||||
item.values = [item.values];
|
||||
}
|
||||
// Convert all values to lowerCase if operator_type is 'equal_to' or 'not_equal_to'
|
||||
item.values = lowerCaseValues(item.filter_operator, item.values);
|
||||
return item;
|
||||
});
|
||||
// For every query added, the query_operator is set default to and so the
|
||||
|
||||
@@ -5,7 +5,7 @@ const testData = [
|
||||
attribute_key: 'status',
|
||||
filter_operator: 'equal_to',
|
||||
values: [
|
||||
{ id: 'PENDING', name: 'Pending' },
|
||||
{ id: 'pending', name: 'Pending' },
|
||||
{ id: 'resolved', name: 'Resolved' },
|
||||
],
|
||||
query_operator: 'and',
|
||||
@@ -18,7 +18,7 @@ const testData = [
|
||||
account_id: 1,
|
||||
auto_offline: true,
|
||||
confirmed: true,
|
||||
email: 'fayazara@gmail.com',
|
||||
email: 'fayaz@test.com',
|
||||
available_name: 'Fayaz',
|
||||
name: 'Fayaz',
|
||||
role: 'agent',
|
||||
@@ -52,7 +52,7 @@ const finalResult = {
|
||||
{
|
||||
attribute_key: 'id',
|
||||
filter_operator: 'equal_to',
|
||||
values: ['this is a test'],
|
||||
values: ['This is a test'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user