feat: Add advanced contact filters (#3471)

Co-authored-by: Tejaswini <tejaswini@chatwoot.com>
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Fayaz Ahmed
2021-12-03 08:42:44 +05:30
committed by GitHub
parent 1c29f5bbe4
commit d7cfe6858e
30 changed files with 716 additions and 43 deletions

View File

@@ -179,4 +179,27 @@ export const actions = {
commit(types.SET_CONTACT_UI_FLAG, { isUpdating: false });
}
},
filter: async ({ commit }, { page = 1, sortAttr, queryPayload } = {}) => {
commit(types.SET_CONTACT_UI_FLAG, { isFetching: true });
try {
const {
data: { payload, meta },
} = await ContactAPI.filter(page, sortAttr, queryPayload);
commit(types.CLEAR_CONTACTS);
commit(types.SET_CONTACTS, payload);
commit(types.SET_CONTACT_META, meta);
commit(types.SET_CONTACT_UI_FLAG, { isFetching: false });
} catch (error) {
commit(types.SET_CONTACT_UI_FLAG, { isFetching: false });
}
},
setContactFilters({ commit }, data) {
commit(types.SET_CONTACT_FILTERS, data);
},
clearContactFilters({ commit }) {
commit(types.CLEAR_CONTACT_FILTERS);
},
};

View File

@@ -12,4 +12,7 @@ export const getters = {
getMeta: $state => {
return $state.meta;
},
getAppliedContactFilters: _state => {
return _state.appliedFilters;
},
};

View File

@@ -17,6 +17,7 @@ const state = {
isDeleting: false,
},
sortOrder: [],
appliedFilters: [],
};
export default {

View File

@@ -66,4 +66,12 @@ export const mutations = {
}
});
},
[types.SET_CONTACT_FILTERS](_state, data) {
_state.appliedFilters = data;
},
[types.CLEAR_CONTACT_FILTERS](_state) {
_state.appliedFilters = [];
},
};

View File

@@ -31,7 +31,7 @@ const getters = {
return isChatMine;
});
},
getAppliedFilters: _state => {
getAppliedConversationFilters: _state => {
return _state.appliedFilters;
},
getUnAssignedChats: _state => activeFilters => {

View File

@@ -6,9 +6,21 @@ import {
DuplicateContactException,
ExceptionWithMessage,
} from '../../../../../shared/helpers/CustomErrors';
import { filterApiResponse } from './filterApiResponse';
const { actions } = Contacts;
const filterQueryData = {
payload: [
{
attribute_key: 'email',
filter_operator: 'contains',
values: ['fayaz'],
query_operator: null,
},
],
};
const commit = jest.fn();
global.axios = axios;
jest.mock('axios');
@@ -228,7 +240,7 @@ describe('#actions', () => {
]);
});
});
describe('#deleteCustomAttributes', () => {
describe('#deleteCustomAttributes', () => {
it('sends correct mutations if API is success', async () => {
axios.post.mockResolvedValue({ data: { payload: contactList[0] } });
await actions.deleteCustomAttributes(
@@ -247,4 +259,43 @@ describe('#actions', () => {
).rejects.toThrow(Error);
});
});
describe('#fetchFilteredContacts', () => {
it('fetches filtered conversations with a mock commit', async () => {
axios.post.mockResolvedValue({
data: filterApiResponse,
});
await actions.filter({ commit }, filterQueryData);
expect(commit).toHaveBeenCalledTimes(5);
expect(commit.mock.calls).toEqual([
['SET_CONTACT_UI_FLAG', { isFetching: true }],
['CLEAR_CONTACTS'],
['SET_CONTACTS', filterApiResponse.payload],
['SET_CONTACT_META', filterApiResponse.meta],
['SET_CONTACT_UI_FLAG', { isFetching: false }],
]);
});
});
describe('#setContactsFilter', () => {
it('commits the correct mutation and sets filter state', () => {
const filters = [
{
attribute_key: 'email',
filter_operator: 'contains',
values: ['fayaz'],
query_operator: 'and',
},
];
actions.setContactFilters({ commit }, filters);
expect(commit.mock.calls).toEqual([[types.SET_CONTACT_FILTERS, filters]]);
});
});
describe('#clearContactFilters', () => {
it('commits the correct mutation and clears filter state', () => {
actions.clearContactFilters({ commit });
expect(commit.mock.calls).toEqual([[types.CLEAR_CONTACT_FILTERS]]);
});
});
});

View File

@@ -0,0 +1,38 @@
export const filterApiResponse = {
meta: {
count: {
all_count: 2,
},
current_page: '1',
},
payload: [
{
additional_attributes: {},
availability_status: 'offline',
email: 'fayaz@g.com',
id: 8,
name: 'fayaz',
phone_number: null,
identifier: null,
thumbnail:
'https://www.gravatar.com/avatar/f2e86d3a78353cdf51002f44cf6ea846?d=404',
custom_attributes: {},
conversations_count: 1,
last_activity_at: 1631081845,
},
{
additional_attributes: {},
availability_status: 'offline',
email: 'fayaz@gma.com',
id: 9,
name: 'fayaz',
phone_number: null,
identifier: null,
thumbnail:
'https://www.gravatar.com/avatar/792af86e3ad4591552e1025a6415baa6?d=404',
custom_attributes: {},
conversations_count: 1,
last_activity_at: 1631614585,
},
],
};

View File

@@ -36,4 +36,18 @@ describe('#getters', () => {
isUpdating: false,
});
});
it('getAppliedContactFilters', () => {
const filters = [
{
attribute_key: 'email',
filter_operator: 'contains',
values: 'a',
query_operator: null,
},
];
const state = {
appliedFilters: filters,
};
expect(getters.getAppliedContactFilters(state)).toEqual(filters);
});
});

