feat(auth): RoleGate component
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { useAuth } from "./useAuth";
|
||||
|
||||
type Role = "admin" | "user" | "viewer";
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* Roles allowed to interact with the gated affordance. The user's
|
||||
* role must be in this list for children to render normally.
|
||||
*/
|
||||
allow: Role[];
|
||||
children: ReactNode;
|
||||
/**
|
||||
* What to render instead when the user lacks the role AND we don't
|
||||
* want to show the disabled affordance at all (e.g. "contact your
|
||||
* admin" hint, or just nothing).
|
||||
*/
|
||||
fallback?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* RoleGate wraps any write affordance (parse button, resubmit action,
|
||||
* "match selected", etc.) so it visibly disables itself when the
|
||||
* current user's role isn't in `allow`.
|
||||
*
|
||||
* Behavior matrix:
|
||||
*
|
||||
* - user is null → render nothing (the route guard
|
||||
* already redirects to /login; this
|
||||
* is belt-and-braces).
|
||||
* - user.role in `allow` → render children unchanged.
|
||||
* - user.role NOT in `allow` and
|
||||
* `fallback` is provided → render `fallback`.
|
||||
* - user.role NOT in `allow` and
|
||||
* no `fallback` → wrap children in a span carrying
|
||||
* `opacity-50` + `pointer-events-none`
|
||||
* + a `title` attribute that
|
||||
* explains why. We use the native
|
||||
* HTML `title` instead of a custom
|
||||
* Tooltip component to keep this
|
||||
* dependency-free and accessible
|
||||
* by default.
|
||||
*/
|
||||
export function RoleGate({ allow, children, fallback }: Props) {
|
||||
const { user } = useAuth();
|
||||
if (!user) return null;
|
||||
if (allow.includes(user.role)) return <>{children}</>;
|
||||
if (fallback !== undefined) return <>{fallback}</>;
|
||||
return (
|
||||
<span
|
||||
className="pointer-events-none opacity-50 inline-flex"
|
||||
title={`Your role (${user.role}) cannot perform this action.`}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user