Merge develop into feat/voice-channel
Resolved merge conflicts in: - MessagesView.vue: Combined VoiceTimelineView component from voice channel feature with component list updates from develop - actionCable.js: Merged call event handlers (incoming_call, call_status_changed) with copilot message event handler from develop - message.rb: Combined voice call helper methods with send_update_event method from develop All conflicts resolved while preserving functionality from both branches.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import CopilotMessagesAPI from 'dashboard/api/captain/copilotMessages';
|
||||
import { createStore } from './storeFactory';
|
||||
|
||||
export default createStore({
|
||||
name: 'CopilotMessages',
|
||||
API: CopilotMessagesAPI,
|
||||
getters: {
|
||||
getMessagesByThreadId: state => copilotThreadId => {
|
||||
return state.records
|
||||
.filter(record => record.copilot_thread?.id === Number(copilotThreadId))
|
||||
.sort((a, b) => a.id - b.id);
|
||||
},
|
||||
},
|
||||
actions: mutationTypes => ({
|
||||
upsert({ commit }, data) {
|
||||
commit(mutationTypes.UPSERT, data);
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
import CopilotThreadsAPI from 'dashboard/api/captain/copilotThreads';
|
||||
import { createStore } from './storeFactory';
|
||||
|
||||
export default createStore({
|
||||
name: 'CopilotThreads',
|
||||
API: CopilotThreadsAPI,
|
||||
});
|
||||
@@ -1,5 +1,11 @@
|
||||
import { throwErrorMessage } from 'dashboard/store/utils/api';
|
||||
import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
|
||||
import {
|
||||
createRecord,
|
||||
deleteRecord,
|
||||
getRecords,
|
||||
showRecord,
|
||||
updateRecord,
|
||||
} from './storeFactoryHelper';
|
||||
|
||||
export const generateMutationTypes = name => {
|
||||
const capitalizedName = name.toUpperCase();
|
||||
@@ -10,6 +16,7 @@ export const generateMutationTypes = name => {
|
||||
EDIT: `EDIT_${capitalizedName}`,
|
||||
DELETE: `DELETE_${capitalizedName}`,
|
||||
SET_META: `SET_${capitalizedName}_META`,
|
||||
UPSERT: `UPSERT_${capitalizedName}`,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -33,7 +40,6 @@ export const createGetters = () => ({
|
||||
getMeta: state => state.meta,
|
||||
});
|
||||
|
||||
// store/mutations.js
|
||||
export const createMutations = mutationTypes => ({
|
||||
[mutationTypes.SET_UI_FLAG](state, data) {
|
||||
state.uiFlags = {
|
||||
@@ -51,78 +57,19 @@ export const createMutations = mutationTypes => ({
|
||||
[mutationTypes.ADD]: MutationHelpers.create,
|
||||
[mutationTypes.EDIT]: MutationHelpers.update,
|
||||
[mutationTypes.DELETE]: MutationHelpers.destroy,
|
||||
[mutationTypes.UPSERT]: MutationHelpers.setSingleRecord,
|
||||
});
|
||||
|
||||
// store/actions/crud.js
|
||||
export const createCrudActions = (API, mutationTypes) => ({
|
||||
async get({ commit }, params = {}) {
|
||||
commit(mutationTypes.SET_UI_FLAG, { fetchingList: true });
|
||||
try {
|
||||
const response = await API.get(params);
|
||||
commit(mutationTypes.SET, response.data.payload);
|
||||
commit(mutationTypes.SET_META, response.data.meta);
|
||||
return response.data.payload;
|
||||
} catch (error) {
|
||||
return throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(mutationTypes.SET_UI_FLAG, { fetchingList: false });
|
||||
}
|
||||
},
|
||||
|
||||
async show({ commit }, id) {
|
||||
commit(mutationTypes.SET_UI_FLAG, { fetchingItem: true });
|
||||
try {
|
||||
const response = await API.show(id);
|
||||
commit(mutationTypes.ADD, response.data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
return throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(mutationTypes.SET_UI_FLAG, { fetchingItem: false });
|
||||
}
|
||||
},
|
||||
|
||||
async create({ commit }, dataObj) {
|
||||
commit(mutationTypes.SET_UI_FLAG, { creatingItem: true });
|
||||
try {
|
||||
const response = await API.create(dataObj);
|
||||
commit(mutationTypes.ADD, response.data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
return throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(mutationTypes.SET_UI_FLAG, { creatingItem: false });
|
||||
}
|
||||
},
|
||||
|
||||
async update({ commit }, { id, ...updateObj }) {
|
||||
commit(mutationTypes.SET_UI_FLAG, { updatingItem: true });
|
||||
try {
|
||||
const response = await API.update(id, updateObj);
|
||||
commit(mutationTypes.EDIT, response.data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
return throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(mutationTypes.SET_UI_FLAG, { updatingItem: false });
|
||||
}
|
||||
},
|
||||
|
||||
async delete({ commit }, id) {
|
||||
commit(mutationTypes.SET_UI_FLAG, { deletingItem: true });
|
||||
try {
|
||||
await API.delete(id);
|
||||
commit(mutationTypes.DELETE, id);
|
||||
return id;
|
||||
} catch (error) {
|
||||
return throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(mutationTypes.SET_UI_FLAG, { deletingItem: false });
|
||||
}
|
||||
},
|
||||
get: getRecords(mutationTypes, API),
|
||||
show: showRecord(mutationTypes, API),
|
||||
create: createRecord(mutationTypes, API),
|
||||
update: updateRecord(mutationTypes, API),
|
||||
delete: deleteRecord(mutationTypes, API),
|
||||
});
|
||||
|
||||
export const createStore = options => {
|
||||
const { name, API, actions } = options;
|
||||
const { name, API, actions, getters } = options;
|
||||
const mutationTypes = generateMutationTypes(name);
|
||||
|
||||
const customActions = actions ? actions(mutationTypes) : {};
|
||||
@@ -130,7 +77,10 @@ export const createStore = options => {
|
||||
return {
|
||||
namespaced: true,
|
||||
state: createInitialState(),
|
||||
getters: createGetters(),
|
||||
getters: {
|
||||
...createGetters(),
|
||||
...(getters || {}),
|
||||
},
|
||||
mutations: createMutations(mutationTypes),
|
||||
actions: {
|
||||
...createCrudActions(API, mutationTypes),
|
||||
|
||||
@@ -0,0 +1,380 @@
|
||||
import { throwErrorMessage } from 'dashboard/store/utils/api';
|
||||
import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
|
||||
import {
|
||||
generateMutationTypes,
|
||||
createInitialState,
|
||||
createGetters,
|
||||
createMutations,
|
||||
createCrudActions,
|
||||
createStore,
|
||||
} from './storeFactory';
|
||||
|
||||
vi.mock('dashboard/store/utils/api', () => ({
|
||||
throwErrorMessage: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('shared/helpers/vuex/mutationHelpers', () => ({
|
||||
set: vi.fn(),
|
||||
create: vi.fn(),
|
||||
update: vi.fn(),
|
||||
destroy: vi.fn(),
|
||||
setSingleRecord: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('storeFactory', () => {
|
||||
describe('generateMutationTypes', () => {
|
||||
it('generates correct mutation types with capitalized name', () => {
|
||||
const result = generateMutationTypes('test');
|
||||
expect(result).toEqual({
|
||||
SET_UI_FLAG: 'SET_TEST_UI_FLAG',
|
||||
SET: 'SET_TEST',
|
||||
ADD: 'ADD_TEST',
|
||||
EDIT: 'EDIT_TEST',
|
||||
DELETE: 'DELETE_TEST',
|
||||
SET_META: 'SET_TEST_META',
|
||||
UPSERT: 'UPSERT_TEST',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createInitialState', () => {
|
||||
it('returns the correct initial state structure', () => {
|
||||
const result = createInitialState();
|
||||
expect(result).toEqual({
|
||||
records: [],
|
||||
meta: {},
|
||||
uiFlags: {
|
||||
fetchingList: false,
|
||||
fetchingItem: false,
|
||||
creatingItem: false,
|
||||
updatingItem: false,
|
||||
deletingItem: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createGetters', () => {
|
||||
it('returns getters with correct implementations', () => {
|
||||
const getters = createGetters();
|
||||
|
||||
const state = {
|
||||
records: [{ id: 2 }, { id: 1 }, { id: 3 }],
|
||||
uiFlags: { fetchingList: true },
|
||||
meta: { totalCount: 10, page: 1 },
|
||||
};
|
||||
expect(getters.getRecords(state)).toEqual([
|
||||
{ id: 3 },
|
||||
{ id: 2 },
|
||||
{ id: 1 },
|
||||
]);
|
||||
|
||||
expect(getters.getRecord(state)(2)).toEqual({ id: 2 });
|
||||
expect(getters.getRecord(state)(4)).toEqual({});
|
||||
|
||||
expect(getters.getUIFlags(state)).toEqual({
|
||||
fetchingList: true,
|
||||
});
|
||||
|
||||
expect(getters.getMeta(state)).toEqual({
|
||||
totalCount: 10,
|
||||
page: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createMutations', () => {
|
||||
it('creates mutations with correct implementations', () => {
|
||||
const mutationTypes = generateMutationTypes('test');
|
||||
const mutations = createMutations(mutationTypes);
|
||||
|
||||
const state = { uiFlags: { fetchingList: false } };
|
||||
mutations[mutationTypes.SET_UI_FLAG](state, { fetchingList: true });
|
||||
expect(state.uiFlags).toEqual({ fetchingList: true });
|
||||
|
||||
const metaState = { meta: {} };
|
||||
mutations[mutationTypes.SET_META](metaState, {
|
||||
total_count: '10',
|
||||
page: '2',
|
||||
});
|
||||
expect(metaState.meta).toEqual({ totalCount: 10, page: 2 });
|
||||
|
||||
expect(mutations[mutationTypes.SET]).toBe(MutationHelpers.set);
|
||||
expect(mutations[mutationTypes.ADD]).toBe(MutationHelpers.create);
|
||||
expect(mutations[mutationTypes.EDIT]).toBe(MutationHelpers.update);
|
||||
expect(mutations[mutationTypes.DELETE]).toBe(MutationHelpers.destroy);
|
||||
expect(mutations[mutationTypes.UPSERT]).toBe(
|
||||
MutationHelpers.setSingleRecord
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createCrudActions', () => {
|
||||
let API;
|
||||
let commit;
|
||||
let mutationTypes;
|
||||
let actions;
|
||||
|
||||
beforeEach(() => {
|
||||
API = {
|
||||
get: vi.fn(),
|
||||
show: vi.fn(),
|
||||
create: vi.fn(),
|
||||
update: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
};
|
||||
commit = vi.fn();
|
||||
mutationTypes = generateMutationTypes('test');
|
||||
actions = createCrudActions(API, mutationTypes);
|
||||
});
|
||||
|
||||
describe('get action', () => {
|
||||
it('handles successful API response', async () => {
|
||||
const payload = [{ id: 1 }];
|
||||
const meta = { total_count: 10, page: 1 };
|
||||
API.get.mockResolvedValue({ data: { payload, meta } });
|
||||
|
||||
const result = await actions.get({ commit });
|
||||
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
fetchingList: true,
|
||||
});
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET, payload);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_META, meta);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
fetchingList: false,
|
||||
});
|
||||
expect(result).toEqual(payload);
|
||||
});
|
||||
|
||||
it('handles API error', async () => {
|
||||
const error = new Error('API Error');
|
||||
API.get.mockRejectedValue(error);
|
||||
throwErrorMessage.mockReturnValue('Error thrown');
|
||||
|
||||
const result = await actions.get({ commit });
|
||||
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
fetchingList: true,
|
||||
});
|
||||
expect(throwErrorMessage).toHaveBeenCalledWith(error);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
fetchingList: false,
|
||||
});
|
||||
expect(result).toEqual('Error thrown');
|
||||
});
|
||||
});
|
||||
|
||||
describe('show action', () => {
|
||||
it('handles successful API response', async () => {
|
||||
const data = { id: 1, name: 'Test' };
|
||||
API.show.mockResolvedValue({ data });
|
||||
|
||||
const result = await actions.show({ commit }, 1);
|
||||
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
fetchingItem: true,
|
||||
});
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.ADD, data);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
fetchingItem: false,
|
||||
});
|
||||
expect(result).toEqual(data);
|
||||
});
|
||||
|
||||
it('handles API error', async () => {
|
||||
const error = new Error('API Error');
|
||||
API.show.mockRejectedValue(error);
|
||||
throwErrorMessage.mockReturnValue('Error thrown');
|
||||
|
||||
const result = await actions.show({ commit }, 1);
|
||||
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
fetchingItem: true,
|
||||
});
|
||||
expect(throwErrorMessage).toHaveBeenCalledWith(error);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
fetchingItem: false,
|
||||
});
|
||||
expect(result).toEqual('Error thrown');
|
||||
});
|
||||
});
|
||||
|
||||
describe('create action', () => {
|
||||
it('handles successful API response', async () => {
|
||||
const data = { id: 1, name: 'Test' };
|
||||
API.create.mockResolvedValue({ data });
|
||||
|
||||
const result = await actions.create({ commit }, { name: 'Test' });
|
||||
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
creatingItem: true,
|
||||
});
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.UPSERT, data);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
creatingItem: false,
|
||||
});
|
||||
expect(result).toEqual(data);
|
||||
});
|
||||
|
||||
it('handles API error', async () => {
|
||||
const error = new Error('API Error');
|
||||
API.create.mockRejectedValue(error);
|
||||
throwErrorMessage.mockReturnValue('Error thrown');
|
||||
|
||||
const result = await actions.create({ commit }, { name: 'Test' });
|
||||
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
creatingItem: true,
|
||||
});
|
||||
expect(throwErrorMessage).toHaveBeenCalledWith(error);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
creatingItem: false,
|
||||
});
|
||||
expect(result).toEqual('Error thrown');
|
||||
});
|
||||
});
|
||||
|
||||
describe('update action', () => {
|
||||
it('handles successful API response', async () => {
|
||||
const data = { id: 1, name: 'Updated' };
|
||||
API.update.mockResolvedValue({ data });
|
||||
|
||||
const result = await actions.update(
|
||||
{ commit },
|
||||
{ id: 1, name: 'Updated' }
|
||||
);
|
||||
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
updatingItem: true,
|
||||
});
|
||||
expect(API.update).toHaveBeenCalledWith(1, { name: 'Updated' });
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.EDIT, data);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
updatingItem: false,
|
||||
});
|
||||
expect(result).toEqual(data);
|
||||
});
|
||||
|
||||
it('handles API error', async () => {
|
||||
const error = new Error('API Error');
|
||||
API.update.mockRejectedValue(error);
|
||||
throwErrorMessage.mockReturnValue('Error thrown');
|
||||
|
||||
const result = await actions.update(
|
||||
{ commit },
|
||||
{ id: 1, name: 'Updated' }
|
||||
);
|
||||
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
updatingItem: true,
|
||||
});
|
||||
expect(throwErrorMessage).toHaveBeenCalledWith(error);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
updatingItem: false,
|
||||
});
|
||||
expect(result).toEqual('Error thrown');
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete action', () => {
|
||||
it('handles successful API response', async () => {
|
||||
API.delete.mockResolvedValue({});
|
||||
|
||||
const result = await actions.delete({ commit }, 1);
|
||||
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
deletingItem: true,
|
||||
});
|
||||
expect(API.delete).toHaveBeenCalledWith(1);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.DELETE, 1);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
deletingItem: false,
|
||||
});
|
||||
expect(result).toEqual(1);
|
||||
});
|
||||
|
||||
it('handles API error', async () => {
|
||||
const error = new Error('API Error');
|
||||
API.delete.mockRejectedValue(error);
|
||||
throwErrorMessage.mockReturnValue('Error thrown');
|
||||
|
||||
const result = await actions.delete({ commit }, 1);
|
||||
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
deletingItem: true,
|
||||
});
|
||||
expect(throwErrorMessage).toHaveBeenCalledWith(error);
|
||||
expect(commit).toHaveBeenCalledWith(mutationTypes.SET_UI_FLAG, {
|
||||
deletingItem: false,
|
||||
});
|
||||
expect(result).toEqual('Error thrown');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createStore', () => {
|
||||
it('creates a complete store with default options', () => {
|
||||
const API = {};
|
||||
const store = createStore({ name: 'test', API });
|
||||
|
||||
expect(store.namespaced).toBe(true);
|
||||
expect(store.state).toEqual(createInitialState());
|
||||
expect(Object.keys(store.getters)).toEqual([
|
||||
'getRecords',
|
||||
'getRecord',
|
||||
'getUIFlags',
|
||||
'getMeta',
|
||||
]);
|
||||
expect(Object.keys(store.mutations)).toEqual([
|
||||
'SET_TEST_UI_FLAG',
|
||||
'SET_TEST_META',
|
||||
'SET_TEST',
|
||||
'ADD_TEST',
|
||||
'EDIT_TEST',
|
||||
'DELETE_TEST',
|
||||
'UPSERT_TEST',
|
||||
]);
|
||||
expect(Object.keys(store.actions)).toEqual([
|
||||
'get',
|
||||
'show',
|
||||
'create',
|
||||
'update',
|
||||
'delete',
|
||||
]);
|
||||
});
|
||||
|
||||
it('creates a store with custom actions and getters', () => {
|
||||
const API = {};
|
||||
const customGetters = { customGetter: () => 'custom' };
|
||||
const customActions = () => ({
|
||||
customAction: () => 'custom',
|
||||
});
|
||||
|
||||
const store = createStore({
|
||||
name: 'test',
|
||||
API,
|
||||
getters: customGetters,
|
||||
actions: customActions,
|
||||
});
|
||||
|
||||
expect(store.getters).toHaveProperty('customGetter');
|
||||
expect(store.actions).toHaveProperty('customAction');
|
||||
expect(Object.keys(store.getters)).toEqual([
|
||||
'getRecords',
|
||||
'getRecord',
|
||||
'getUIFlags',
|
||||
'getMeta',
|
||||
'customGetter',
|
||||
]);
|
||||
expect(Object.keys(store.actions)).toEqual([
|
||||
'get',
|
||||
'show',
|
||||
'create',
|
||||
'update',
|
||||
'delete',
|
||||
'customAction',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
import { throwErrorMessage } from 'dashboard/store/utils/api';
|
||||
|
||||
export const getRecords =
|
||||
(mutationTypes, API) =>
|
||||
async ({ commit }, params = {}) => {
|
||||
commit(mutationTypes.SET_UI_FLAG, { fetchingList: true });
|
||||
try {
|
||||
const response = await API.get(params);
|
||||
commit(mutationTypes.SET, response.data.payload);
|
||||
commit(mutationTypes.SET_META, response.data.meta);
|
||||
return response.data.payload;
|
||||
} catch (error) {
|
||||
return throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(mutationTypes.SET_UI_FLAG, { fetchingList: false });
|
||||
}
|
||||
};
|
||||
|
||||
export const showRecord =
|
||||
(mutationTypes, API) =>
|
||||
async ({ commit }, id) => {
|
||||
commit(mutationTypes.SET_UI_FLAG, { fetchingItem: true });
|
||||
try {
|
||||
const response = await API.show(id);
|
||||
commit(mutationTypes.ADD, response.data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
return throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(mutationTypes.SET_UI_FLAG, { fetchingItem: false });
|
||||
}
|
||||
};
|
||||
|
||||
export const createRecord =
|
||||
(mutationTypes, API) =>
|
||||
async ({ commit }, dataObj) => {
|
||||
commit(mutationTypes.SET_UI_FLAG, { creatingItem: true });
|
||||
try {
|
||||
const response = await API.create(dataObj);
|
||||
commit(mutationTypes.UPSERT, response.data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
return throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(mutationTypes.SET_UI_FLAG, { creatingItem: false });
|
||||
}
|
||||
};
|
||||
|
||||
export const updateRecord =
|
||||
(mutationTypes, API) =>
|
||||
async ({ commit }, { id, ...updateObj }) => {
|
||||
commit(mutationTypes.SET_UI_FLAG, { updatingItem: true });
|
||||
try {
|
||||
const response = await API.update(id, updateObj);
|
||||
commit(mutationTypes.EDIT, response.data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
return throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(mutationTypes.SET_UI_FLAG, { updatingItem: false });
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteRecord =
|
||||
(mutationTypes, API) =>
|
||||
async ({ commit }, id) => {
|
||||
commit(mutationTypes.SET_UI_FLAG, { deletingItem: true });
|
||||
try {
|
||||
await API.delete(id);
|
||||
commit(mutationTypes.DELETE, id);
|
||||
return id;
|
||||
} catch (error) {
|
||||
return throwErrorMessage(error);
|
||||
} finally {
|
||||
commit(mutationTypes.SET_UI_FLAG, { deletingItem: false });
|
||||
}
|
||||
};
|
||||
@@ -52,6 +52,9 @@ import captainDocuments from './captain/document';
|
||||
import captainResponses from './captain/response';
|
||||
import captainInboxes from './captain/inboxes';
|
||||
import captainBulkActions from './captain/bulkActions';
|
||||
import copilotThreads from './captain/copilotThreads';
|
||||
import copilotMessages from './captain/copilotMessages';
|
||||
|
||||
const plugins = [];
|
||||
|
||||
export default createStore({
|
||||
@@ -108,6 +111,8 @@ export default createStore({
|
||||
captainResponses,
|
||||
captainInboxes,
|
||||
captainBulkActions,
|
||||
copilotThreads,
|
||||
copilotMessages,
|
||||
},
|
||||
plugins,
|
||||
});
|
||||
|
||||
@@ -63,8 +63,11 @@ export const actions = {
|
||||
});
|
||||
}
|
||||
},
|
||||
update: async ({ commit }, updateObj) => {
|
||||
commit(types.default.SET_ACCOUNT_UI_FLAG, { isUpdating: true });
|
||||
update: async ({ commit }, { options, ...updateObj }) => {
|
||||
if (options?.silent !== true) {
|
||||
commit(types.default.SET_ACCOUNT_UI_FLAG, { isUpdating: true });
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await AccountAPI.update('', updateObj);
|
||||
commit(types.default.EDIT_ACCOUNT, response.data);
|
||||
|
||||
@@ -172,6 +172,17 @@ export const actions = {
|
||||
commit(types.SET_AGENT_BOT_UI_FLAG, { isDisconnecting: false });
|
||||
}
|
||||
},
|
||||
|
||||
resetAccessToken: async ({ commit }, botId) => {
|
||||
try {
|
||||
const response = await AgentBotsAPI.resetAccessToken(botId);
|
||||
commit(types.EDIT_AGENT_BOT, response.data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throwErrorMessage(error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const mutations = {
|
||||
|
||||
@@ -135,6 +135,16 @@ export const actions = {
|
||||
}
|
||||
},
|
||||
|
||||
updatePassword: async ({ commit }, params) => {
|
||||
// eslint-disable-next-line no-useless-catch
|
||||
try {
|
||||
const response = await authAPI.profilePasswordUpdate(params);
|
||||
commit(types.SET_CURRENT_USER, response.data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
deleteAvatar: async ({ commit }) => {
|
||||
try {
|
||||
const response = await authAPI.deleteAvatar();
|
||||
@@ -213,6 +223,16 @@ export const actions = {
|
||||
}
|
||||
},
|
||||
|
||||
resetAccessToken: async ({ commit }) => {
|
||||
try {
|
||||
const response = await authAPI.resetAccessToken();
|
||||
commit(types.SET_CURRENT_USER, response.data);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
resendConfirmation: async () => {
|
||||
try {
|
||||
await authAPI.resendConfirmation();
|
||||
|
||||
@@ -75,6 +75,21 @@ export const actions = {
|
||||
}
|
||||
},
|
||||
|
||||
active: async ({ commit }, { page = 1, sortAttr } = {}) => {
|
||||
commit(types.SET_CONTACT_UI_FLAG, { isFetching: true });
|
||||
try {
|
||||
const {
|
||||
data: { payload, meta },
|
||||
} = await ContactAPI.active(page, sortAttr);
|
||||
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 });
|
||||
}
|
||||
},
|
||||
|
||||
show: async ({ commit }, { id }) => {
|
||||
commit(types.SET_CONTACT_UI_FLAG, { isFetchingItem: true });
|
||||
try {
|
||||
|
||||
@@ -5,12 +5,14 @@ export const initialState = {
|
||||
contactRecords: [],
|
||||
conversationRecords: [],
|
||||
messageRecords: [],
|
||||
articleRecords: [],
|
||||
uiFlags: {
|
||||
isFetching: false,
|
||||
isSearchCompleted: false,
|
||||
contact: { isFetching: false },
|
||||
conversation: { isFetching: false },
|
||||
message: { isFetching: false },
|
||||
article: { isFetching: false },
|
||||
},
|
||||
};
|
||||
|
||||
@@ -27,6 +29,9 @@ export const getters = {
|
||||
getMessageRecords(state) {
|
||||
return state.messageRecords;
|
||||
},
|
||||
getArticleRecords(state) {
|
||||
return state.articleRecords;
|
||||
},
|
||||
getUIFlags(state) {
|
||||
return state.uiFlags;
|
||||
},
|
||||
@@ -65,6 +70,7 @@ export const actions = {
|
||||
dispatch('contactSearch', { q }),
|
||||
dispatch('conversationSearch', { q }),
|
||||
dispatch('messageSearch', { q }),
|
||||
dispatch('articleSearch', { q }),
|
||||
]);
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
@@ -108,6 +114,17 @@ export const actions = {
|
||||
commit(types.MESSAGE_SEARCH_SET_UI_FLAG, { isFetching: false });
|
||||
}
|
||||
},
|
||||
async articleSearch({ commit }, { q, page = 1 }) {
|
||||
commit(types.ARTICLE_SEARCH_SET_UI_FLAG, { isFetching: true });
|
||||
try {
|
||||
const { data } = await SearchAPI.articles({ q, page });
|
||||
commit(types.ARTICLE_SEARCH_SET, data.payload.articles);
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
} finally {
|
||||
commit(types.ARTICLE_SEARCH_SET_UI_FLAG, { isFetching: false });
|
||||
}
|
||||
},
|
||||
async clearSearchResults({ commit }) {
|
||||
commit(types.CLEAR_SEARCH_RESULTS);
|
||||
},
|
||||
@@ -126,6 +143,9 @@ export const mutations = {
|
||||
[types.MESSAGE_SEARCH_SET](state, records) {
|
||||
state.messageRecords = [...state.messageRecords, ...records];
|
||||
},
|
||||
[types.ARTICLE_SEARCH_SET](state, records) {
|
||||
state.articleRecords = [...state.articleRecords, ...records];
|
||||
},
|
||||
[types.SEARCH_CONVERSATIONS_SET_UI_FLAG](state, uiFlags) {
|
||||
state.uiFlags = { ...state.uiFlags, ...uiFlags };
|
||||
},
|
||||
@@ -141,10 +161,14 @@ export const mutations = {
|
||||
[types.MESSAGE_SEARCH_SET_UI_FLAG](state, uiFlags) {
|
||||
state.uiFlags.message = { ...state.uiFlags.message, ...uiFlags };
|
||||
},
|
||||
[types.ARTICLE_SEARCH_SET_UI_FLAG](state, uiFlags) {
|
||||
state.uiFlags.article = { ...state.uiFlags.article, ...uiFlags };
|
||||
},
|
||||
[types.CLEAR_SEARCH_RESULTS](state) {
|
||||
state.contactRecords = [];
|
||||
state.conversationRecords = [];
|
||||
state.messageRecords = [];
|
||||
state.articleRecords = [];
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -327,6 +327,16 @@ const actions = {
|
||||
}
|
||||
},
|
||||
|
||||
deleteConversation: async ({ commit, dispatch }, conversationId) => {
|
||||
try {
|
||||
await ConversationApi.delete(conversationId);
|
||||
commit(types.DELETE_CONVERSATION, conversationId);
|
||||
dispatch('conversationStats/get', {}, { root: true });
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
},
|
||||
|
||||
addConversation({ commit, state, dispatch, rootState }, conversation) {
|
||||
const { currentInbox, appliedFilters } = state;
|
||||
const {
|
||||
|
||||
@@ -18,13 +18,34 @@ const getters = {
|
||||
getAllConversations: ({ allConversations, chatSortFilter: sortKey }) => {
|
||||
return allConversations.sort((a, b) => sortComparator(a, b, sortKey));
|
||||
},
|
||||
getFilteredConversations: ({
|
||||
allConversations,
|
||||
chatSortFilter,
|
||||
appliedFilters,
|
||||
}) => {
|
||||
getFilteredConversations: (
|
||||
{ allConversations, chatSortFilter, appliedFilters },
|
||||
_,
|
||||
__,
|
||||
rootGetters
|
||||
) => {
|
||||
const currentUser = rootGetters.getCurrentUser;
|
||||
const currentUserId = rootGetters.getCurrentUser.id;
|
||||
const currentAccountId = rootGetters.getCurrentAccountId;
|
||||
|
||||
const permissions = getUserPermissions(currentUser, currentAccountId);
|
||||
const userRole = getUserRole(currentUser, currentAccountId);
|
||||
|
||||
return allConversations
|
||||
.filter(conversation => matchesFilters(conversation, appliedFilters))
|
||||
.filter(conversation => {
|
||||
const matchesFilterResult = matchesFilters(
|
||||
conversation,
|
||||
appliedFilters
|
||||
);
|
||||
const allowedForRole = applyRoleFilter(
|
||||
conversation,
|
||||
userRole,
|
||||
permissions,
|
||||
currentUserId
|
||||
);
|
||||
|
||||
return matchesFilterResult && allowedForRole;
|
||||
})
|
||||
.sort((a, b) => sortComparator(a, b, chatSortFilter));
|
||||
},
|
||||
getSelectedChat: ({ selectedChatId, allConversations }) => {
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
* 3. Nested properties in custom_attributes (conversation_type, etc.)
|
||||
*/
|
||||
import jsonLogic from 'json-logic-js';
|
||||
import { coerceToDate } from '@chatwoot/utils';
|
||||
|
||||
/**
|
||||
* Gets a value from a conversation based on the attribute key
|
||||
@@ -157,6 +158,20 @@ const contains = (filterValue, conversationValue) => {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares two date values using a comparison function
|
||||
* @param {*} conversationValue - The conversation value to compare
|
||||
* @param {*} filterValue - The filter value to compare against
|
||||
* @param {Function} compareFn - The comparison function to apply
|
||||
* @returns {Boolean} - Returns true if the comparison succeeds, false otherwise
|
||||
*/
|
||||
const compareDates = (conversationValue, filterValue, compareFn) => {
|
||||
const conversationDate = coerceToDate(conversationValue);
|
||||
const filterDate = coerceToDate(filterValue);
|
||||
if (conversationDate === null || filterDate === null) return false;
|
||||
return compareFn(conversationDate, filterDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a value matches a filter condition
|
||||
* @param {*} conversationValue - The value to check
|
||||
@@ -195,10 +210,10 @@ const matchesCondition = (conversationValue, filter) => {
|
||||
return false; // We already handled null/undefined above
|
||||
|
||||
case 'is_greater_than':
|
||||
return new Date(conversationValue) > new Date(filterValue);
|
||||
return compareDates(conversationValue, filterValue, (a, b) => a > b);
|
||||
|
||||
case 'is_less_than':
|
||||
return new Date(conversationValue) < new Date(filterValue);
|
||||
return compareDates(conversationValue, filterValue, (a, b) => a < b);
|
||||
|
||||
case 'days_before': {
|
||||
const today = new Date();
|
||||
@@ -347,6 +362,7 @@ export const matchesFilters = (conversation, filters) => {
|
||||
conversation,
|
||||
filters[0].attribute_key
|
||||
);
|
||||
|
||||
return matchesCondition(value, filters[0]);
|
||||
}
|
||||
|
||||
|
||||
+235
@@ -463,6 +463,241 @@ describe('filterHelpers', () => {
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test conversation with 10-digit timestamp (seconds) vs standard date filter
|
||||
it('should match conversation with 10-digit timestamp against date string filter', () => {
|
||||
const conversation = { created_at: 1647777600 }; // March 20, 2022 in seconds (10 digits)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19', // Standard YYYY-MM-DD format
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test conversation with 13-digit timestamp (milliseconds) vs standard date filter
|
||||
it('should match conversation with 13-digit timestamp against date string filter', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022 in milliseconds (13 digits)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19', // Standard YYYY-MM-DD format
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test conversation with string timestamp vs standard date filter
|
||||
it('should match conversation with string 10-digit timestamp against date string filter', () => {
|
||||
const conversation = { created_at: '1647777600' }; // March 20, 2022 as string (10 digits)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19', // Standard YYYY-MM-DD format
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test conversation with string 13-digit timestamp vs standard date filter
|
||||
it('should match conversation with string 13-digit timestamp against date string filter', () => {
|
||||
const conversation = { created_at: '1647777600000' }; // March 20, 2022 as string (13 digits)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19', // Standard YYYY-MM-DD format
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test conversation with mixed format vs standard date filter with time
|
||||
it('should match conversation with numeric timestamp against ISO date string filter', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022 12:00:00 GMT (numeric)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19T10:30:00Z', // Standard ISO format from filter
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test parseDate with date string without time (should default to 00:00:00)
|
||||
it('should match conversation with is_greater_than operator using date string without time', () => {
|
||||
const conversation = { created_at: 1647820800000 }; // March 21, 2022 00:00:00 GMT
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-20', // March 20, 2022 (should become 00:00:00)
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test parseDate with ISO date string
|
||||
it('should match conversation with is_greater_than operator using ISO date string', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19T00:00:00.000Z', // March 19, 2022 ISO format
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test parseDate with null/undefined values
|
||||
it('should handle null filter values in date comparison', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: null,
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle undefined filter values in date comparison', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: undefined,
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
// Test parseDate with invalid date strings
|
||||
it('should handle invalid date strings in date comparison', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: 'invalid-date-string',
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle non-date string values in date comparison', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: 'not-a-date',
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
// Test is_less_than with various date formats
|
||||
it('should match conversation with is_less_than operator using numeric timestamp', () => {
|
||||
const conversation = { created_at: 1647691200000 }; // March 19, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_less_than',
|
||||
values: 1647777600, // March 20, 2022 as 10-digit timestamp
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not match conversation with is_less_than operator when date is later', () => {
|
||||
const conversation = { created_at: 1647864000000 }; // March 21, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_less_than',
|
||||
values: '2022-03-20T12:00:00Z', // March 20, 2022 with time
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
// Edge case: Test with conversation having string timestamp
|
||||
it('should handle conversation with string timestamp value', () => {
|
||||
const conversation = { created_at: '1647777600000' }; // March 20, 2022 as string
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19',
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Edge case: Test with conversation having 10-digit timestamp
|
||||
it('should handle conversation with 10-digit timestamp value', () => {
|
||||
const conversation = { created_at: 1647777600 }; // March 20, 2022 as seconds (10 digits)
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19',
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test date string with different time formats
|
||||
it('should handle date string with space-separated time', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: '2022-03-19 10:30:00', // Date with space-separated time
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(true);
|
||||
});
|
||||
|
||||
// Test parseDate with object input (should return null and fail comparison)
|
||||
it('should handle non-string, non-number filter values', () => {
|
||||
const conversation = { created_at: 1647777600000 }; // March 20, 2022
|
||||
const filters = [
|
||||
{
|
||||
attribute_key: 'created_at',
|
||||
filter_operator: 'is_greater_than',
|
||||
values: { date: '2022-03-19' }, // Object instead of string/number
|
||||
query_operator: 'and',
|
||||
},
|
||||
];
|
||||
expect(matchesFilters(conversation, filters)).toBe(false);
|
||||
});
|
||||
|
||||
describe('days_before operator', () => {
|
||||
beforeEach(() => {
|
||||
// Set the date to March 25, 2022
|
||||
|
||||
@@ -204,6 +204,12 @@ export const mutations = {
|
||||
_state.allConversations.push(conversation);
|
||||
},
|
||||
|
||||
[types.DELETE_CONVERSATION](_state, conversationId) {
|
||||
_state.allConversations = _state.allConversations.filter(
|
||||
c => c.id !== conversationId
|
||||
);
|
||||
},
|
||||
|
||||
[types.UPDATE_CONVERSATION](_state, conversation) {
|
||||
const { allConversations } = _state;
|
||||
const index = allConversations.findIndex(c => c.id === conversation.id);
|
||||
|
||||
@@ -26,6 +26,9 @@ export const getters = {
|
||||
.filter(record => record.show_on_sidebar)
|
||||
.sort((a, b) => a.title.localeCompare(b.title));
|
||||
},
|
||||
getLabelById: _state => id => {
|
||||
return _state.records.find(record => record.id === Number(id));
|
||||
},
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
|
||||
@@ -170,4 +170,21 @@ describe('#actions', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('#resetAccessToken', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
const mockResponse = {
|
||||
data: { ...agentBotRecords[0], access_token: 'new_token_123' },
|
||||
};
|
||||
axios.post.mockResolvedValue(mockResponse);
|
||||
const result = await actions.resetAccessToken(
|
||||
{ commit },
|
||||
agentBotRecords[0].id
|
||||
);
|
||||
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.EDIT_AGENT_BOT, mockResponse.data],
|
||||
]);
|
||||
expect(result).toBe(mockResponse.data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -228,4 +228,20 @@ describe('#actions', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#resetAccessToken', () => {
|
||||
it('sends correct actions if API is success', async () => {
|
||||
const mockResponse = {
|
||||
data: { id: 1, name: 'John', access_token: 'new_token_123' },
|
||||
headers: { expiry: 581842904 },
|
||||
};
|
||||
axios.post.mockResolvedValue(mockResponse);
|
||||
const result = await actions.resetAccessToken({ commit });
|
||||
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_CURRENT_USER, mockResponse.data],
|
||||
]);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -70,6 +70,30 @@ describe('#actions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#active', () => {
|
||||
it('sends correct mutations if API is success', async () => {
|
||||
axios.get.mockResolvedValue({
|
||||
data: { payload: contactList, meta: { count: 100, current_page: 1 } },
|
||||
});
|
||||
await actions.active({ commit });
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_CONTACT_UI_FLAG, { isFetching: true }],
|
||||
[types.CLEAR_CONTACTS],
|
||||
[types.SET_CONTACTS, contactList],
|
||||
[types.SET_CONTACT_META, { count: 100, current_page: 1 }],
|
||||
[types.SET_CONTACT_UI_FLAG, { isFetching: false }],
|
||||
]);
|
||||
});
|
||||
it('sends correct mutations if API is error', async () => {
|
||||
axios.get.mockRejectedValue({ message: 'Incorrect header' });
|
||||
await actions.active({ commit });
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.SET_CONTACT_UI_FLAG, { isFetching: true }],
|
||||
[types.SET_CONTACT_UI_FLAG, { isFetching: false }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#update', () => {
|
||||
it('sends correct mutations if API is success', async () => {
|
||||
axios.patch.mockResolvedValue({ data: { payload: contactList[0] } });
|
||||
|
||||
@@ -75,6 +75,7 @@ describe('#actions', () => {
|
||||
q: 'test',
|
||||
});
|
||||
expect(dispatch).toHaveBeenCalledWith('messageSearch', { q: 'test' });
|
||||
expect(dispatch).toHaveBeenCalledWith('articleSearch', { q: 'test' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -150,6 +151,30 @@ describe('#actions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#articleSearch', () => {
|
||||
it('should handle successful article search', async () => {
|
||||
axios.get.mockResolvedValue({
|
||||
data: { payload: { articles: [{ id: 1 }] } },
|
||||
});
|
||||
|
||||
await actions.articleSearch({ commit }, { q: 'test', page: 1 });
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.ARTICLE_SEARCH_SET_UI_FLAG, { isFetching: true }],
|
||||
[types.ARTICLE_SEARCH_SET, [{ id: 1 }]],
|
||||
[types.ARTICLE_SEARCH_SET_UI_FLAG, { isFetching: false }],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle failed article search', async () => {
|
||||
axios.get.mockRejectedValue({});
|
||||
await actions.articleSearch({ commit }, { q: 'test' });
|
||||
expect(commit.mock.calls).toEqual([
|
||||
[types.ARTICLE_SEARCH_SET_UI_FLAG, { isFetching: true }],
|
||||
[types.ARTICLE_SEARCH_SET_UI_FLAG, { isFetching: false }],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#clearSearchResults', () => {
|
||||
it('should commit clear search results mutation', () => {
|
||||
actions.clearSearchResults({ commit });
|
||||
|
||||
@@ -37,6 +37,15 @@ describe('#getters', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('getArticleRecords', () => {
|
||||
const state = {
|
||||
articleRecords: [{ id: 1, title: 'Article 1' }],
|
||||
};
|
||||
expect(getters.getArticleRecords(state)).toEqual([
|
||||
{ id: 1, title: 'Article 1' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('getUIFlags', () => {
|
||||
const state = {
|
||||
uiFlags: {
|
||||
@@ -45,6 +54,7 @@ describe('#getters', () => {
|
||||
contact: { isFetching: true },
|
||||
message: { isFetching: false },
|
||||
conversation: { isFetching: false },
|
||||
article: { isFetching: false },
|
||||
},
|
||||
};
|
||||
expect(getters.getUIFlags(state)).toEqual({
|
||||
@@ -53,6 +63,7 @@ describe('#getters', () => {
|
||||
contact: { isFetching: true },
|
||||
message: { isFetching: false },
|
||||
conversation: { isFetching: false },
|
||||
article: { isFetching: false },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -101,17 +101,39 @@ describe('#mutations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#ARTICLE_SEARCH_SET', () => {
|
||||
it('should append new article records to existing ones', () => {
|
||||
const state = { articleRecords: [{ id: 1 }] };
|
||||
mutations[types.ARTICLE_SEARCH_SET](state, [{ id: 2 }]);
|
||||
expect(state.articleRecords).toEqual([{ id: 1 }, { id: 2 }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#ARTICLE_SEARCH_SET_UI_FLAG', () => {
|
||||
it('set article search UI flags correctly', () => {
|
||||
const state = {
|
||||
uiFlags: {
|
||||
article: { isFetching: true },
|
||||
},
|
||||
};
|
||||
mutations[types.ARTICLE_SEARCH_SET_UI_FLAG](state, { isFetching: false });
|
||||
expect(state.uiFlags.article).toEqual({ isFetching: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('#CLEAR_SEARCH_RESULTS', () => {
|
||||
it('should clear all search records', () => {
|
||||
const state = {
|
||||
contactRecords: [{ id: 1 }],
|
||||
conversationRecords: [{ id: 1 }],
|
||||
messageRecords: [{ id: 1 }],
|
||||
articleRecords: [{ id: 1 }],
|
||||
};
|
||||
mutations[types.CLEAR_SEARCH_RESULTS](state);
|
||||
expect(state.contactRecords).toEqual([]);
|
||||
expect(state.conversationRecords).toEqual([]);
|
||||
expect(state.messageRecords).toEqual([]);
|
||||
expect(state.articleRecords).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -513,6 +513,28 @@ describe('#deleteMessage', () => {
|
||||
expect(commit.mock.calls).toEqual([]);
|
||||
});
|
||||
|
||||
describe('#deleteConversation', () => {
|
||||
it('send correct actions if API is success', async () => {
|
||||
axios.delete.mockResolvedValue({
|
||||
data: { id: 1 },
|
||||
});
|
||||
await actions.deleteConversation({ commit, dispatch }, 1);
|
||||
expect(commit.mock.calls).toEqual([[types.DELETE_CONVERSATION, 1]]);
|
||||
expect(dispatch.mock.calls).toEqual([
|
||||
['conversationStats/get', {}, { root: true }],
|
||||
]);
|
||||
});
|
||||
|
||||
it('send no actions if API is error', async () => {
|
||||
axios.delete.mockRejectedValue({ message: 'Incorrect header' });
|
||||
await expect(
|
||||
actions.deleteConversation({ commit, dispatch }, 1)
|
||||
).rejects.toThrow(Error);
|
||||
expect(commit.mock.calls).toEqual([]);
|
||||
expect(dispatch.mock.calls).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#updateCustomAttributes', () => {
|
||||
it('update conversation custom attributes', async () => {
|
||||
axios.post.mockResolvedValue({
|
||||
|
||||
@@ -325,4 +325,308 @@ describe('#getters', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getFilteredConversations', () => {
|
||||
const mockConversations = [
|
||||
{
|
||||
id: 1,
|
||||
status: 'open',
|
||||
meta: { assignee: { id: 1 } },
|
||||
last_activity_at: 1000,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
status: 'open',
|
||||
meta: {},
|
||||
last_activity_at: 2000,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
status: 'resolved',
|
||||
meta: { assignee: { id: 2 } },
|
||||
last_activity_at: 3000,
|
||||
},
|
||||
];
|
||||
|
||||
const mockRootGetters = {
|
||||
getCurrentUser: {
|
||||
id: 1,
|
||||
accounts: [{ id: 1, role: 'agent', permissions: [] }],
|
||||
},
|
||||
getCurrentAccountId: 1,
|
||||
};
|
||||
|
||||
it('filters conversations based on role permissions for administrator', () => {
|
||||
const state = {
|
||||
allConversations: mockConversations,
|
||||
chatSortFilter: 'last_activity_at_desc',
|
||||
appliedFilters: [],
|
||||
};
|
||||
|
||||
const rootGetters = {
|
||||
...mockRootGetters,
|
||||
getCurrentUser: {
|
||||
...mockRootGetters.getCurrentUser,
|
||||
accounts: [{ id: 1, role: 'administrator', permissions: [] }],
|
||||
},
|
||||
};
|
||||
|
||||
const result = getters.getFilteredConversations(
|
||||
state,
|
||||
{},
|
||||
{},
|
||||
rootGetters
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
mockConversations[2],
|
||||
mockConversations[1],
|
||||
mockConversations[0],
|
||||
]);
|
||||
});
|
||||
|
||||
it('filters conversations based on role permissions for agent', () => {
|
||||
const state = {
|
||||
allConversations: mockConversations,
|
||||
chatSortFilter: 'last_activity_at_desc',
|
||||
appliedFilters: [],
|
||||
};
|
||||
|
||||
const rootGetters = {
|
||||
...mockRootGetters,
|
||||
getCurrentUser: {
|
||||
...mockRootGetters.getCurrentUser,
|
||||
accounts: [{ id: 1, role: 'agent', permissions: [] }],
|
||||
},
|
||||
};
|
||||
|
||||
const result = getters.getFilteredConversations(
|
||||
state,
|
||||
{},
|
||||
{},
|
||||
rootGetters
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
mockConversations[2],
|
||||
mockConversations[1],
|
||||
mockConversations[0],
|
||||
]);
|
||||
});
|
||||
|
||||
it('filters conversations for custom role with conversation_manage permission', () => {
|
||||
const state = {
|
||||
allConversations: mockConversations,
|
||||
chatSortFilter: 'last_activity_at_desc',
|
||||
appliedFilters: [],
|
||||
};
|
||||
|
||||
const rootGetters = {
|
||||
...mockRootGetters,
|
||||
getCurrentUser: {
|
||||
...mockRootGetters.getCurrentUser,
|
||||
accounts: [
|
||||
{
|
||||
id: 1,
|
||||
custom_role_id: 5,
|
||||
permissions: ['conversation_manage'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const result = getters.getFilteredConversations(
|
||||
state,
|
||||
{},
|
||||
{},
|
||||
rootGetters
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
mockConversations[2],
|
||||
mockConversations[1],
|
||||
mockConversations[0],
|
||||
]);
|
||||
});
|
||||
|
||||
it('filters conversations for custom role with conversation_unassigned_manage permission', () => {
|
||||
const state = {
|
||||
allConversations: mockConversations,
|
||||
chatSortFilter: 'last_activity_at_desc',
|
||||
appliedFilters: [],
|
||||
};
|
||||
|
||||
const rootGetters = {
|
||||
...mockRootGetters,
|
||||
getCurrentUser: {
|
||||
...mockRootGetters.getCurrentUser,
|
||||
accounts: [
|
||||
{
|
||||
id: 1,
|
||||
custom_role_id: 5,
|
||||
permissions: ['conversation_unassigned_manage'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const result = getters.getFilteredConversations(
|
||||
state,
|
||||
{},
|
||||
{},
|
||||
rootGetters
|
||||
);
|
||||
|
||||
// Should include conversation assigned to user (id: 1) and unassigned conversation
|
||||
expect(result).toEqual([mockConversations[1], mockConversations[0]]);
|
||||
});
|
||||
|
||||
it('filters conversations for custom role with conversation_participating_manage permission', () => {
|
||||
const state = {
|
||||
allConversations: mockConversations,
|
||||
chatSortFilter: 'last_activity_at_desc',
|
||||
appliedFilters: [],
|
||||
};
|
||||
|
||||
const rootGetters = {
|
||||
...mockRootGetters,
|
||||
getCurrentUser: {
|
||||
...mockRootGetters.getCurrentUser,
|
||||
accounts: [
|
||||
{
|
||||
id: 1,
|
||||
custom_role_id: 5,
|
||||
permissions: ['conversation_participating_manage'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const result = getters.getFilteredConversations(
|
||||
state,
|
||||
{},
|
||||
{},
|
||||
rootGetters
|
||||
);
|
||||
|
||||
// Should only include conversation assigned to user (id: 1)
|
||||
expect(result).toEqual([mockConversations[0]]);
|
||||
});
|
||||
|
||||
it('filters conversations for custom role with no permissions', () => {
|
||||
const state = {
|
||||
allConversations: mockConversations,
|
||||
chatSortFilter: 'last_activity_at_desc',
|
||||
appliedFilters: [],
|
||||
};
|
||||
|
||||
const rootGetters = {
|
||||
...mockRootGetters,
|
||||
getCurrentUser: {
|
||||
...mockRootGetters.getCurrentUser,
|
||||
accounts: [
|
||||
{
|
||||
id: 1,
|
||||
custom_role_id: 5,
|
||||
permissions: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const result = getters.getFilteredConversations(
|
||||
state,
|
||||
{},
|
||||
{},
|
||||
rootGetters
|
||||
);
|
||||
|
||||
// Should return empty array as user has no permissions
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('applies filters and role permissions together', () => {
|
||||
const state = {
|
||||
allConversations: mockConversations,
|
||||
chatSortFilter: 'last_activity_at_desc',
|
||||
appliedFilters: [
|
||||
{
|
||||
attribute_key: 'status',
|
||||
filter_operator: 'equal_to',
|
||||
values: ['open'],
|
||||
query_operator: 'and',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const rootGetters = {
|
||||
...mockRootGetters,
|
||||
getCurrentUser: {
|
||||
...mockRootGetters.getCurrentUser,
|
||||
accounts: [
|
||||
{
|
||||
id: 1,
|
||||
custom_role_id: 5,
|
||||
permissions: ['conversation_participating_manage'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const result = getters.getFilteredConversations(
|
||||
state,
|
||||
{},
|
||||
{},
|
||||
rootGetters
|
||||
);
|
||||
|
||||
// Should only include open conversation assigned to user (id: 1)
|
||||
expect(result).toEqual([mockConversations[0]]);
|
||||
});
|
||||
|
||||
it('returns empty array when no conversations match filters', () => {
|
||||
const state = {
|
||||
allConversations: mockConversations,
|
||||
chatSortFilter: 'last_activity_at_desc',
|
||||
appliedFilters: [
|
||||
{
|
||||
attribute_key: 'status',
|
||||
filter_operator: 'equal_to',
|
||||
values: ['pending'],
|
||||
query_operator: 'and',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = getters.getFilteredConversations(
|
||||
state,
|
||||
{},
|
||||
{},
|
||||
mockRootGetters
|
||||
);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('sorts filtered conversations according to chatSortFilter', () => {
|
||||
const state = {
|
||||
allConversations: mockConversations,
|
||||
chatSortFilter: 'last_activity_at_asc',
|
||||
appliedFilters: [],
|
||||
};
|
||||
|
||||
const result = getters.getFilteredConversations(
|
||||
state,
|
||||
{},
|
||||
{},
|
||||
mockRootGetters
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
mockConversations[0],
|
||||
mockConversations[1],
|
||||
mockConversations[2],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -884,6 +884,17 @@ describe('#mutations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#DELETE_CONVERSATION', () => {
|
||||
it('should delete a conversation', () => {
|
||||
const state = {
|
||||
allConversations: [{ id: 1, messages: [] }],
|
||||
};
|
||||
|
||||
mutations[types.DELETE_CONVERSATION](state, 1);
|
||||
expect(state.allConversations).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#SET_LIST_LOADING_STATUS', () => {
|
||||
it('should set listLoadingStatus to true', () => {
|
||||
const state = {
|
||||
|
||||
@@ -7,6 +7,7 @@ vi.mock('dashboard/api/summaryReports', () => ({
|
||||
getInboxReports: vi.fn(),
|
||||
getAgentReports: vi.fn(),
|
||||
getTeamReports: vi.fn(),
|
||||
getLabelReports: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -25,10 +26,12 @@ describe('Summary Reports Store', () => {
|
||||
inboxSummaryReports: [],
|
||||
agentSummaryReports: [],
|
||||
teamSummaryReports: [],
|
||||
labelSummaryReports: [],
|
||||
uiFlags: {
|
||||
isFetchingInboxSummaryReports: false,
|
||||
isFetchingAgentSummaryReports: false,
|
||||
isFetchingTeamSummaryReports: false,
|
||||
isFetchingLabelSummaryReports: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -39,6 +42,7 @@ describe('Summary Reports Store', () => {
|
||||
inboxSummaryReports: [{ id: 1 }],
|
||||
agentSummaryReports: [{ id: 2 }],
|
||||
teamSummaryReports: [{ id: 3 }],
|
||||
labelSummaryReports: [{ id: 4 }],
|
||||
uiFlags: { isFetchingInboxSummaryReports: true },
|
||||
};
|
||||
|
||||
@@ -54,6 +58,10 @@ describe('Summary Reports Store', () => {
|
||||
expect(store.getters.getTeamSummaryReports(state)).toEqual([{ id: 3 }]);
|
||||
});
|
||||
|
||||
it('should return label summary reports', () => {
|
||||
expect(store.getters.getLabelSummaryReports(state)).toEqual([{ id: 4 }]);
|
||||
});
|
||||
|
||||
it('should return UI flags', () => {
|
||||
expect(store.getters.getUIFlags(state)).toEqual({
|
||||
isFetchingInboxSummaryReports: true,
|
||||
@@ -86,6 +94,14 @@ describe('Summary Reports Store', () => {
|
||||
expect(state.teamSummaryReports).toEqual(data);
|
||||
});
|
||||
|
||||
it('should set label summary report', () => {
|
||||
const state = { ...initialState };
|
||||
const data = [{ id: 4 }];
|
||||
|
||||
store.mutations.setLabelSummaryReport(state, data);
|
||||
expect(state.labelSummaryReports).toEqual(data);
|
||||
});
|
||||
|
||||
it('should merge UI flags with existing flags', () => {
|
||||
const state = {
|
||||
uiFlags: { flag1: true, flag2: false },
|
||||
@@ -185,5 +201,29 @@ describe('Summary Reports Store', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchLabelSummaryReports', () => {
|
||||
it('should fetch label reports successfully', async () => {
|
||||
const params = { labelId: 789 };
|
||||
const mockResponse = {
|
||||
data: [{ label_id: 789, label_name: 'Test Label' }],
|
||||
};
|
||||
|
||||
SummaryReportsAPI.getLabelReports.mockResolvedValue(mockResponse);
|
||||
|
||||
await store.actions.fetchLabelSummaryReports({ commit }, params);
|
||||
|
||||
expect(commit).toHaveBeenCalledWith('setUIFlags', {
|
||||
isFetchingLabelSummaryReports: true,
|
||||
});
|
||||
expect(SummaryReportsAPI.getLabelReports).toHaveBeenCalledWith(params);
|
||||
expect(commit).toHaveBeenCalledWith('setLabelSummaryReport', [
|
||||
{ labelId: 789, labelName: 'Test Label' },
|
||||
]);
|
||||
expect(commit).toHaveBeenCalledWith('setUIFlags', {
|
||||
isFetchingLabelSummaryReports: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,11 @@ const typeMap = {
|
||||
apiMethod: 'getTeamReports',
|
||||
mutationKey: 'setTeamSummaryReport',
|
||||
},
|
||||
label: {
|
||||
flagKey: 'isFetchingLabelSummaryReports',
|
||||
apiMethod: 'getLabelReports',
|
||||
mutationKey: 'setLabelSummaryReport',
|
||||
},
|
||||
};
|
||||
|
||||
async function fetchSummaryReports(type, params, { commit }) {
|
||||
@@ -38,10 +43,12 @@ export const initialState = {
|
||||
inboxSummaryReports: [],
|
||||
agentSummaryReports: [],
|
||||
teamSummaryReports: [],
|
||||
labelSummaryReports: [],
|
||||
uiFlags: {
|
||||
isFetchingInboxSummaryReports: false,
|
||||
isFetchingAgentSummaryReports: false,
|
||||
isFetchingTeamSummaryReports: false,
|
||||
isFetchingLabelSummaryReports: false,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -55,6 +62,9 @@ export const getters = {
|
||||
getTeamSummaryReports(state) {
|
||||
return state.teamSummaryReports;
|
||||
},
|
||||
getLabelSummaryReports(state) {
|
||||
return state.labelSummaryReports;
|
||||
},
|
||||
getUIFlags(state) {
|
||||
return state.uiFlags;
|
||||
},
|
||||
@@ -72,6 +82,10 @@ export const actions = {
|
||||
fetchTeamSummaryReports({ commit }, params) {
|
||||
return fetchSummaryReports('team', params, { commit });
|
||||
},
|
||||
|
||||
fetchLabelSummaryReports({ commit }, params) {
|
||||
return fetchSummaryReports('label', params, { commit });
|
||||
},
|
||||
};
|
||||
|
||||
export const mutations = {
|
||||
@@ -84,6 +98,9 @@ export const mutations = {
|
||||
setTeamSummaryReport(state, data) {
|
||||
state.teamSummaryReports = data;
|
||||
},
|
||||
setLabelSummaryReport(state, data) {
|
||||
state.labelSummaryReports = data;
|
||||
},
|
||||
setUIFlags(state, uiFlag) {
|
||||
state.uiFlags = { ...state.uiFlags, ...uiFlag };
|
||||
},
|
||||
|
||||
@@ -56,6 +56,7 @@ export default {
|
||||
SET_ALL_ATTACHMENTS: 'SET_ALL_ATTACHMENTS',
|
||||
ADD_CONVERSATION_ATTACHMENTS: 'ADD_CONVERSATION_ATTACHMENTS',
|
||||
DELETE_CONVERSATION_ATTACHMENTS: 'DELETE_CONVERSATION_ATTACHMENTS',
|
||||
DELETE_CONVERSATION: 'DELETE_CONVERSATION',
|
||||
|
||||
SET_CONVERSATION_CAN_REPLY: 'SET_CONVERSATION_CAN_REPLY',
|
||||
|
||||
@@ -317,8 +318,10 @@ export default {
|
||||
CONVERSATION_SEARCH_SET: 'CONVERSATION_SEARCH_SET',
|
||||
CONVERSATION_SEARCH_SET_UI_FLAG: 'CONVERSATION_SEARCH_SET_UI_FLAG',
|
||||
MESSAGE_SEARCH_SET: 'MESSAGE_SEARCH_SET',
|
||||
ARTICLE_SEARCH_SET: 'ARTICLE_SEARCH_SET',
|
||||
CLEAR_SEARCH_RESULTS: 'CLEAR_SEARCH_RESULTS',
|
||||
MESSAGE_SEARCH_SET_UI_FLAG: 'MESSAGE_SEARCH_SET_UI_FLAG',
|
||||
ARTICLE_SEARCH_SET_UI_FLAG: 'ARTICLE_SEARCH_SET_UI_FLAG',
|
||||
FULL_SEARCH_SET_UI_FLAG: 'FULL_SEARCH_SET_UI_FLAG',
|
||||
SET_CONVERSATION_PARTICIPANTS_UI_FLAG:
|
||||
'SET_CONVERSATION_PARTICIPANTS_UI_FLAG',
|
||||
|
||||
Reference in New Issue
Block a user