mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: added hoc, resolved dropdown type issue
This commit is contained in:
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
2
frontend-v2/src/hoc/index.tsx
Normal file
2
frontend-v2/src/hoc/index.tsx
Normal file
@@ -0,0 +1,2 @@
|
||||
export { withPermission } from "./withPermission";
|
||||
export { withProjectPermission } from "./withProjectPermission";
|
||||
1
frontend-v2/src/hoc/withPermission/index.tsx
Normal file
1
frontend-v2/src/hoc/withPermission/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { withPermission } from "./withPermission";
|
||||
61
frontend-v2/src/hoc/withPermission/withPermission.tsx
Normal file
61
frontend-v2/src/hoc/withPermission/withPermission.tsx
Normal 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;
|
||||
};
|
||||
1
frontend-v2/src/hoc/withProjectPermission/index.tsx
Normal file
1
frontend-v2/src/hoc/withProjectPermission/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { withProjectPermission } from "./withProjectPermission";
|
||||
@@ -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;
|
||||
};
|
||||
6
frontend-v2/src/types/org.ts
Normal file
6
frontend-v2/src/types/org.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum TabSections {
|
||||
Member = "members",
|
||||
Groups = "groups",
|
||||
Roles = "roles",
|
||||
Identities = "identities"
|
||||
}
|
||||
Reference in New Issue
Block a user