chore: Sets up store for teams settings page (#1727)

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
Nithin David Thomas 2021-02-04 13:19:59 +05:30 committed by GitHub
parent c61edff189
commit 6a614a520b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 608 additions and 0 deletions

View file

@ -0,0 +1,15 @@
import teams from '../teams';
import ApiClient from '../ApiClient';
describe('#TeamsAPI', () => {
it('creates correct instance', () => {
expect(teams).toBeInstanceOf(ApiClient);
expect(teams).toHaveProperty('get');
expect(teams).toHaveProperty('show');
expect(teams).toHaveProperty('create');
expect(teams).toHaveProperty('update');
expect(teams).toHaveProperty('delete');
expect(teams).toHaveProperty('getAgents');
expect(teams).toHaveProperty('addAgents');
});
});

View file

@ -0,0 +1,20 @@
/* global axios */
import ApiClient from './ApiClient';
export class TeamsAPI extends ApiClient {
constructor() {
super('teams', { accountScoped: true });
}
getAgents({ teamId }) {
return axios.get(`${this.url}/${teamId}/team_members`);
}
addAgents({ teamId, agentsList }) {
return axios.post(`${this.url}/${teamId}/team_members`, {
user_ids: agentsList,
});
}
}
export default new TeamsAPI();

View file

@ -23,6 +23,8 @@ import labels from './modules/labels';
import reports from './modules/reports'; import reports from './modules/reports';
import userNotificationSettings from './modules/userNotificationSettings'; import userNotificationSettings from './modules/userNotificationSettings';
import webhooks from './modules/webhooks'; import webhooks from './modules/webhooks';
import teams from './modules/teams';
import teamMembers from './modules/teamMembers';
Vue.use(Vuex); Vue.use(Vuex);
export default new Vuex.Store({ export default new Vuex.Store({
@ -49,5 +51,7 @@ export default new Vuex.Store({
reports, reports,
userNotificationSettings, userNotificationSettings,
webhooks, webhooks,
teams,
teamMembers,
}, },
}); });

View file

@ -0,0 +1,59 @@
import axios from 'axios';
import {
actions,
SET_TEAM_MEMBERS_UI_FLAG,
ADD_AGENTS_TO_TEAM,
} from '../../teamMembers';
import teamMembers from './fixtures';
const commit = jest.fn();
global.axios = axios;
jest.mock('axios');
describe('#actions', () => {
describe('#get', () => {
it('sends correct actions if API is success', async () => {
axios.get.mockResolvedValue({ data: teamMembers[1] });
await actions.get({ commit }, { teamId: 1 });
expect(commit.mock.calls).toEqual([
[SET_TEAM_MEMBERS_UI_FLAG, { isFetching: true }],
[ADD_AGENTS_TO_TEAM, { data: teamMembers[1], teamId: 1 }],
[SET_TEAM_MEMBERS_UI_FLAG, { isFetching: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.get.mockRejectedValue({ message: 'Incorrect header' });
await expect(actions.get({ commit }, { teamId: 1 })).rejects.toThrow(
Error
);
expect(commit.mock.calls).toEqual([
[SET_TEAM_MEMBERS_UI_FLAG, { isFetching: true }],
[SET_TEAM_MEMBERS_UI_FLAG, { isFetching: false }],
]);
});
});
describe('#create', () => {
it('sends correct actions if API is success', async () => {
axios.post.mockResolvedValue({ data: teamMembers });
await actions.create({ commit }, { agentsList: teamMembers, teamId: 1 });
expect(commit.mock.calls).toEqual([
[SET_TEAM_MEMBERS_UI_FLAG, { isCreating: true }],
[ADD_AGENTS_TO_TEAM, { data: teamMembers, teamId: 1 }],
[SET_TEAM_MEMBERS_UI_FLAG, { isCreating: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.post.mockRejectedValue({ message: 'Incorrect header' });
await expect(
actions.create({ commit }, { agentsList: teamMembers, teamId: 1 })
).rejects.toThrow(Error);
expect(commit.mock.calls).toEqual([
[SET_TEAM_MEMBERS_UI_FLAG, { isCreating: true }],
[SET_TEAM_MEMBERS_UI_FLAG, { isCreating: false }],
]);
});
});
});

View file

@ -0,0 +1,28 @@
export default [
{
id: 1,
provider: 'email',
uid: 'agent1@chatwoot.com',
name: 'Agent1',
email: 'agent1@chatwoot.com',
account_id: 1,
created_at: '2019-11-18T02:21:06.225Z',
updated_at: '2019-12-20T07:43:35.794Z',
pubsub_token: 'random-1',
role: 'agent',
confirmed: true,
},
{
id: 2,
provider: 'email',
uid: 'agent2@chatwoot.com',
name: 'Agent2',
email: 'agent2@chatwoot.com',
account_id: 1,
created_at: '2019-11-18T02:21:06.225Z',
updated_at: '2019-12-20T07:43:35.794Z',
pubsub_token: 'random-2',
role: 'agent',
confirmed: true,
},
];

View file

@ -0,0 +1,30 @@
import { getters } from '../../teamMembers';
import teamMembers from './fixtures';
describe('#getters', () => {
it('getTeamMembers', () => {
const state = {
records: {
1: [teamMembers[0]],
},
};
expect(getters.getTeamMembers(state)(1)).toEqual([teamMembers[0]]);
});
it('getUIFlags', () => {
const state = {
uiFlags: {
isFetching: false,
isCreating: false,
isUpdating: false,
isDeleting: false,
},
};
expect(getters.getUIFlags(state)).toEqual({
isFetching: false,
isCreating: false,
isUpdating: false,
isDeleting: false,
});
});
});

View file

@ -0,0 +1,12 @@
import { mutations, ADD_AGENTS_TO_TEAM } from '../../teamMembers';
import teamMembers from './fixtures';
describe('#mutations', () => {
describe('#ADD_AGENTS_TO_TEAM', () => {
it('Adds team members to records', () => {
const state = { records: {} };
mutations[ADD_AGENTS_TO_TEAM](state, { data: teamMembers[0], teamId: 1 });
expect(state.records).toEqual({ 1: teamMembers[0] });
});
});
});

View file

@ -0,0 +1,103 @@
import axios from 'axios';
import { actions } from '../../teams/actions';
import {
SET_TEAM_UI_FLAG,
CLEAR_TEAMS,
SET_TEAMS,
SET_TEAM_ITEM,
EDIT_TEAM,
DELETE_TEAM,
} from '../../teams/types';
import teamsList from './fixtures';
const commit = jest.fn();
global.axios = axios;
jest.mock('axios');
describe('#actions', () => {
describe('#get', () => {
it('sends correct actions if API is success', async () => {
axios.get.mockResolvedValue({ data: teamsList[1] });
await actions.get({ commit });
expect(commit.mock.calls).toEqual([
[SET_TEAM_UI_FLAG, { isFetching: true }],
[CLEAR_TEAMS],
[SET_TEAMS, teamsList[1]],
[SET_TEAM_UI_FLAG, { isFetching: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.get.mockRejectedValue({ message: 'Incorrect header' });
await expect(actions.get({ commit })).rejects.toThrow(Error);
expect(commit.mock.calls).toEqual([
[SET_TEAM_UI_FLAG, { isFetching: true }],
[SET_TEAM_UI_FLAG, { isFetching: false }],
]);
});
});
describe('#create', () => {
it('sends correct actions if API is success', async () => {
axios.post.mockResolvedValue({ data: teamsList[1] });
await actions.create({ commit }, teamsList[1]);
expect(commit.mock.calls).toEqual([
[SET_TEAM_UI_FLAG, { isCreating: true }],
[SET_TEAM_ITEM, teamsList[1]],
[SET_TEAM_UI_FLAG, { isCreating: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.post.mockRejectedValue({ message: 'Incorrect header' });
await expect(actions.create({ commit })).rejects.toThrow(Error);
expect(commit.mock.calls).toEqual([
[SET_TEAM_UI_FLAG, { isCreating: true }],
[SET_TEAM_UI_FLAG, { isCreating: false }],
]);
});
});
describe('#update', () => {
it('sends correct actions if API is success', async () => {
axios.patch.mockResolvedValue({ data: { payload: teamsList[1] } });
await actions.update({ commit }, teamsList[1]);
expect(commit.mock.calls).toEqual([
[SET_TEAM_UI_FLAG, { isUpdating: true }],
[EDIT_TEAM, teamsList[1]],
[SET_TEAM_UI_FLAG, { isUpdating: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.patch.mockRejectedValue({ message: 'Incorrect header' });
await expect(actions.update({ commit }, teamsList[1])).rejects.toThrow(
Error
);
expect(commit.mock.calls).toEqual([
[SET_TEAM_UI_FLAG, { isUpdating: true }],
[SET_TEAM_UI_FLAG, { isUpdating: false }],
]);
});
});
describe('#delete', () => {
it('sends correct actions if API is success', async () => {
axios.delete.mockResolvedValue();
await actions.delete({ commit }, 1);
expect(commit.mock.calls).toEqual([
[SET_TEAM_UI_FLAG, { isDeleting: true }],
[DELETE_TEAM, 1],
[SET_TEAM_UI_FLAG, { isDeleting: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.delete.mockRejectedValue({ message: 'Incorrect header' });
await expect(actions.delete({ commit }, 1)).rejects.toThrow(Error);
expect(commit.mock.calls).toEqual([
[SET_TEAM_UI_FLAG, { isDeleting: true }],
[SET_TEAM_UI_FLAG, { isDeleting: false }],
]);
});
});
});

View file

@ -0,0 +1,8 @@
export default {
1: {
id: 1,
account_id: 1,
name: 'Test',
description: 'Some team',
},
};

View file

@ -0,0 +1,40 @@
import { getters } from '../../teams/getters';
import teamsList from './fixtures';
describe('#getters', () => {
it('getTeams', () => {
const state = {
records: teamsList,
};
expect(getters.getTeams(state)).toEqual([teamsList[1]]);
});
it('getTeam', () => {
const state = {
records: teamsList,
};
expect(getters.getTeam(state)(1)).toEqual({
id: 1,
account_id: 1,
name: 'Test',
description: 'Some team',
});
});
it('getUIFlags', () => {
const state = {
uiFlags: {
isFetching: false,
isCreating: false,
isUpdating: false,
isDeleting: false,
},
};
expect(getters.getUIFlags(state)).toEqual({
isFetching: false,
isCreating: false,
isUpdating: false,
isDeleting: false,
});
});
});

View file

@ -0,0 +1,53 @@
import {
CLEAR_TEAMS,
SET_TEAMS,
SET_TEAM_ITEM,
EDIT_TEAM,
DELETE_TEAM,
} from '../../teams/types';
import { mutations } from '../../teams/mutations';
import teams from './fixtures';
describe('#mutations', () => {
describe('#SET_teams', () => {
it('set teams records', () => {
const state = { records: {} };
mutations[SET_TEAMS](state, [teams[1]]);
expect(state.records).toEqual(teams);
});
});
describe('#ADD_TEAM', () => {
it('push newly created teams to the store', () => {
const state = { records: {} };
mutations[SET_TEAM_ITEM](state, teams[1]);
expect(state.records).toEqual({ 1: teams[1] });
});
});
describe('#EDIT_TEAM', () => {
it('update teams record', () => {
const state = { records: [teams[1]] };
mutations[EDIT_TEAM](state, {
id: 1,
name: 'customer-support',
});
expect(state.records[1].name).toEqual('customer-support');
});
});
describe('#DELETE_TEAM', () => {
it('delete teams record', () => {
const state = { records: { 1: teams[1] } };
mutations[DELETE_TEAM](state, 1);
expect(state.records).toEqual({});
});
});
describe('#CLEAR_TEAMS', () => {
it('delete teams record', () => {
const state = { records: { 1: teams[1] } };
mutations[CLEAR_TEAMS](state);
expect(state.records).toEqual({});
});
});
});

View file

@ -0,0 +1,69 @@
import Vue from 'vue';
import TeamsAPI from '../../api/teams';
export const SET_TEAM_MEMBERS_UI_FLAG = 'SET_TEAM_MEMBERS_UI_FLAG';
export const ADD_AGENTS_TO_TEAM = 'ADD_AGENTS_TO_TEAM';
export const state = {
records: {},
uiFlags: {
isFetching: false,
isCreating: false,
isUpdating: false,
isDeleting: false,
},
};
export const getters = {
getUIFlags(_state) {
return _state.uiFlags;
},
getTeamMembers: $state => id => {
return $state.records[id] || [];
},
};
export const actions = {
get: async ({ commit }, { teamId }) => {
commit(SET_TEAM_MEMBERS_UI_FLAG, { isFetching: true });
try {
const { data } = await TeamsAPI.getAgents({ teamId });
commit(ADD_AGENTS_TO_TEAM, { data, teamId });
} catch (error) {
throw new Error(error);
} finally {
commit(SET_TEAM_MEMBERS_UI_FLAG, { isFetching: false });
}
},
create: async ({ commit }, { agentsList, teamId }) => {
commit(SET_TEAM_MEMBERS_UI_FLAG, { isCreating: true });
try {
const { data } = await TeamsAPI.addAgents({ agentsList, teamId });
commit(ADD_AGENTS_TO_TEAM, { teamId, data });
} catch (error) {
throw new Error(error);
} finally {
commit(SET_TEAM_MEMBERS_UI_FLAG, { isCreating: false });
}
},
};
export const mutations = {
[SET_TEAM_MEMBERS_UI_FLAG]($state, data) {
$state.uiFlags = {
...$state.uiFlags,
...data,
};
},
[ADD_AGENTS_TO_TEAM]($state, { data, teamId }) {
Vue.set($state.records, teamId, data);
},
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
};

View file

@ -0,0 +1,78 @@
import {
SET_TEAM_UI_FLAG,
CLEAR_TEAMS,
SET_TEAMS,
SET_TEAM_ITEM,
EDIT_TEAM,
DELETE_TEAM,
} from './types';
import TeamsAPI from '../../../api/teams';
export const actions = {
create: async ({ commit }, teamInfo) => {
commit(SET_TEAM_UI_FLAG, { isCreating: true });
try {
const response = await TeamsAPI.create(teamInfo);
const team = response.data;
commit(SET_TEAM_ITEM, team);
return team;
} catch (error) {
throw new Error(error);
} finally {
commit(SET_TEAM_UI_FLAG, { isCreating: false });
}
},
get: async ({ commit }) => {
commit(SET_TEAM_UI_FLAG, { isFetching: true });
try {
const { data } = await TeamsAPI.get();
commit(CLEAR_TEAMS);
commit(SET_TEAMS, data);
} catch (error) {
throw new Error(error);
} finally {
commit(SET_TEAM_UI_FLAG, { isFetching: false });
}
},
show: async ({ commit }, { id }) => {
commit(SET_TEAM_UI_FLAG, { isFetchingItem: true });
try {
const response = await TeamsAPI.show(id);
commit(SET_TEAM_ITEM, response.data.payload);
commit(SET_TEAM_UI_FLAG, {
isFetchingItem: false,
});
} catch (error) {
throw new Error(error);
} finally {
commit(SET_TEAM_UI_FLAG, {
isFetchingItem: false,
});
}
},
update: async ({ commit }, { id, ...updateObj }) => {
commit(SET_TEAM_UI_FLAG, { isUpdating: true });
try {
const response = await TeamsAPI.update(id, updateObj);
commit(EDIT_TEAM, response.data.payload);
} catch (error) {
throw new Error(error);
} finally {
commit(SET_TEAM_UI_FLAG, { isUpdating: false });
}
},
delete: async ({ commit }, teamId) => {
commit(SET_TEAM_UI_FLAG, { isDeleting: true });
try {
await TeamsAPI.delete(teamId);
commit(DELETE_TEAM, teamId);
} catch (error) {
throw new Error(error);
} finally {
commit(SET_TEAM_UI_FLAG, { isDeleting: false });
}
},
};

View file

@ -0,0 +1,12 @@
export const getters = {
getTeams($state) {
return Object.values($state.records).sort((a, b) => a.id - b.id);
},
getUIFlags($state) {
return $state.uiFlags;
},
getTeam: $state => id => {
const team = $state.records[id];
return team || {};
},
};

View file

@ -0,0 +1,22 @@
import { getters } from './getters';
import { actions } from './actions';
import { mutations } from './mutations';
const state = {
meta: {},
records: {},
uiFlags: {
isFetching: false,
isCreating: false,
isUpdating: false,
isDeleting: false,
},
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
};

View file

@ -0,0 +1,47 @@
import Vue from 'vue';
import {
SET_TEAM_UI_FLAG,
CLEAR_TEAMS,
SET_TEAMS,
SET_TEAM_ITEM,
EDIT_TEAM,
DELETE_TEAM,
} from './types';
export const mutations = {
[SET_TEAM_UI_FLAG]($state, data) {
$state.uiFlags = {
...$state.uiFlags,
...data,
};
},
[CLEAR_TEAMS]: $state => {
Vue.set($state, 'records', {});
},
[SET_TEAMS]: ($state, data) => {
data.forEach(team => {
Vue.set($state.records, team.id, {
...($state.records[team.id] || {}),
...team,
});
});
},
[SET_TEAM_ITEM]: ($state, data) => {
Vue.set($state.records, data.id, {
...($state.records[data.id] || {}),
...data,
});
},
[EDIT_TEAM]: ($state, data) => {
Vue.set($state.records, data.id, data);
},
[DELETE_TEAM]: ($state, teamId) => {
const { [teamId]: toDelete, ...records } = $state.records;
Vue.set($state, 'records', records);
},
};

View file

@ -0,0 +1,8 @@
export const SET_TEAM_UI_FLAG = 'SET_TEAM_UI_FLAG';
export const CLEAR_TEAMS = 'CLEAR_TEAMS';
export const SET_TEAMS = 'SET_TEAMS';
export const SET_TEAM_ITEM = 'SET_TEAM_ITEM';
export const EDIT_TEAM = 'EDIT_TEAM';
export const DELETE_TEAM = 'DELETE_TEAM';
export const ADD_AGENTS_TO_TEAM = 'ADD_AGENTS_TO_TEAM';
export const UPDATE_TEAMS_PRESENCE = 'UPDATE_TEAMS_PRESENCE';