From 1cb4dc9e844e6f919eeed34da182687322bb0673 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Thu, 25 Jul 2024 06:47:18 -0700 Subject: [PATCH] Start project role concept --- .../project/[id]/roles/[roleId]/index.tsx | 20 ++ .../ProjectRoleList/ProjectRoleList.tsx | 153 +++++++++++----- .../src/views/Project/RolePage/RolePage.tsx | 173 ++++++++++++++++++ frontend/src/views/Project/RolePage/index.tsx | 1 + 4 files changed, 301 insertions(+), 46 deletions(-) create mode 100644 frontend/src/pages/project/[id]/roles/[roleId]/index.tsx create mode 100644 frontend/src/views/Project/RolePage/RolePage.tsx create mode 100644 frontend/src/views/Project/RolePage/index.tsx diff --git a/frontend/src/pages/project/[id]/roles/[roleId]/index.tsx b/frontend/src/pages/project/[id]/roles/[roleId]/index.tsx new file mode 100644 index 000000000..13a6278bc --- /dev/null +++ b/frontend/src/pages/project/[id]/roles/[roleId]/index.tsx @@ -0,0 +1,20 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { useTranslation } from "react-i18next"; +import Head from "next/head"; + +import { RolePage } from "@app/views/Project/RolePage"; + +export default function Role() { + const { t } = useTranslation(); + return ( + <> + + {t("common.head-title", { title: t("settings.org.title") })} + + + + + ); +} + +Role.requireAuth = true; diff --git a/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleList/ProjectRoleList.tsx b/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleList/ProjectRoleList.tsx index e19e61aec..4ea04c8f6 100644 --- a/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleList/ProjectRoleList.tsx +++ b/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleList/ProjectRoleList.tsx @@ -1,14 +1,17 @@ -import { useState } from "react"; -import { faEdit, faMagnifyingGlass, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons"; +import { useRouter } from "next/router"; +import { faEllipsis, faPlus } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { twMerge } from "tailwind-merge"; import { createNotification } from "@app/components/notifications"; import { ProjectPermissionCan } from "@app/components/permissions"; import { Button, DeleteActionModal, - IconButton, - Input, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, Table, TableContainer, TableSkeleton, @@ -28,11 +31,11 @@ type Props = { }; export const ProjectRoleList = ({ onSelectRole }: Props) => { - const [searchRoles, setSearchRoles] = useState(""); - + const router = useRouter(); const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp(["deleteRole"] as const); const { currentWorkspace } = useWorkspace(); const projectSlug = currentWorkspace?.slug || ""; + const projectId = currentWorkspace?.id || ""; const { data: roles, isLoading: isRolesLoading } = useGetProjectRoles(projectSlug); @@ -54,21 +57,20 @@ export const ProjectRoleList = ({ onSelectRole }: Props) => { }; return ( -
-
-
- setSearchRoles(e.target.value)} - leftIcon={} - placeholder="Search roles..." - /> -
+
+
+

Project Roles

