mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 11:37:58 +00:00
This pull request includes significant changes to the filtering logic for conversations in the frontend, here's a summary of the changes This includes adding a `matchesFilters` method that evaluates a conversation against the applied filters. It does so by first evaluating all the conditions, and later converting the results into a JSONLogic object that can be evaluated according to Postgres operator precedence ### Alignment Specs To ensure the frontend and backend implementations always align, we've added tests on both sides with same cases, for anyone fixing any regressions found in the frontend implementation, they need to ensure the existing tests always pass. Test Case | JavaScript Spec | Ruby Spec | Match? -- | -- | -- | -- **A AND B OR C** | Present | Present | Yes Matches when all conditions are true | Present | Present | Yes Matches when first condition is false but third is true | Present | Present | Yes Matches when first and second conditions are false but third is true | Present | Present | Yes Does not match when all conditions are false | Present | Present | Yes **A OR B AND C** | Present | Present | Yes Matches when first condition is true | Present | Present | Yes Matches when second and third conditions are true | Present | Present | Yes **A AND B OR C AND D** | Present | Present | Yes Matches when first two conditions are true | Present | Present | Yes Matches when last two conditions are true | Present | Present | Yes **Mixed Operators (A AND (B OR C) AND D)** | Present | Present | Yes Matches when all conditions in the chain are true | Present | Present | Yes Does not match when the last condition is false | Present | Present | Yes --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Pranav <pranav@chatwoot.com>
131 lines
3.6 KiB
JavaScript
131 lines
3.6 KiB
JavaScript
export const getInputType = (key, operator, filterTypes) => {
|
|
if (key === 'created_at' || key === 'last_activity_at')
|
|
if (operator === 'days_before') return 'plain_text';
|
|
const type = filterTypes.find(filter => filter.attributeKey === key);
|
|
return type?.inputType;
|
|
};
|
|
|
|
export const generateCustomAttributesInputType = type => {
|
|
const filterInputTypes = {
|
|
text: 'string',
|
|
number: 'string',
|
|
date: 'string',
|
|
checkbox: 'multi_select',
|
|
list: 'multi_select',
|
|
link: 'string',
|
|
};
|
|
return filterInputTypes[type];
|
|
};
|
|
|
|
export const getAttributeInputType = (key, allCustomAttributes) => {
|
|
const customAttribute = allCustomAttributes.find(
|
|
attr => attr.attribute_key === key
|
|
);
|
|
|
|
const { attribute_display_type } = customAttribute;
|
|
const filterInputTypes = generateCustomAttributesInputType(
|
|
attribute_display_type
|
|
);
|
|
return filterInputTypes;
|
|
};
|
|
|
|
export const getValuesName = (values, list, idKey, nameKey) => {
|
|
const item = list?.find(v => v[idKey] === values[0]);
|
|
return {
|
|
id: values[0],
|
|
name: item ? item[nameKey] : values[0],
|
|
};
|
|
};
|
|
|
|
export const getValuesForStatus = values => {
|
|
return values.map(value => ({ id: value, name: value }));
|
|
};
|
|
|
|
const getValuesForLabels = (values, labels) => {
|
|
const selectedLabels = labels.filter(label => values.includes(label.title));
|
|
return selectedLabels.map(({ title }) => ({
|
|
id: title,
|
|
name: title,
|
|
}));
|
|
};
|
|
|
|
const getValuesForLanguages = (values, languages) => {
|
|
const selectedLanguages = languages.filter(language =>
|
|
values.includes(language.id)
|
|
);
|
|
return selectedLanguages.map(({ id, name }) => ({
|
|
id: id.toLowerCase(),
|
|
name: name,
|
|
}));
|
|
};
|
|
|
|
const getValuesForCountries = (values, countries) => {
|
|
const selectedCountries = countries.filter(country =>
|
|
values.includes(country.id)
|
|
);
|
|
return selectedCountries.map(({ id, name }) => ({
|
|
id: id,
|
|
name: name,
|
|
}));
|
|
};
|
|
|
|
const getValuesForPriority = (values, priority) => {
|
|
return priority.filter(option => values.includes(option.id));
|
|
};
|
|
|
|
export const getValuesForFilter = (filter, params) => {
|
|
const { attribute_key, values } = filter;
|
|
const {
|
|
languages,
|
|
countries,
|
|
agents,
|
|
inboxes,
|
|
teams,
|
|
campaigns,
|
|
labels,
|
|
priority,
|
|
} = params;
|
|
switch (attribute_key) {
|
|
case 'status':
|
|
return getValuesForStatus(values);
|
|
case 'assignee_id':
|
|
return getValuesName(values, agents, 'id', 'name');
|
|
case 'inbox_id':
|
|
return getValuesName(values, inboxes, 'id', 'name');
|
|
case 'team_id':
|
|
return getValuesName(values, teams, 'id', 'name');
|
|
case 'campaign_id':
|
|
return getValuesName(values, campaigns, 'id', 'title');
|
|
case 'labels':
|
|
return getValuesForLabels(values, labels);
|
|
case 'priority':
|
|
return getValuesForPriority(values, priority);
|
|
case 'browser_language':
|
|
return getValuesForLanguages(values, languages);
|
|
case 'country_code':
|
|
return getValuesForCountries(values, countries);
|
|
default:
|
|
return { id: values[0], name: values[0] };
|
|
}
|
|
};
|
|
|
|
export const generateValuesForEditCustomViews = (filter, params) => {
|
|
const { attribute_key, filter_operator, values } = filter;
|
|
const { filterTypes, allCustomAttributes } = params;
|
|
const inputType = getInputType(attribute_key, filter_operator, filterTypes);
|
|
|
|
if (inputType === undefined) {
|
|
const filterInputTypes = getAttributeInputType(
|
|
attribute_key,
|
|
allCustomAttributes
|
|
);
|
|
return filterInputTypes === 'string'
|
|
? values[0].toString()
|
|
: { id: values[0], name: values[0] };
|
|
}
|
|
|
|
return inputType === 'multi_select' || inputType === 'search_select'
|
|
? getValuesForFilter(filter, params)
|
|
: values[0].toString();
|
|
};
|