# Pull Request Template ## Description Adds a Contact condition to the conversation advanced filter so agents can search for an existing contact and filter conversations by `conversations.contact_id`. Fixes [CW-7239](https://linear.app/chatwoot/issue/CW-7239/contact-filter-for-conversations) ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? - `eval "$(rbenv init -)" && bundle exec rspec spec/services/conversations/filter_service_spec.rb` - `pnpm exec vitest --no-watch --no-cache --no-coverage --logHeapUsage app/javascript/dashboard/components-next/filter/helper/filterHelper.spec.js app/javascript/dashboard/store/modules/conversations/helpers/specs/filterHelpers.spec.js app/javascript/dashboard/helper/specs/customViewsHelper.spec.js app/javascript/dashboard/helper/specs/filterQueryGenerator.spec.js` - `pnpm eslint` - `eval "$(rbenv init -)" && bundle exec rubocop spec/services/conversations/filter_service_spec.rb` ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
141 lines
3.9 KiB
JavaScript
141 lines
3.9 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],
|
|
};
|
|
};
|
|
|
|
const getValuesForContact = (values, contacts) => ({
|
|
id: values[0],
|
|
name:
|
|
contacts?.find(contact => contact.id === values[0])?.name ||
|
|
`Contact #${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,
|
|
contacts,
|
|
} = 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 'contact_id':
|
|
return getValuesForContact(values, contacts);
|
|
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();
|
|
};
|