View File

@@ -64,4 +64,42 @@ describe('#mutations', () => {
});
});
});
describe('#SET_CONTACT_FILTERS', () => {
it('set contact filter', () => {
const appliedFilters = [
{
attribute_key: 'name',
filter_operator: 'equal_to',
values: ['fayaz'],
query_operator: 'and',
},
];
mutations[types.SET_CONTACT_FILTERS](appliedFilters);
expect(appliedFilters).toEqual([
{
attribute_key: 'name',
filter_operator: 'equal_to',
values: ['fayaz'],
query_operator: 'and',
},
]);
});
});
describe('#CLEAR_CONTACT_FILTERS', () => {
it('clears applied contact filters', () => {
const state = {
appliedFilters: [
{
attribute_key: 'name',
filter_operator: 'equal_to',
values: ['fayaz'],
query_operator: 'and',
},
],
};
mutations[types.CLEAR_CONTACT_FILTERS](state);
expect(state.appliedFilters).toEqual([]);
});
});
});

View File

@@ -115,8 +115,8 @@ describe('#getters', () => {
});
});
describe('#getAppliedFilters', () => {
it('getAppliedFilters', () => {
describe('#getAppliedConversationFilters', () => {
it('getAppliedConversationFilters', () => {
const filtersList = [
{
attribute_key: 'status',
@@ -128,7 +128,7 @@ describe('#getters', () => {
const state = {
appliedFilters: filtersList,
};
expect(getters.getAppliedFilters(state)).toEqual(filtersList);
expect(getters.getAppliedConversationFilters(state)).toEqual(filtersList);
});
});
});

View File

@@ -108,6 +108,8 @@ export default {
EDIT_CONTACT: 'EDIT_CONTACT',
DELETE_CONTACT: 'DELETE_CONTACT',
UPDATE_CONTACTS_PRESENCE: 'UPDATE_CONTACTS_PRESENCE',
SET_CONTACT_FILTERS: 'SET_CONTACT_FILTERS',
CLEAR_CONTACT_FILTERS: 'CLEAR_CONTACT_FILTERS',
// Notifications
SET_NOTIFICATIONS_META: 'SET_NOTIFICATIONS_META',