[WEB-2357] fix: update and redefine user roles across the platform (#5466)
* chore: removed viewer role * chore: indentation * chore: remove viewer role * chore: handled user permissions in store * chore: updated the migration file * chore: updated user permissions store * chore: removed the owner key * chore: code refactor * chore: code refactor * chore: code refactor * chore: code refactor * chore: code refactor * fix: build error * chore: updated user permissions store and handled the permissions fetch in workspace and project wrappers * chore: package user enum updated * chore: user permission updated * chore: user permission updated * chore: resolved build errors * chore: resolved build error * chore: resolved build errors * chore: computedFn deep map issue resolved * chore: added back migration * chore: added new field in project table * chore: removed member store in users * chore: private project for admins * chore: workspace notification access validation updated * fix: workspace member edit option * fix: project intake permission validation updated * chore: workspace export settings permission updated * chore: guest_view_all_issues added * chore: guest_view_all_issues added * chore: key changed for guest access * chore: added validation for individual issues * chore: changed the dashboard issues count * chore: added new yarn file * chore: modified yarn file * chore: project page permission updated * chore: project page permission updated * chore: member setting ux updated * chore: build error * fix: yarn lock * fix: build error --------- Co-authored-by: gurusainath <gurusainath007@gmail.com> Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
This commit is contained in:
parent
7013a36629
commit
fdcd9a376c
172 changed files with 2057 additions and 1627 deletions
|
|
@ -4,10 +4,9 @@ import { action, makeObservable, observable, runInAction, computed } from "mobx"
|
|||
// types
|
||||
import { IUser } from "@plane/types";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
// services
|
||||
import { AuthService } from "@/services/auth.service";
|
||||
import { UserService } from "@/services/user.service";
|
||||
|
|
@ -15,8 +14,9 @@ import { UserService } from "@/services/user.service";
|
|||
import { CoreRootStore } from "@/store/root.store";
|
||||
import { IAccountStore } from "@/store/user/account.store";
|
||||
import { ProfileStore, IUserProfileStore } from "@/store/user/profile.store";
|
||||
import { IUserMembershipStore, UserMembershipStore } from "@/store/user/user-membership.store";
|
||||
import { IUserPermissionStore, UserPermissionStore } from "./permissions.store";
|
||||
import { IUserSettingsStore, UserSettingsStore } from "./settings.store";
|
||||
import { TUserPermissions } from "@plane/types/src/enums";
|
||||
|
||||
type TUserErrorStatus = {
|
||||
status: string;
|
||||
|
|
@ -33,7 +33,7 @@ export interface IUserStore {
|
|||
userProfile: IUserProfileStore;
|
||||
userSettings: IUserSettingsStore;
|
||||
accounts: Record<string, IAccountStore>;
|
||||
membership: IUserMembershipStore;
|
||||
permission: IUserPermissionStore;
|
||||
// actions
|
||||
fetchCurrentUser: () => Promise<IUser | undefined>;
|
||||
updateCurrentUser: (data: Partial<IUser>) => Promise<IUser | undefined>;
|
||||
|
|
@ -42,18 +42,6 @@ export interface IUserStore {
|
|||
reset: () => void;
|
||||
signOut: () => Promise<void>;
|
||||
// computed
|
||||
|
||||
// workspace level
|
||||
canPerformWorkspaceAdminActions: boolean;
|
||||
canPerformWorkspaceMemberActions: boolean;
|
||||
canPerformWorkspaceViewerActions: boolean;
|
||||
canPerformWorkspaceGuestActions: boolean;
|
||||
|
||||
// project level
|
||||
canPerformProjectAdminActions: boolean;
|
||||
canPerformProjectMemberActions: boolean;
|
||||
canPerformProjectViewerActions: boolean;
|
||||
canPerformProjectGuestActions: boolean;
|
||||
canPerformAnyCreateAction: boolean;
|
||||
projectsWithCreatePermissions: { [projectId: string]: number } | null;
|
||||
}
|
||||
|
|
@ -68,7 +56,7 @@ export class UserStore implements IUserStore {
|
|||
userProfile: IUserProfileStore;
|
||||
userSettings: IUserSettingsStore;
|
||||
accounts: Record<string, IAccountStore> = {};
|
||||
membership: IUserMembershipStore;
|
||||
permission: IUserPermissionStore;
|
||||
// service
|
||||
userService: UserService;
|
||||
authService: AuthService;
|
||||
|
|
@ -77,7 +65,7 @@ export class UserStore implements IUserStore {
|
|||
// stores
|
||||
this.userProfile = new ProfileStore(store);
|
||||
this.userSettings = new UserSettingsStore();
|
||||
this.membership = new UserMembershipStore(store);
|
||||
this.permission = new UserPermissionStore(store);
|
||||
// service
|
||||
this.userService = new UserService();
|
||||
this.authService = new AuthService();
|
||||
|
|
@ -92,7 +80,7 @@ export class UserStore implements IUserStore {
|
|||
userProfile: observable,
|
||||
userSettings: observable,
|
||||
accounts: observable,
|
||||
membership: observable,
|
||||
permission: observable,
|
||||
// actions
|
||||
fetchCurrentUser: action,
|
||||
updateCurrentUser: action,
|
||||
|
|
@ -101,16 +89,6 @@ export class UserStore implements IUserStore {
|
|||
reset: action,
|
||||
signOut: action,
|
||||
// computed
|
||||
canPerformWorkspaceAdminActions: computed,
|
||||
canPerformWorkspaceMemberActions: computed,
|
||||
canPerformWorkspaceViewerActions: computed,
|
||||
canPerformWorkspaceGuestActions: computed,
|
||||
|
||||
canPerformProjectAdminActions: computed,
|
||||
canPerformProjectMemberActions: computed,
|
||||
canPerformProjectViewerActions: computed,
|
||||
canPerformProjectGuestActions: computed,
|
||||
|
||||
canPerformAnyCreateAction: computed,
|
||||
projectsWithCreatePermissions: computed,
|
||||
});
|
||||
|
|
@ -158,26 +136,6 @@ export class UserStore implements IUserStore {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description fetches the prjects with write permissions
|
||||
* @returns {{[projectId: string]: number} || null}
|
||||
*/
|
||||
fetchProjectsWithCreatePermissions() {
|
||||
const allWorkspaceRoles =
|
||||
this.membership.workspaceProjectsRole &&
|
||||
this.membership.workspaceProjectsRole[this.membership.router.workspaceSlug || ""];
|
||||
return (
|
||||
(allWorkspaceRoles &&
|
||||
Object.keys(allWorkspaceRoles)
|
||||
.filter((key) => allWorkspaceRoles[key] >= EUserProjectRoles.MEMBER)
|
||||
.reduce(
|
||||
(res: { [projectId: string]: number }, key: string) => ((res[key] = allWorkspaceRoles[key]), res),
|
||||
{}
|
||||
)) ||
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description updates the current user
|
||||
* @param data
|
||||
|
|
@ -258,7 +216,7 @@ export class UserStore implements IUserStore {
|
|||
this.data = undefined;
|
||||
this.userProfile = new ProfileStore(this.store);
|
||||
this.userSettings = new UserSettingsStore();
|
||||
this.membership = new UserMembershipStore(this.store);
|
||||
this.permission = new UserPermissionStore(this.store);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -271,6 +229,30 @@ export class UserStore implements IUserStore {
|
|||
this.store.resetOnSignOut();
|
||||
};
|
||||
|
||||
// helper actions
|
||||
/**
|
||||
* @description fetches the prjects with write permissions
|
||||
* @returns {{[projectId: string]: number} || null}
|
||||
*/
|
||||
fetchProjectsWithCreatePermissions = (): { [key: string]: TUserPermissions } => {
|
||||
const { workspaceSlug } = this.store.router;
|
||||
|
||||
const allWorkspaceProjectRoles =
|
||||
this.permission.workspaceProjectsPermissions && this.permission.workspaceProjectsPermissions[workspaceSlug || ""];
|
||||
|
||||
const userPermissions =
|
||||
(allWorkspaceProjectRoles &&
|
||||
Object.keys(allWorkspaceProjectRoles)
|
||||
.filter((key) => allWorkspaceProjectRoles[key] >= EUserPermissions.MEMBER)
|
||||
.reduce(
|
||||
(res: { [projectId: string]: number }, key: string) => ((res[key] = allWorkspaceProjectRoles[key]), res),
|
||||
{}
|
||||
)) ||
|
||||
null;
|
||||
|
||||
return userPermissions;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description returns projects where user has permissions
|
||||
* @returns {{[projectId: string]: number} || null}
|
||||
|
|
@ -287,70 +269,4 @@ export class UserStore implements IUserStore {
|
|||
const filteredProjects = this.fetchProjectsWithCreatePermissions();
|
||||
return filteredProjects ? Object.keys(filteredProjects).length > 0 : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has workspace admin actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformWorkspaceAdminActions() {
|
||||
return !!this.membership.currentWorkspaceRole && this.membership.currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has workspace member actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformWorkspaceMemberActions() {
|
||||
return !!this.membership.currentWorkspaceRole && this.membership.currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has workspace viewer actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
||||
get canPerformWorkspaceViewerActions() {
|
||||
return !!this.membership.currentWorkspaceRole && this.membership.currentWorkspaceRole >= EUserWorkspaceRoles.VIEWER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has workspace guest actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformWorkspaceGuestActions() {
|
||||
return !!this.membership.currentWorkspaceRole && this.membership.currentWorkspaceRole >= EUserWorkspaceRoles.GUEST;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has project admin actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformProjectAdminActions() {
|
||||
return !!this.membership.currentProjectRole && this.membership.currentProjectRole === EUserProjectRoles.ADMIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has project member actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformProjectMemberActions() {
|
||||
return !!this.membership.currentProjectRole && this.membership.currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has project viewer actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
||||
get canPerformProjectViewerActions() {
|
||||
return !!this.membership.currentProjectRole && this.membership.currentProjectRole >= EUserProjectRoles.VIEWER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has project guest actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformProjectGuestActions() {
|
||||
return !!this.membership.currentProjectRole && this.membership.currentProjectRole >= EUserProjectRoles.GUEST;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
270
web/core/store/user/permissions.store.ts
Normal file
270
web/core/store/user/permissions.store.ts
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
import set from "lodash/set";
|
||||
import unset from "lodash/unset";
|
||||
import { action, makeObservable, observable, runInAction } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// types
|
||||
import { IProjectMember, IUserProjectsRole, IWorkspaceMemberMe } from "@plane/types";
|
||||
// plane web types
|
||||
import {
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
TUserPermissions,
|
||||
TUserPermissionsLevel,
|
||||
} from "@/plane-web/constants/user-permissions";
|
||||
// plane web services
|
||||
import { WorkspaceService } from "@/plane-web/services/workspace.service";
|
||||
// services
|
||||
import projectMemberService from "@/services/project/project-member.service";
|
||||
import userService from "@/services/user.service";
|
||||
// store
|
||||
import { CoreRootStore } from "@/store/root.store";
|
||||
|
||||
// derived services
|
||||
const workspaceService = new WorkspaceService();
|
||||
|
||||
export interface IUserPermissionStore {
|
||||
// observables
|
||||
workspaceUserInfo: Record<string, IWorkspaceMemberMe>; // workspaceSlug -> IWorkspaceMemberMe
|
||||
projectUserInfo: Record<string, Record<string, IProjectMember>>; // workspaceSlug -> projectId -> IProjectMember
|
||||
workspaceProjectsPermissions: Record<string, IUserProjectsRole>; // workspaceSlug -> IUserProjectsRole
|
||||
// computed
|
||||
// computed helpers
|
||||
workspaceInfoBySlug: (workspaceSlug: string) => IWorkspaceMemberMe | undefined;
|
||||
projectPermissionsByWorkspaceSlugAndProjectId: (
|
||||
workspaceSlug: string,
|
||||
projectId: string
|
||||
) => TUserPermissions | undefined;
|
||||
allowPermissions: (
|
||||
allowPermissions: TUserPermissions[],
|
||||
level: TUserPermissionsLevel,
|
||||
workspaceSlug?: string,
|
||||
projectId?: string,
|
||||
onPermissionAllowed?: () => boolean
|
||||
) => boolean;
|
||||
// action helpers
|
||||
// actions
|
||||
fetchUserWorkspaceInfo: (workspaceSlug: string) => Promise<IWorkspaceMemberMe | undefined>;
|
||||
leaveWorkspace: (workspaceSlug: string) => Promise<void>;
|
||||
fetchUserProjectInfo: (workspaceSlug: string, projectId: string) => Promise<IProjectMember | undefined>;
|
||||
fetchUserProjectPermissions: (workspaceSlug: string) => Promise<IUserProjectsRole | undefined>;
|
||||
joinProject: (workspaceSlug: string, projectId: string) => Promise<void | undefined>;
|
||||
leaveProject: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export class UserPermissionStore implements IUserPermissionStore {
|
||||
// constants
|
||||
workspaceUserInfo: Record<string, IWorkspaceMemberMe> = {};
|
||||
projectUserInfo: Record<string, Record<string, IProjectMember>> = {};
|
||||
workspaceProjectsPermissions: Record<string, IUserProjectsRole> = {};
|
||||
// observables
|
||||
|
||||
constructor(private store: CoreRootStore) {
|
||||
makeObservable(this, {
|
||||
// observables
|
||||
workspaceUserInfo: observable,
|
||||
projectUserInfo: observable,
|
||||
workspaceProjectsPermissions: observable,
|
||||
// computed
|
||||
// actions
|
||||
fetchUserWorkspaceInfo: action,
|
||||
leaveWorkspace: action,
|
||||
fetchUserProjectInfo: action,
|
||||
fetchUserProjectPermissions: action,
|
||||
joinProject: action,
|
||||
leaveProject: action,
|
||||
});
|
||||
}
|
||||
|
||||
// computed
|
||||
|
||||
// computed helpers
|
||||
/**
|
||||
* @description Returns the current workspace information
|
||||
* @param { string } workspaceSlug
|
||||
* @returns { IWorkspaceMemberMe | undefined }
|
||||
*/
|
||||
workspaceInfoBySlug = computedFn((workspaceSlug: string): IWorkspaceMemberMe | undefined => {
|
||||
if (!workspaceSlug) return undefined;
|
||||
return this.workspaceUserInfo[workspaceSlug] || undefined;
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Returns the current project permissions
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { IUserProjectsRole | undefined }
|
||||
*/
|
||||
projectPermissionsByWorkspaceSlugAndProjectId = computedFn(
|
||||
(workspaceSlug: string, projectId: string): TUserPermissions | undefined => {
|
||||
if (!workspaceSlug || !projectId) return undefined;
|
||||
return this.workspaceProjectsPermissions?.[workspaceSlug]?.[projectId] || undefined;
|
||||
}
|
||||
);
|
||||
|
||||
// action helpers
|
||||
/**
|
||||
* @description Returns whether the user has the permission to perform an action
|
||||
* @param { TUserPermissions[] } allowPermissions
|
||||
* @param { TUserPermissionsLevel } level
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @param { () => boolean } onPermissionAllowed
|
||||
* @returns { boolean }
|
||||
*/
|
||||
allowPermissions = (
|
||||
allowPermissions: TUserPermissions[],
|
||||
level: TUserPermissionsLevel,
|
||||
workspaceSlug?: string,
|
||||
projectId?: string,
|
||||
onPermissionAllowed?: () => boolean
|
||||
): boolean => {
|
||||
const { workspaceSlug: currentWorkspaceSlug, projectId: currentProjectId } = this.store.router;
|
||||
if (!workspaceSlug) workspaceSlug = currentWorkspaceSlug;
|
||||
if (!projectId) projectId = currentProjectId;
|
||||
|
||||
let currentUserRole: TUserPermissions | undefined = undefined;
|
||||
|
||||
if (level === EUserPermissionsLevel.WORKSPACE) {
|
||||
const workspaceInfoBySlug = workspaceSlug && this.workspaceInfoBySlug(workspaceSlug);
|
||||
if (workspaceInfoBySlug) {
|
||||
currentUserRole = workspaceInfoBySlug?.role as unknown as EUserPermissions;
|
||||
}
|
||||
}
|
||||
|
||||
if (level === EUserPermissionsLevel.PROJECT) {
|
||||
currentUserRole = (workspaceSlug &&
|
||||
projectId &&
|
||||
this.projectPermissionsByWorkspaceSlugAndProjectId(workspaceSlug, projectId)) as EUserPermissions | undefined;
|
||||
}
|
||||
|
||||
if (currentUserRole && allowPermissions.includes(currentUserRole)) {
|
||||
if (onPermissionAllowed) {
|
||||
return onPermissionAllowed();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// actions
|
||||
/**
|
||||
* @description Fetches the user's workspace information
|
||||
* @param { string } workspaceSlug
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
fetchUserWorkspaceInfo = async (workspaceSlug: string): Promise<IWorkspaceMemberMe | undefined> => {
|
||||
try {
|
||||
const response = await workspaceService.workspaceMemberMe(workspaceSlug);
|
||||
if (response) {
|
||||
runInAction(() => {
|
||||
set(this.workspaceUserInfo, [workspaceSlug], response);
|
||||
});
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Error fetching user workspace information", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Leaves a workspace
|
||||
* @param { string } workspaceSlug
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
leaveWorkspace = async (workspaceSlug: string): Promise<void> => {
|
||||
try {
|
||||
await userService.leaveWorkspace(workspaceSlug);
|
||||
runInAction(() => {
|
||||
unset(this.workspaceUserInfo, workspaceSlug);
|
||||
unset(this.projectUserInfo, workspaceSlug);
|
||||
unset(this.workspaceProjectsPermissions, workspaceSlug);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error user leaving the workspace", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Fetches the user's project information
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
fetchUserProjectInfo = async (workspaceSlug: string, projectId: string): Promise<IProjectMember | undefined> => {
|
||||
try {
|
||||
const response = await projectMemberService.projectMemberMe(workspaceSlug, projectId);
|
||||
if (response) {
|
||||
runInAction(() => {
|
||||
set(this.projectUserInfo, [workspaceSlug, projectId], response);
|
||||
set(this.workspaceProjectsPermissions, [workspaceSlug, projectId], response.role);
|
||||
});
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Error fetching user project information", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Fetches the user's project permissions
|
||||
* @param { string } workspaceSlug
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
fetchUserProjectPermissions = async (workspaceSlug: string): Promise<IUserProjectsRole | undefined> => {
|
||||
try {
|
||||
const response = await workspaceService.getWorkspaceUserProjectsRole(workspaceSlug);
|
||||
runInAction(() => {
|
||||
set(this.workspaceProjectsPermissions, [workspaceSlug], response);
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Error fetching user project permissions", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Joins a project
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
joinProject = async (workspaceSlug: string, projectId: string): Promise<void | undefined> => {
|
||||
try {
|
||||
const response = await userService.joinProject(workspaceSlug, [projectId]);
|
||||
if (response) {
|
||||
runInAction(() => {
|
||||
set(this.workspaceProjectsPermissions, [workspaceSlug, projectId], response);
|
||||
});
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Error user joining the project", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Leaves a project
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
leaveProject = async (workspaceSlug: string, projectId: string): Promise<void> => {
|
||||
try {
|
||||
await userService.leaveProject(workspaceSlug, projectId);
|
||||
runInAction(() => {
|
||||
unset(this.workspaceProjectsPermissions, [workspaceSlug, projectId]);
|
||||
unset(this.projectUserInfo, [workspaceSlug, projectId]);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error user leaving the project", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,283 +0,0 @@
|
|||
import set from "lodash/set";
|
||||
import update from "lodash/update";
|
||||
import { action, observable, runInAction, makeObservable, computed } from "mobx";
|
||||
// types
|
||||
import { IWorkspaceMemberMe, IProjectMember, IUserProjectsRole, IProjectMemberLite } from "@plane/types";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// services
|
||||
import { WorkspaceService } from "@/plane-web/services";
|
||||
import { ProjectMemberService } from "@/services/project";
|
||||
import { UserService } from "@/services/user.service";
|
||||
// plane web store
|
||||
import { CoreRootStore } from "../root.store";
|
||||
import { IRouterStore } from "../router.store";
|
||||
|
||||
export interface IUserMembershipStore {
|
||||
// observables
|
||||
workspaceMemberInfo: {
|
||||
[workspaceSlug: string]: IWorkspaceMemberMe;
|
||||
};
|
||||
hasPermissionToWorkspace: {
|
||||
[workspaceSlug: string]: boolean | null;
|
||||
};
|
||||
projectMemberInfo: {
|
||||
[projectId: string]: IProjectMember;
|
||||
};
|
||||
hasPermissionToProject: {
|
||||
[projectId: string]: boolean | null;
|
||||
};
|
||||
workspaceProjectsRole: { [workspaceSlug: string]: IUserProjectsRole };
|
||||
// computed
|
||||
currentProjectMemberInfo: IProjectMember | undefined;
|
||||
currentWorkspaceMemberInfo: IWorkspaceMemberMe | undefined;
|
||||
currentProjectRole: EUserProjectRoles | undefined;
|
||||
currentWorkspaceRole: EUserWorkspaceRoles | undefined;
|
||||
currentWorkspaceAllProjectsRole: IUserProjectsRole | undefined;
|
||||
|
||||
// computed functions
|
||||
currentProjectRoleByProjectId: (projectId: string) => EUserProjectRoles | undefined;
|
||||
|
||||
hasPermissionToCurrentWorkspace: boolean | undefined;
|
||||
hasPermissionToCurrentProject: boolean | undefined;
|
||||
// fetch actions
|
||||
fetchUserWorkspaceInfo: (workspaceSlug: string) => Promise<IWorkspaceMemberMe>;
|
||||
fetchUserProjectInfo: (workspaceSlug: string, projectId: string) => Promise<IProjectMember>;
|
||||
fetchUserWorkspaceProjectsRole: (workspaceSlug: string) => Promise<IUserProjectsRole>;
|
||||
// crud actions
|
||||
leaveWorkspace: (workspaceSlug: string) => Promise<void>;
|
||||
joinProject: (workspaceSlug: string, projectIds: string[]) => Promise<any>;
|
||||
leaveProject: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||
|
||||
router: IRouterStore;
|
||||
}
|
||||
|
||||
export class UserMembershipStore implements IUserMembershipStore {
|
||||
workspaceMemberInfo: {
|
||||
[workspaceSlug: string]: IWorkspaceMemberMe;
|
||||
} = {};
|
||||
hasPermissionToWorkspace: {
|
||||
[workspaceSlug: string]: boolean;
|
||||
} = {};
|
||||
projectMemberInfo: {
|
||||
[projectId: string]: IProjectMember;
|
||||
} = {};
|
||||
hasPermissionToProject: {
|
||||
[projectId: string]: boolean;
|
||||
} = {};
|
||||
workspaceProjectsRole: { [workspaceSlug: string]: IUserProjectsRole } = {};
|
||||
// stores
|
||||
router;
|
||||
store;
|
||||
// services
|
||||
userService;
|
||||
workspaceService;
|
||||
projectMemberService;
|
||||
|
||||
constructor(_rootStore: CoreRootStore) {
|
||||
makeObservable(this, {
|
||||
// observables
|
||||
workspaceMemberInfo: observable,
|
||||
hasPermissionToWorkspace: observable,
|
||||
projectMemberInfo: observable,
|
||||
hasPermissionToProject: observable,
|
||||
workspaceProjectsRole: observable,
|
||||
// computed
|
||||
currentWorkspaceMemberInfo: computed,
|
||||
currentWorkspaceRole: computed,
|
||||
currentProjectMemberInfo: computed,
|
||||
currentProjectRole: computed,
|
||||
currentWorkspaceAllProjectsRole: computed,
|
||||
hasPermissionToCurrentWorkspace: computed,
|
||||
hasPermissionToCurrentProject: computed,
|
||||
// actions
|
||||
fetchUserWorkspaceInfo: action,
|
||||
fetchUserProjectInfo: action,
|
||||
leaveWorkspace: action,
|
||||
joinProject: action,
|
||||
leaveProject: action,
|
||||
fetchUserWorkspaceProjectsRole: action,
|
||||
});
|
||||
this.router = _rootStore.router;
|
||||
this.store = _rootStore;
|
||||
// services
|
||||
this.userService = new UserService();
|
||||
this.workspaceService = new WorkspaceService();
|
||||
this.projectMemberService = new ProjectMemberService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current workspace member info
|
||||
*/
|
||||
get currentWorkspaceMemberInfo() {
|
||||
if (!this.router.workspaceSlug) return;
|
||||
return this.workspaceMemberInfo[this.router.workspaceSlug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current workspace role
|
||||
*/
|
||||
get currentWorkspaceRole() {
|
||||
if (!this.router.workspaceSlug) return;
|
||||
return this.workspaceMemberInfo[this.router.workspaceSlug]?.role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current project member info
|
||||
*/
|
||||
get currentProjectMemberInfo() {
|
||||
if (!this.router.projectId) return;
|
||||
return this.projectMemberInfo[this.router.projectId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current project role
|
||||
*/
|
||||
get currentProjectRole() {
|
||||
if (!this.router.projectId) return;
|
||||
return this.projectMemberInfo[this.router.projectId]?.role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all projects role for the current workspace
|
||||
*/
|
||||
get currentWorkspaceAllProjectsRole() {
|
||||
if (!this.router.workspaceSlug) return;
|
||||
return this.workspaceProjectsRole?.[this.router.workspaceSlug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the user has permission to the current workspace
|
||||
*/
|
||||
get hasPermissionToCurrentWorkspace() {
|
||||
if (!this.router.workspaceSlug) return;
|
||||
return this.hasPermissionToWorkspace[this.router.workspaceSlug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the user has permission to the current project
|
||||
*/
|
||||
get hasPermissionToCurrentProject() {
|
||||
if (!this.router.projectId) return;
|
||||
return this.hasPermissionToProject[this.router.projectId];
|
||||
}
|
||||
|
||||
// computed functions
|
||||
/**
|
||||
* Returns the current project role by project id
|
||||
* @param projectId
|
||||
* @returns EUserProjectRoles
|
||||
*/
|
||||
currentProjectRoleByProjectId = (projectId: string) => this.projectMemberInfo[projectId]?.role || undefined;
|
||||
|
||||
/**
|
||||
* Fetches the current user workspace info
|
||||
* @param workspaceSlug
|
||||
* @returns Promise<IWorkspaceMemberMe>
|
||||
*/
|
||||
fetchUserWorkspaceInfo = async (workspaceSlug: string) =>
|
||||
await this.workspaceService.workspaceMemberMe(workspaceSlug).then((response) => {
|
||||
runInAction(() => {
|
||||
set(this.workspaceMemberInfo, [workspaceSlug], response);
|
||||
set(this.hasPermissionToWorkspace, [workspaceSlug], true);
|
||||
});
|
||||
return response;
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetches the current user project info
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
* @returns Promise<IProjectMember>
|
||||
*/
|
||||
fetchUserProjectInfo = async (workspaceSlug: string, projectId: string) =>
|
||||
await this.projectMemberService.projectMemberMe(workspaceSlug, projectId).then((response) => {
|
||||
runInAction(() => {
|
||||
this.projectMemberInfo = {
|
||||
...this.projectMemberInfo,
|
||||
[projectId]: response,
|
||||
};
|
||||
this.hasPermissionToProject = {
|
||||
...this.hasPermissionToProject,
|
||||
[projectId]: true,
|
||||
};
|
||||
});
|
||||
return response;
|
||||
});
|
||||
|
||||
/**
|
||||
* Leaves a workspace
|
||||
* @param workspaceSlug
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
leaveWorkspace = async (workspaceSlug: string) => {
|
||||
const currentWorksSpace = this.store.workspaceRoot?.currentWorkspace;
|
||||
await this.userService.leaveWorkspace(workspaceSlug).then(() => {
|
||||
runInAction(() => {
|
||||
if (currentWorksSpace) delete this.store.workspaceRoot?.workspaces?.[currentWorksSpace?.id];
|
||||
delete this.workspaceMemberInfo[workspaceSlug];
|
||||
delete this.hasPermissionToWorkspace[workspaceSlug];
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Joins a project
|
||||
* @param workspaceSlug
|
||||
* @param projectIds
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
joinProject = async (workspaceSlug: string, projectIds: string[]) =>
|
||||
await this.userService.joinProject(workspaceSlug, projectIds).then(() => {
|
||||
const newPermissions: { [projectId: string]: boolean } = {};
|
||||
projectIds.forEach((projectId) => {
|
||||
newPermissions[projectId] = true;
|
||||
});
|
||||
runInAction(() => {
|
||||
this.hasPermissionToProject = {
|
||||
...this.hasPermissionToProject,
|
||||
...newPermissions,
|
||||
};
|
||||
projectIds.forEach((projectId) => {
|
||||
set(this.workspaceProjectsRole, [workspaceSlug, projectId], EUserProjectRoles.MEMBER);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Leaves a project
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
leaveProject = async (workspaceSlug: string, projectId: string) =>
|
||||
await this.userService.leaveProject(workspaceSlug, projectId).then(() => {
|
||||
// remove the user membership for a project
|
||||
set(this.hasPermissionToProject, [projectId], false);
|
||||
// update the project member list with the new permissions
|
||||
set(this.store.projectRoot.project.projectMap, [projectId, "is_member"], false);
|
||||
// remove user from project members list
|
||||
update(this.store.projectRoot.project.projectMap, projectId, (project) => {
|
||||
if (project) {
|
||||
project.members = project.members.filter(
|
||||
(member: IProjectMemberLite) => member.member_id !== this.store.user?.data?.id
|
||||
);
|
||||
}
|
||||
return project;
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetches the current user workspace projects role
|
||||
* @param workspaceSlug
|
||||
* @returns Promise<IUserProjectsRole>
|
||||
*/
|
||||
fetchUserWorkspaceProjectsRole = async (workspaceSlug: string) =>
|
||||
await this.workspaceService.getWorkspaceUserProjectsRole(workspaceSlug).then((response) => {
|
||||
runInAction(() => {
|
||||
set(this.workspaceProjectsRole, [workspaceSlug], response);
|
||||
});
|
||||
return response;
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue