diff --git a/apps/web/src/components/Toggle.tsx b/apps/web/src/components/Toggle.tsx
index 0c0e657c..3efe3b5b 100644
--- a/apps/web/src/components/Toggle.tsx
+++ b/apps/web/src/components/Toggle.tsx
@@ -6,16 +6,20 @@ const Toggle = ({
onChange,
label,
disabled,
+ showLabel = true,
}: {
isChecked: boolean;
onChange: () => void;
label: string;
disabled?: boolean;
+ showLabel?: boolean;
}) => (
-
- {label}
-
+ {showLabel && (
+
+ {label}
+
+ )}
{
+ showPopup({
+ header: t`Permissions updated`,
+ message: t`The member's permissions have been updated.`,
+ icon: "success",
+ });
+ },
+ onError: () => {
+ showPopup({
+ header: t`Unable to update permissions`,
+ message: t`Please try again later, or contact customer support.`,
+ icon: "error",
+ });
+ },
+ onSettled: async () => {
+ await utils.permission.getMemberPermissions.invalidate({
+ workspacePublicId: workspace.publicId,
+ memberPublicId: entityId,
+ });
+ },
+ });
+
+ const revokeMutation = api.permission.revokePermission.useMutation({
+ onSuccess: () => {
+ showPopup({
+ header: t`Permissions updated`,
+ message: t`The member's permissions have been updated.`,
+ icon: "success",
+ });
+ },
+ onError: () => {
+ showPopup({
+ header: t`Unable to update permissions`,
+ message: t`Please try again later, or contact customer support.`,
+ icon: "error",
+ });
+ },
+ onSettled: async () => {
+ await utils.permission.getMemberPermissions.invalidate({
+ workspacePublicId: workspace.publicId,
+ memberPublicId: entityId,
+ });
+ },
+ });
+
+ const effectivePermissions = (data?.permissions ?? []) as Permission[];
+ const isBusy = grantMutation.isPending || revokeMutation.isPending;
+
+ const handleToggle = (permission: Permission, nextState: boolean) => {
+ if (!workspace.publicId || !entityId) return;
+
+ if (nextState) {
+ grantMutation.mutate({
+ workspacePublicId: workspace.publicId,
+ memberPublicId: entityId,
+ permission,
+ });
+ } else {
+ revokeMutation.mutate({
+ workspacePublicId: workspace.publicId,
+ memberPublicId: entityId,
+ permission,
+ });
+ }
+ };
+
+ const permissionLabels: Record = {
+ "workspace:view": t`Can view workspace`,
+ "workspace:edit": t`Can edit workspace`,
+ "workspace:delete": t`Can delete workspace`,
+ "workspace:manage": t`Can manage workspace settings and billing`,
+
+ "board:view": t`Can view boards`,
+ "board:create": t`Can create boards`,
+ "board:edit": t`Can edit boards`,
+ "board:delete": t`Can delete boards`,
+
+ "list:view": t`Can view lists`,
+ "list:create": t`Can create lists`,
+ "list:edit": t`Can edit lists`,
+ "list:delete": t`Can delete lists`,
+
+ "card:view": t`Can view cards`,
+ "card:create": t`Can create cards`,
+ "card:edit": t`Can edit cards`,
+ "card:delete": t`Can delete cards`,
+
+ "comment:view": t`Can view comments`,
+ "comment:create": t`Can add comments`,
+ "comment:edit": t`Can edit comments`,
+ "comment:delete": t`Can delete comments`,
+
+ "member:view": t`Can view members`,
+ "member:invite": t`Can invite members`,
+ "member:edit": t`Can edit member roles and permissions`,
+ "member:remove": t`Can remove members`,
+ };
+
+ return (
+
+
+
+
+ {t`Edit permissions`}
+
+
+ {entityLabel || "\u00A0"}
+
+
+
+
+
+ {isLoading ? (
+
+ {t`Loading permissions...`}
+
+ ) : (
+
+ {Object.values(permissionCategories).map((category, index) => (
+
0 ? "border-t border-light-300 dark:border-dark-300" : ""}`}
+ >
+
+ {category.label}
+
+
+ {category.permissions.map((permission) => {
+ const label =
+ permissionLabels[permission] ?? (permission as string);
+
+ return (
+
+
+ {label}
+
+
+ handleToggle(
+ permission,
+ !effectivePermissions.includes(permission),
+ )
+ }
+ />
+
+ );
+ })}
+
+
+ ))}
+
+ )}
+
+
+ );
+}
+
+
diff --git a/apps/web/src/views/members/index.tsx b/apps/web/src/views/members/index.tsx
index e40d5578..7afb0f6c 100644
--- a/apps/web/src/views/members/index.tsx
+++ b/apps/web/src/views/members/index.tsx
@@ -28,6 +28,7 @@ import { api } from "~/utils/api";
import { getAvatarUrl } from "~/utils/helpers";
import { DeleteMemberConfirmation } from "./components/DeleteMemberConfirmation";
import { InviteMemberForm } from "./components/InviteMemberForm";
+import { EditMemberPermissionsModal } from "./components/EditMemberPermissionsModal";
export default function MembersPage() {
const { modalContentType, openModal, isOpen } = useModal();
@@ -218,6 +219,15 @@ export default function MembersPage() {
{session?.user.id !== memberId && (
+ openModal(
+ "EDIT_MEMBER_PERMISSIONS",
+ memberPublicId,
+ memberEmail ?? "",
+ ),
+ },
{
label: t`Remove member`,
action: () =>
@@ -231,7 +241,7 @@ export default function MembersPage() {
>
)}
@@ -386,6 +396,13 @@ export default function MembersPage() {
>
+
+
+
+
>
>