[WEB-3551] fix: role improvements (#6763)
* Return Cycle start and end dates in project's timezone * fix: role improvements * chore: role updates * chore: update role endpoint to update workspace admin permissions * fix: conditions * chore: update member role for workspace members * chore: update workspace permission role * fix: currentAdmin permissions --------- Co-authored-by: Dheeraj Kumar Ketireddy <dheeru0198@gmail.com> Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
This commit is contained in:
parent
cbe248591e
commit
4032aa62c5
9 changed files with 94 additions and 61 deletions
|
|
@ -10,11 +10,7 @@ from plane.app.serializers import (
|
||||||
ProjectMemberRoleSerializer,
|
ProjectMemberRoleSerializer,
|
||||||
)
|
)
|
||||||
|
|
||||||
from plane.app.permissions import (
|
from plane.app.permissions import WorkspaceUserPermission
|
||||||
ProjectMemberPermission,
|
|
||||||
ProjectLitePermission,
|
|
||||||
WorkspaceUserPermission,
|
|
||||||
)
|
|
||||||
|
|
||||||
from plane.db.models import Project, ProjectMember, IssueUserProperty, WorkspaceMember
|
from plane.db.models import Project, ProjectMember, IssueUserProperty, WorkspaceMember
|
||||||
from plane.bgtasks.project_add_user_email_task import project_add_user_email
|
from plane.bgtasks.project_add_user_email_task import project_add_user_email
|
||||||
|
|
@ -26,14 +22,6 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
serializer_class = ProjectMemberAdminSerializer
|
serializer_class = ProjectMemberAdminSerializer
|
||||||
model = ProjectMember
|
model = ProjectMember
|
||||||
|
|
||||||
def get_permissions(self):
|
|
||||||
if self.action == "leave":
|
|
||||||
self.permission_classes = [ProjectLitePermission]
|
|
||||||
else:
|
|
||||||
self.permission_classes = [ProjectMemberPermission]
|
|
||||||
|
|
||||||
return super(ProjectMemberViewSet, self).get_permissions()
|
|
||||||
|
|
||||||
search_fields = ["member__display_name", "member__first_name"]
|
search_fields = ["member__display_name", "member__first_name"]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
|
@ -187,12 +175,20 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
)
|
)
|
||||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@allow_permission([ROLE.ADMIN])
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
def partial_update(self, request, slug, project_id, pk):
|
def partial_update(self, request, slug, project_id, pk):
|
||||||
project_member = ProjectMember.objects.get(
|
project_member = ProjectMember.objects.get(
|
||||||
pk=pk, workspace__slug=slug, project_id=project_id, is_active=True
|
pk=pk, workspace__slug=slug, project_id=project_id, is_active=True
|
||||||
)
|
)
|
||||||
if request.user.id == project_member.member_id:
|
|
||||||
|
# Fetch the workspace role of the project member
|
||||||
|
workspace_role = WorkspaceMember.objects.get(
|
||||||
|
workspace__slug=slug, member=project_member.member, is_active=True
|
||||||
|
).role
|
||||||
|
is_workspace_admin = workspace_role == ROLE.ADMIN.value
|
||||||
|
|
||||||
|
# Check if the user is not editing their own role if they are not an admin
|
||||||
|
if request.user.id == project_member.member_id and not is_workspace_admin:
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "You cannot update your own role"},
|
{"error": "You cannot update your own role"},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
|
|
@ -205,9 +201,6 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
is_active=True,
|
is_active=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
workspace_role = WorkspaceMember.objects.get(
|
|
||||||
workspace__slug=slug, member=project_member.member, is_active=True
|
|
||||||
).role
|
|
||||||
if workspace_role in [5] and int(
|
if workspace_role in [5] and int(
|
||||||
request.data.get("role", project_member.role)
|
request.data.get("role", project_member.role)
|
||||||
) in [15, 20]:
|
) in [15, 20]:
|
||||||
|
|
@ -222,6 +215,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||||
"role" in request.data
|
"role" in request.data
|
||||||
and int(request.data.get("role", project_member.role))
|
and int(request.data.get("role", project_member.role))
|
||||||
> requested_project_member.role
|
> requested_project_member.role
|
||||||
|
and not is_workspace_admin
|
||||||
):
|
):
|
||||||
return Response(
|
return Response(
|
||||||
{"error": "You cannot update a role that is higher than your own role"},
|
{"error": "You cannot update a role that is higher than your own role"},
|
||||||
|
|
|
||||||
|
|
@ -68,10 +68,11 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
if workspace_member.role > int(request.data.get("role")):
|
# If a user is moved to a guest role he can't have any other role in projects
|
||||||
_ = ProjectMember.objects.filter(
|
if "role" in request.data and int(request.data.get("role")) == 5:
|
||||||
|
ProjectMember.objects.filter(
|
||||||
workspace__slug=slug, member_id=workspace_member.member_id
|
workspace__slug=slug, member_id=workspace_member.member_id
|
||||||
).update(role=int(request.data.get("role")))
|
).update(role=5)
|
||||||
|
|
||||||
serializer = WorkSpaceMemberSerializer(
|
serializer = WorkSpaceMemberSerializer(
|
||||||
workspace_member, data=request.data, partial=True
|
workspace_member, data=request.data, partial=True
|
||||||
|
|
|
||||||
|
|
@ -83,14 +83,14 @@ export const WORKSPACE_SETTINGS = {
|
||||||
key: "general",
|
key: "general",
|
||||||
i18n_label: "workspace_settings.settings.general.title",
|
i18n_label: "workspace_settings.settings.general.title",
|
||||||
href: `/settings`,
|
href: `/settings`,
|
||||||
access: [EUserWorkspaceRoles.ADMIN],
|
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`,
|
||||||
},
|
},
|
||||||
members: {
|
members: {
|
||||||
key: "members",
|
key: "members",
|
||||||
i18n_label: "workspace_settings.settings.members.title",
|
i18n_label: "workspace_settings.settings.members.title",
|
||||||
href: `/settings/members`,
|
href: `/settings/members`,
|
||||||
access: [EUserWorkspaceRoles.ADMIN],
|
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
|
||||||
},
|
},
|
||||||
"billing-and-plans": {
|
"billing-and-plans": {
|
||||||
|
|
@ -123,6 +123,10 @@ export const WORKSPACE_SETTINGS = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const WORKSPACE_SETTINGS_ACCESS = Object.fromEntries(
|
||||||
|
Object.entries(WORKSPACE_SETTINGS).map(([_, { href, access }]) => [href, access])
|
||||||
|
);
|
||||||
|
|
||||||
export const WORKSPACE_SETTINGS_LINKS: {
|
export const WORKSPACE_SETTINGS_LINKS: {
|
||||||
key: string;
|
key: string;
|
||||||
i18n_label: string;
|
i18n_label: string;
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,12 @@ const MembersSettingsPage = observer(() => {
|
||||||
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
||||||
// derived values
|
// derived values
|
||||||
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Members` : undefined;
|
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Members` : undefined;
|
||||||
const canPerformProjectMemberActions = allowPermissions(
|
const isProjectMemberOrAdmin = allowPermissions(
|
||||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
EUserPermissionsLevel.PROJECT
|
EUserPermissionsLevel.PROJECT
|
||||||
);
|
);
|
||||||
|
const isWorkspaceAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||||
|
const canPerformProjectMemberActions = isProjectMemberOrAdmin || isWorkspaceAdmin;
|
||||||
|
|
||||||
if (workspaceUserInfo && !canPerformProjectMemberActions) {
|
if (workspaceUserInfo && !canPerformProjectMemberActions) {
|
||||||
return <NotAuthorizedView section="settings" isProjectView />;
|
return <NotAuthorizedView section="settings" isProjectView />;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
import { FC, ReactNode } from "react";
|
import { FC, ReactNode } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// components
|
// components
|
||||||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
import { useParams, usePathname } from "next/navigation";
|
||||||
|
import { EUserWorkspaceRoles, WORKSPACE_SETTINGS_ACCESS } from "@plane/constants";
|
||||||
import { NotAuthorizedView } from "@/components/auth-screens";
|
import { NotAuthorizedView } from "@/components/auth-screens";
|
||||||
import { AppHeader } from "@/components/core";
|
import { AppHeader } from "@/components/core";
|
||||||
// hooks
|
// hooks
|
||||||
|
|
@ -21,17 +22,26 @@ export interface IWorkspaceSettingLayout {
|
||||||
const WorkspaceSettingLayout: FC<IWorkspaceSettingLayout> = observer((props) => {
|
const WorkspaceSettingLayout: FC<IWorkspaceSettingLayout> = observer((props) => {
|
||||||
const { children } = props;
|
const { children } = props;
|
||||||
|
|
||||||
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
|
const { workspaceUserInfo } = useUserPermissions();
|
||||||
|
const pathname = usePathname();
|
||||||
|
const { workspaceSlug } = useParams();
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
const isWorkspaceAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
const userWorkspaceRole = workspaceUserInfo?.[workspaceSlug.toString()]?.role;
|
||||||
|
const isAuthorized =
|
||||||
|
pathname &&
|
||||||
|
workspaceSlug &&
|
||||||
|
userWorkspaceRole &&
|
||||||
|
WORKSPACE_SETTINGS_ACCESS[pathname.replace(`/${workspaceSlug}`, "").slice(0, -1)]?.includes(
|
||||||
|
userWorkspaceRole as EUserWorkspaceRoles
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AppHeader header={<WorkspaceSettingHeader />} />
|
<AppHeader header={<WorkspaceSettingHeader />} />
|
||||||
<MobileWorkspaceSettingsTabs />
|
<MobileWorkspaceSettingsTabs />
|
||||||
<div className="inset-y-0 flex flex-row vertical-scrollbar scrollbar-lg h-full w-full overflow-y-auto">
|
<div className="inset-y-0 flex flex-row vertical-scrollbar scrollbar-lg h-full w-full overflow-y-auto">
|
||||||
{workspaceUserInfo && !isWorkspaceAdmin ? (
|
{workspaceUserInfo && !isAuthorized ? (
|
||||||
<NotAuthorizedView section="settings" />
|
<NotAuthorizedView section="settings" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export const PROJECT_SETTINGS = {
|
||||||
key: "members",
|
key: "members",
|
||||||
i18n_label: "members",
|
i18n_label: "members",
|
||||||
href: `/settings/members`,
|
href: `/settings/members`,
|
||||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
|
||||||
Icon: SettingIcon,
|
Icon: SettingIcon,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import { CustomSelect, PopoverMenu, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { getFileURL } from "@/helpers/file.helper";
|
import { getFileURL } from "@/helpers/file.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMember, useUser } from "@/hooks/store";
|
import { useMember, useUser, useUserPermissions } from "@/hooks/store";
|
||||||
|
|
||||||
export interface RowData {
|
export interface RowData {
|
||||||
member: IWorkspaceMember;
|
member: IWorkspaceMember;
|
||||||
|
|
@ -91,7 +91,7 @@ export const NameColumn: React.FC<NameProps> = (props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) => {
|
export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) => {
|
||||||
const { rowData, currentProjectRole, projectId, workspaceSlug } = props;
|
const { rowData, projectId, workspaceSlug } = props;
|
||||||
// form info
|
// form info
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
|
@ -99,48 +99,56 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
|
||||||
} = useForm();
|
} = useForm();
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const {
|
||||||
project: { updateMember, getProjectMemberDetails },
|
project: { updateMember },
|
||||||
workspace: { getWorkspaceMemberDetails },
|
workspace: { getWorkspaceMemberDetails },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
const { data: currentUser } = useUser();
|
const { data: currentUser } = useUser();
|
||||||
|
const { projectUserInfo } = useUserPermissions();
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
const isCurrentUser = currentUser?.id === rowData.member.id;
|
const isCurrentUser = currentUser?.id === rowData.member.id;
|
||||||
const isProjectAdminOrGuest = [EUserPermissions.ADMIN, EUserPermissions.GUEST].includes(rowData.role);
|
const isRowDataWorkspaceAdmin = [EUserPermissions.ADMIN].includes(
|
||||||
const isWorkspaceMember = [EUserPermissions.MEMBER].includes(
|
|
||||||
Number(getWorkspaceMemberDetails(rowData.member.id)?.role) ?? EUserPermissions.GUEST
|
Number(getWorkspaceMemberDetails(rowData.member.id)?.role) ?? EUserPermissions.GUEST
|
||||||
);
|
);
|
||||||
const isCurrentUserProjectMember = currentUser
|
const isCurrentUserWorkspaceAdmin = currentUser
|
||||||
? getProjectMemberDetails(currentUser.id, projectId)?.role === EUserPermissions.MEMBER
|
? [EUserPermissions.ADMIN].includes(
|
||||||
|
Number(getWorkspaceMemberDetails(currentUser.id)?.role) ?? EUserPermissions.GUEST
|
||||||
|
)
|
||||||
: false;
|
: false;
|
||||||
const isRoleNonEditable =
|
const currentProjectRole = projectUserInfo?.[workspaceSlug.toString()]?.[projectId.toString()]
|
||||||
isCurrentUser || (isProjectAdminOrGuest && !isWorkspaceMember) || isCurrentUserProjectMember;
|
?.role as unknown as EUserPermissions;
|
||||||
|
|
||||||
|
const isCurrentUserProjectAdmin = currentProjectRole
|
||||||
|
? ![EUserPermissions.MEMBER, EUserPermissions.GUEST].includes(Number(currentProjectRole) ?? EUserPermissions.GUEST)
|
||||||
|
: false;
|
||||||
|
|
||||||
|
// logic
|
||||||
|
// Workspace admin can change his own role
|
||||||
|
// Project admin can change any role except his own and workspace admin's role
|
||||||
|
const isRoleEditable =
|
||||||
|
(isCurrentUserWorkspaceAdmin && isCurrentUser) ||
|
||||||
|
(isCurrentUserProjectAdmin && !isRowDataWorkspaceAdmin && !isCurrentUser);
|
||||||
const checkCurrentOptionWorkspaceRole = (value: string) => {
|
const checkCurrentOptionWorkspaceRole = (value: string) => {
|
||||||
const currentMemberWorkspaceRole = getWorkspaceMemberDetails(value)?.role as EUserPermissions | undefined;
|
const currentMemberWorkspaceRole = getWorkspaceMemberDetails(value)?.role as EUserPermissions | undefined;
|
||||||
if (!value || !currentMemberWorkspaceRole) return ROLE;
|
if (!value || !currentMemberWorkspaceRole) return ROLE;
|
||||||
|
|
||||||
const isGuestOROwner = [EUserPermissions.ADMIN, EUserPermissions.GUEST].includes(currentMemberWorkspaceRole);
|
const isGuest = [EUserPermissions.GUEST].includes(currentMemberWorkspaceRole);
|
||||||
|
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(ROLE).filter(([key]) => !isGuestOROwner || [currentMemberWorkspaceRole].includes(parseInt(key)))
|
Object.entries(ROLE).filter(([key]) => !isGuest || parseInt(key) === EUserPermissions.GUEST)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isRoleNonEditable ? (
|
{isRoleEditable ? (
|
||||||
<div className="w-32 flex ">
|
|
||||||
<span>{ROLE[rowData.role]}</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Controller
|
<Controller
|
||||||
name="role"
|
name="role"
|
||||||
control={control}
|
control={control}
|
||||||
rules={{ required: "Role is required." }}
|
rules={{ required: "Role is required." }}
|
||||||
render={({ field: { value } }) => (
|
render={() => (
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
value={value}
|
value={rowData.role?.toString()}
|
||||||
onChange={(value: EUserPermissions) => {
|
onChange={(value: EUserPermissions) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
|
|
@ -168,17 +176,18 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
|
||||||
optionsClassName="w-full"
|
optionsClassName="w-full"
|
||||||
input
|
input
|
||||||
>
|
>
|
||||||
{Object.entries(checkCurrentOptionWorkspaceRole(rowData.member.id)).map(([key, label]) => {
|
{Object.entries(checkCurrentOptionWorkspaceRole(rowData.member.id)).map(([key, label]) => (
|
||||||
if (parseInt(key) > (currentProjectRole ?? EUserPermissions.GUEST)) return null;
|
<CustomSelect.Option key={key} value={key}>
|
||||||
return (
|
{label}
|
||||||
<CustomSelect.Option key={key} value={key}>
|
</CustomSelect.Option>
|
||||||
{label}
|
))}
|
||||||
</CustomSelect.Option>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</CustomSelect>
|
</CustomSelect>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="w-32 flex ">
|
||||||
|
<span>{ROLE[rowData.role]}</span>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,8 @@ const SidebarDropdownItem = observer((props: TProps) => {
|
||||||
</div>
|
</div>
|
||||||
{workspace.id === activeWorkspace?.id && (
|
{workspace.id === activeWorkspace?.id && (
|
||||||
<>
|
<>
|
||||||
{workspace?.role === EUserPermissions.ADMIN && (
|
<div className="mt-2 mb-1 flex gap-2">
|
||||||
<div className="mt-2 mb-1 flex gap-2">
|
{[EUserPermissions.ADMIN, EUserPermissions.MEMBER].includes(workspace?.role) && (
|
||||||
<Link
|
<Link
|
||||||
href={`/${workspace.slug}/settings`}
|
href={`/${workspace.slug}/settings`}
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
|
|
@ -96,6 +96,8 @@ const SidebarDropdownItem = observer((props: TProps) => {
|
||||||
<Settings className="h-4 w-4 my-auto" />
|
<Settings className="h-4 w-4 my-auto" />
|
||||||
<span className="text-sm font-medium my-auto">{t("settings")}</span>
|
<span className="text-sm font-medium my-auto">{t("settings")}</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
)}
|
||||||
|
{[EUserPermissions.ADMIN].includes(workspace?.role) && (
|
||||||
<Link
|
<Link
|
||||||
href={`/${workspace.slug}/settings/members`}
|
href={`/${workspace.slug}/settings/members`}
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
|
|
@ -106,8 +108,8 @@ const SidebarDropdownItem = observer((props: TProps) => {
|
||||||
{t("project_settings.members.invite_members.title")}
|
{t("project_settings.members.invite_members.title")}
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ export class ProjectMemberStore implements IProjectMemberStore {
|
||||||
userStore: IUserStore;
|
userStore: IUserStore;
|
||||||
memberRoot: IMemberRootStore;
|
memberRoot: IMemberRootStore;
|
||||||
projectRoot: IProjectStore;
|
projectRoot: IProjectStore;
|
||||||
|
rootStore: CoreRootStore;
|
||||||
// services
|
// services
|
||||||
projectMemberService;
|
projectMemberService;
|
||||||
|
|
||||||
|
|
@ -86,6 +87,7 @@ export class ProjectMemberStore implements IProjectMemberStore {
|
||||||
});
|
});
|
||||||
|
|
||||||
// root store
|
// root store
|
||||||
|
this.rootStore = _rootStore;
|
||||||
this.routerStore = _rootStore.router;
|
this.routerStore = _rootStore.router;
|
||||||
this.userStore = _rootStore.user;
|
this.userStore = _rootStore.user;
|
||||||
this.memberRoot = _memberRoot;
|
this.memberRoot = _memberRoot;
|
||||||
|
|
@ -199,10 +201,13 @@ export class ProjectMemberStore implements IProjectMemberStore {
|
||||||
const memberDetails = this.getProjectMemberDetails(userId, projectId);
|
const memberDetails = this.getProjectMemberDetails(userId, projectId);
|
||||||
if (!memberDetails) throw new Error("Member not found");
|
if (!memberDetails) throw new Error("Member not found");
|
||||||
// original data to revert back in case of error
|
// original data to revert back in case of error
|
||||||
const originalProjectMemberData = this.projectMemberMap?.[projectId]?.[userId];
|
const originalProjectMemberData = this.projectMemberMap?.[projectId]?.[userId]?.role;
|
||||||
|
const isCurrentUser = this.rootStore.user.data?.id === userId;
|
||||||
try {
|
try {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
set(this.projectMemberMap, [projectId, userId, "role"], data.role);
|
set(this.projectMemberMap, [projectId, userId, "role"], data.role);
|
||||||
|
if (isCurrentUser)
|
||||||
|
set(this.rootStore.user.permission.projectUserInfo, [workspaceSlug, projectId, "role"], data.role);
|
||||||
});
|
});
|
||||||
const response = await this.projectMemberService.updateProjectMember(
|
const response = await this.projectMemberService.updateProjectMember(
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
|
|
@ -214,7 +219,13 @@ export class ProjectMemberStore implements IProjectMemberStore {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// revert back to original members in case of error
|
// revert back to original members in case of error
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
set(this.projectMemberMap, [projectId, userId], originalProjectMemberData);
|
set(this.projectMemberMap, [projectId, userId, "role"], originalProjectMemberData);
|
||||||
|
if (isCurrentUser)
|
||||||
|
set(
|
||||||
|
this.rootStore.user.permission.projectUserInfo,
|
||||||
|
[workspaceSlug, projectId, "role"],
|
||||||
|
originalProjectMemberData
|
||||||
|
);
|
||||||
});
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue