feat: Ability to edit saved folders (#7236)

* feat: Ability to edit saved filters

Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
This commit is contained in:
Sivin Varghese
2023-06-08 14:58:57 +05:30
committed by GitHub
parent 1017903ee1
commit d7314079c9
13 changed files with 706 additions and 68 deletions

View File

@@ -47,6 +47,14 @@
/>
</div>
<div v-if="hasActiveFolders">
<woot-button
v-tooltip.top-end="$t('FILTER.CUSTOM_VIEWS.EDIT.EDIT_BUTTON')"
size="tiny"
variant="smooth"
color-scheme="secondary"
icon="edit"
@click="onToggleAdvanceFiltersModal"
/>
<woot-button
v-tooltip.top-end="$t('FILTER.CUSTOM_VIEWS.DELETE.DELETE_BUTTON')"
size="tiny"
@@ -168,8 +176,11 @@
v-if="showAdvancedFilters"
:initial-filter-types="advancedFilterTypes"
:initial-applied-filters="appliedFilter"
:active-folder-name="activeFolderName"
:on-close="closeAdvanceFiltersModal"
:is-folder-view="hasActiveFolders"
@applyFilter="onApplyFilter"
@updateFolder="onUpdateSavedFilter"
/>
</woot-modal>
</div>
@@ -193,6 +204,9 @@ import DeleteCustomViews from 'dashboard/routes/dashboard/customviews/DeleteCust
import ConversationBulkActions from './widgets/conversation/conversationBulkActions/Index.vue';
import alertMixin from 'shared/mixins/alertMixin';
import filterMixin from 'shared/mixins/filterMixin';
import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
import countries from 'shared/constants/countries';
import { generateValuesForEditCustomViews } from 'dashboard/helper/customViewsHelper';
import {
hasPressedAltAndJKey,
@@ -289,6 +303,11 @@ export default {
appliedFilters: 'getAppliedConversationFilters',
folders: 'customViews/getCustomViews',
inboxes: 'inboxes/getInboxes',
agentList: 'agents/getAgents',
teamsList: 'teams/getTeams',
inboxesList: 'inboxes/getInboxes',
campaigns: 'campaigns/getAllCampaigns',
labels: 'labels/getLabels',
}),
hasAppliedFilters() {
return this.appliedFilters.length !== 0;
@@ -451,6 +470,9 @@ export default {
}
return undefined;
},
activeFolderName() {
return this.activeFolder?.name;
},
activeTeam() {
if (this.teamId) {
return this.$store.getters['teams/getTeam'](this.teamId);
@@ -483,9 +505,7 @@ export default {
this.resetAndFetchData();
},
activeFolder() {
if (!this.hasAppliedFilters) {
this.resetAndFetchData();
}
},
chatLists() {
this.chatsOnView = this.conversationList;
@@ -496,6 +516,10 @@ export default {
this.$store.dispatch('setChatSortFilter', this.activeSortBy);
this.resetAndFetchData();
if (this.hasActiveFolders) {
this.$store.dispatch('campaigns/get');
}
bus.$on('fetch_conversation_stats', () => {
this.$store.dispatch('conversationStats/get', this.conversationFilters);
});
@@ -508,6 +532,15 @@ export default {
this.$store.dispatch('emptyAllConversations');
this.fetchFilteredConversations(payload);
},
onUpdateSavedFilter(payload, folderName) {
const payloadData = {
...this.activeFolder,
name: folderName,
query: filterQueryGenerator(payload),
};
this.$store.dispatch('customViews/update', payloadData);
this.closeAdvanceFiltersModal();
},
onClickOpenAddFoldersModal() {
this.showAddFoldersModal = true;
},
@@ -521,15 +554,70 @@ export default {
this.showDeleteFoldersModal = false;
},
onToggleAdvanceFiltersModal() {
if (!this.hasAppliedFilters) {
if (!this.hasAppliedFilters && !this.hasActiveFolders) {
this.initializeExistingFilterToModal();
}
if (this.hasActiveFolders) {
this.initializeFolderToFilterModal(this.activeFolder);
}
this.showAdvancedFilters = true;
},
closeAdvanceFiltersModal() {
this.showAdvancedFilters = false;
this.appliedFilter = [];
},
setParamsForEditFolderModal() {
// Here we are setting the params for edit folder modal to show the existing values.
// For agent, team, inboxes,and campaigns we get only the id's from the query.
// So we are mapping the id's to the actual values.
// For labels we get the name of the label from the query.
// If we delete the label from the label list then we will not be able to show the label name.
// For custom attributes we get only attribute key.
// So we are mapping it to find the input type of the attribute to show in the edit folder modal.
const params = {
agents: this.agentList,
teams: this.teamsList,
inboxes: this.inboxesList,
labels: this.labels,
campaigns: this.campaigns,
languages: languages,
countries: countries,
filterTypes: advancedFilterTypes,
allCustomAttributes: this.$store.getters[
'attributes/getAttributesByModel'
]('conversation_attribute'),
};
return params;
},
initializeFolderToFilterModal(activeFolder) {
// Here we are setting the params for edit folder modal.
// To show the existing values. when we click on edit folder button.
// Here we get the query from the active folder.
// And we are mapping the query to the actual values.
// To show in the edit folder modal by the help of generateValuesForEditCustomViews helper.
const query = activeFolder?.query?.payload;
if (!Array.isArray(query)) return;
this.appliedFilter.push(
...query.map(filter => ({
attribute_key: filter.attribute_key,
attribute_model: filter.attribute_model,
filter_operator: filter.filter_operator,
values: Array.isArray(filter.values)
? generateValuesForEditCustomViews(
filter,
this.setParamsForEditFolderModal()
)
: [],
query_operator: filter.query_operator,
custom_attribute_type: filter.custom_attribute_type,
}))
);
},
getKeyboardListenerParams() {
const allConversations = this.$refs.activeConversation.querySelectorAll(
'div.conversations-list div.conversation'
@@ -575,6 +663,7 @@ export default {
}
},
resetAndFetchData() {
this.appliedFilter = [];
this.resetBulkActions();
this.$store.dispatch('conversationPage/reset');
this.$store.dispatch('emptyAllConversations');
@@ -587,7 +676,6 @@ export default {
return;
}
this.fetchConversations();
this.appliedFilter = [];
},
fetchConversations() {
this.$store

View File

@@ -1,9 +1,21 @@
<template>
<div class="column">
<woot-modal-header :header-title="$t('FILTER.TITLE')">
<p>{{ $t('FILTER.SUBTITLE') }}</p>
<woot-modal-header :header-title="filterModalHeaderTitle">
<p>{{ filterModalSubTitle }}</p>
</woot-modal-header>
<div class="row modal-content">
<div class="column modal-content">
<div v-if="isFolderView" class="columns">
<label class="input-label" :class="{ error: !activeFolderNewName }">
{{ $t('FILTER.FOLDER_LABEL') }}
<input v-model="activeFolderNewName" type="text" class="name-input" />
<span v-if="!activeFolderNewName" class="message">
{{ $t('FILTER.EMPTY_VALUE_ERROR') }}
</span>
</label>
<label class="input-label">
{{ $t('FILTER.FOLDER_QUERY_LABEL') }}
</label>
</div>
<div class="medium-12 columns filters-wrap">
<filter-input-box
v-for="(filter, i) in appliedFilters"
@@ -42,7 +54,14 @@
<woot-button class="button clear" @click.prevent="onClose">
{{ $t('FILTER.CANCEL_BUTTON_LABEL') }}
</woot-button>
<woot-button @click="submitFilterQuery">
<woot-button
v-if="isFolderView"
:disabled="!activeFolderNewName"
@click="updateSavedCustomViews"
>
{{ $t('FILTER.UPDATE_BUTTON_LABEL') }}
</woot-button>
<woot-button v-else @click="submitFilterQuery">
{{ $t('FILTER.SUBMIT_BUTTON_LABEL') }}
</woot-button>
</div>
@@ -81,6 +100,14 @@ export default {
type: Array,
default: () => [],
},
activeFolderName: {
type: String,
default: '',
},
isFolderView: {
type: Boolean,
default: false,
},
},
validations: {
appliedFilters: {
@@ -107,6 +134,7 @@ export default {
return {
show: true,
appliedFilters: this.initialAppliedFilters,
activeFolderNewName: this.activeFolderName,
filterTypes: this.initialFilterTypes,
filterAttributeGroups,
filterGroups: [],
@@ -119,6 +147,16 @@ export default {
...mapGetters({
getAppliedConversationFilters: 'getAppliedConversationFilters',
}),
filterModalHeaderTitle() {
return !this.isFolderView
? this.$t('FILTER.TITLE')
: this.$t('FILTER.EDIT_CUSTOM_FILTER');
},
filterModalSubTitle() {
return !this.isFolderView
? this.$t('FILTER.SUBTITLE')
: this.$t('FILTER.CUSTOM_VIEWS_SUBTITLE');
},
},
mounted() {
this.setFilterAttributes();
@@ -126,7 +164,7 @@ export default {
if (this.getAppliedConversationFilters.length) {
this.appliedFilters = [];
this.appliedFilters = [...this.getAppliedConversationFilters];
} else {
} else if (!this.isFolderView) {
this.appliedFilters.push({
attribute_key: 'status',
filter_operator: 'equal_to',
@@ -177,11 +215,11 @@ export default {
if (key === 'created_at' || key === 'last_activity_at')
if (operator === 'days_before') return 'plain_text';
const type = this.filterTypes.find(filter => filter.attributeKey === key);
return type.inputType;
return type?.inputType;
},
getOperators(key) {
const type = this.filterTypes.find(filter => filter.attributeKey === key);
return type.filterOperators;
return type?.filterOperators;
},
getDropdownValues(type) {
const statusFilters = this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS');
@@ -267,12 +305,31 @@ export default {
}
},
appendNewFilter() {
if (this.isFolderView) {
this.setQueryOperatorOnLastQuery();
} else {
this.appliedFilters.push({
attribute_key: 'status',
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
});
}
},
setQueryOperatorOnLastQuery() {
const lastItemIndex = this.appliedFilters.length - 1;
this.appliedFilters[lastItemIndex] = {
...this.appliedFilters[lastItemIndex],
query_operator: 'and',
};
this.$nextTick(() => {
this.appliedFilters.push({
attribute_key: 'status',
filter_operator: 'equal_to',
values: '',
query_operator: 'and',
});
});
},
removeFilter(index) {
if (this.appliedFilters.length <= 1) {
@@ -296,6 +353,9 @@ export default {
})),
});
},
updateSavedCustomViews() {
this.$emit('updateFolder', this.appliedFilters, this.activeFolderNewName);
},
resetFilter(index, currentFilter) {
this.appliedFilters[index].filter_operator = this.filterTypes.find(
filter => filter.attributeKey === currentFilter.attribute_key
@@ -322,4 +382,12 @@ export default {
.filter-actions {
margin-top: var(--space-normal);
}
.input-label {
margin-bottom: var(--space-smaller);
.name-input {
width: 50%;
}
}
</style>

View File

@@ -0,0 +1,119 @@
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 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,
}));
};
export const getValuesForFilter = (filter, params) => {
const { attribute_key, values } = filter;
const {
languages,
countries,
agents,
inboxes,
teams,
campaigns,
labels,
} = params;
switch (attribute_key) {
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 '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 inboxType = getInputType(attribute_key, filter_operator, filterTypes);
if (inboxType === undefined) {
const filterInputTypes = getAttributeInputType(
attribute_key,
allCustomAttributes
);
return filterInputTypes === 'string'
? values[0].toString()
: { id: values[0], name: values[0] };
}
return inboxType === 'multi_select' || inboxType === 'search_select'
? getValuesForFilter(filter, params)
: values[0].toString();
};

View File

@@ -1,9 +1,12 @@
const setArrayValues = item => {
return item.values[0]?.id ? item.values.map(val => val.id) : item.values;
};
const generatePayload = data => {
// Make a copy of data to avoid vue data reactivity issues
const filters = JSON.parse(JSON.stringify(data));
let payload = filters.map(item => {
if (Array.isArray(item.values)) {
item.values = item.values.map(val => val.id);
item.values = setArrayValues(item);
} else if (typeof item.values === 'object') {
item.values = [item.values.id];
} else if (!item.values) {

View File

@@ -0,0 +1,278 @@
import {
getAttributeInputType,
getInputType,
getValuesName,
getValuesForFilter,
generateValuesForEditCustomViews,
generateCustomAttributesInputType,
} from '../customViewsHelper';
import advancedFilterTypes from 'dashboard/components/widgets/conversation/advancedFilterItems/index';
describe('customViewsHelper', () => {
describe('#getInputType', () => {
it('should return plain_text if key is created_at or last_activity_at and operator is days_before', () => {
const filterTypes = [
{ attributeKey: 'created_at', inputType: 'date' },
{ attributeKey: 'last_activity_at', inputType: 'date' },
];
expect(getInputType('created_at', 'days_before', filterTypes)).toEqual(
'plain_text'
);
expect(
getInputType('last_activity_at', 'days_before', filterTypes)
).toEqual('plain_text');
});
it('should return inputType if key is not created_at or last_activity_at', () => {
const filterTypes = [
{ attributeKey: 'created_at', inputType: 'date' },
{ attributeKey: 'last_activity_at', inputType: 'date' },
{ attributeKey: 'test', inputType: 'string' },
];
expect(getInputType('test', 'days_before', filterTypes)).toEqual(
'string'
);
});
it('should return undefined if key is not created_at or last_activity_at and inputType is not present', () => {
const filterTypes = [
{ attributeKey: 'created_at', inputType: 'date' },
{ attributeKey: 'last_activity_at', inputType: 'date' },
{ attributeKey: 'test', inputType: 'string' },
];
expect(getInputType('test', 'days_before', filterTypes)).toEqual(
'string'
);
});
});
describe('#getAttributeInputType', () => {
it('should return multi_select if attribute_display_type is checkbox or list', () => {
const allCustomAttributes = [
{ attribute_key: 'test', attribute_display_type: 'checkbox' },
{ attribute_key: 'test2', attribute_display_type: 'list' },
];
expect(getAttributeInputType('test', allCustomAttributes)).toEqual(
'multi_select'
);
expect(getAttributeInputType('test2', allCustomAttributes)).toEqual(
'multi_select'
);
});
it('should return string if attribute_display_type is text, number, date or link', () => {
const allCustomAttributes = [
{ attribute_key: 'test', attribute_display_type: 'text' },
{ attribute_key: 'test2', attribute_display_type: 'number' },
{ attribute_key: 'test3', attribute_display_type: 'date' },
{ attribute_key: 'test4', attribute_display_type: 'link' },
];
expect(getAttributeInputType('test', allCustomAttributes)).toEqual(
'string'
);
expect(getAttributeInputType('test2', allCustomAttributes)).toEqual(
'string'
);
expect(getAttributeInputType('test3', allCustomAttributes)).toEqual(
'string'
);
expect(getAttributeInputType('test4', allCustomAttributes)).toEqual(
'string'
);
});
});
describe('#getValuesName', () => {
it('should return id and name if item is present', () => {
const list = [{ id: 1, name: 'test' }];
const idKey = 'id';
const nameKey = 'name';
const values = [1];
expect(getValuesName(values, list, idKey, nameKey)).toEqual({
id: 1,
name: 'test',
});
});
it('should return id and value if item is not present', () => {
const list = [{ id: 1, name: 'test' }];
const idKey = 'id';
const nameKey = 'name';
const values = [2];
expect(getValuesName(values, list, idKey, nameKey)).toEqual({
id: 2,
name: 2,
});
});
});
describe('#getValuesForFilter', () => {
it('should return id and name if attribute_key is assignee_id', () => {
const filter = { attribute_key: 'assignee_id', values: [1] };
const params = { agents: [{ id: 1, name: 'test' }] };
expect(getValuesForFilter(filter, params)).toEqual({
id: 1,
name: 'test',
});
});
it('should return id and name if attribute_key is inbox_id', () => {
const filter = { attribute_key: 'inbox_id', values: [1] };
const params = { inboxes: [{ id: 1, name: 'test' }] };
expect(getValuesForFilter(filter, params)).toEqual({
id: 1,
name: 'test',
});
});
it('should return id and name if attribute_key is team_id', () => {
const filter = { attribute_key: 'team_id', values: [1] };
const params = { teams: [{ id: 1, name: 'test' }] };
expect(getValuesForFilter(filter, params)).toEqual({
id: 1,
name: 'test',
});
});
it('should return id and title if attribute_key is campaign_id', () => {
const filter = { attribute_key: 'campaign_id', values: [1] };
const params = { campaigns: [{ id: 1, title: 'test' }] };
expect(getValuesForFilter(filter, params)).toEqual({
id: 1,
name: 'test',
});
});
it('should return id and title if attribute_key is labels', () => {
const filter = { attribute_key: 'labels', values: ['test'] };
const params = { labels: [{ title: 'test' }] };
expect(getValuesForFilter(filter, params)).toEqual([
{ id: 'test', name: 'test' },
]);
});
it('should return id and name if attribute_key is browser_language', () => {
const filter = { attribute_key: 'browser_language', values: ['en'] };
const params = { languages: [{ id: 'en', name: 'English' }] };
expect(getValuesForFilter(filter, params)).toEqual([
{ id: 'en', name: 'English' },
]);
});
it('should return id and name if attribute_key is country_code', () => {
const filter = { attribute_key: 'country_code', values: ['IN'] };
const params = { countries: [{ id: 'IN', name: 'India' }] };
expect(getValuesForFilter(filter, params)).toEqual([
{ id: 'IN', name: 'India' },
]);
});
it('should return id and name if attribute_key is not present', () => {
const filter = { attribute_key: 'test', values: [1] };
const params = {};
expect(getValuesForFilter(filter, params)).toEqual({
id: 1,
name: 1,
});
});
});
describe('#generateValuesForEditCustomViews', () => {
it('should return id and name if inboxType is multi_select or search_select', () => {
const filter = {
attribute_key: 'assignee_id',
filter_operator: 'and',
values: [1],
};
const params = {
filterTypes: advancedFilterTypes,
allCustomAttributes: [],
agents: [{ id: 1, name: 'test' }],
};
expect(generateValuesForEditCustomViews(filter, params)).toEqual({
id: 1,
name: 'test',
});
});
it('should return id and name if inboxType is not multi_select or search_select', () => {
const filter = {
attribute_key: 'assignee_id',
filter_operator: 'and',
values: [1],
};
const params = {
filterTypes: advancedFilterTypes,
allCustomAttributes: [],
agents: [{ id: 1, name: 'test' }],
};
expect(generateValuesForEditCustomViews(filter, params)).toEqual({
id: 1,
name: 'test',
});
});
it('should return id and name if inboxType is undefined', () => {
const filter = {
attribute_key: 'test2',
filter_operator: 'and',
values: [1],
};
const params = {
filterTypes: advancedFilterTypes,
allCustomAttributes: [
{ attribute_key: 'test', attribute_display_type: 'checkbox' },
{ attribute_key: 'test2', attribute_display_type: 'list' },
],
};
expect(generateValuesForEditCustomViews(filter, params)).toEqual({
id: 1,
name: 1,
});
});
it('should return value as string if filterInputTypes is string', () => {
const filter = {
attribute_key: 'test',
filter_operator: 'and',
values: [1],
};
const params = {
filterTypes: advancedFilterTypes,
allCustomAttributes: [
{ attribute_key: 'test', attribute_display_type: 'date' },
{ attribute_key: 'test2', attribute_display_type: 'list' },
],
};
expect(generateValuesForEditCustomViews(filter, params)).toEqual('1');
});
});
describe('#generateCustomAttributesInputType', () => {
it('should return string if type is text', () => {
expect(generateCustomAttributesInputType('text')).toEqual('string');
});
it('should return string if type is number', () => {
expect(generateCustomAttributesInputType('number')).toEqual('string');
});
it('should return string if type is date', () => {
expect(generateCustomAttributesInputType('date')).toEqual('string');
});
it('should return multi_select if type is checkbox', () => {
expect(generateCustomAttributesInputType('checkbox')).toEqual(
'multi_select'
);
});
it('should return multi_select if type is list', () => {
expect(generateCustomAttributesInputType('list')).toEqual('multi_select');
});
it('should return string if type is link', () => {
expect(generateCustomAttributesInputType('link')).toEqual('string');
});
});
});

View File

@@ -2,12 +2,17 @@
"FILTER": {
"TITLE": "Filter Conversations",
"SUBTITLE": "Add filters below and hit 'Apply filters' to filter conversations.",
"EDIT_CUSTOM_FILTER": "Edit Folder",
"CUSTOM_VIEWS_SUBTITLE": "Add or remove filters and update your folder.",
"ADD_NEW_FILTER": "Add Filter",
"FILTER_DELETE_ERROR": "You should have atleast one filter to save",
"SUBMIT_BUTTON_LABEL": "Apply filters",
"UPDATE_BUTTON_LABEL": "Update folder",
"CANCEL_BUTTON_LABEL": "Cancel",
"CLEAR_BUTTON_LABEL": "Clear Filters",
"EMPTY_VALUE_ERROR": "Value is required",
"FOLDER_LABEL": "Folder Name",
"FOLDER_QUERY_LABEL": "Folder Query",
"TOOLTIP_LABEL": "Filter conversations",
"QUERY_DROPDOWN_LABELS": {
"AND": "AND",
@@ -71,6 +76,9 @@
"ERROR_MESSAGE": "Error while creating segment"
}
},
"EDIT": {
"EDIT_BUTTON": "Edit folder"
},
"DELETE": {
"DELETE_BUTTON": "Delete filter",
"MODAL": {

View File

@@ -49,6 +49,18 @@ export const actions = {
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: false });
}
},
update: async function updateCustomViews({ commit }, obj) {
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true });
try {
const response = await CustomViewsAPI.update(obj.id, obj);
commit(types.UPDATE_CUSTOM_VIEW, response.data);
} catch (error) {
const errorMessage = error?.response?.data?.message;
throw new Error(errorMessage);
} finally {
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: false });
}
},
delete: async ({ commit }, { id, filterType }) => {
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: true });
try {
@@ -72,6 +84,7 @@ export const mutations = {
[types.ADD_CUSTOM_VIEW]: MutationHelpers.create,
[types.SET_CUSTOM_VIEW]: MutationHelpers.set,
[types.UPDATE_CUSTOM_VIEW]: MutationHelpers.update,
[types.DELETE_CUSTOM_VIEW]: MutationHelpers.destroy,
};

View File

@@ -1,7 +1,7 @@
import axios from 'axios';
import { actions } from '../../customViews';
import * as types from '../../../mutation-types';
import customViewList from './fixtures';
import { customViewList, updateCustomViewList } from './fixtures';
const commit = jest.fn();
global.axios = axios;
@@ -67,4 +67,28 @@ describe('#actions', () => {
]);
});
});
describe('#update', () => {
it('sends correct actions if API is success', async () => {
axios.patch.mockResolvedValue({ data: updateCustomViewList[0] });
await actions.update(
{ commit },
updateCustomViewList[0].id,
updateCustomViewList[0]
);
expect(commit.mock.calls).toEqual([
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true }],
[types.default.UPDATE_CUSTOM_VIEW, updateCustomViewList[0]],
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.patch.mockRejectedValue({ message: 'Incorrect header' });
await expect(actions.update({ commit }, 1)).rejects.toThrow(Error);
expect(commit.mock.calls).toEqual([
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true }],
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: false }],
]);
});
});
});

View File

@@ -1,4 +1,4 @@
export default [
export const customViewList = [
{
name: 'Custom view',
filter_type: 0,
@@ -34,3 +34,31 @@ export default [
},
},
];
export const updateCustomViewList = [
{
id: 1,
name: 'Open',
filter_type: 'conversation',
query: {
payload: [
{
attribute_key: 'status',
attribute_model: 'standard',
filter_operator: 'equal_to',
values: ['open'],
query_operator: 'and',
custom_attribute_type: '',
},
{
attribute_key: 'assignee_id',
filter_operator: 'equal_to',
values: [52],
custom_attribute_type: '',
},
],
},
created_at: '2022-02-08T03:17:38.761Z',
updated_at: '2023-06-05T13:57:48.478Z',
},
];

View File

@@ -1,5 +1,5 @@
import { getters } from '../../customViews';
import customViewList from './fixtures';
import { customViewList } from './fixtures';
describe('#getters', () => {
it('getCustomViews', () => {

View File

@@ -1,6 +1,6 @@
import types from '../../../mutation-types';
import { mutations } from '../../customViews';
import customViewList from './fixtures';
import { customViewList, updateCustomViewList } from './fixtures';
describe('#mutations', () => {
describe('#SET_CUSTOM_VIEW', () => {
@@ -26,4 +26,12 @@ describe('#mutations', () => {
expect(state.records).toEqual([customViewList[0]]);
});
});
describe('#UPDATE_CUSTOM_VIEW', () => {
it('update custom view record', () => {
const state = { records: [updateCustomViewList[0]] };
mutations[types.UPDATE_CUSTOM_VIEW](state, updateCustomViewList[0]);
expect(state.records).toEqual(updateCustomViewList);
});
});
});

View File

@@ -221,6 +221,7 @@ export default {
SET_CUSTOM_VIEW_UI_FLAG: 'SET_CUSTOM_VIEW_UI_FLAG',
SET_CUSTOM_VIEW: 'SET_CUSTOM_VIEW',
ADD_CUSTOM_VIEW: 'ADD_CUSTOM_VIEW',
UPDATE_CUSTOM_VIEW: 'UPDATE_CUSTOM_VIEW',
DELETE_CUSTOM_VIEW: 'DELETE_CUSTOM_VIEW',
// Bulk Actions