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) => (
}
onClick={() => onSelectRole()}
+ // onClick={() => {
+ // TODO
+ // handlePopUpOpen("role");
+ // }}
isDisabled={!isAllowed}
>
Add Role
@@ -76,27 +78,86 @@ export const ProjectRoleList = ({ onSelectRole }: Props) => {
)}
-
-
-
-
-
- | Name |
- Slug |
- |
-
-
-
- {isRolesLoading && }
- {roles?.map((role) => {
- const { id, name, slug } = role;
- const isNonMutatable = ["admin", "member", "viewer", "no-access"].includes(slug);
+
+
+
+
+ | Name |
+ Slug |
+ |
+
+
+
+ {isRolesLoading && }
+ {roles?.map((role) => {
+ const { id, name, slug } = role;
+ const isNonMutatable = ["admin", "member", "viewer", "no-access"].includes(slug);
- return (
-
- | {name} |
- {slug} |
-
+ return (
+ |
router.push(`/project/${projectId}/roles/${slug}`)}
+ >
+ | {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 && (
+
+
}
+ onClick={() => {
+ router.push(`/project/${projectId}/members`);
+ }}
+ className="mb-4"
+ >
+ Roles
+
+
+
{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";