diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/DeleteProjectSection/DeleteProjectSection.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/DeleteProjectSection/DeleteProjectSection.tsx index 12d735952..c8c373613 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/DeleteProjectSection/DeleteProjectSection.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/DeleteProjectSection/DeleteProjectSection.tsx @@ -1,29 +1,52 @@ +import { useMemo } from "react"; import { useRouter } from "next/router"; import { createNotification } from "@app/components/notifications"; import { ProjectPermissionCan } from "@app/components/permissions"; import { Button, DeleteActionModal } from "@app/components/v2"; +import { LeaveProjectModal } from "@app/components/v2/LeaveProjectModal"; import { ProjectPermissionActions, ProjectPermissionSub, useOrganization, + useProjectPermission, useWorkspace } from "@app/context"; 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"; export const DeleteProjectSection = () => { const router = useRouter(); - + const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ - "deleteWorkspace" + "deleteWorkspace", + "leaveWorkspace" ] as const); const { currentOrg } = useOrganization(); + const { hasProjectRole, membership } = useProjectPermission(); const { currentWorkspace } = useWorkspace(); const [isDeleting, setIsDeleting] = useToggle(); + const [isLeaving, setIsLeaving] = useToggle(); 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 () => { 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 (
Danger Zone
-