diff --git a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupProjectsModal.tsx b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupProjectsModal.tsx
new file mode 100644
index 000000000..b00d44df1
--- /dev/null
+++ b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupProjectsModal.tsx
@@ -0,0 +1,160 @@
+import { useCallback, useMemo, useState } from "react";
+import Link from "next/link";
+import { faFolder, faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+import { createNotification } from "@app/components/notifications";
+import { OrgPermissionCan } from "@app/components/permissions";
+import {
+ Button,
+ EmptyState,
+ Input,
+ Modal,
+ ModalContent,
+ Table,
+ TableContainer,
+ TBody,
+ Td,
+ Th,
+ THead,
+ Tooltip,
+ Tr
+} from "@app/components/v2";
+import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
+import { useDeleteGroupFromWorkspace } from "@app/hooks/api";
+import { TGroupWithProjectMemberships } from "@app/hooks/api/groups/types";
+import { UsePopUpState } from "@app/hooks/usePopUp";
+
+export type UserGroupsProjectsModalData = {
+ group: TGroupWithProjectMemberships;
+ groupSlug: string;
+ username: string;
+};
+
+export const UserGroupsProjectsModal = ({
+ popUp,
+ handlePopUpToggle
+}: {
+ popUp: UsePopUpState<["userGroupsProjects"]>;
+ handlePopUpToggle: (
+ popUpName: keyof UsePopUpState<["userGroupsProjects"]>,
+ state?: boolean
+ ) => void;
+}) => {
+ const popupData = popUp.userGroupsProjects.data as UserGroupsProjectsModalData;
+
+ const [filteredProjectName, setFilteredProjectName] = useState("");
+
+ const { mutateAsync: deleteGroupFromProject } = useDeleteGroupFromWorkspace();
+
+ const filteredMemberships = useMemo(() => {
+ if (!popupData?.group) return [];
+
+ return popupData.group.projectMemberships.filter(({ project }) => {
+ return project.name.toLowerCase().includes(filteredProjectName.toLowerCase());
+ });
+ }, [filteredProjectName, popupData?.group?.projectMemberships]);
+
+ const removeProjectFromGroup = useCallback(
+ async (projectSlug: string) => {
+ await deleteGroupFromProject({
+ groupSlug: popupData.groupSlug,
+ projectSlug,
+ username: popupData.username
+ });
+
+ createNotification({
+ type: "success",
+ text: "Project removed from group"
+ });
+
+ // Manually remove the project from the list, since the query invalidation won't trigger the popup data to be updated
+ popupData.group.projectMemberships = popupData.group.projectMemberships.filter(
+ (membership) => membership.project.slug !== projectSlug
+ );
+ },
+ [popupData]
+ );
+
+ return (
+ {
+ handlePopUpToggle("userGroupsProjects", open);
+ }}
+ >
+
+ setFilteredProjectName(e.target.value)}
+ leftIcon={}
+ placeholder="Search projects..."
+ />
+
+
+
+
+ | Project |
+
+
+
+ {filteredMemberships?.map(({ project, roles }) => {
+ return (
+
+ |
+
+
+ {project.name}
+
+
+ {/* If there's only one role, display the role.name. If more, display role.name (+X more) */}
+ role.role.charAt(0).toUpperCase() + role.role.slice(1))
+ .join(", ")}
+ >
+
+ {roles.length === 1
+ ? roles[0].role
+ : `${roles[0].role} (+${roles.length - 1} more)`}
+
+
+ |
+
+
+ {(isAllowed) => {
+ return (
+
+
+
+ );
+ }}
+
+ |
+
+ );
+ })}
+
+
+ {!filteredMemberships.length && }
+
+
+
+ );
+};
diff --git a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsRow.tsx b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsRow.tsx
index 3e99d8e78..d3de7919a 100644
--- a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsRow.tsx
+++ b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsRow.tsx
@@ -1,161 +1,36 @@
-import { useCallback, useMemo, useState } from "react";
-import Link from "next/link";
-import { faFolder, faMagnifyingGlass, faTrash } from "@fortawesome/free-solid-svg-icons";
+/* eslint-disable react/jsx-no-useless-fragment */
+import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { createNotification } from "@app/components/notifications";
-import { OrgPermissionCan } from "@app/components/permissions";
-import {
- Button,
- EmptyState,
- IconButton,
- Input,
- Modal,
- ModalContent,
- Table,
- TableContainer,
- TBody,
- Td,
- Th,
- THead,
- Tooltip,
- Tr
-} from "@app/components/v2";
-import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
-import { useDeleteGroupFromWorkspace } from "@app/hooks/api";
+import { IconButton, Td, Tooltip, Tr } from "@app/components/v2";
import { TGroupWithProjectMemberships } from "@app/hooks/api/groups/types";
+import { OrgUser } from "@app/hooks/api/types";
import { UsePopUpState } from "@app/hooks/usePopUp";
+import { UserGroupsProjectsModalData } from "./UserGroupProjectsModal";
+
type Props = {
group: TGroupWithProjectMemberships;
- handlePopUpOpen: (popUpName: keyof UsePopUpState<["removeUserFromGroup"]>, data?: {}) => void;
+ orgMembership: OrgUser;
+ handlePopUpOpen: (
+ popUpName: keyof UsePopUpState<["removeUserFromGroup", "userGroupsProjects"]>,
+ data?: {}
+ ) => void;
};
-const GroupProjectsModal = ({
- isOpen,
- group,
- groupSlug,
- setIsOpen
-}: {
- setIsOpen: (open: boolean) => void;
- isOpen: boolean;
- group: TGroupWithProjectMemberships;
- groupSlug: string;
-}) => {
- const [filteredProjectName, setFilteredProjectName] = useState("");
-
- const { mutateAsync: deleteGroupFromProject } = useDeleteGroupFromWorkspace();
-
- const filteredMemberships = useMemo(() => {
- return group.projectMemberships.filter(({ project }) => {
- return project.name.toLowerCase().includes(filteredProjectName.toLowerCase());
- });
- }, [filteredProjectName, group]);
-
- const removeProjectFromGroup = useCallback(async (projectSlug: string) => {
- await deleteGroupFromProject({
- groupSlug,
- projectSlug
- });
-
- createNotification({
- type: "success",
- text: "Project removed from group"
- });
- }, []);
-
- return (
- {
- setIsOpen(open);
- }}
- >
-
- setFilteredProjectName(e.target.value)}
- leftIcon={}
- placeholder="Search projects..."
- />
-
-
-
-
- | Project |
-
-
-
- {filteredMemberships?.map(({ project, roles }) => {
- return (
-
- |
-
-
- {project.name}
-
-
- {/* If there's only one role, display the role.name. If more, display role.name (+X more) */}
- role.role.charAt(0).toUpperCase() + role.role.slice(1))
- .join(", ")}
- >
-
- {roles.length === 1
- ? roles[0].role
- : `${roles[0].role} (+${roles.length - 1} more)`}
-
-
- |
-
-
- {(isAllowed) => {
- return (
-
-
-
- );
- }}
-
- |
-
- );
- })}
-
-
- {!filteredMemberships.length && }
-
-
-
- );
-};
-
-export const UserGroupsRow = ({ group, handlePopUpOpen }: Props) => {
- const [isProjectsModalOpen, setIsProjectsModalOpen] = useState(false);
-
+export const UserGroupsRow = ({ group, orgMembership, handlePopUpOpen }: Props) => {
return (
<>
setIsProjectsModalOpen(true)}
+ onClick={() => {
+ handlePopUpOpen("userGroupsProjects", {
+ groupSlug: group.slug,
+ username: orgMembership.user.username,
+ group
+ } as UserGroupsProjectsModalData);
+ }}
>
| {group.name} |
@@ -185,12 +60,6 @@ export const UserGroupsRow = ({ group, handlePopUpOpen }: Props) => {
)}
|
-
>
);
};
diff --git a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsSection.tsx b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsSection.tsx
index 41340aec4..ace999286 100644
--- a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsSection.tsx
+++ b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsSection.tsx
@@ -6,6 +6,7 @@ import { useRemoveUserFromGroup } from "@app/hooks/api";
import { OrgUser } from "@app/hooks/api/users/types";
import { usePopUp } from "@app/hooks/usePopUp";
+import { UserGroupsProjectsModal } from "./UserGroupProjectsModal";
import { UserGroupsTable } from "./UserGroupsTable";
type Props = {
@@ -14,7 +15,8 @@ type Props = {
export const UserGroupsSection = ({ orgMembership }: Props) => {
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
- "removeUserFromGroup"
+ "removeUserFromGroup",
+ "userGroupsProjects"
] as const);
const { mutateAsync: removeUserFromGroup } = useRemoveUserFromGroup();
@@ -63,6 +65,8 @@ export const UserGroupsSection = ({ orgMembership }: Props) => {
return handleRemoveUserFromGroup(popupData.groupSlug);
}}
/>
+
+
>
);
};
diff --git a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsTable.tsx b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsTable.tsx
index 914df9985..7cf1ff225 100644
--- a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsTable.tsx
+++ b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserGroupsTable.tsx
@@ -9,7 +9,10 @@ import { UserGroupsRow } from "./UserGroupsRow";
type Props = {
orgMembership: OrgUser;
- handlePopUpOpen: (popUpName: keyof UsePopUpState<["removeUserFromGroup"]>, data?: {}) => void;
+ handlePopUpOpen: (
+ popUpName: keyof UsePopUpState<["removeUserFromGroup", "userGroupsProjects"]>,
+ data?: {}
+ ) => void;
};
export const UserGroupsTable = ({ handlePopUpOpen, orgMembership }: Props) => {
@@ -28,6 +31,7 @@ export const UserGroupsTable = ({ handlePopUpOpen, orgMembership }: Props) => {
{groups?.map((group) => (