Files
edr-platform/apps/edr-passenger-web/backoffice/src/components/layout/PermissionGuard.tsx

57 lines
2.1 KiB
TypeScript

'use client';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { ShieldOff } from 'lucide-react';
import { useAuthStore } from '@/lib/auth-store';
interface Props {
/**
* A single key, or several of which the user needs **any one**. The any-of form
* is how a report page accepts either its own key or the `reports:view`
* umbrella — mirroring the OR semantics of the API's `PassengerPermissionGuard`.
*/
permission?: string | string[];
children: React.ReactNode;
}
/**
* Wraps a page to enforce auth + optional permission check.
* - Not logged in → redirect to /login
* - Missing permission → render an explanation (see below)
*
* This used to redirect a user without the permission to /dashboard. That is a
* dead end for anyone lacking `dashboard:view`, since that page renders nothing
* either — they got a blank screen with no explanation. Saying what happened is
* both kinder and easier to support.
*/
export function PermissionGuard({ permission, children }: Props) {
const router = useRouter();
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
const hasPermission = useAuthStore((s) => s.hasPermission);
useEffect(() => {
if (!isAuthenticated) router.replace('/login');
}, [isAuthenticated, router]);
if (!isAuthenticated) return null;
const keys = permission === undefined ? [] : Array.isArray(permission) ? permission : [permission];
const allowed = keys.length === 0 || keys.some((key) => hasPermission(key));
if (!allowed) {
return (
<div className="flex min-h-[60vh] flex-col items-center justify-center gap-3 px-6 text-center">
<ShieldOff className="h-10 w-10 text-muted-foreground" />
<h2 className="text-lg font-semibold text-foreground">You don&apos;t have access to this page</h2>
<p className="max-w-md text-sm text-muted-foreground">
Your account is missing the permission this page requires. Ask an administrator to grant
it if you need access.
</p>
</div>
);
}
return <>{children}</>;
}