mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 07:26:45 +00:00
feat: role list section completed with delete
This commit is contained in:
@@ -19,14 +19,14 @@ export const useCreateProjectRole = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<TProjectRole, {}, TCreateProjectRoleDTO>({
|
||||
mutationFn: async ({ projectSlug, ...dto }: TCreateProjectRoleDTO) => {
|
||||
mutationFn: async ({ projectId, ...dto }: TCreateProjectRoleDTO) => {
|
||||
const {
|
||||
data: { role }
|
||||
} = await apiRequest.post(`/api/v2/workspace/${projectSlug}/roles`, dto);
|
||||
} = await apiRequest.post(`/api/v2/workspace/${projectId}/roles`, dto);
|
||||
return role;
|
||||
},
|
||||
onSuccess: (_, { projectSlug }) => {
|
||||
queryClient.invalidateQueries(roleQueryKeys.getProjectRoles(projectSlug));
|
||||
onSuccess: (_, { projectId }) => {
|
||||
queryClient.invalidateQueries(roleQueryKeys.getProjectRoles(projectId));
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -35,14 +35,14 @@ export const useUpdateProjectRole = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<TProjectRole, {}, TUpdateProjectRoleDTO>({
|
||||
mutationFn: async ({ id, projectSlug, ...dto }: TUpdateProjectRoleDTO) => {
|
||||
mutationFn: async ({ id, projectId, ...dto }: TUpdateProjectRoleDTO) => {
|
||||
const {
|
||||
data: { role }
|
||||
} = await apiRequest.patch(`/api/v2/workspace/${projectSlug}/roles/${id}`, dto);
|
||||
} = await apiRequest.patch(`/api/v2/workspace/${projectId}/roles/${id}`, dto);
|
||||
return role;
|
||||
},
|
||||
onSuccess: (_, { projectSlug }) => {
|
||||
queryClient.invalidateQueries(roleQueryKeys.getProjectRoles(projectSlug));
|
||||
onSuccess: (_, { projectId }) => {
|
||||
queryClient.invalidateQueries(roleQueryKeys.getProjectRoles(projectId));
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -50,14 +50,14 @@ export const useUpdateProjectRole = () => {
|
||||
export const useDeleteProjectRole = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<TProjectRole, {}, TDeleteProjectRoleDTO>({
|
||||
mutationFn: async ({ projectSlug, id }: TDeleteProjectRoleDTO) => {
|
||||
mutationFn: async ({ projectId, id }: TDeleteProjectRoleDTO) => {
|
||||
const {
|
||||
data: { role }
|
||||
} = await apiRequest.delete(`/api/v2/workspace/${projectSlug}/roles/${id}`);
|
||||
} = await apiRequest.delete(`/api/v2/workspace/${projectId}/roles/${id}`);
|
||||
return role;
|
||||
},
|
||||
onSuccess: (_, { projectSlug }) => {
|
||||
queryClient.invalidateQueries(roleQueryKeys.getProjectRoles(projectSlug));
|
||||
onSuccess: (_, { projectId }) => {
|
||||
queryClient.invalidateQueries(roleQueryKeys.getProjectRoles(projectId));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -38,7 +38,7 @@ const glob: JsInterpreter<FieldCondition<string>> = (node, object, context) => {
|
||||
const conditionsMatcher = buildMongoQueryMatcher({ $glob }, { glob });
|
||||
|
||||
export const roleQueryKeys = {
|
||||
getProjectRoles: (projectSlug: string) => ["roles", { projectSlug }] as const,
|
||||
getProjectRoles: (projectId: string) => ["roles", { projectSlug: projectId }] as const,
|
||||
getProjectRoleBySlug: (projectSlug: string, roleSlug: string) =>
|
||||
["roles", { projectSlug, roleSlug }] as const,
|
||||
getOrgRoles: (orgId: string) => ["org-roles", { orgId }] as const,
|
||||
@@ -56,11 +56,11 @@ export const getProjectRoles = async (projectId: string) => {
|
||||
return data.roles;
|
||||
};
|
||||
|
||||
export const useGetProjectRoles = (projectSlug: string) =>
|
||||
export const useGetProjectRoles = (projectId: string) =>
|
||||
useQuery({
|
||||
queryKey: roleQueryKeys.getProjectRoles(projectSlug),
|
||||
queryFn: () => getProjectRoles(projectSlug),
|
||||
enabled: Boolean(projectSlug)
|
||||
queryKey: roleQueryKeys.getProjectRoles(projectId),
|
||||
queryFn: () => getProjectRoles(projectId),
|
||||
enabled: Boolean(projectId)
|
||||
});
|
||||
|
||||
export const useGetProjectRoleBySlug = (projectSlug: string, roleSlug: string) =>
|
||||
|
||||
@@ -72,7 +72,7 @@ export type TDeleteOrgRoleDTO = {
|
||||
};
|
||||
|
||||
export type TCreateProjectRoleDTO = {
|
||||
projectSlug: string;
|
||||
projectId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
slug: string;
|
||||
@@ -80,11 +80,11 @@ export type TCreateProjectRoleDTO = {
|
||||
};
|
||||
|
||||
export type TUpdateProjectRoleDTO = {
|
||||
projectSlug: string;
|
||||
projectId: string;
|
||||
id: string;
|
||||
} & Partial<Omit<TCreateProjectRoleDTO, "orgId">>;
|
||||
|
||||
export type TDeleteProjectRoleDTO = {
|
||||
projectSlug: string;
|
||||
projectId: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { UserWsKeyPair } from "../keys/types";
|
||||
import { ProjectUserMembershipTemporaryMode } from "../workspace/types";
|
||||
|
||||
export enum AuthMethod {
|
||||
EMAIL = "email",
|
||||
@@ -85,6 +86,7 @@ export type TWorkspaceUser = {
|
||||
id: string;
|
||||
publicKey: string;
|
||||
};
|
||||
createdAt: string;
|
||||
projectId: string;
|
||||
isGroupMember: boolean;
|
||||
project: {
|
||||
@@ -114,7 +116,7 @@ export type TWorkspaceUser = {
|
||||
customRoleSlug: string;
|
||||
isTemporary: true;
|
||||
temporaryRange: string;
|
||||
temporaryMode: string;
|
||||
temporaryMode: ProjectUserMembershipTemporaryMode;
|
||||
temporaryAccessEndTime: string;
|
||||
temporaryAccessStartTime: string;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ export {
|
||||
useUpdateIdentityWorkspaceRole,
|
||||
useUpdateUserWorkspaceRole,
|
||||
useUpdateWsEnvironment,
|
||||
useUpgradeProject
|
||||
useUpgradeProject,
|
||||
useGetWorkspaceUserDetails
|
||||
} from "./queries";
|
||||
export { workspaceKeys } from "./query-keys";
|
||||
|
||||
@@ -365,6 +365,21 @@ export const useGetWorkspaceUsers = (workspaceId: string, includeGroupMembers?:
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetWorkspaceUserDetails = (workspaceId: string, membershipId: string) => {
|
||||
return useQuery({
|
||||
queryKey: workspaceKeys.getWorkspaceUserDetails(workspaceId, membershipId),
|
||||
queryFn: async () => {
|
||||
const {
|
||||
data: { membership }
|
||||
} = await apiRequest.get<{ membership: TWorkspaceUser }>(
|
||||
`/api/v1/workspace/${workspaceId}/memberships/${membershipId}`
|
||||
);
|
||||
return membership;
|
||||
},
|
||||
enabled: Boolean(workspaceId) && Boolean(membershipId)
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteUserFromWorkspace = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ export const workspaceKeys = {
|
||||
getWorkspaceAuditLogs: (workspaceId: string) =>
|
||||
[{ workspaceId }, "workspace-audit-logs"] as const,
|
||||
getWorkspaceUsers: (workspaceId: string) => [{ workspaceId }, "workspace-users"] as const,
|
||||
getWorkspaceUserDetails: (workspaceId: string, membershipId: string) =>
|
||||
[{ workspaceId, membershipId }, "workspace-user-details"] as const,
|
||||
getWorkspaceIdentityMemberships: (workspaceId: string) =>
|
||||
[{ workspaceId }, "workspace-identity-memberships"] as const,
|
||||
// allows invalidation using above key without knowing params
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Head from "next/head";
|
||||
import { MemberDetailsPage } from "@app/views/Project/MemberDetailsPage";
|
||||
|
||||
export default function Page() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{t("common.head-title", { title: t("settings.members.title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<MemberDetailsPage />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Page.requireAuth = true;
|
||||
+2
-2
@@ -5,7 +5,7 @@ import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Modal, ModalContent, Select, SelectItem } from "@app/components/v2";
|
||||
import { useOrganization,useWorkspace } from "@app/context";
|
||||
import { useOrganization, useWorkspace } from "@app/context";
|
||||
import {
|
||||
useAddIdentityToWorkspace,
|
||||
useGetIdentityProjectMemberships,
|
||||
@@ -50,7 +50,7 @@ export const IdentityAddToProjectModal = ({ identityId, popUp, handlePopUpToggle
|
||||
const projectId = watch("projectId");
|
||||
const { data: projectMemberships } = useGetIdentityProjectMemberships(identityId);
|
||||
const { data: project } = useGetWorkspaceById(projectId);
|
||||
const { data: roles } = useGetProjectRoles(project?.slug ?? "");
|
||||
const { data: roles } = useGetProjectRoles(project?.id ?? "");
|
||||
|
||||
const filteredWorkspaces = useMemo(() => {
|
||||
const wsWorkspaceIds = new Map();
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Button } from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
import { withProjectPermission } from "@app/hoc";
|
||||
import { useGetWorkspaceUserDetails } from "@app/hooks/api";
|
||||
import { faChevronLeft, faUser } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { format } from "date-fns";
|
||||
import { useRouter } from "next/router";
|
||||
import { MemberRoleDetailsSection } from "./components/MemberRoleDetailsSection";
|
||||
|
||||
export const MemberDetailsPage = withProjectPermission(
|
||||
() => {
|
||||
const router = useRouter();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
const membershipId = router.query.membershipId as string;
|
||||
|
||||
const { data: membershipDetails, isLoading: isMembershipDetailsLoading } =
|
||||
useGetWorkspaceUserDetails(workspaceId, membershipId);
|
||||
|
||||
return (
|
||||
<div className="container mx-auto flex max-w-7xl flex-col justify-between bg-bunker-800 p-6 text-white">
|
||||
<div className="mb-4">
|
||||
<Button
|
||||
variant="link"
|
||||
type="submit"
|
||||
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
|
||||
onClick={() => {
|
||||
router.push(`/project/${workspaceId}/members`);
|
||||
}}
|
||||
className="mb-4"
|
||||
>
|
||||
Project Access Control
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="mb-4 flex items-center justify-between ">
|
||||
<h3 className="text-lg font-semibold text-mineshaft-100">Project User Management</h3>
|
||||
<div>
|
||||
<Button colorSchema="danger" variant="outline_bg" size="xs">
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="">
|
||||
{membershipDetails && (
|
||||
<p className="capitalize">
|
||||
{membershipDetails.user.firstName || membershipDetails.user.lastName
|
||||
? `${membershipDetails.user.firstName} ${membershipDetails.user.lastName}`
|
||||
: "-"}
|
||||
</p>
|
||||
)}
|
||||
<div className="mt-2 text-sm text-gray-400">
|
||||
Joined on{" "}
|
||||
{membershipDetails?.createdAt &&
|
||||
format(new Date(membershipDetails?.createdAt || ""), "yyyy-MM-dd")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MemberRoleDetailsSection
|
||||
membershipDetails={membershipDetails}
|
||||
isMembershipDetailsLoading={isMembershipDetailsLoading}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
{
|
||||
action: ProjectPermissionActions.Read,
|
||||
subject: ProjectPermissionSub.Member
|
||||
}
|
||||
);
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import {
|
||||
DeleteActionModal,
|
||||
IconButton,
|
||||
TableContainer,
|
||||
Td,
|
||||
Table,
|
||||
Tr,
|
||||
Th,
|
||||
THead,
|
||||
TableSkeleton,
|
||||
EmptyState,
|
||||
TBody,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { useUser, useWorkspace } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import {
|
||||
useDeleteProjectRole,
|
||||
useGetWorkspaceUserDetails,
|
||||
useUpdateUserWorkspaceRole
|
||||
} from "@app/hooks/api";
|
||||
import { TProjectRole } from "@app/hooks/api/roles/types";
|
||||
import { TWorkspaceUser } from "@app/hooks/api/types";
|
||||
import { faFolder, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
type Props = {
|
||||
membershipDetails: TWorkspaceUser;
|
||||
isMembershipDetailsLoading?: boolean;
|
||||
};
|
||||
|
||||
export const MemberRoleDetailsSection = ({
|
||||
membershipDetails,
|
||||
isMembershipDetailsLoading
|
||||
}: Props) => {
|
||||
const router = useRouter();
|
||||
const { user } = useUser();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
|
||||
"deleteRole"
|
||||
] as const);
|
||||
const { mutateAsync: updateUserWorkspaceRole } = useUpdateUserWorkspaceRole();
|
||||
|
||||
const userId = user?.id;
|
||||
|
||||
const handleRoleDelete = async () => {
|
||||
const { id } = popUp?.deleteRole?.data as TProjectRole;
|
||||
try {
|
||||
const updatedRole = membershipDetails?.roles?.filter((el) => el.id !== id);
|
||||
await updateUserWorkspaceRole({
|
||||
workspaceId: currentWorkspace?.id || "",
|
||||
roles: updatedRole,
|
||||
membershipId: membershipDetails.id
|
||||
});
|
||||
createNotification({ type: "success", text: "Successfully removed the role" });
|
||||
handlePopUpClose("deleteRole");
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
createNotification({ type: "error", text: "Failed to delete role" });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||
<h3 className="text-lg font-semibold text-mineshaft-100">Project Roles</h3>
|
||||
{userId !== membershipDetails?.user?.id && membershipDetails?.status !== "invited" && (
|
||||
<IconButton
|
||||
ariaLabel="copy icon"
|
||||
variant="plain"
|
||||
className="group relative"
|
||||
onClick={() => {}}
|
||||
>
|
||||
<FontAwesomeIcon icon={faPlus} />
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
<div className="py-4">
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th>Role</Th>
|
||||
<Th>Type</Th>
|
||||
<Th className="w-5" />
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
{isMembershipDetailsLoading && (
|
||||
<TableSkeleton columns={3} innerKey="user-project-memberships" />
|
||||
)}
|
||||
{!isMembershipDetailsLoading &&
|
||||
membershipDetails?.roles?.map((roleDetails) => {
|
||||
return (
|
||||
<Tr className="group h-10" key={`user-project-membership-${roleDetails?.id}`}>
|
||||
<Td>
|
||||
{roleDetails.role === "custom"
|
||||
? roleDetails.customRoleName
|
||||
: roleDetails.role}
|
||||
</Td>
|
||||
<Td>{!roleDetails.isTemporary ? "permanent" : "temporary"}</Td>
|
||||
<Td>
|
||||
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
|
||||
<Tooltip content="Remove">
|
||||
<IconButton
|
||||
colorSchema="danger"
|
||||
ariaLabel="copy icon"
|
||||
variant="plain"
|
||||
className="group relative"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handlePopUpOpen("deleteRole", {
|
||||
id: roleDetails?.id,
|
||||
slug: roleDetails?.customRoleSlug || roleDetails?.role
|
||||
});
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</TBody>
|
||||
</Table>
|
||||
{!isMembershipDetailsLoading && !membershipDetails?.roles?.length && (
|
||||
<EmptyState title="This user has not been assigned to any projects" icon={faFolder} />
|
||||
)}
|
||||
</TableContainer>
|
||||
</div>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteRole.isOpen}
|
||||
deleteKey="remove"
|
||||
title={`Do you want to remove role ${(popUp?.deleteRole?.data as TProjectRole)?.slug}?`}
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteRole", isOpen)}
|
||||
onDeleteApproved={() => handleRoleDelete()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { MemberRoleDetailsSection } from "./MemberRoleDetailsSection";
|
||||
@@ -0,0 +1 @@
|
||||
export { MemberDetailsPage } from "./MemberDetailPage";
|
||||
+1
-2
@@ -32,12 +32,11 @@ export const GroupModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
|
||||
const orgId = currentOrg?.id || "";
|
||||
const projectSlug = currentWorkspace?.slug || "";
|
||||
|
||||
const { data: groups } = useGetOrganizationGroups(orgId);
|
||||
const { data: groupMemberships } = useListWorkspaceGroups(currentWorkspace?.id || "");
|
||||
|
||||
const { data: roles } = useGetProjectRoles(projectSlug);
|
||||
const { data: roles } = useGetProjectRoles(currentWorkspace?.id || "");
|
||||
|
||||
const { mutateAsync: addGroupToWorkspaceMutateAsync } = useAddGroupToWorkspace();
|
||||
|
||||
|
||||
+3
-3
@@ -216,9 +216,9 @@ export const GroupRoles = ({ roles = [], disableEdit = false, groupId }: TMember
|
||||
resolver: zodResolver(formSchema)
|
||||
});
|
||||
|
||||
const projectSlug = currentWorkspace?.slug || "";
|
||||
|
||||
const { data: projectRoles, isLoading: isRolesLoading } = useGetProjectRoles(projectSlug);
|
||||
const { data: projectRoles, isLoading: isRolesLoading } = useGetProjectRoles(
|
||||
currentWorkspace?.id ?? ""
|
||||
);
|
||||
const userRolesGroupBySlug = groupBy(roles, ({ customRoleSlug, role }) => customRoleSlug || role);
|
||||
|
||||
const updateGroupWorkspaceRole = useUpdateGroupWorkspaceRole();
|
||||
|
||||
+1
-2
@@ -42,7 +42,6 @@ export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
|
||||
const organizationId = currentOrg?.id || "";
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
const projectSlug = currentWorkspace?.slug || "";
|
||||
|
||||
const { data: identityMembershipOrgsData } = useGetIdentityMembershipOrgs({
|
||||
organizationId,
|
||||
@@ -59,7 +58,7 @@ export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
data: roles,
|
||||
isLoading: isRolesLoading,
|
||||
isFetched: isRolesFetched
|
||||
} = useGetProjectRoles(projectSlug);
|
||||
} = useGetProjectRoles(workspaceId);
|
||||
|
||||
const { mutateAsync: addIdentityToWorkspaceMutateAsync } = useAddIdentityToWorkspace();
|
||||
|
||||
|
||||
+1
-2
@@ -65,8 +65,7 @@ export const IdentityRbacSection = ({ identityProjectMember, onOpenUpgradeModal
|
||||
const { subscription } = useSubscription();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
const projectSlug = currentWorkspace?.slug || "";
|
||||
const { data: projectRoles, isLoading: isRolesLoading } = useGetProjectRoles(projectSlug);
|
||||
const { data: projectRoles, isLoading: isRolesLoading } = useGetProjectRoles(workspaceId);
|
||||
const { permission } = useProjectPermission();
|
||||
const isMemberEditDisabled = permission.cannot(
|
||||
ProjectPermissionActions.Edit,
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ export const AddMemberModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
const { data: members } = useGetWorkspaceUsers(workspaceId);
|
||||
const { data: orgUsers } = useGetOrgUsers(orgId);
|
||||
|
||||
const { data: roles } = useGetProjectRoles(currentWorkspace?.slug || "");
|
||||
const { data: roles } = useGetProjectRoles(currentWorkspace?.id || "");
|
||||
|
||||
const {
|
||||
control,
|
||||
|
||||
+1
-2
@@ -65,8 +65,7 @@ export const MemberRbacSection = ({ projectMember, onOpenUpgradeModal }: Props)
|
||||
const { subscription } = useSubscription();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
const projectSlug = currentWorkspace?.slug || "";
|
||||
const { data: projectRoles, isLoading: isRolesLoading } = useGetProjectRoles(projectSlug);
|
||||
const { data: projectRoles, isLoading: isRolesLoading } = useGetProjectRoles(workspaceId);
|
||||
const { permission } = useProjectPermission();
|
||||
const isMemberEditDisabled = permission.cannot(
|
||||
ProjectPermissionActions.Edit,
|
||||
|
||||
+24
-7
@@ -7,6 +7,7 @@ import {
|
||||
faUsers
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useRouter } from "next/router";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
@@ -65,6 +66,7 @@ export const MembersTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Prop
|
||||
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { user } = useUser();
|
||||
const router = useRouter();
|
||||
|
||||
const userId = user?.id || "";
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
@@ -111,7 +113,18 @@ export const MembersTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Prop
|
||||
const email = u?.email || inviteEmail;
|
||||
|
||||
return (
|
||||
<Tr key={`membership-${membershipId}`} className="group w-full">
|
||||
<Tr
|
||||
key={`membership-${membershipId}`}
|
||||
className="group w-full cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(evt) => {
|
||||
if (evt.key === "Enter") {
|
||||
router.push(`/project/${workspaceId}/members/${membershipId}`);
|
||||
}
|
||||
}}
|
||||
onClick={() => router.push(`/project/${workspaceId}/members/${membershipId}`)}
|
||||
>
|
||||
<Td>{name}</Td>
|
||||
<Td>{email}</Td>
|
||||
<Td>
|
||||
@@ -202,9 +215,11 @@ export const MembersTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Prop
|
||||
size="sm"
|
||||
variant="plain"
|
||||
ariaLabel="update-role"
|
||||
onClick={() =>
|
||||
handlePopUpOpen("updateRole", { ...projectMember, index })
|
||||
}
|
||||
onClick={(evt) => {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
handlePopUpOpen("updateRole", { ...projectMember, index });
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={faEdit} />
|
||||
</IconButton>
|
||||
@@ -226,9 +241,11 @@ export const MembersTable = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Prop
|
||||
ariaLabel="update"
|
||||
className="ml-4"
|
||||
isDisabled={userId === u?.id || !isAllowed}
|
||||
onClick={() =>
|
||||
handlePopUpOpen("removeMember", { username: u.username })
|
||||
}
|
||||
onClick={(evt) => {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
handlePopUpOpen("removeMember", { username: u.username });
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrash} />
|
||||
</IconButton>
|
||||
|
||||
+2
-3
@@ -34,10 +34,9 @@ export const ProjectRoleList = () => {
|
||||
"deleteRole"
|
||||
] as const);
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const projectSlug = currentWorkspace?.slug || "";
|
||||
const projectId = currentWorkspace?.id || "";
|
||||
|
||||
const { data: roles, isLoading: isRolesLoading } = useGetProjectRoles(projectSlug);
|
||||
const { data: roles, isLoading: isRolesLoading } = useGetProjectRoles(projectId);
|
||||
|
||||
const { mutateAsync: deleteRole } = useDeleteProjectRole();
|
||||
|
||||
@@ -45,7 +44,7 @@ export const ProjectRoleList = () => {
|
||||
const { id } = popUp?.deleteRole?.data as TProjectRole;
|
||||
try {
|
||||
await deleteRole({
|
||||
projectSlug,
|
||||
projectId,
|
||||
id
|
||||
});
|
||||
createNotification({ type: "success", text: "Successfully removed the role" });
|
||||
|
||||
Reference in New Issue
Block a user