From bf8df14b013a6ec08def5ff1f98209d79e93c564 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 16 Dec 2024 21:15:45 +0530 Subject: [PATCH] feat: added hoc, resolved dropdown type issue --- .../src/components/v2/Dropdown/Dropdown.tsx | 8 +-- .../OrgPermissionContext.tsx | 4 +- frontend-v2/src/hoc/index.tsx | 2 + frontend-v2/src/hoc/withPermission/index.tsx | 1 + .../src/hoc/withPermission/withPermission.tsx | 61 +++++++++++++++++++ .../src/hoc/withProjectPermission/index.tsx | 1 + .../withProjectPermission.tsx | 60 ++++++++++++++++++ frontend-v2/src/types/org.ts | 6 ++ 8 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 frontend-v2/src/hoc/index.tsx create mode 100644 frontend-v2/src/hoc/withPermission/index.tsx create mode 100644 frontend-v2/src/hoc/withPermission/withPermission.tsx create mode 100644 frontend-v2/src/hoc/withProjectPermission/index.tsx create mode 100644 frontend-v2/src/hoc/withProjectPermission/withProjectPermission.tsx create mode 100644 frontend-v2/src/types/org.ts diff --git a/frontend-v2/src/components/v2/Dropdown/Dropdown.tsx b/frontend-v2/src/components/v2/Dropdown/Dropdown.tsx index b02684154..4eb66670b 100644 --- a/frontend-v2/src/components/v2/Dropdown/Dropdown.tsx +++ b/frontend-v2/src/components/v2/Dropdown/Dropdown.tsx @@ -14,7 +14,7 @@ export type DropdownMenuTriggerProps = DropdownMenuPrimitive.DropdownMenuTrigger export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; // item container -export type DropdownMenuContentProps = DropdownMenuPrimitive.MenuContentProps; +export type DropdownMenuContentProps = DropdownMenuPrimitive.DropdownMenuContentProps; export const DropdownMenuContent = forwardRef( ({ children, className, ...props }, forwardedRef) => { return ( @@ -38,7 +38,7 @@ export const DropdownMenuContent = forwardRef( ({ children, className, ...props }, forwardedRef) => { return ( @@ -62,7 +62,7 @@ export const DropdownSubMenuContent = forwardRef ( = - DropdownMenuPrimitive.MenuContentProps & { + DropdownMenuPrimitive.DropdownMenuContentProps & { icon?: ReactNode; as?: T; inputRef?: Ref; diff --git a/frontend-v2/src/context/OrgPermissionContext/OrgPermissionContext.tsx b/frontend-v2/src/context/OrgPermissionContext/OrgPermissionContext.tsx index a7b1e1db0..c3cde82fb 100644 --- a/frontend-v2/src/context/OrgPermissionContext/OrgPermissionContext.tsx +++ b/frontend-v2/src/context/OrgPermissionContext/OrgPermissionContext.tsx @@ -50,10 +50,10 @@ export const OrgPermissionProvider = ({ children }: Props): JSX.Element => { }; export const useOrgPermission = () => { - const { permission } = useRouteContext({ + const ctx = useRouteContext({ from: "/_authenticate/_org_details", select: (el) => el.orgPermission }); - return permission; + return ctx; }; diff --git a/frontend-v2/src/hoc/index.tsx b/frontend-v2/src/hoc/index.tsx new file mode 100644 index 000000000..4efe5f1f9 --- /dev/null +++ b/frontend-v2/src/hoc/index.tsx @@ -0,0 +1,2 @@ +export { withPermission } from "./withPermission"; +export { withProjectPermission } from "./withProjectPermission"; diff --git a/frontend-v2/src/hoc/withPermission/index.tsx b/frontend-v2/src/hoc/withPermission/index.tsx new file mode 100644 index 000000000..d5dfb8dc0 --- /dev/null +++ b/frontend-v2/src/hoc/withPermission/index.tsx @@ -0,0 +1 @@ +export { withPermission } from "./withPermission"; diff --git a/frontend-v2/src/hoc/withPermission/withPermission.tsx b/frontend-v2/src/hoc/withPermission/withPermission.tsx new file mode 100644 index 000000000..ef814d958 --- /dev/null +++ b/frontend-v2/src/hoc/withPermission/withPermission.tsx @@ -0,0 +1,61 @@ +import { ComponentType } from "react"; +import { Abilities, AbilityTuple, Generics, SubjectType } from "@casl/ability"; +import { faLock } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { twMerge } from "tailwind-merge"; + +import { TOrgPermission, useOrgPermission } from "@app/context"; + +type Props = (T extends AbilityTuple + ? { + action: T[0]; + subject: Extract; + } + : { + action: string; + subject: string; + }) & { className?: string; containerClassName?: string }; + +export const withPermission = ( + Component: ComponentType, + { action, subject, className, containerClassName }: Props["abilities"]> +) => { + const HOC = (hocProps: T) => { + const { permission } = useOrgPermission(); + + // akhilmhdh: Set as any due to casl/react ts type bug + // REASON: casl due to its type checking can't seem to union even if union intersection is applied + if (permission.cannot(action as any, subject)) { + return ( +
+
+
+ +
+
+
Access Restricted
+
+ Your role has limited permissions, please
contact your admin to gain access +
+
+
+
+ ); + } + + return ; + }; + + HOC.displayName = "WithPermission"; + return HOC; +}; diff --git a/frontend-v2/src/hoc/withProjectPermission/index.tsx b/frontend-v2/src/hoc/withProjectPermission/index.tsx new file mode 100644 index 000000000..34171ed28 --- /dev/null +++ b/frontend-v2/src/hoc/withProjectPermission/index.tsx @@ -0,0 +1 @@ +export { withProjectPermission } from "./withProjectPermission"; diff --git a/frontend-v2/src/hoc/withProjectPermission/withProjectPermission.tsx b/frontend-v2/src/hoc/withProjectPermission/withProjectPermission.tsx new file mode 100644 index 000000000..564d44adc --- /dev/null +++ b/frontend-v2/src/hoc/withProjectPermission/withProjectPermission.tsx @@ -0,0 +1,60 @@ +import { ComponentType } from "react"; +import { AbilityTuple } from "@casl/ability"; +import { faLock } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { twMerge } from "tailwind-merge"; + +import { useProjectPermission } from "@app/context"; +import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext"; + +type Props = { + className?: string; + containerClassName?: string; + action: T[0]; + subject: T[1]; +}; + +export const withProjectPermission = ( + Component: ComponentType, "action" | "subject"> & T>, + { action, subject, className, containerClassName }: Props +) => { + const HOC = (hocProps: Omit, "action" | "subject"> & T) => { + const { permission } = useProjectPermission(); + + // akhilmhdh: Set as any due to casl/react ts type bug + // REASON: casl due to its type checking can't seem to union even if union intersection is applied + if (permission.cannot(action as any, subject as any)) { + return ( +
+
+
+ +
+
+
Permission Denied
+
+ You do not have permission to this page.
Kindly contact your organization + administrator +
+
+
+
+ ); + } + + return ; + }; + + HOC.displayName = "WithProjectPermission"; + return HOC; +}; diff --git a/frontend-v2/src/types/org.ts b/frontend-v2/src/types/org.ts new file mode 100644 index 000000000..1030a0fd1 --- /dev/null +++ b/frontend-v2/src/types/org.ts @@ -0,0 +1,6 @@ +export enum TabSections { + Member = "members", + Groups = "groups", + Roles = "roles", + Identities = "identities" +}