mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 10:27:26 +00:00
Added leave project support
This commit is contained in:
+116
-9
@@ -1,29 +1,52 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
import { Button, DeleteActionModal } from "@app/components/v2";
|
import { Button, DeleteActionModal } from "@app/components/v2";
|
||||||
|
import { LeaveProjectModal } from "@app/components/v2/LeaveProjectModal";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
useOrganization,
|
useOrganization,
|
||||||
|
useProjectPermission,
|
||||||
useWorkspace
|
useWorkspace
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { useToggle } from "@app/hooks";
|
import { useToggle } from "@app/hooks";
|
||||||
import { useDeleteWorkspace } from "@app/hooks/api";
|
import { useDeleteWorkspace, useGetWorkspaceUsers, useLeaveProject } from "@app/hooks/api";
|
||||||
import { usePopUp } from "@app/hooks/usePopUp";
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
export const DeleteProjectSection = () => {
|
export const DeleteProjectSection = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
"deleteWorkspace"
|
"deleteWorkspace",
|
||||||
|
"leaveWorkspace"
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
|
const { hasProjectRole, membership } = useProjectPermission();
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const [isDeleting, setIsDeleting] = useToggle();
|
const [isDeleting, setIsDeleting] = useToggle();
|
||||||
|
const [isLeaving, setIsLeaving] = useToggle();
|
||||||
const deleteWorkspace = useDeleteWorkspace();
|
const deleteWorkspace = useDeleteWorkspace();
|
||||||
|
const leaveProject = useLeaveProject();
|
||||||
|
const { data: members, isLoading: isMembersLoading } = useGetWorkspaceUsers(
|
||||||
|
currentWorkspace?.id || ""
|
||||||
|
);
|
||||||
|
|
||||||
|
// If isNoAccessMember is true, then the user can't read the workspace members. So we need to handle this case separately.
|
||||||
|
const isNoAccessMember = hasProjectRole("no-access");
|
||||||
|
|
||||||
|
const isOnlyAdminMember = useMemo(() => {
|
||||||
|
if (!members || !membership || !hasProjectRole("admin")) return false;
|
||||||
|
|
||||||
|
const adminMembers = members.filter(
|
||||||
|
(member) => member.roles.map((r) => r.role).includes("admin") && member.id !== membership.id // exclude the current user
|
||||||
|
);
|
||||||
|
|
||||||
|
return !adminMembers.length;
|
||||||
|
}, [members, membership]);
|
||||||
|
|
||||||
const handleDeleteWorkspaceSubmit = async () => {
|
const handleDeleteWorkspaceSubmit = async () => {
|
||||||
setIsDeleting.on();
|
setIsDeleting.on();
|
||||||
@@ -53,23 +76,97 @@ export const DeleteProjectSection = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLeaveWorkspaceSubmit = async () => {
|
||||||
|
console.log({
|
||||||
|
currentWorkspace,
|
||||||
|
currentOrg,
|
||||||
|
members,
|
||||||
|
isNoAccessMember,
|
||||||
|
membership
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsLeaving.on();
|
||||||
|
|
||||||
|
if (!currentWorkspace?.id || !currentOrg?.id) return;
|
||||||
|
|
||||||
|
// If there's no members, and the user has access to read members, something went wrong.
|
||||||
|
if (!members && !isNoAccessMember) return;
|
||||||
|
|
||||||
|
// If the user has access to read members, and there's less than 2 members, they can't leave the project.
|
||||||
|
if (!isNoAccessMember && members!.length < 2) {
|
||||||
|
createNotification({
|
||||||
|
text: "You can't leave the project as you are the only member",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user has access to read members, and there's less than 1 admin member excluding the current user, they can't leave the project.
|
||||||
|
if (!isNoAccessMember) {
|
||||||
|
if (!members) return;
|
||||||
|
|
||||||
|
if (isOnlyAdminMember) {
|
||||||
|
createNotification({
|
||||||
|
text: "You can't leave a project with no admin members left. Promote another member to admin first.",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await leaveProject.mutateAsync({
|
||||||
|
workspaceId: currentWorkspace.id,
|
||||||
|
organizationId: currentOrg.id
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push(`/org/${currentOrg.id}/overview`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to leave project",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsLeaving.off();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<p className="mb-4 text-xl font-semibold text-mineshaft-100">Danger Zone</p>
|
<p className="mb-4 text-xl font-semibold text-mineshaft-100">Danger Zone</p>
|
||||||
<ProjectPermissionCan I={ProjectPermissionActions.Delete} a={ProjectPermissionSub.Workspace}>
|
<div className="space-x-4">
|
||||||
{(isAllowed) => (
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Delete}
|
||||||
|
a={ProjectPermissionSub.Workspace}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Button
|
||||||
|
isLoading={isDeleting}
|
||||||
|
isDisabled={!isAllowed || isDeleting}
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="outline_bg"
|
||||||
|
type="submit"
|
||||||
|
onClick={() => handlePopUpOpen("deleteWorkspace")}
|
||||||
|
>
|
||||||
|
{`Delete ${currentWorkspace?.name}`}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
{!!isOnlyAdminMember && (
|
||||||
<Button
|
<Button
|
||||||
isLoading={isDeleting}
|
disabled={isMembersLoading || (members && members?.length < 2)}
|
||||||
isDisabled={!isAllowed || isDeleting}
|
isLoading={isLeaving}
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
type="submit"
|
type="submit"
|
||||||
onClick={() => handlePopUpOpen("deleteWorkspace")}
|
onClick={() => handlePopUpOpen("leaveWorkspace")}
|
||||||
>
|
>
|
||||||
{`Delete ${currentWorkspace?.name}`}
|
{`Leave ${currentWorkspace?.name}`}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</div>
|
||||||
|
|
||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
isOpen={popUp.deleteWorkspace.isOpen}
|
isOpen={popUp.deleteWorkspace.isOpen}
|
||||||
title="Are you sure want to delete this project?"
|
title="Are you sure want to delete this project?"
|
||||||
@@ -79,6 +176,16 @@ export const DeleteProjectSection = () => {
|
|||||||
buttonText="Delete Project"
|
buttonText="Delete Project"
|
||||||
onDeleteApproved={handleDeleteWorkspaceSubmit}
|
onDeleteApproved={handleDeleteWorkspaceSubmit}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<LeaveProjectModal
|
||||||
|
isOpen={popUp.leaveWorkspace.isOpen}
|
||||||
|
title="Are you sure want to leave this project?"
|
||||||
|
subTitle={`If you leave ${currentWorkspace?.name} you will lose access to the project and it's contents.`}
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("leaveWorkspace", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
buttonText="Leave Project"
|
||||||
|
onLeaveApproved={handleLeaveWorkspaceSubmit}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user