import type { PropsWithChildren, ReactNode } from "react"
import type {
Permission,
PermissionOperation,
PermissionResource,
} from "../../../lib/permissions"
import { buildPermission } from "../../../lib/permissions"
import {
usePermissions,
useRegisterPermissions,
} from "../../../providers/permissions-provider"
interface BasePermissionGuardProps extends PropsWithChildren {
/**
* Content to render when permission is denied.
* If not provided, nothing is rendered.
*/
fallback?: ReactNode
/**
* If true, shows a loading state while permissions are being loaded.
*/
showLoading?: boolean
/**
* Custom loading component.
*/
loadingComponent?: ReactNode
}
interface PermissionGuardWithPermission extends BasePermissionGuardProps {
/**
* Single permission to check.
*/
permission: Permission
permissions?: never
resource?: never
operation?: never
requireAll?: never
}
interface PermissionGuardWithPermissions extends BasePermissionGuardProps {
/**
* Multiple permissions to check.
*/
permissions: Permission[]
/**
* If true, requires ALL permissions. Default is ANY.
*/
requireAll?: boolean
permission?: never
resource?: never
operation?: never
}
interface PermissionGuardWithResourceOperation
extends BasePermissionGuardProps {
/**
* Resource to check permission for.
*/
resource: PermissionResource
/**
* Operation to check permission for.
*/
operation: PermissionOperation
permission?: never
permissions?: never
requireAll?: never
}
export type PermissionGuardProps =
| PermissionGuardWithPermission
| PermissionGuardWithPermissions
| PermissionGuardWithResourceOperation
/**
* Component that conditionally renders children based on user permissions.
*
* @example
* ```tsx
* // Using resource and operation
*
*
*
*
* // Using permission string
*
*
*
*
* // Using multiple permissions (ANY)
*
*
*
*
* // Using multiple permissions (ALL)
*
*
*
*
* // With fallback
* You don't have permission to delete customers}
* >
*
*
* ```
*/
export const PermissionGuard = ({
children,
fallback = null,
showLoading = false,
loadingComponent = null,
...props
}: PermissionGuardProps) => {
let requiredPermissions: Permission[] | null = null
let requireAll = false
if ("permission" in props && props.permission) {
requiredPermissions = [props.permission]
} else if ("permissions" in props && props.permissions) {
requiredPermissions = props.permissions
requireAll = !!props.requireAll
} else if ("resource" in props && "operation" in props) {
requiredPermissions = [buildPermission(props.resource, props.operation)]
}
useRegisterPermissions(requiredPermissions, { requireAll })
const { hasAnyPermission, hasAllPermissions, isLoading } = usePermissions()
if (isLoading && showLoading) {
return <>{loadingComponent}>
}
const hasAccess = props.requireAll
? hasAllPermissions(requiredPermissions ?? [])
: hasAnyPermission(requiredPermissions ?? [])
if (!hasAccess) {
return <>{fallback}>
}
return <>{children}>
}