Introduces a capability layer on top of existing roles that controls feature visibility and access. Capabilities follow an area.feature.action taxonomy (~35 capabilities) with sensible defaults per role. Tenant admins can customize via grant/revoke overrides stored in org settings JSONB. Key changes: - Add vice_president role to DB schema - Backend: capability constants, resolution logic, CapabilityGuard (global), @RequireCapability decorator on all 16 tenant controllers - Frontend: permission hooks (useCanEdit, useHasCapability), CapabilityGate component, sidebar filtering by capability, all 17 pages migrated from useIsReadOnly to capability-based checks - New admin UI: /settings/permissions matrix page for per-tenant role customization with grant/revoke delta model - GET /organizations/my-capabilities endpoint for capability refresh - Validation of permissionOverrides in settings updates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
23 lines
785 B
TypeScript
23 lines
785 B
TypeScript
import { ReactNode } from 'react';
|
|
import { useHasCapability, useHasAnyCapability } from './useCapability';
|
|
|
|
interface CapabilityGateProps {
|
|
/** Single capability required */
|
|
capability?: string;
|
|
/** Multiple capabilities — user needs at least one */
|
|
anyOf?: string[];
|
|
/** Content shown when user has the capability */
|
|
children: ReactNode;
|
|
/** Optional fallback shown when user lacks the capability */
|
|
fallback?: ReactNode;
|
|
}
|
|
|
|
export function CapabilityGate({ capability, anyOf, children, fallback = null }: CapabilityGateProps) {
|
|
const hasSingle = useHasCapability(capability || '');
|
|
const hasAny = useHasAnyCapability(...(anyOf || []));
|
|
|
|
const allowed = capability ? hasSingle : anyOf ? hasAny : true;
|
|
|
|
return allowed ? <>{children}</> : <>{fallback}</>;
|
|
}
|