feat: added hoc, resolved dropdown type issue

This commit is contained in:
=
2024-12-16 21:15:45 +05:30
parent 1ba8b6394b
commit bf8df14b01
8 changed files with 137 additions and 6 deletions

View File

@@ -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<HTMLDivElement, DropdownMenuContentProps>(
({ children, className, ...props }, forwardedRef) => {
return (
@@ -38,7 +38,7 @@ export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuConten
DropdownMenuContent.displayName = "DropdownMenuContent";
// item container
export type DropdownSubMenuContentProps = DropdownMenuPrimitive.MenuSubContentProps;
export type DropdownSubMenuContentProps = DropdownMenuPrimitive.DropdownMenuSubContentProps;
export const DropdownSubMenuContent = forwardRef<HTMLDivElement, DropdownSubMenuContentProps>(
({ children, className, ...props }, forwardedRef) => {
return (
@@ -62,7 +62,7 @@ export const DropdownSubMenuContent = forwardRef<HTMLDivElement, DropdownSubMenu
DropdownSubMenuContent.displayName = "DropdownMenuContent";
// item label component
export type DropdownLabelProps = DropdownMenuPrimitive.MenuLabelProps;
export type DropdownLabelProps = DropdownMenuPrimitive.DropdownMenuLabelProps;
export const DropdownMenuLabel = ({ className, ...props }: DropdownLabelProps) => (
<DropdownMenuPrimitive.Label
{...props}
@@ -72,7 +72,7 @@ export const DropdownMenuLabel = ({ className, ...props }: DropdownLabelProps) =
// dropdown items
export type DropdownMenuItemProps<T extends ElementType> =
DropdownMenuPrimitive.MenuContentProps & {
DropdownMenuPrimitive.DropdownMenuContentProps & {
icon?: ReactNode;
as?: T;
inputRef?: Ref<T>;

View File

@@ -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;
};

View File

@@ -0,0 +1,2 @@
export { withPermission } from "./withPermission";
export { withProjectPermission } from "./withProjectPermission";

View File

@@ -0,0 +1 @@
export { withPermission } from "./withPermission";

View File

@@ -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 Abilities> = (T extends AbilityTuple
? {
action: T[0];
subject: Extract<T[1], SubjectType>;
}
: {
action: string;
subject: string;
}) & { className?: string; containerClassName?: string };
export const withPermission = <T extends object, J extends TOrgPermission>(
Component: ComponentType<T>,
{ action, subject, className, containerClassName }: Props<Generics<J>["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 (
<div
className={twMerge(
"container mx-auto flex h-full items-center justify-center",
containerClassName
)}
>
<div
className={twMerge(
"flex items-end space-x-12 rounded-md bg-mineshaft-800 p-16 text-bunker-300",
className
)}
>
<div>
<FontAwesomeIcon icon={faLock} size="6x" />
</div>
<div>
<div className="mb-2 text-4xl font-medium">Access Restricted</div>
<div className="text-sm">
Your role has limited permissions, please <br /> contact your admin to gain access
</div>
</div>
</div>
</div>
);
}
return <Component {...hocProps} />;
};
HOC.displayName = "WithPermission";
return HOC;
};

View File

@@ -0,0 +1 @@
export { withProjectPermission } from "./withProjectPermission";

View File

@@ -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<T extends AbilityTuple> = {
className?: string;
containerClassName?: string;
action: T[0];
subject: T[1];
};
export const withProjectPermission = <T extends object>(
Component: ComponentType<Omit<Props<ProjectPermissionSet>, "action" | "subject"> & T>,
{ action, subject, className, containerClassName }: Props<ProjectPermissionSet>
) => {
const HOC = (hocProps: Omit<Props<ProjectPermissionSet>, "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 (
<div
className={twMerge(
"container mx-auto flex h-full items-center justify-center",
containerClassName
)}
>
<div
className={twMerge(
"flex items-end space-x-12 rounded-md bg-mineshaft-800 p-16 text-bunker-300",
className
)}
>
<div>
<FontAwesomeIcon icon={faLock} size="6x" />
</div>
<div>
<div className="mb-2 text-4xl font-medium">Permission Denied</div>
<div className="text-sm">
You do not have permission to this page. <br /> Kindly contact your organization
administrator
</div>
</div>
</div>
</div>
);
}
return <Component {...hocProps} />;
};
HOC.displayName = "WithProjectPermission";
return HOC;
};

View File

@@ -0,0 +1,6 @@
export enum TabSections {
Member = "members",
Groups = "groups",
Roles = "roles",
Identities = "identities"
}