// Per-route capability guard. Wrap the page body — if the active
// session doesn't hold the route's capability, render a 403 instead of
// the page. Server-side authz is still the real gate; this is UX so a
// deep link doesn't 500 inside a route loader that assumes access.
import { useLocation } from "react-router"
import { ShieldAlert } from "lucide-react"
import {
capabilityForPath,
useCapabilities,
type Capability,
} from "~/lib/capabilities"
import { Card, CardContent } from "~/components/ui/card"
type RouteGuardProps = {
children: React.ReactNode
/** Override the capability derived from the current path. Useful for
* nested routes where you want to check a specific cap. */
capability?: Capability
}
export function RouteGuard({ children, capability }: RouteGuardProps) {
const caps = useCapabilities()
const location = useLocation()
const required = capability ?? capabilityForPath(location.pathname)
// No mapping = route is intentionally unguarded (e.g. login flows
// never reach AppShell anyway).
if (!required) return <>{children}>
if (caps.has(required)) return <>{children}>
return
This view requires the {capability}{" "}
capability on your active tenant. If you think you should have it,
switch tenants from the avatar menu or ask an admin.