web/satellite: project members frontend selection caching added (#3238)
This commit is contained in:
parent
f1867a954b
commit
875c7dfe72
@ -35,9 +35,10 @@
|
||||
is-white="true"
|
||||
:on-press="onClearSelection"
|
||||
/>
|
||||
<span class="header-selected-members__info-text"><b>{{selectedProjectMembersCount}}</b> users selected</span>
|
||||
</div>
|
||||
<div class="header-after-delete-click" v-if="areSelectedProjectMembersBeingDeleted">
|
||||
<span class="header-after-delete-click__delete-confirmation">Are you sure you want to delete {{selectedProjectMembersCount}} {{userCountTitle}}?</span>
|
||||
<span class="header-after-delete-click__delete-confirmation">Are you sure you want to delete <b>{{selectedProjectMembersCount}}</b> {{userCountTitle}}?</span>
|
||||
<div class="header-after-delete-click__button-area">
|
||||
<VButton
|
||||
class="button deletion"
|
||||
@ -71,7 +72,7 @@ import VButton from '@/components/common/VButton.vue';
|
||||
import VHeader from '@/components/common/VHeader.vue';
|
||||
import AddUserPopup from '@/components/team/AddUserPopup.vue';
|
||||
|
||||
import { ProjectMember, ProjectMemberHeaderState } from '@/types/projectMembers';
|
||||
import { ProjectMemberHeaderState } from '@/types/projectMembers';
|
||||
import { APP_STATE_ACTIONS, NOTIFICATION_ACTIONS, PM_ACTIONS } from '@/utils/constants/actionNames';
|
||||
|
||||
declare interface ClearSearch {
|
||||
@ -119,22 +120,20 @@ export default class HeaderArea extends Vue {
|
||||
this.$store.dispatch(PM_ACTIONS.CLEAR_SELECTION);
|
||||
this.isDeleteClicked = false;
|
||||
|
||||
this.$emit('onSuccessAction');
|
||||
this.$refs.headerComponent.clearSearch();
|
||||
}
|
||||
|
||||
public async onDelete(): Promise<void> {
|
||||
const projectMemberEmails: string[] = this.$store.getters.selectedProjectMembers.map((member: ProjectMember) => {
|
||||
return member.user.email;
|
||||
});
|
||||
|
||||
try {
|
||||
await this.$store.dispatch(PM_ACTIONS.DELETE, projectMemberEmails);
|
||||
await this.$store.dispatch(PM_ACTIONS.DELETE);
|
||||
} catch (error) {
|
||||
this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, `Error while deleting users from projectMembers. ${error.message}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.$emit('onSuccessAction');
|
||||
this.$store.dispatch(NOTIFICATION_ACTIONS.SUCCESS, 'Members was successfully removed from project');
|
||||
this.isDeleteClicked = false;
|
||||
|
||||
@ -212,6 +211,11 @@ export default class HeaderArea extends Vue {
|
||||
align-items: flex-end;
|
||||
height: 85px;
|
||||
justify-content: center;
|
||||
|
||||
&__info-text {
|
||||
margin-left: 25px;
|
||||
line-height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
|
@ -6,7 +6,8 @@
|
||||
<div class="team-area__header">
|
||||
<HeaderArea
|
||||
:header-state="headerState"
|
||||
:selected-project-members-count="selectedProjectMembers.length"
|
||||
:selected-project-members-count="selectedProjectMembersLength"
|
||||
@onSuccessAction="resetPaginator"
|
||||
/>
|
||||
</div>
|
||||
<div class="team-area__container" id="team-container" v-if="isTeamAreaShown">
|
||||
@ -99,7 +100,7 @@ export default class ProjectMembersArea extends Vue {
|
||||
}
|
||||
|
||||
public onMemberClick(member: ProjectMember): void {
|
||||
this.$store.dispatch(PM_ACTIONS.TOGGLE_SELECTION, member.user.id);
|
||||
this.$store.dispatch(PM_ACTIONS.TOGGLE_SELECTION, member);
|
||||
}
|
||||
|
||||
public get projectMembers(): ProjectMember[] {
|
||||
@ -122,12 +123,12 @@ export default class ProjectMembersArea extends Vue {
|
||||
return this.$store.state.projectMembersModule.page.pageCount;
|
||||
}
|
||||
|
||||
public get selectedProjectMembers(): ProjectMember[] {
|
||||
return this.$store.getters.selectedProjectMembers;
|
||||
public get selectedProjectMembersLength(): number {
|
||||
return this.$store.state.projectMembersModule.selectedProjectMembersEmails.length;
|
||||
}
|
||||
|
||||
public get headerState(): number {
|
||||
if (this.selectedProjectMembers.length > 0) {
|
||||
if (this.selectedProjectMembersLength > 0) {
|
||||
return ProjectMemberHeaderState.ON_SELECT;
|
||||
}
|
||||
|
||||
@ -159,6 +160,10 @@ export default class ProjectMembersArea extends Vue {
|
||||
await this.$store.dispatch(NOTIFICATION_ACTIONS.ERROR, `Unable to fetch project members. ${error.message}`);
|
||||
}
|
||||
|
||||
this.resetPaginator();
|
||||
}
|
||||
|
||||
public resetPaginator(): void {
|
||||
if (this.totalPageCount > 1) {
|
||||
this.$refs.pagination.resetPageIndex();
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ const {
|
||||
class ProjectMembersState {
|
||||
public cursor: ProjectMemberCursor = new ProjectMemberCursor();
|
||||
public page: ProjectMembersPage = new ProjectMembersPage();
|
||||
public selectedProjectMembersEmails: string[] = [];
|
||||
}
|
||||
|
||||
export function makeProjectMembersModule(api: ProjectMembersApi): StoreModule<ProjectMembersState> {
|
||||
@ -44,6 +45,13 @@ export function makeProjectMembersModule(api: ProjectMembersApi): StoreModule<Pr
|
||||
mutations: {
|
||||
[FETCH](state: ProjectMembersState, page: ProjectMembersPage) {
|
||||
state.page = page;
|
||||
state.page.projectMembers = state.page.projectMembers.map(member => {
|
||||
if (state.selectedProjectMembersEmails.includes(member.user.email)) {
|
||||
member.isSelected = true;
|
||||
}
|
||||
|
||||
return member;
|
||||
});
|
||||
},
|
||||
[SET_PAGE](state: ProjectMembersState, page: number) {
|
||||
state.cursor.page = page;
|
||||
@ -60,17 +68,23 @@ export function makeProjectMembersModule(api: ProjectMembersApi): StoreModule<Pr
|
||||
[CLEAR](state: ProjectMembersState) {
|
||||
state.cursor = new ProjectMemberCursor();
|
||||
state.page = new ProjectMembersPage();
|
||||
state.selectedProjectMembersEmails = [];
|
||||
},
|
||||
[TOGGLE_SELECTION](state: ProjectMembersState, projectMemberId: string) {
|
||||
state.page.projectMembers = state.page.projectMembers.map((projectMember: ProjectMember) => {
|
||||
if (projectMember.user.id === projectMemberId) {
|
||||
projectMember.isSelected = !projectMember.isSelected;
|
||||
}
|
||||
[TOGGLE_SELECTION](state: ProjectMembersState, projectMember: ProjectMember) {
|
||||
if (!state.selectedProjectMembersEmails.includes(projectMember.user.email)) {
|
||||
projectMember.isSelected = true;
|
||||
state.selectedProjectMembersEmails.push(projectMember.user.email);
|
||||
|
||||
return projectMember;
|
||||
return;
|
||||
}
|
||||
|
||||
projectMember.isSelected = false;
|
||||
state.selectedProjectMembersEmails = state.selectedProjectMembersEmails.filter(projectMemberEmail => {
|
||||
return projectMemberEmail !== projectMember.user.email;
|
||||
});
|
||||
},
|
||||
[CLEAR_SELECTION](state: ProjectMembersState) {
|
||||
state.selectedProjectMembersEmails = [];
|
||||
state.page.projectMembers = state.page.projectMembers.map((projectMember: ProjectMember) => {
|
||||
projectMember.isSelected = false;
|
||||
|
||||
@ -84,10 +98,12 @@ export function makeProjectMembersModule(api: ProjectMembersApi): StoreModule<Pr
|
||||
|
||||
await api.add(projectId, emails);
|
||||
},
|
||||
deleteProjectMembers: async function ({rootGetters}: any, projectMemberEmails: string[]): Promise<void> {
|
||||
deleteProjectMembers: async function ({rootGetters, state, commit}: any): Promise<void> {
|
||||
const projectId = rootGetters.selectedProject.id;
|
||||
|
||||
await api.delete(projectId, projectMemberEmails);
|
||||
await api.delete(projectId, state.selectedProjectMembersEmails);
|
||||
|
||||
commit(CLEAR_SELECTION);
|
||||
},
|
||||
fetchProjectMembers: async function ({commit, rootGetters, state}: any, page: number): Promise<ProjectMembersPage> {
|
||||
const projectID = rootGetters.selectedProject.id;
|
||||
@ -112,15 +128,17 @@ export function makeProjectMembersModule(api: ProjectMembersApi): StoreModule<Pr
|
||||
clearProjectMembers: function ({commit}) {
|
||||
commit(CLEAR);
|
||||
},
|
||||
toggleProjectMemberSelection: function ({commit}: any, projectMemberId: string) {
|
||||
commit(TOGGLE_SELECTION, projectMemberId);
|
||||
toggleProjectMemberSelection: function ({commit}: any, projectMember: ProjectMember) {
|
||||
commit(TOGGLE_SELECTION, projectMember);
|
||||
},
|
||||
clearProjectMemberSelection: function ({commit}: any) {
|
||||
commit(CLEAR_SELECTION);
|
||||
},
|
||||
},
|
||||
getters: {
|
||||
selectedProjectMembers: (state: any) => state.page.projectMembers.filter((member: ProjectMember) => member.isSelected),
|
||||
selectedProjectMembers: (state: ProjectMembersState) =>
|
||||
state.page.projectMembers.filter((member: ProjectMember) =>
|
||||
state.selectedProjectMembersEmails.includes(member.user.email)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -93,8 +93,8 @@ export class ProjectMember {
|
||||
public joinedAt: string;
|
||||
public isSelected: boolean;
|
||||
|
||||
public constructor(fullName: string, shortName: string, email: string, joinedAt: string, id?: string) {
|
||||
this.user = new User(id || '', fullName, shortName, email);
|
||||
public constructor(fullName: string, shortName: string, email: string, joinedAt: string, id: string = '') {
|
||||
this.user = new User(id, fullName, shortName, email);
|
||||
this.joinedAt = joinedAt;
|
||||
this.isSelected = false;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ exports[`Team HeaderArea renders correctly with 1 selected user and delete click
|
||||
<vheader-stub placeholder="Team Members" search="function () { [native code] }">
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="header-after-delete-click"><span class="header-after-delete-click__delete-confirmation">Are you sure you want to delete 1 user?</span>
|
||||
<div class="header-after-delete-click"><span class="header-after-delete-click__delete-confirmation">Are you sure you want to delete <b>1</b> user?</span>
|
||||
<div class="header-after-delete-click__button-area">
|
||||
<vbutton-stub label="Delete" width="122px" height="48px" onpress="function () { [native code] }" class="button deletion"></vbutton-stub>
|
||||
<vbutton-stub label="Cancel" width="122px" height="48px" iswhite="true" onpress="function () { [native code] }" class="button"></vbutton-stub>
|
||||
@ -46,7 +46,7 @@ exports[`Team HeaderArea renders correctly with 2 selected users and delete clic
|
||||
<vheader-stub placeholder="Team Members" search="function () { [native code] }">
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="header-after-delete-click"><span class="header-after-delete-click__delete-confirmation">Are you sure you want to delete 2 users?</span>
|
||||
<div class="header-after-delete-click"><span class="header-after-delete-click__delete-confirmation">Are you sure you want to delete <b>2</b> users?</span>
|
||||
<div class="header-after-delete-click__button-area">
|
||||
<vbutton-stub label="Delete" width="122px" height="48px" onpress="function () { [native code] }" class="button deletion"></vbutton-stub>
|
||||
<vbutton-stub label="Cancel" width="122px" height="48px" iswhite="true" onpress="function () { [native code] }" class="button"></vbutton-stub>
|
||||
@ -86,7 +86,7 @@ exports[`Team HeaderArea renders correctly with selected users 1`] = `
|
||||
<!---->
|
||||
<div class="header-selected-members">
|
||||
<vbutton-stub label="Delete" width="122px" height="48px" onpress="function () { [native code] }" class="button deletion"></vbutton-stub>
|
||||
<vbutton-stub label="Cancel" width="122px" height="48px" iswhite="true" onpress="function () { [native code] }" class="button"></vbutton-stub>
|
||||
<vbutton-stub label="Cancel" width="122px" height="48px" iswhite="true" onpress="function () { [native code] }" class="button"></vbutton-stub> <span class="header-selected-members__info-text"><b>2</b> users selected</span>
|
||||
</div>
|
||||
<!---->
|
||||
</vheader-stub>
|
||||
|
@ -79,9 +79,24 @@ describe('mutations', () => {
|
||||
});
|
||||
|
||||
it('toggle selection', function () {
|
||||
store.commit(PROJECT_MEMBER_MUTATIONS.TOGGLE_SELECTION, projectMember1.user.id);
|
||||
const testProjectMembersPage = new ProjectMembersPage();
|
||||
testProjectMembersPage.projectMembers = [projectMember1];
|
||||
testProjectMembersPage.totalCount = 1;
|
||||
testProjectMembersPage.pageCount = 1;
|
||||
|
||||
store.commit(PROJECT_MEMBER_MUTATIONS.TOGGLE_SELECTION, projectMember1);
|
||||
|
||||
expect(state.page.projectMembers[0].isSelected).toBe(true);
|
||||
expect(state.selectedProjectMembersEmails.length).toBe(1);
|
||||
|
||||
store.commit(PROJECT_MEMBER_MUTATIONS.FETCH, testProjectMembersPage);
|
||||
|
||||
expect(state.selectedProjectMembersEmails.length).toBe(1);
|
||||
|
||||
store.commit(PROJECT_MEMBER_MUTATIONS.TOGGLE_SELECTION, projectMember1);
|
||||
|
||||
expect(state.page.projectMembers[0].isSelected).toBe(false);
|
||||
expect(state.selectedProjectMembersEmails.length).toBe(0);
|
||||
});
|
||||
|
||||
it('clear selection', function () {
|
||||
@ -90,6 +105,8 @@ describe('mutations', () => {
|
||||
state.page.projectMembers.forEach((pm: ProjectMember) => {
|
||||
expect(pm.isSelected).toBe(false);
|
||||
});
|
||||
|
||||
expect(state.selectedProjectMembersEmails.length).toBe(0);
|
||||
});
|
||||
|
||||
it('clear store', function () {
|
||||
@ -100,6 +117,7 @@ describe('mutations', () => {
|
||||
expect(state.cursor.order).toBe(ProjectMemberOrderBy.NAME);
|
||||
expect(state.cursor.orderDirection).toBe(SortDirection.ASCENDING);
|
||||
expect(state.page.projectMembers.length).toBe(0);
|
||||
expect(state.selectedProjectMembersEmails.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@ -243,9 +261,25 @@ describe('actions', async () => {
|
||||
);
|
||||
|
||||
await store.dispatch(PM_ACTIONS.FETCH, FIRST_PAGE);
|
||||
store.dispatch(PM_ACTIONS.TOGGLE_SELECTION, projectMember1.user.id);
|
||||
store.dispatch(PM_ACTIONS.TOGGLE_SELECTION, projectMember1);
|
||||
|
||||
expect(state.page.projectMembers[0].isSelected).toBe(true);
|
||||
expect(state.selectedProjectMembersEmails.length).toBe(1);
|
||||
|
||||
store.dispatch(PM_ACTIONS.TOGGLE_SELECTION, projectMember2);
|
||||
|
||||
expect(state.page.projectMembers[1].isSelected).toBe(true);
|
||||
expect(state.selectedProjectMembersEmails.length).toBe(2);
|
||||
|
||||
await store.dispatch(PM_ACTIONS.FETCH, FIRST_PAGE);
|
||||
|
||||
expect(state.page.projectMembers[1].isSelected).toBe(true);
|
||||
expect(state.selectedProjectMembersEmails.length).toBe(2);
|
||||
|
||||
store.dispatch(PM_ACTIONS.TOGGLE_SELECTION, projectMember1);
|
||||
|
||||
expect(state.page.projectMembers[0].isSelected).toBe(false);
|
||||
expect(state.selectedProjectMembersEmails.length).toBe(1);
|
||||
});
|
||||
|
||||
it('clear selection', function () {
|
||||
@ -269,7 +303,6 @@ describe('actions', async () => {
|
||||
|
||||
describe('getters', () => {
|
||||
const selectedProjectMember = new ProjectMember('testFullName2', 'testShortName2', 'test2@example.com', 'now2', '2');
|
||||
selectedProjectMember.isSelected = true;
|
||||
|
||||
it('selected project members', function () {
|
||||
const testProjectMembersPage = new ProjectMembersPage();
|
||||
@ -278,6 +311,7 @@ describe('getters', () => {
|
||||
testProjectMembersPage.pageCount = 1;
|
||||
|
||||
store.commit(PROJECT_MEMBER_MUTATIONS.FETCH, testProjectMembersPage);
|
||||
store.commit(PROJECT_MEMBER_MUTATIONS.TOGGLE_SELECTION, selectedProjectMember);
|
||||
|
||||
const retrievedProjectMembers = store.getters.selectedProjectMembers;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user