Skip to content

Commit 8339daa

Browse files
fix: member role edit validation (#5236)
1 parent 4a9e09a commit 8339daa

File tree

2 files changed

+121
-95
lines changed

2 files changed

+121
-95
lines changed

web/core/components/project/settings/member-columns.tsx

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { observer } from "mobx-react";
12
import Link from "next/link";
23
import { Controller, useForm } from "react-hook-form";
34
import { Trash2 } from "lucide-react";
@@ -6,7 +7,7 @@ import { IUser, IWorkspaceMember } from "@plane/types";
67
import { CustomSelect, PopoverMenu, TOAST_TYPE, setToast } from "@plane/ui";
78
import { EUserProjectRoles } from "@/constants/project";
89
import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace";
9-
import { useMember } from "@/hooks/store";
10+
import { useMember, useUser } from "@/hooks/store";
1011

1112
export interface RowData {
1213
member: IWorkspaceMember;
@@ -80,62 +81,74 @@ export const NameColumn: React.FC<NameProps> = (props) => {
8081
);
8182
};
8283

83-
export const AccountTypeColumn: React.FC<AccountTypeProps> = (props) => {
84+
export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) => {
8485
const { rowData, currentProjectRole, projectId, workspaceSlug } = props;
8586
// form info
8687
const {
8788
control,
8889
formState: { errors },
8990
} = useForm();
91+
// store hooks
9092
const {
9193
project: { updateMember },
9294
} = useMember();
93-
return rowData.role === EUserWorkspaceRoles.ADMIN || currentProjectRole !== EUserProjectRoles.ADMIN ? (
94-
<div className="w-32 flex ">
95-
<span>{ROLE[rowData.role as keyof typeof ROLE]}</span>
96-
</div>
97-
) : (
98-
<Controller
99-
name="role"
100-
control={control}
101-
rules={{ required: "Role is required." }}
102-
render={({ field: { value } }) => (
103-
<CustomSelect
104-
value={value}
105-
onChange={(value: EUserProjectRoles) => {
106-
if (!workspaceSlug) return;
95+
const { data: currentUser } = useUser();
10796

108-
updateMember(workspaceSlug.toString(), projectId.toString(), rowData.member.id, {
109-
role: value as unknown as EUserProjectRoles, // Cast value to unknown first, then to EUserWorkspaceRoles
110-
}).catch((err) => {
111-
console.log(err, "err");
112-
const error = err.error;
113-
const errorString = Array.isArray(error) ? error[0] : error;
97+
// derived values
98+
const isCurrentUser = currentUser?.id === rowData.member.id;
99+
const isAdminRole = currentProjectRole === EUserProjectRoles.ADMIN;
100+
const isRoleEditable = isCurrentUser && isAdminRole;
114101

115-
setToast({
116-
type: TOAST_TYPE.ERROR,
117-
title: "Error!",
118-
message: errorString ?? "An error occurred while updating member role. Please try again.",
119-
});
120-
});
121-
}}
122-
label={
123-
<div className="flex ">
124-
<span>{ROLE[rowData.role as keyof typeof ROLE]}</span>
125-
</div>
126-
}
127-
buttonClassName={`!px-0 !justify-start hover:bg-custom-background-100 ${errors.role ? "border-red-500" : "border-none"}`}
128-
className="rounded-md p-0 w-32"
129-
optionsClassName="w-full"
130-
input
131-
>
132-
{Object.keys(ROLE).map((item) => (
133-
<CustomSelect.Option key={item} value={item as unknown as EUserProjectRoles}>
134-
{ROLE[item as unknown as keyof typeof ROLE]}
135-
</CustomSelect.Option>
136-
))}
137-
</CustomSelect>
102+
return (
103+
<>
104+
{isRoleEditable ? (
105+
<div className="w-32 flex ">
106+
<span>{ROLE[rowData.role as keyof typeof ROLE]}</span>
107+
</div>
108+
) : (
109+
<Controller
110+
name="role"
111+
control={control}
112+
rules={{ required: "Role is required." }}
113+
render={({ field: { value } }) => (
114+
<CustomSelect
115+
value={value}
116+
onChange={(value: EUserProjectRoles) => {
117+
if (!workspaceSlug) return;
118+
119+
updateMember(workspaceSlug.toString(), projectId.toString(), rowData.member.id, {
120+
role: value as unknown as EUserProjectRoles, // Cast value to unknown first, then to EUserWorkspaceRoles
121+
}).catch((err) => {
122+
console.log(err, "err");
123+
const error = err.error;
124+
const errorString = Array.isArray(error) ? error[0] : error;
125+
126+
setToast({
127+
type: TOAST_TYPE.ERROR,
128+
title: "Error!",
129+
message: errorString ?? "An error occurred while updating member role. Please try again.",
130+
});
131+
});
132+
}}
133+
label={
134+
<div className="flex ">
135+
<span>{ROLE[rowData.role as keyof typeof ROLE]}</span>
136+
</div>
137+
}
138+
buttonClassName={`!px-0 !justify-start hover:bg-custom-background-100 ${errors.role ? "border-red-500" : "border-none"}`}
139+
className="rounded-md p-0 w-32"
140+
optionsClassName="w-full"
141+
input
142+
>
143+
{Object.keys(ROLE).map((item) => (
144+
<CustomSelect.Option key={item} value={item as unknown as EUserProjectRoles}>
145+
{ROLE[item as unknown as keyof typeof ROLE]}
146+
</CustomSelect.Option>
147+
))}
148+
</CustomSelect>
149+
)}
150+
/>
138151
)}
139-
/>
152+
</>
140153
);
141-
};
154+
});

web/core/components/workspace/settings/member-columns.tsx

Lines changed: 61 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { observer } from "mobx-react";
12
import Link from "next/link";
23
import { Controller, useForm } from "react-hook-form";
34
import { Trash2 } from "lucide-react";
@@ -6,7 +7,7 @@ import { IUser, IWorkspaceMember } from "@plane/types";
67
import { CustomSelect, PopoverMenu, TOAST_TYPE, setToast } from "@plane/ui";
78
import { EUserProjectRoles } from "@/constants/project";
89
import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace";
9-
import { useMember } from "@/hooks/store";
10+
import { useMember, useUser } from "@/hooks/store";
1011

1112
export interface RowData {
1213
member: IWorkspaceMember;
@@ -79,63 +80,75 @@ export const NameColumn: React.FC<NameProps> = (props) => {
7980
);
8081
};
8182

82-
export const AccountTypeColumn: React.FC<AccountTypeProps> = (props) => {
83+
export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) => {
8384
const { rowData, currentWorkspaceRole, workspaceSlug } = props;
8485
// form info
8586
const {
8687
control,
8788
formState: { errors },
8889
} = useForm();
90+
// store hooks
8991
const {
9092
workspace: { updateMember },
9193
} = useMember();
92-
return rowData.role === EUserWorkspaceRoles.ADMIN || currentWorkspaceRole !== EUserWorkspaceRoles.ADMIN ? (
93-
<div className="w-32 flex ">
94-
<span>{ROLE[rowData.role as keyof typeof ROLE]}</span>
95-
</div>
96-
) : (
97-
<Controller
98-
name="role"
99-
control={control}
100-
rules={{ required: "Role is required." }}
101-
render={({ field: { value } }) => (
102-
<CustomSelect
103-
value={value}
104-
onChange={(value: EUserProjectRoles) => {
105-
console.log({ value, workspaceSlug }, "onChange");
106-
if (!workspaceSlug) return;
94+
const { data: currentUser } = useUser();
10795

108-
updateMember(workspaceSlug.toString(), rowData.member.id, {
109-
role: value as unknown as EUserWorkspaceRoles, // Cast value to unknown first, then to EUserWorkspaceRoles
110-
}).catch((err) => {
111-
console.log(err, "err");
112-
const error = err.error;
113-
const errorString = Array.isArray(error) ? error[0] : error;
96+
// derived values
97+
const isCurrentUser = currentUser?.id === rowData.member.id;
98+
const isAdminRole = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
99+
const isRoleEditable = isCurrentUser && isAdminRole;
114100

115-
setToast({
116-
type: TOAST_TYPE.ERROR,
117-
title: "Error!",
118-
message: errorString ?? "An error occurred while updating member role. Please try again.",
119-
});
120-
});
121-
}}
122-
label={
123-
<div className="flex ">
124-
<span>{ROLE[rowData.role as keyof typeof ROLE]}</span>
125-
</div>
126-
}
127-
buttonClassName={`!px-0 !justify-start hover:bg-custom-background-100 ${errors.role ? "border-red-500" : "border-none"}`}
128-
className="rounded-md p-0 w-32"
129-
optionsClassName="w-full"
130-
input
131-
>
132-
{Object.keys(ROLE).map((item) => (
133-
<CustomSelect.Option key={item} value={item as unknown as EUserProjectRoles}>
134-
{ROLE[item as unknown as keyof typeof ROLE]}
135-
</CustomSelect.Option>
136-
))}
137-
</CustomSelect>
101+
return (
102+
<>
103+
{isRoleEditable ? (
104+
<div className="w-32 flex ">
105+
<span>{ROLE[rowData.role as keyof typeof ROLE]}</span>
106+
</div>
107+
) : (
108+
<Controller
109+
name="role"
110+
control={control}
111+
rules={{ required: "Role is required." }}
112+
render={({ field: { value } }) => (
113+
<CustomSelect
114+
value={value}
115+
onChange={(value: EUserProjectRoles) => {
116+
console.log({ value, workspaceSlug }, "onChange");
117+
if (!workspaceSlug) return;
118+
119+
updateMember(workspaceSlug.toString(), rowData.member.id, {
120+
role: value as unknown as EUserWorkspaceRoles, // Cast value to unknown first, then to EUserWorkspaceRoles
121+
}).catch((err) => {
122+
console.log(err, "err");
123+
const error = err.error;
124+
const errorString = Array.isArray(error) ? error[0] : error;
125+
126+
setToast({
127+
type: TOAST_TYPE.ERROR,
128+
title: "Error!",
129+
message: errorString ?? "An error occurred while updating member role. Please try again.",
130+
});
131+
});
132+
}}
133+
label={
134+
<div className="flex ">
135+
<span>{ROLE[rowData.role as keyof typeof ROLE]}</span>
136+
</div>
137+
}
138+
buttonClassName={`!px-0 !justify-start hover:bg-custom-background-100 ${errors.role ? "border-red-500" : "border-none"}`}
139+
className="rounded-md p-0 w-32"
140+
optionsClassName="w-full"
141+
input
142+
>
143+
{Object.keys(ROLE).map((item) => (
144+
<CustomSelect.Option key={item} value={item as unknown as EUserProjectRoles}>
145+
{ROLE[item as unknown as keyof typeof ROLE]}
146+
</CustomSelect.Option>
147+
))}
148+
</CustomSelect>
149+
)}
150+
/>
138151
)}
139-
/>
152+
</>
140153
);
141-
};
154+
});

0 commit comments

Comments
 (0)