{(isAllowed) => (
-
- - - - - - - - - - {isRolesLoading && } - {roles?.map((role) => { - const { id, name, slug } = role; - const isNonMutatable = ["admin", "member", "viewer", "no-access"].includes(slug); + +
NameSlug -
+ + + + + + + + {isRolesLoading && } + {roles?.map((role) => { + const { id, name, slug } = role; + const isNonMutatable = ["admin", "member", "viewer", "no-access"].includes(slug); - return ( - - - - router.push(`/project/${projectId}/roles/${slug}`)} + > + + + + {/* - - ); - })} - -
NameSlug +
{name}{slug} + return ( +
{name}{slug} + + +
+ +
+
+ + + {(isAllowed) => ( + { + e.stopPropagation(); + + // TODO: remove/replace + onSelectRole(role.slug); + // router.push(`/project/${projectId}/roles/${id}`); + }} + disabled={!isAllowed} + > + {`${isNonMutatable ? "View" : "Edit"} Role`} + + )} + + {!isNonMutatable && ( + + {(isAllowed) => ( + { + e.stopPropagation(); + handlePopUpOpen("deleteRole", role); + }} + disabled={!isAllowed} + > + Delete Role + + )} + + )} + +
+
{ )}
-
-
-
+ */} + + ); + })} + + + handlePopUpClose("deleteRole")} onDeleteApproved={handleRoleDelete} diff --git a/frontend/src/views/Project/RolePage/RolePage.tsx b/frontend/src/views/Project/RolePage/RolePage.tsx new file mode 100644 index 000000000..4294a018b --- /dev/null +++ b/frontend/src/views/Project/RolePage/RolePage.tsx @@ -0,0 +1,173 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { useRouter } from "next/router"; +import { faChevronLeft, faEllipsis } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { twMerge } from "tailwind-merge"; + +import { createNotification } from "@app/components/notifications"; +// import { OrgPermissionCan } from "@app/components/permissions"; +// import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context"; +import { ProjectPermissionCan } from "@app/components/permissions"; +import { + Button, + DeleteActionModal, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + Tooltip +} from "@app/components/v2"; +import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; +// import { withPermission } from "@app/hoc"; +import { withProjectPermission } from "@app/hoc"; +import { useDeleteOrgRole, useGetOrgRole, useGetProjectRoleBySlug } from "@app/hooks/api"; +import { usePopUp } from "@app/hooks/usePopUp"; + +// import { RoleDetailsSection, RoleModal, RolePermissionsSection } from "./components"; + +export const RolePage = withProjectPermission( + () => { + const router = useRouter(); + const roleSlug = router.query.roleSlug as string; + const { currentWorkspace } = useWorkspace(); + const projectId = currentWorkspace?.id || ""; + + console.log("RolePage currentWorkspace: ", currentWorkspace); + + // const { currentOrg } = useOrganization(); + // const orgId = currentOrg?.id || ""; + + const { data } = useGetProjectRoleBySlug(currentWorkspace?.slug ?? "", roleSlug as string); + + console.log("useGetProjectRoleBySlug data: ", data); + + // const { data } = useGetOrgRole(orgId, roleId); // TODO: get project role + const { mutateAsync: deleteOrgRole } = useDeleteOrgRole(); + + const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ + "role", + "deleteOrgRole" + ] as const); + + const onDeleteOrgRoleSubmit = async () => { + try { + // if (!orgId || !roleId) return; + // await deleteOrgRole({ + // orgId, + // id: roleId + // }); + // createNotification({ + // text: "Successfully deleted organization role", + // type: "success" + // }); + // handlePopUpClose("deleteOrgRole"); + // router.push(`/org/${orgId}/members`); + } catch (err) { + console.error(err); + const error = err as any; + const text = error?.response?.data?.message ?? "Failed to delete organization role"; + + createNotification({ + text, + type: "error" + }); + } + }; + + const isCustomRole = !["admin", "member", "no-access"].includes(data?.slug ?? ""); + + return ( +
+ {data && ( +
+ +
+

{data.name}

+ {isCustomRole && ( + + +
+ + + +
+
+ + + {(isAllowed) => ( + { + // TODO + // handlePopUpOpen("role", { + // roleId + // }); + }} + disabled={!isAllowed} + > + Edit Role + + )} + + + {(isAllowed) => ( + { + handlePopUpOpen("deleteOrgRole"); + }} + disabled={!isAllowed} + > + Delete Role + + )} + + +
+ )} +
+
+
+ RoleDetailsSection here + {/* */} +
+ RolePermissionsSection here + {/* */} +
+
+ )} + {/* */} + handlePopUpToggle("deleteOrgRole", isOpen)} + deleteKey="confirm" + onDeleteApproved={() => onDeleteOrgRoleSubmit()} + /> +
+ ); + }, + { action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.Role } +); diff --git a/frontend/src/views/Project/RolePage/index.tsx b/frontend/src/views/Project/RolePage/index.tsx new file mode 100644 index 000000000..71e7114fc --- /dev/null +++ b/frontend/src/views/Project/RolePage/index.tsx @@ -0,0 +1 @@ +export { RolePage } from "./RolePage";