@jmruthers/pace-core 0.5.165 → 0.5.167

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/{DataTable-HAATGNJY.js → DataTable-QMER2BPR.js} +3 -3
  2. package/dist/{chunk-23YYDN24.js → chunk-4AJ2ZWQW.js} +2 -2
  3. package/dist/{chunk-DDB2M4XO.js → chunk-AXFP27TJ.js} +2 -2
  4. package/dist/{chunk-Q4NUEMNV.js → chunk-OX3NRR4N.js} +9 -20
  5. package/dist/chunk-OX3NRR4N.js.map +1 -0
  6. package/dist/{chunk-GFSYFEVZ.js → chunk-PKYRTRZA.js} +35 -24
  7. package/dist/chunk-PKYRTRZA.js.map +1 -0
  8. package/dist/components.js +3 -3
  9. package/dist/index.js +4 -4
  10. package/dist/rbac/index.d.ts +10 -3
  11. package/dist/rbac/index.js +2 -2
  12. package/dist/utils.js +1 -1
  13. package/docs/api/classes/ColumnFactory.md +1 -1
  14. package/docs/api/classes/ErrorBoundary.md +1 -1
  15. package/docs/api/classes/InvalidScopeError.md +1 -1
  16. package/docs/api/classes/MissingUserContextError.md +1 -1
  17. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  18. package/docs/api/classes/PermissionDeniedError.md +1 -1
  19. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  20. package/docs/api/classes/RBACAuditManager.md +1 -1
  21. package/docs/api/classes/RBACCache.md +1 -1
  22. package/docs/api/classes/RBACEngine.md +1 -1
  23. package/docs/api/classes/RBACError.md +1 -1
  24. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  25. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  26. package/docs/api/classes/StorageUtils.md +1 -1
  27. package/docs/api/enums/FileCategory.md +1 -1
  28. package/docs/api/interfaces/AggregateConfig.md +1 -1
  29. package/docs/api/interfaces/BadgeProps.md +1 -1
  30. package/docs/api/interfaces/ButtonProps.md +1 -1
  31. package/docs/api/interfaces/CalendarProps.md +1 -1
  32. package/docs/api/interfaces/CardProps.md +1 -1
  33. package/docs/api/interfaces/ColorPalette.md +1 -1
  34. package/docs/api/interfaces/ColorShade.md +1 -1
  35. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  36. package/docs/api/interfaces/DataRecord.md +1 -1
  37. package/docs/api/interfaces/DataTableAction.md +1 -1
  38. package/docs/api/interfaces/DataTableColumn.md +1 -1
  39. package/docs/api/interfaces/DataTableProps.md +1 -1
  40. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  41. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  42. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  43. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  44. package/docs/api/interfaces/EventLogoProps.md +1 -1
  45. package/docs/api/interfaces/ExportColumn.md +1 -1
  46. package/docs/api/interfaces/ExportOptions.md +1 -1
  47. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  48. package/docs/api/interfaces/FileMetadata.md +1 -1
  49. package/docs/api/interfaces/FileReference.md +1 -1
  50. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  51. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  52. package/docs/api/interfaces/FileUploadProps.md +1 -1
  53. package/docs/api/interfaces/FooterProps.md +1 -1
  54. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  55. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  56. package/docs/api/interfaces/InputProps.md +1 -1
  57. package/docs/api/interfaces/LabelProps.md +1 -1
  58. package/docs/api/interfaces/LoginFormProps.md +1 -1
  59. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  60. package/docs/api/interfaces/NavigationContextType.md +1 -1
  61. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  62. package/docs/api/interfaces/NavigationItem.md +1 -1
  63. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  64. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  65. package/docs/api/interfaces/Organisation.md +1 -1
  66. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  67. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  68. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  69. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  70. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  71. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  72. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  73. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  74. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  75. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  76. package/docs/api/interfaces/PaletteData.md +1 -1
  77. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  78. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  79. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  80. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  81. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  82. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  83. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  84. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  85. package/docs/api/interfaces/RBACConfig.md +1 -1
  86. package/docs/api/interfaces/RBACLogger.md +1 -1
  87. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  88. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  89. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  90. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  91. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  92. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  93. package/docs/api/interfaces/RouteConfig.md +1 -1
  94. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  95. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  96. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  97. package/docs/api/interfaces/StorageConfig.md +1 -1
  98. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  99. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  100. package/docs/api/interfaces/StorageListOptions.md +1 -1
  101. package/docs/api/interfaces/StorageListResult.md +1 -1
  102. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  103. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  104. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  105. package/docs/api/interfaces/StyleImport.md +1 -1
  106. package/docs/api/interfaces/SwitchProps.md +1 -1
  107. package/docs/api/interfaces/TabsContentProps.md +1 -1
  108. package/docs/api/interfaces/TabsListProps.md +1 -1
  109. package/docs/api/interfaces/TabsProps.md +1 -1
  110. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  111. package/docs/api/interfaces/TextareaProps.md +1 -1
  112. package/docs/api/interfaces/ToastActionElement.md +1 -1
  113. package/docs/api/interfaces/ToastProps.md +1 -1
  114. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  115. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  116. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  117. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  118. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  119. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  120. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  121. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  122. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  123. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  124. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  125. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  126. package/docs/api/interfaces/UserEventAccess.md +1 -1
  127. package/docs/api/interfaces/UserMenuProps.md +1 -1
  128. package/docs/api/interfaces/UserProfile.md +1 -1
  129. package/docs/api/modules.md +19 -12
  130. package/package.json +1 -1
  131. package/src/__tests__/hooks/usePermissions.test.ts +12 -9
  132. package/src/components/NavigationMenu/NavigationMenu.tsx +9 -21
  133. package/src/hooks/__tests__/ServiceHooks.test.tsx +2 -2
  134. package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +32 -34
  135. package/src/rbac/hooks/usePermissions.test.ts +34 -32
  136. package/src/rbac/hooks/usePermissions.ts +56 -28
  137. package/dist/chunk-GFSYFEVZ.js.map +0 -1
  138. package/dist/chunk-Q4NUEMNV.js.map +0 -1
  139. /package/dist/{DataTable-HAATGNJY.js.map → DataTable-QMER2BPR.js.map} +0 -0
  140. /package/dist/{chunk-23YYDN24.js.map → chunk-4AJ2ZWQW.js.map} +0 -0
  141. /package/dist/{chunk-DDB2M4XO.js.map → chunk-AXFP27TJ.js.map} +0 -0
@@ -403,38 +403,34 @@ function useResolvedScope({
403
403
 
404
404
  // src/rbac/hooks/usePermissions.ts
405
405
  import React, { useState as useState3, useEffect as useEffect3, useCallback as useCallback2, useMemo as useMemo2, useRef as useRef2 } from "react";
406
- function usePermissions(userId, scope) {
406
+ function usePermissions(userId, organisationId, eventId, appId) {
407
407
  const [permissions, setPermissions] = useState3({});
408
408
  const [isLoading, setIsLoading] = useState3(true);
409
409
  const [error, setError] = useState3(null);
410
410
  const isFetchingRef = useRef2(false);
411
411
  const logger = getRBACLogger();
412
- const orgId = scope.organisationId || "";
413
- const eventId = scope.eventId;
414
- const appId = scope.appId;
412
+ const prevAppIdRef = useRef2(appId);
413
+ const orgId = organisationId || "";
415
414
  logger.warn("[usePermissions] Hook called with scope", {
416
415
  userId,
417
- extractedOrgId: orgId,
418
- extractedEventId: eventId,
419
- extractedAppId: appId,
420
- scopeOrgId: scope.organisationId,
421
- scopeEventId: scope.eventId,
422
- scopeAppId: scope.appId,
416
+ organisationId: orgId,
417
+ eventId,
418
+ appId,
423
419
  hasAppId: !!appId,
424
420
  hasOrganisationId: !!orgId
425
421
  });
426
422
  React.useEffect(() => {
427
423
  logger.warn("[usePermissions] Scope changed (useEffect)", {
428
424
  userId,
429
- organisationId: scope.organisationId,
430
- eventId: scope.eventId,
431
- appId: scope.appId,
432
- hasAppId: !!scope.appId,
433
- hasOrganisationId: !!scope.organisationId
425
+ organisationId,
426
+ eventId,
427
+ appId,
428
+ hasAppId: !!appId,
429
+ hasOrganisationId: !!organisationId
434
430
  });
435
- }, [scope.organisationId, scope.eventId, scope.appId, userId]);
431
+ }, [userId, organisationId, eventId, appId]);
436
432
  useEffect3(() => {
437
- if (!scope.organisationId || scope.organisationId === null || typeof scope.organisationId === "string" && scope.organisationId.trim() === "") {
433
+ if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
438
434
  const timeoutId = setTimeout(() => {
439
435
  setError(new Error("Organisation context is required for permission checks"));
440
436
  setIsLoading(false);
@@ -444,15 +440,25 @@ function usePermissions(userId, scope) {
444
440
  if (error?.message === "Organisation context is required for permission checks") {
445
441
  setError(null);
446
442
  }
447
- }, [scope.organisationId, error]);
443
+ }, [organisationId, error]);
448
444
  useEffect3(() => {
449
445
  const fetchPermissions = async () => {
446
+ const appIdChanged = prevAppIdRef.current !== appId;
447
+ if (appIdChanged) {
448
+ logger.warn("[usePermissions] appId changed detected!", {
449
+ prevAppId: prevAppIdRef.current,
450
+ newAppId: appId,
451
+ hasAppId: !!appId
452
+ });
453
+ prevAppIdRef.current = appId;
454
+ }
450
455
  logger.warn("[usePermissions] Fetch useEffect triggered", {
451
456
  userId,
452
457
  orgId,
453
458
  eventId,
454
459
  appId,
455
460
  hasAppId: !!appId,
461
+ appIdChanged,
456
462
  isFetching: isFetchingRef.current
457
463
  });
458
464
  if (isFetchingRef.current) {
@@ -505,12 +511,12 @@ function usePermissions(userId, scope) {
505
511
  isFetchingRef.current = true;
506
512
  setIsLoading(true);
507
513
  setError(null);
508
- const scopeForFetch = {
514
+ const scope = {
509
515
  organisationId: orgId,
510
516
  eventId,
511
517
  appId
512
518
  };
513
- const permissionMap = await getPermissionMap({ userId, scope: scopeForFetch });
519
+ const permissionMap = await getPermissionMap({ userId, scope });
514
520
  logger.warn("[usePermissions] Permissions fetched successfully", {
515
521
  permissionCount: Object.keys(permissionMap).length,
516
522
  hasWildcard: !!permissionMap["*"],
@@ -530,7 +536,7 @@ function usePermissions(userId, scope) {
530
536
  }
531
537
  };
532
538
  fetchPermissions();
533
- }, [userId, scope.organisationId, scope.eventId, scope.appId]);
539
+ }, [userId, organisationId, eventId, appId]);
534
540
  const hasPermission = useCallback2((permission) => {
535
541
  if (permissions["*"]) {
536
542
  return true;
@@ -558,7 +564,7 @@ function usePermissions(userId, scope) {
558
564
  setIsLoading(false);
559
565
  return;
560
566
  }
561
- if (!scope.organisationId || scope.organisationId === null || typeof scope.organisationId === "string" && scope.organisationId.trim() === "") {
567
+ if (!orgId || orgId === null || typeof orgId === "string" && orgId.trim() === "") {
562
568
  setIsLoading(true);
563
569
  setError(null);
564
570
  return;
@@ -567,6 +573,11 @@ function usePermissions(userId, scope) {
567
573
  isFetchingRef.current = true;
568
574
  setIsLoading(true);
569
575
  setError(null);
576
+ const scope = {
577
+ organisationId: orgId,
578
+ eventId,
579
+ appId
580
+ };
570
581
  const permissionMap = await getPermissionMap({ userId, scope });
571
582
  setPermissions(permissionMap);
572
583
  } catch (err) {
@@ -577,7 +588,7 @@ function usePermissions(userId, scope) {
577
588
  setIsLoading(false);
578
589
  isFetchingRef.current = false;
579
590
  }
580
- }, [userId, scope.organisationId, scope.eventId, scope.appId]);
591
+ }, [userId, organisationId, eventId, appId]);
581
592
  return useMemo2(() => ({
582
593
  permissions,
583
594
  isLoading,
@@ -1140,4 +1151,4 @@ export {
1140
1151
  useResourcePermissions,
1141
1152
  useRoleManagement
1142
1153
  };
1143
- //# sourceMappingURL=chunk-GFSYFEVZ.js.map
1154
+ //# sourceMappingURL=chunk-PKYRTRZA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rbac/hooks/useRBAC.ts","../src/rbac/hooks/useResolvedScope.ts","../src/rbac/utils/eventContext.ts","../src/rbac/hooks/usePermissions.ts","../src/rbac/hooks/useResourcePermissions.ts","../src/rbac/hooks/useRoleManagement.ts"],"sourcesContent":["/**\n * @file RBAC Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 0.3.0\n *\n * A React hook that provides access to the RBAC (Role-Based Access Control) system\n * through the hardened RBAC engine API. The hook defers all permission and role\n * resolution to the shared engine to ensure consistent security behaviour across\n * applications.\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';\nimport { useOrganisations } from '../../hooks/useOrganisations';\nimport { useEvents } from '../../hooks/useEvents';\nimport {\n getPermissionMap,\n getAccessLevel,\n resolveAppContext,\n getRoleContext,\n} from '../api';\nimport { getRBACLogger } from '../config';\nimport type {\n UserRBACContext,\n GlobalRole,\n OrganisationRole,\n EventAppRole,\n Permission,\n Scope,\n PermissionMap,\n UUID,\n} from '../types';\n\nfunction mapAccessLevelToEventRole(level: string | null): EventAppRole | null {\n switch (level) {\n case 'viewer':\n return 'viewer';\n case 'participant':\n return 'participant';\n case 'planner':\n return 'planner';\n case 'admin':\n case 'super':\n return 'event_admin';\n default:\n return null;\n }\n}\n\nexport function useRBAC(pageId?: string): UserRBACContext {\n const logger = getRBACLogger();\n // Get all context from UnifiedAuth - it already provides selectedOrganisation, isContextReady, selectedEvent, and eventLoading\n // This is more reliable than calling useOrganisations()/useEvents() separately which might throw or return stale values\n const { \n user, \n session, \n appName, \n appConfig,\n selectedOrganisation,\n isContextReady: orgContextReady,\n organisationLoading: orgLoading,\n selectedEvent,\n eventLoading\n } = useUnifiedAuth();\n \n // Check if app requires event context\n // IMPORTANT: If appConfig is null initially, default to true (safer for event-based apps)\n // This prevents premature loading when appConfig hasn't loaded yet\n const requiresEvent = appConfig?.requires_event ?? (appConfig === null ? true : false);\n \n // Only log hook initialization if user is authenticated to avoid noise during login\n // Log hook initialization for debugging (use warn level so it's always visible)\n // Also use direct console.log as fallback to ensure visibility\n if (user && session) {\n const hookInitLog = {\n appName,\n requiresEvent,\n appConfig: appConfig ? JSON.stringify(appConfig) : 'null',\n hasUser: !!user,\n hasSession: !!session,\n hasSelectedEvent: !!selectedEvent,\n eventLoading,\n selectedEventId: selectedEvent?.event_id,\n hasSelectedOrganisation: !!selectedOrganisation,\n organisationId: selectedOrganisation?.id,\n orgContextReady,\n orgLoading\n };\n logger.warn('[useRBAC] Hook initialized', hookInitLog);\n // Direct console.log as fallback to ensure we can see if hook is called\n console.warn('[useRBAC] Hook initialized (direct log)', hookInitLog);\n }\n\n const [globalRole, setGlobalRole] = useState<GlobalRole | null>(null);\n const [organisationRole, setOrganisationRole] = useState<OrganisationRole | null>(null);\n const [eventAppRole, setEventAppRole] = useState<EventAppRole | null>(null);\n const [permissionMap, setPermissionMap] = useState<PermissionMap>({} as PermissionMap);\n const [currentScope, setCurrentScope] = useState<Scope | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const resetState = useCallback(() => {\n setGlobalRole(null);\n setOrganisationRole(null);\n setEventAppRole(null);\n setPermissionMap({} as PermissionMap);\n setCurrentScope(null);\n }, []);\n\n const loadRBACContext = useCallback(async () => {\n // Early return if user is not authenticated - don't do anything\n if (!user || !session) {\n resetState();\n setIsLoading(false);\n return;\n }\n\n // Wait for organisation context to be ready before loading RBAC\n // This is critical - without organisation ID, RPC calls can't resolve permissions\n if (orgLoading || !orgContextReady || !selectedOrganisation?.id) {\n setIsLoading(true);\n logger.warn('[useRBAC] Waiting for organisation context before loading RBAC context', {\n orgLoading,\n orgContextReady,\n hasSelectedOrganisation: !!selectedOrganisation,\n organisationId: selectedOrganisation?.id,\n appName\n });\n return;\n }\n\n // For event-based apps, wait for event context to be ready before loading RBAC\n // This prevents NetworkError when RPC calls are made before event context is available\n if (requiresEvent) {\n if (eventLoading || !selectedEvent) {\n // Event context not ready yet - don't load RBAC yet\n // This prevents premature RPC calls that can cause NetworkError\n // Set loading state so React knows we're waiting\n setIsLoading(true);\n logger.warn('[useRBAC] Waiting for event context before loading RBAC context', {\n eventLoading,\n hasSelectedEvent: !!selectedEvent,\n appName,\n selectedEventId: selectedEvent?.event_id,\n organisationId: selectedOrganisation?.id\n });\n return;\n }\n }\n\n setIsLoading(true);\n setError(null);\n\n logger.warn('[useRBAC] Loading RBAC context', {\n appName,\n requiresEvent,\n hasSelectedEvent: !!selectedEvent,\n selectedEventId: selectedEvent?.event_id,\n organisationId: selectedOrganisation?.id\n });\n\n try {\n let appId: UUID | undefined;\n if (appName) {\n // Wrap RPC call in try-catch to handle NetworkError gracefully\n try {\n const resolved = await resolveAppContext({ userId: user.id as UUID, appName });\n if (!resolved || !resolved.hasAccess) {\n throw new Error(`User does not have access to app \"${appName}\"`);\n }\n appId = resolved.appId;\n } catch (rpcError: any) {\n // Handle NetworkError - might be due to timing issue\n if (rpcError?.message?.includes('NetworkError') || rpcError?.message?.includes('fetch')) {\n logger.warn('[useRBAC] NetworkError resolving app context - may be timing issue, will retry when context is ready', {\n appName,\n error: rpcError.message,\n requiresEvent,\n eventLoading,\n hasSelectedEvent: !!selectedEvent\n });\n // Don't throw - let it retry when dependencies change\n setIsLoading(false);\n return;\n }\n // Re-throw other errors\n throw rpcError;\n }\n }\n\n const scope: Scope = {\n organisationId: selectedOrganisation?.id,\n eventId: selectedEvent?.event_id || undefined,\n appId,\n };\n \n // Log scope to verify it's built correctly\n logger.warn('[useRBAC] Building scope for RBAC context', {\n organisationId: scope.organisationId,\n eventId: scope.eventId,\n appId: scope.appId,\n hasOrganisationId: !!scope.organisationId,\n hasEventId: !!scope.eventId\n });\n \n setCurrentScope(scope);\n\n const [map, roleContext, accessLevel] = await Promise.all([\n getPermissionMap({ userId: user.id as UUID, scope }),\n getRoleContext({ userId: user.id as UUID, scope }),\n getAccessLevel({ userId: user.id as UUID, scope }),\n ]);\n\n setPermissionMap(map);\n setGlobalRole(roleContext.globalRole);\n setOrganisationRole(roleContext.organisationRole);\n setEventAppRole(roleContext.eventAppRole || mapAccessLevelToEventRole(accessLevel));\n \n logger.warn('[useRBAC] RBAC context loaded successfully', {\n appName,\n permissionCount: Object.keys(map).length,\n globalRole: roleContext.globalRole,\n organisationRole: roleContext.organisationRole,\n eventAppRole: roleContext.eventAppRole || mapAccessLevelToEventRole(accessLevel)\n });\n } catch (err) {\n const handledError = err instanceof Error ? err : new Error('Failed to load RBAC context');\n logger.error('[useRBAC] Error loading RBAC context:', handledError);\n setError(handledError);\n resetState();\n } finally {\n setIsLoading(false);\n }\n }, [appName, logger, resetState, selectedEvent, selectedEvent?.event_id, selectedOrganisation, selectedOrganisation?.id, session, user, requiresEvent, eventLoading, appConfig, orgContextReady, orgLoading]);\n\n const hasGlobalPermission = useCallback(\n (permission: string): boolean => {\n if (globalRole === 'super_admin' || permissionMap['*']) {\n return true;\n }\n\n if (permission === 'super_admin') {\n return globalRole === 'super_admin';\n }\n\n if (permission === 'org_admin') {\n return organisationRole === 'org_admin';\n }\n\n return permissionMap[permission as Permission] === true;\n },\n [globalRole, organisationRole, permissionMap],\n );\n\n const isSuperAdmin = useMemo(() => globalRole === 'super_admin' || permissionMap['*'] === true, [globalRole, permissionMap]);\n const isOrgAdmin = useMemo(() => organisationRole === 'org_admin' || isSuperAdmin, [organisationRole, isSuperAdmin]);\n const isEventAdmin = useMemo(() => eventAppRole === 'event_admin' || isSuperAdmin, [eventAppRole, isSuperAdmin]);\n const canManageOrganisation = useMemo(() => isSuperAdmin || organisationRole === 'org_admin', [isSuperAdmin, organisationRole]);\n const canManageEvent = useMemo(() => isSuperAdmin || eventAppRole === 'event_admin', [isSuperAdmin, eventAppRole]);\n\n useEffect(() => {\n // Log when effect runs to help debug\n logger.warn('[useRBAC] useEffect triggered - calling loadRBACContext', {\n appName,\n requiresEvent,\n eventLoading,\n hasSelectedEvent: !!selectedEvent,\n hasUser: !!user,\n hasSession: !!session,\n hasSelectedOrganisation: !!selectedOrganisation,\n organisationId: selectedOrganisation?.id,\n orgContextReady,\n orgLoading\n });\n loadRBACContext();\n }, [loadRBACContext, appName, requiresEvent, eventLoading, selectedEvent, user, session, selectedOrganisation, orgContextReady, orgLoading]);\n\n return {\n user,\n globalRole,\n organisationRole,\n eventAppRole,\n hasGlobalPermission,\n isSuperAdmin,\n isOrgAdmin,\n isEventAdmin,\n canManageOrganisation,\n canManageEvent,\n isLoading,\n error,\n };\n}\n","/**\n * @file useResolvedScope Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n * \n * Shared hook for resolving RBAC scope from various contexts.\n * This hook is used by both DataTable and PagePermissionGuard to ensure\n * consistent scope resolution logic.\n */\n\nimport { useEffect, useState, useRef } from 'react';\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport type { Scope } from '../types';\nimport { createScopeFromEvent } from '../utils/eventContext';\nimport { getCurrentAppName } from '../../utils/app/appNameResolver';\nimport { createLogger } from '../../utils/core/logger';\n\nconst log = createLogger('useResolvedScope');\n\nexport interface UseResolvedScopeOptions {\n /** Supabase client instance */\n supabase: SupabaseClient<Database> | null;\n /** Selected organisation ID */\n selectedOrganisationId: string | null;\n /** Selected event ID */\n selectedEventId: string | null;\n}\n\nexport interface UseResolvedScopeReturn {\n /** Resolved scope, or null if not yet resolved */\n resolvedScope: Scope | null;\n /** Whether the scope resolution is in progress */\n isLoading: boolean;\n /** Error if scope resolution failed */\n error: Error | null;\n}\n\n/**\n * Resolves RBAC scope from organisation and event context\n * \n * This hook handles the complex logic of determining the correct RBAC scope\n * based on available context (organisation, event, app). It ensures consistent\n * scope resolution across the application.\n * \n * @param options - Hook options\n * @returns Resolved scope and loading state\n * \n * @example\n * ```tsx\n * const { resolvedScope, isLoading } = useResolvedScope({\n * supabase,\n * selectedOrganisationId,\n * selectedEventId\n * });\n * \n * if (isLoading) return <Loading />;\n * if (!resolvedScope) return <Error />;\n * \n * const permission = useCan(userId, resolvedScope, permission);\n * ```\n */\nexport function useResolvedScope({\n supabase,\n selectedOrganisationId,\n selectedEventId\n}: UseResolvedScopeOptions): UseResolvedScopeReturn {\n const [resolvedScope, setResolvedScope] = useState<Scope | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n \n // Use a ref to track the stable scope and only update it when it actually changes\n const stableScopeRef = useRef<{ organisationId: string; appId: string; eventId: string | undefined }>({ \n organisationId: '', \n appId: '', \n eventId: undefined \n });\n \n // Update stable scope ref in useEffect to avoid updates during render\n useEffect(() => {\n if (resolvedScope && resolvedScope.organisationId) {\n const newScope = {\n organisationId: resolvedScope.organisationId,\n appId: resolvedScope.appId,\n eventId: resolvedScope.eventId\n };\n \n // Only update if the scope has actually changed\n if (stableScopeRef.current.organisationId !== newScope.organisationId ||\n stableScopeRef.current.eventId !== newScope.eventId ||\n stableScopeRef.current.appId !== newScope.appId) {\n stableScopeRef.current = {\n organisationId: newScope.organisationId,\n appId: newScope.appId || '',\n eventId: newScope.eventId\n };\n }\n } else if (!resolvedScope) {\n // Reset to empty scope when no resolved scope\n stableScopeRef.current = { organisationId: '', appId: '', eventId: undefined };\n }\n }, [resolvedScope]);\n \n const stableScope = stableScopeRef.current;\n \n useEffect(() => {\n let cancelled = false;\n \n const resolveScope = async () => {\n setIsLoading(true);\n setError(null);\n \n try {\n // Get app ID from package.json or environment\n let appId: string | undefined = undefined;\n \n // Try to resolve from database\n if (supabase) {\n const appName = getCurrentAppName();\n if (appName) {\n try {\n const { data: app, error } = await supabase\n .from('rbac_apps')\n .select('id, name, is_active')\n .eq('name', appName)\n .eq('is_active', true)\n .single() as { data: { id: string; name: string; is_active: boolean } | null; error: any };\n \n if (error) {\n // Check if app exists but is inactive\n const { data: inactiveApp } = await supabase\n .from('rbac_apps')\n .select('id, name, is_active')\n .eq('name', appName)\n .single() as { data: { id: string; name: string; is_active: boolean } | null };\n \n if (inactiveApp) {\n log.error(`App \"${appName}\" exists but is inactive (is_active: ${inactiveApp.is_active})`);\n } else {\n log.error(`App \"${appName}\" not found in rbac_apps table`);\n }\n } else if (app) {\n appId = app.id;\n }\n } catch (error) {\n log.error('Unexpected error resolving app ID:', error);\n }\n }\n }\n\n // Resolve scope based on available context\n\n // If we have both organisation and event, use them directly\n if (selectedOrganisationId && selectedEventId) {\n if (!cancelled) {\n setResolvedScope({\n organisationId: selectedOrganisationId,\n eventId: selectedEventId,\n appId: appId\n });\n setIsLoading(false);\n }\n return;\n }\n\n // If we only have organisation, use it\n if (selectedOrganisationId) {\n if (!cancelled) {\n setResolvedScope({\n organisationId: selectedOrganisationId,\n eventId: selectedEventId || undefined,\n appId: appId\n });\n setIsLoading(false);\n }\n return;\n }\n\n // If we only have event, resolve organisation from event\n if (selectedEventId && supabase) {\n try {\n const eventScope = await createScopeFromEvent(supabase, selectedEventId, appId);\n if (!eventScope) {\n log.error('Could not resolve organization from event context');\n if (!cancelled) {\n setResolvedScope(null);\n setError(new Error('Could not resolve organisation from event context'));\n setIsLoading(false);\n }\n return;\n }\n // Preserve the resolved app ID\n if (!cancelled) {\n setResolvedScope({\n ...eventScope,\n appId: appId || eventScope.appId\n });\n setIsLoading(false);\n }\n } catch (err) {\n log.error('Error resolving scope from event:', err);\n if (!cancelled) {\n setResolvedScope(null);\n setError(err as Error);\n setIsLoading(false);\n }\n }\n return;\n }\n\n // No context available\n log.error('No organisation or event context available');\n if (!cancelled) {\n setResolvedScope(null);\n setError(new Error('No organisation or event context available'));\n setIsLoading(false);\n }\n } catch (err) {\n if (!cancelled) {\n setError(err as Error);\n setIsLoading(false);\n }\n }\n };\n\n resolveScope();\n \n return () => {\n cancelled = true;\n };\n }, [selectedOrganisationId, selectedEventId, supabase]);\n \n return {\n resolvedScope: stableScope.organisationId ? stableScope as Scope : null,\n isLoading,\n error\n };\n}\n","/**\n * Event Context Utilities for RBAC\n * @package @jmruthers/pace-core\n * @module RBAC/EventContext\n * @since 1.0.0\n * \n * This module provides utilities for event-based RBAC operations where\n * the organization context is derived from the event context.\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { Database } from '../../types/database';\nimport { UUID, Scope } from '../types';\n\n/**\n * Get organization ID from event ID\n * \n * @param supabase - Supabase client\n * @param eventId - Event ID\n * @returns Promise resolving to organization ID or null\n */\nexport async function getOrganisationFromEvent(\n supabase: SupabaseClient<Database>,\n eventId: string\n): Promise<UUID | null> {\n const { data, error } = await supabase\n .from('event')\n .select('organisation_id')\n .eq('event_id', eventId)\n .single() as { data: { organisation_id: string } | null; error: any };\n\n if (error || !data) {\n return null;\n }\n\n return data.organisation_id;\n}\n\n/**\n * Create a complete scope from event context\n * \n * @param supabase - Supabase client\n * @param eventId - Event ID\n * @param appId - Optional app ID\n * @returns Promise resolving to complete scope\n */\nexport async function createScopeFromEvent(\n supabase: SupabaseClient<Database>,\n eventId: string,\n appId?: UUID\n): Promise<Scope | null> {\n const organisationId = await getOrganisationFromEvent(supabase, eventId);\n \n if (!organisationId) {\n return null;\n }\n\n return {\n organisationId,\n eventId,\n appId\n };\n}\n\n/**\n * Check if a scope is event-based (has eventId but no explicit organisationId)\n * \n * @param scope - Permission scope\n * @returns True if scope is event-based\n */\nexport function isEventBasedScope(scope: Scope): boolean {\n return !scope.organisationId && !!scope.eventId;\n}\n\n/**\n * Validate that an event-based scope has the required context\n * \n * @param scope - Permission scope\n * @returns True if scope is valid for event-based operations\n */\nexport function isValidEventBasedScope(scope: Scope): boolean {\n return isEventBasedScope(scope) && !!scope.eventId;\n}\n","/**\n * @file RBAC Permission Hooks\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n * \n * This module provides React hooks for RBAC functionality.\n */\n\nimport React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';\nimport { \n UUID, \n Scope, \n Permission, \n PermissionMap\n} from '../types';\nimport { AccessLevel as AccessLevelType } from '../types';\nimport { \n getAccessLevel, \n getPermissionMap, \n isPermitted,\n isPermittedCached \n} from '../api';\nimport { getRBACLogger } from '../config';\n\n/**\n * Hook to get user's permissions in a scope\n * \n * @param userId - User ID\n * @param organisationId - Organisation ID\n * @param eventId - Event ID (optional)\n * @param appId - Application ID (optional)\n * @returns Permission state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { permissions, isLoading, error } = usePermissions(\n * userId, \n * organisationId, \n * eventId, \n * appId\n * );\n * \n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {permissions['read:users'] && <UserList />}\n * {permissions['create:users'] && <CreateUserButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePermissions(\n userId: UUID, \n organisationId: string | undefined, \n eventId: string | undefined, \n appId: string | undefined\n) {\n const [permissions, setPermissions] = useState<PermissionMap>({} as PermissionMap);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const isFetchingRef = useRef(false);\n const logger = getRBACLogger();\n \n // Track previous appId to detect when it becomes available\n const prevAppIdRef = useRef<string | undefined>(appId);\n \n // Normalize organisationId to empty string if undefined\n const orgId = organisationId || '';\n \n // Log when hook is called with new scope (every render)\n // This helps us see if React is calling the hook with updated scope\n logger.warn('[usePermissions] Hook called with scope', {\n userId,\n organisationId: orgId,\n eventId,\n appId,\n hasAppId: !!appId,\n hasOrganisationId: !!orgId\n });\n \n // Log when scope changes to verify React detects the change\n React.useEffect(() => {\n logger.warn('[usePermissions] Scope changed (useEffect)', {\n userId,\n organisationId,\n eventId,\n appId,\n hasAppId: !!appId,\n hasOrganisationId: !!organisationId\n });\n }, [userId, organisationId, eventId, appId]);\n\n // Add timeout for missing organisation context (3 seconds)\n useEffect(() => {\n if (!orgId || orgId === null || (typeof orgId === 'string' && orgId.trim() === '')) {\n const timeoutId = setTimeout(() => {\n setError(new Error('Organisation context is required for permission checks'));\n setIsLoading(false);\n }, 3000); // 3 seconds - typical permission check is < 1 second\n \n return () => clearTimeout(timeoutId);\n }\n // Clear error if organisation context becomes available\n if (error?.message === 'Organisation context is required for permission checks') {\n setError(null);\n }\n }, [organisationId, error]);\n\n useEffect(() => {\n const fetchPermissions = async () => {\n // Log appId change detection\n const appIdChanged = prevAppIdRef.current !== appId;\n if (appIdChanged) {\n logger.warn('[usePermissions] appId changed detected!', {\n prevAppId: prevAppIdRef.current,\n newAppId: appId,\n hasAppId: !!appId\n });\n prevAppIdRef.current = appId;\n }\n \n logger.warn('[usePermissions] Fetch useEffect triggered', {\n userId,\n orgId,\n eventId,\n appId,\n hasAppId: !!appId,\n appIdChanged,\n isFetching: isFetchingRef.current\n });\n\n // Prevent multiple simultaneous fetches\n if (isFetchingRef.current) {\n logger.warn('[usePermissions] Skipping fetch - already fetching', {\n userId,\n scope: {\n organisationId: orgId,\n eventId: eventId,\n appId: appId\n }\n });\n return;\n }\n\n if (!userId) {\n logger.warn('[usePermissions] Skipping fetch - no userId');\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n // Don't fetch permissions if scope is invalid (e.g., organisationId is null/empty)\n // Wait for organisation context to resolve\n // IMPORTANT: Don't clear existing permissions here - keep them until we have new ones\n if (!orgId || orgId === null || (typeof orgId === 'string' && orgId.trim() === '')) {\n logger.warn('[usePermissions] Skipping fetch - no orgId', {\n orgId,\n hasOrgId: !!orgId\n });\n // Keep existing permissions, just mark as loading\n setIsLoading(true);\n setError(null);\n return;\n }\n\n // CRITICAL: getPermissionMap requires appId to fetch pages and permissions\n // If appId is missing, it returns an empty map\n // Wait for appId to be available before fetching\n if (!appId) {\n logger.warn('[usePermissions] Waiting for appId before fetching permissions', {\n userId,\n organisationId: orgId,\n eventId: eventId,\n hasAppId: false\n });\n setIsLoading(true);\n setError(null);\n return;\n }\n logger.warn('[usePermissions] Fetching permissions', {\n userId,\n scope: {\n organisationId: orgId,\n eventId: eventId,\n appId: appId\n },\n hasAppId: !!appId\n });\n\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n \n // Build scope object for API call\n const scope: Scope = {\n organisationId: orgId,\n eventId: eventId,\n appId: appId\n };\n \n // Fetch new permissions - don't clear old ones until we have new ones\n const permissionMap = await getPermissionMap({ userId, scope });\n \n logger.warn('[usePermissions] Permissions fetched successfully', {\n permissionCount: Object.keys(permissionMap).length,\n hasWildcard: !!permissionMap['*'],\n scope: {\n organisationId: orgId,\n eventId: eventId,\n appId: appId\n }\n });\n \n // Only update permissions if fetch was successful\n setPermissions(permissionMap);\n } catch (err) {\n // On error, keep existing permissions but set error state\n // This prevents the UI from losing all items when there's a transient error\n logger.error('[usePermissions] Failed to fetch permissions:', err);\n setError(err instanceof Error ? err : new Error('Failed to fetch permissions'));\n // Don't clear permissions on error - keep what we had\n } finally {\n setIsLoading(false);\n isFetchingRef.current = false;\n }\n };\n\n fetchPermissions();\n }, [userId, organisationId, eventId, appId]);\n\n const hasPermission = useCallback((permission: Permission): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissions[permission] === true;\n }, [permissions]);\n\n const hasAnyPermission = useCallback((permissionList: Permission[]): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissionList.some(p => permissions[p] === true);\n }, [permissions]);\n\n const hasAllPermissions = useCallback((permissionList: Permission[]): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissionList.every(p => permissions[p] === true);\n }, [permissions]);\n\n const refetch = useCallback(async () => {\n // Prevent multiple simultaneous fetches\n if (isFetchingRef.current) {\n return;\n }\n\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n // Don't fetch permissions if scope is invalid (e.g., organisationId is null/empty)\n // IMPORTANT: Don't clear existing permissions - keep them until we have new ones\n if (!orgId || orgId === null || (typeof orgId === 'string' && orgId.trim() === '')) {\n // Keep existing permissions, just mark as loading\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n \n // Build scope object for API call\n const scope: Scope = {\n organisationId: orgId,\n eventId: eventId,\n appId: appId\n };\n \n // Fetch new permissions - don't clear old ones until we have new ones\n const permissionMap = await getPermissionMap({ userId, scope });\n \n // Only update permissions if fetch was successful\n setPermissions(permissionMap);\n } catch (err) {\n // On error, keep existing permissions but set error state\n // This prevents the UI from losing all items when there's a transient error\n const logger = getRBACLogger();\n logger.error('Failed to refetch permissions:', err);\n setError(err instanceof Error ? err : new Error('Failed to fetch permissions'));\n // Don't clear permissions on error - keep what we had\n } finally {\n setIsLoading(false);\n isFetchingRef.current = false;\n }\n }, [userId, organisationId, eventId, appId]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n permissions,\n isLoading,\n error,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n refetch\n }), [permissions, isLoading, error, hasPermission, hasAnyPermission, hasAllPermissions, refetch]);\n}\n\n/**\n * Hook to check if user can perform an action\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permission - Permission to check\n * @param pageId - Optional page ID\n * @param useCache - Whether to use cached results\n * @returns Permission check state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { can, isLoading, error } = useCan(userId, scope, 'read:users');\n * \n * if (isLoading) return <div>Checking permission...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return can ? <UserList /> : <div>Access denied</div>;\n * }\n * ```\n */\nexport function useCan(\n userId: UUID, \n scope: Scope, \n permission: Permission, \n pageId?: UUID,\n useCache: boolean = true\n) {\n const [can, setCan] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Validate scope parameter - handle undefined/null scope gracefully\n const isValidScope = scope && typeof scope === 'object';\n const organisationId = isValidScope ? scope.organisationId : undefined;\n const eventId = isValidScope ? scope.eventId : undefined;\n const appId = isValidScope ? scope.appId : undefined;\n\n // Add timeout for missing organisation context (3 seconds)\n useEffect(() => {\n if (!isValidScope || !organisationId || organisationId === null || (typeof organisationId === 'string' && organisationId.trim() === '')) {\n const timeoutId = setTimeout(() => {\n setError(new Error('Organisation context is required for permission checks'));\n setIsLoading(false);\n setCan(false);\n }, 3000); // 3 seconds - typical permission check is < 1 second\n \n return () => clearTimeout(timeoutId);\n }\n // Clear error if organisation context becomes available\n if (error?.message === 'Organisation context is required for permission checks') {\n setError(null);\n }\n }, [isValidScope, organisationId, error]);\n\n // Use refs to track the last values to prevent unnecessary re-runs\n const lastUserIdRef = useRef<UUID | null>(null);\n const lastScopeRef = useRef<string | null>(null);\n const lastPermissionRef = useRef<Permission | null>(null);\n const lastPageIdRef = useRef<UUID | undefined | null>(null);\n const lastUseCacheRef = useRef<boolean | null>(null);\n\n useEffect(() => {\n // Create a scope key to track changes - use safe property access\n const scopeKey = isValidScope ? `${organisationId}-${eventId}-${appId}` : 'invalid-scope';\n \n // Only run if something has actually changed\n if (\n lastUserIdRef.current !== userId ||\n lastScopeRef.current !== scopeKey ||\n lastPermissionRef.current !== permission ||\n lastPageIdRef.current !== pageId ||\n lastUseCacheRef.current !== useCache\n ) {\n lastUserIdRef.current = userId;\n lastScopeRef.current = scopeKey;\n lastPermissionRef.current = permission;\n lastPageIdRef.current = pageId;\n lastUseCacheRef.current = useCache;\n \n // Inline the permission check logic to avoid useCallback dependency issues\n const checkPermission = async () => {\n if (!userId) {\n setCan(false);\n setIsLoading(false);\n return;\n }\n\n // Validate scope before accessing properties\n if (!isValidScope) {\n setIsLoading(true);\n setCan(false);\n setError(null);\n // Timeout is handled in separate useEffect\n return;\n }\n\n // Don't check permissions if scope is invalid (e.g., organisationId is null/empty)\n // Wait for organisation context to resolve\n if (!organisationId || organisationId === null || (typeof organisationId === 'string' && organisationId.trim() === '')) {\n setIsLoading(true);\n setCan(false);\n setError(null);\n // Timeout is handled in separate useEffect (Phase 1.4)\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n // Create a valid scope object for the API call\n const validScope: Scope = {\n organisationId,\n ...(eventId ? { eventId } : {}),\n ...(appId ? { appId } : {})\n };\n \n const result = useCache \n ? await isPermittedCached({ userId, scope: validScope, permission, pageId })\n : await isPermitted({ userId, scope: validScope, permission, pageId });\n \n setCan(result);\n } catch (err) {\n const logger = getRBACLogger();\n logger.error('Permission check error:', { permission, error: err });\n setError(err instanceof Error ? err : new Error('Failed to check permission'));\n setCan(false);\n } finally {\n setIsLoading(false);\n }\n };\n \n checkPermission();\n }\n }, [userId, isValidScope, organisationId, eventId, appId, permission, pageId, useCache]);\n\n const refetch = useCallback(async () => {\n if (!userId) {\n setCan(false);\n setIsLoading(false);\n return;\n }\n\n // Validate scope before accessing properties\n if (!isValidScope) {\n setCan(false);\n setIsLoading(true);\n setError(null);\n return;\n }\n\n // Don't check permissions if scope is invalid (e.g., organisationId is null/empty)\n if (!organisationId || organisationId === null || (typeof organisationId === 'string' && organisationId.trim() === '')) {\n setCan(false);\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n // Create a valid scope object for the API call\n const validScope: Scope = {\n organisationId,\n ...(eventId ? { eventId } : {}),\n ...(appId ? { appId } : {})\n };\n \n const result = useCache \n ? await isPermittedCached({ userId, scope: validScope, permission, pageId })\n : await isPermitted({ userId, scope: validScope, permission, pageId });\n \n setCan(result);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permission'));\n setCan(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, isValidScope, organisationId, eventId, appId, permission, pageId, useCache]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n can,\n isLoading,\n error,\n refetch\n }), [can, isLoading, error, refetch]);\n}\n\n/**\n * Hook to get user's access level in a scope\n * \n * @param userId - User ID\n * @param scope - Scope for access level checking\n * @returns Access level state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { accessLevel, isLoading, error } = useAccessLevel(userId, scope);\n * \n * if (isLoading) return <div>Loading access level...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * Access Level: {accessLevel}\n * {accessLevel >= AccessLevel.ADMIN && <AdminPanel />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useAccessLevel(userId: UUID, scope: Scope): {\n accessLevel: AccessLevelType;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [accessLevel, setAccessLevel] = useState<AccessLevelType>('viewer');\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchAccessLevel = useCallback(async () => {\n if (!userId) {\n setAccessLevel('viewer');\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const level = await getAccessLevel({ userId, scope });\n setAccessLevel(level);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch access level'));\n setAccessLevel('viewer');\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n useEffect(() => {\n fetchAccessLevel();\n }, [fetchAccessLevel]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n accessLevel,\n isLoading,\n error,\n refetch: fetchAccessLevel\n }), [accessLevel, isLoading, error, fetchAccessLevel]);\n}\n\n/**\n * Hook to check multiple permissions at once\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Multiple permission check results\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { results, isLoading, error } = useMultiplePermissions(\n * userId, \n * scope, \n * ['read:users', 'create:users', 'update:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {results['read:users'] && <UserList />}\n * {results['create:users'] && <CreateUserButton />}\n * {results['update:users'] && <EditUserButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMultiplePermissions(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n results: Record<Permission, boolean>;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [results, setResults] = useState<Record<Permission, boolean>>({} as Record<Permission, boolean>);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkPermissions = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setResults({} as Record<Permission, boolean>);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const permissionResults: Record<Permission, boolean> = {} as Record<Permission, boolean>;\n \n // Check each permission\n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n permissionResults[permission] = result;\n }\n \n setResults(permissionResults);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setResults({} as Record<Permission, boolean>);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkPermissions();\n }, [checkPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n results,\n isLoading,\n error,\n refetch: checkPermissions\n }), [results, isLoading, error, checkPermissions]);\n}\n\n/**\n * Hook to check if user has any of the specified permissions\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Whether user has any of the permissions\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { hasAny, isLoading, error } = useHasAnyPermission(\n * userId, \n * scope, \n * ['read:users', 'create:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return hasAny ? <UserManagementPanel /> : <div>No user permissions</div>;\n * }\n * ```\n */\nexport function useHasAnyPermission(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n hasAny: boolean;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [hasAny, setHasAny] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkAnyPermission = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setHasAny(false);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n let hasAnyPermission = false;\n \n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n \n if (result) {\n hasAnyPermission = true;\n break;\n }\n }\n \n setHasAny(hasAnyPermission);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setHasAny(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkAnyPermission();\n }, [checkAnyPermission]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n hasAny,\n isLoading,\n error,\n refetch: checkAnyPermission\n }), [hasAny, isLoading, error, checkAnyPermission]);\n}\n\n/**\n * Hook to check if user has all of the specified permissions\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Whether user has all of the permissions\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { hasAll, isLoading, error } = useHasAllPermissions(\n * userId, \n * scope, \n * ['read:users', 'create:users', 'update:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return hasAll ? <FullUserManagementPanel /> : <div>Insufficient permissions</div>;\n * }\n * ```\n */\nexport function useHasAllPermissions(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n hasAll: boolean;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [hasAll, setHasAll] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkAllPermissions = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setHasAll(false);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n let hasAllPermissions = true;\n \n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n \n if (!result) {\n hasAllPermissions = false;\n break;\n }\n }\n \n setHasAll(hasAllPermissions);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setHasAll(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkAllPermissions();\n }, [checkAllPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n hasAll,\n isLoading,\n error,\n refetch: checkAllPermissions\n }), [hasAll, isLoading, error, checkAllPermissions]);\n}\n\n/**\n * Hook to get cached permissions with TTL management\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @returns Cached permission state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { permissions, isLoading, error, invalidateCache } = useCachedPermissions(userId, scope);\n * \n * if (isLoading) return <div>Loading cached permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {permissions['read:users'] && <UserList />}\n * <button onClick={invalidateCache}>Refresh Permissions</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useCachedPermissions(userId: UUID, scope: Scope): {\n permissions: PermissionMap;\n isLoading: boolean;\n error: Error | null;\n invalidateCache: () => void;\n refetch: () => Promise<void>;\n} {\n const [permissions, setPermissions] = useState<PermissionMap>({} as PermissionMap);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchCachedPermissions = useCallback(async () => {\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const permissionMap = await getPermissionMap({ userId, scope });\n setPermissions(permissionMap);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch cached permissions'));\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n const invalidateCache = useCallback(() => {\n // This would typically invalidate the cache in the actual implementation\n // For now, we'll just refetch\n fetchCachedPermissions();\n }, [fetchCachedPermissions]);\n\n useEffect(() => {\n fetchCachedPermissions();\n }, [fetchCachedPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n permissions,\n isLoading,\n error,\n invalidateCache,\n refetch: fetchCachedPermissions\n }), [permissions, isLoading, error, invalidateCache, fetchCachedPermissions]);\n}\n","/**\n * @file useResourcePermissions Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n * \n * Hook to check permissions for a specific resource type.\n * This hook centralizes the common pattern of checking create/update/delete/read\n * permissions, eliminating ~30 lines of boilerplate code per hook usage.\n * \n * @example\n * ```tsx\n * import { useResourcePermissions } from '@jmruthers/pace-core/rbac';\n * \n * function ContactsHook() {\n * const { canCreate, canUpdate, canDelete } = useResourcePermissions('contacts');\n * \n * const addContact = async (data: ContactData) => {\n * if (!canCreate('contacts')) {\n * throw new Error(\"Permission denied: You do not have permission to create contacts.\");\n * }\n * // ... perform mutation\n * };\n * }\n * ```\n * \n * @example\n * ```tsx\n * // With read permissions enabled\n * const { canRead } = useResourcePermissions('contacts', { enableRead: true });\n * \n * if (!canRead('contacts')) {\n * return <PermissionDenied />;\n * }\n * ```\n * \n * @security\n * - Requires organisation context (handled by useResolvedScope)\n * - All permission checks are scoped to the current organisation/event/app context\n * - Missing user context results in all permissions being denied\n */\n\nimport { useMemo } from 'react';\nimport { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';\nimport { useOrganisations } from '../../hooks/useOrganisations';\nimport { useEvents } from '../../hooks/useEvents';\nimport { useResolvedScope } from './useResolvedScope';\nimport { useCan } from './usePermissions';\nimport type { Scope } from '../types';\n\nexport interface UseResourcePermissionsOptions {\n /** Whether to check read permissions (default: false) */\n enableRead?: boolean;\n /** Whether scope resolution is required (default: true) */\n requireScope?: boolean;\n}\n\nexport interface ResourcePermissions {\n /** Check if user can create resources of this type */\n canCreate: (resource: string) => boolean;\n /** Check if user can update resources of this type */\n canUpdate: (resource: string) => boolean;\n /** Check if user can delete resources of this type */\n canDelete: (resource: string) => boolean;\n /** Check if user can read resources of this type */\n canRead: (resource: string) => boolean;\n /** The resolved scope object (for advanced use cases) */\n scope: Scope;\n /** Whether any permission check is currently loading */\n isLoading: boolean;\n /** Error from any permission check or scope resolution */\n error: Error | null;\n}\n\n/**\n * Hook to check permissions for a specific resource\n * \n * This hook encapsulates the common pattern of checking create/update/delete/read\n * permissions for a resource type. It handles scope resolution, user context,\n * and provides a simple API for permission checking.\n * \n * @param resource - The resource name (e.g., 'contacts', 'risks', 'journal')\n * @param options - Optional configuration\n * @param options.enableRead - Whether to check read permissions (default: false)\n * @param options.requireScope - Whether scope resolution is required (default: true)\n * @returns Object with permission check functions and scope\n * \n * @example\n * ```tsx\n * function useContacts() {\n * const { canCreate, canUpdate, canDelete } = useResourcePermissions('contacts');\n * \n * const addContact = async (data: ContactData) => {\n * if (!canCreate('contacts')) {\n * throw new Error(\"Permission denied\");\n * }\n * // ... perform mutation\n * };\n * }\n * ```\n */\nexport function useResourcePermissions(\n resource: string,\n options: UseResourcePermissionsOptions = {}\n): ResourcePermissions {\n const { enableRead = false, requireScope = true } = options;\n\n // Get user and supabase client from UnifiedAuth\n const { user, supabase } = useUnifiedAuth();\n \n // Get selected organisation\n const { selectedOrganisation } = useOrganisations();\n \n // Get selected event (optional - wrap in try/catch)\n let selectedEvent: { event_id: string } | null = null;\n try {\n const eventsContext = useEvents();\n selectedEvent = eventsContext.selectedEvent;\n } catch (error) {\n // Event provider not available - continue without event context\n // This is expected in some apps that don't use events\n }\n\n // Resolve scope for permission checks\n const { resolvedScope, isLoading: scopeLoading, error: scopeError } = useResolvedScope({\n supabase,\n selectedOrganisationId: selectedOrganisation?.id || null,\n selectedEventId: selectedEvent?.event_id || null\n });\n\n // Create fallback scope if resolvedScope is not available\n const scope: Scope = resolvedScope || {\n organisationId: selectedOrganisation?.id || '',\n eventId: selectedEvent?.event_id || undefined,\n appId: undefined\n };\n\n // Permission checks for create, update, delete\n const { can: canCreateResult, isLoading: createLoading, error: createError } = useCan(\n user?.id || '',\n scope,\n `create:${resource}` as const,\n undefined, // pageId\n true // useCache\n );\n\n const { can: canUpdateResult, isLoading: updateLoading, error: updateError } = useCan(\n user?.id || '',\n scope,\n `update:${resource}` as const,\n undefined, // pageId\n true // useCache\n );\n\n const { can: canDeleteResult, isLoading: deleteLoading, error: deleteError } = useCan(\n user?.id || '',\n scope,\n `delete:${resource}` as const,\n undefined, // pageId\n true // useCache\n );\n\n // Optional read permission check\n const { can: canReadResult, isLoading: readLoading, error: readError } = useCan(\n user?.id || '',\n scope,\n `read:${resource}` as const,\n undefined, // pageId\n true // useCache\n );\n\n // Aggregate loading states - any permission check or scope resolution loading\n const isLoading = useMemo(() => {\n return scopeLoading || createLoading || updateLoading || deleteLoading || (enableRead && readLoading);\n }, [scopeLoading, createLoading, updateLoading, deleteLoading, readLoading, enableRead]);\n\n // Aggregate errors - prefer scope error, then any permission error\n const error = useMemo(() => {\n if (scopeError) return scopeError;\n if (createError) return createError;\n if (updateError) return updateError;\n if (deleteError) return deleteError;\n if (enableRead && readError) return readError;\n return null;\n }, [scopeError, createError, updateError, deleteError, readError, enableRead]);\n\n // Return wrapper functions that take resource name and return permission result\n // Note: The resource parameter in the function is for consistency with the API,\n // but we're checking permissions for the resource passed to the hook\n return useMemo(() => ({\n canCreate: (res: string) => {\n // For now, we only check the resource passed to the hook\n // Future enhancement could support checking different resources\n if (res !== resource) {\n return false;\n }\n return canCreateResult;\n },\n canUpdate: (res: string) => {\n if (res !== resource) {\n return false;\n }\n return canUpdateResult;\n },\n canDelete: (res: string) => {\n if (res !== resource) {\n return false;\n }\n return canDeleteResult;\n },\n canRead: (res: string) => {\n if (!enableRead) {\n return true; // If read checking is disabled, allow read\n }\n if (res !== resource) {\n return false;\n }\n return canReadResult;\n },\n scope,\n isLoading,\n error\n }), [\n resource,\n canCreateResult,\n canUpdateResult,\n canDeleteResult,\n canReadResult,\n enableRead,\n scope,\n isLoading,\n error\n ]);\n}\n\n","/**\n * @file RBAC Role Management Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 2.1.0\n *\n * React hook for managing RBAC roles safely using RPC functions.\n * This hook provides a secure, type-safe interface for granting and revoking roles\n * that ensures proper audit trails and security checks.\n *\n * @example\n * ```tsx\n * import { useRoleManagement } from '@jmruthers/pace-core/rbac';\n *\n * function UserRolesComponent() {\n * const { revokeEventAppRole, grantEventAppRole, isLoading, error } = useRoleManagement();\n *\n * const handleRevokeRole = async (roleId: string, roleData: EventAppRoleData) => {\n * const result = await revokeEventAppRole({\n * userId: roleData.user_id,\n * organisationId: roleData.organisation_id,\n * eventId: roleData.event_id,\n * appId: roleData.app_id,\n * role: roleData.role\n * });\n *\n * if (result.success) {\n * toast({ title: 'Role revoked successfully' });\n * } else {\n * toast({ title: 'Failed to revoke role', variant: 'destructive' });\n * }\n * };\n *\n * return <button onClick={() => handleRevokeRole(roleId, roleData)}>Revoke Role</button>;\n * }\n * ```\n */\n\nimport { useState, useCallback } from 'react';\nimport { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';\nimport type { UUID } from '../types';\n\nexport interface EventAppRoleData {\n user_id: UUID;\n organisation_id: UUID;\n event_id: string;\n app_id: UUID;\n role: 'viewer' | 'participant' | 'planner' | 'event_admin';\n}\n\nexport interface RevokeEventAppRoleParams extends EventAppRoleData {\n revoked_by?: UUID;\n}\n\nexport interface GrantEventAppRoleParams extends EventAppRoleData {\n granted_by?: UUID;\n valid_from?: string;\n valid_to?: string | null;\n}\n\nexport interface RoleManagementResult {\n success: boolean;\n message?: string;\n error?: string;\n roleId?: UUID;\n}\n\nexport function useRoleManagement() {\n const { user, supabase } = useUnifiedAuth();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n if (!supabase) {\n throw new Error('useRoleManagement requires a Supabase client. Ensure UnifiedAuthProvider is configured.');\n }\n\n /**\n * Revoke an event app role using the secure RPC function\n * \n * This function uses the `revoke_event_app_role` RPC which:\n * - Runs with SECURITY DEFINER privileges\n * - Includes proper permission checks\n * - Automatically populates audit fields (revoked_by, timestamps)\n * - Complies with Row-Level Security policies\n * \n * @param params - Role revocation parameters\n * @returns Promise resolving to operation result\n */\n const revokeEventAppRole = useCallback(async (\n params: RevokeEventAppRoleParams\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { data, error: rpcError } = await supabase.rpc('revoke_event_app_role', {\n p_user_id: params.user_id,\n p_organisation_id: params.organisation_id,\n p_event_id: params.event_id,\n p_app_id: params.app_id,\n p_role: params.role,\n p_revoked_by: params.revoked_by || user?.id || undefined\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to revoke role');\n }\n\n return {\n success: data === true,\n message: data === true ? 'Role revoked successfully' : 'No role found to revoke',\n error: data === false ? 'No matching role found' : undefined\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id]);\n\n /**\n * Grant an event app role using the secure RPC function\n * \n * This function uses the `grant_event_app_role` RPC which:\n * - Runs with SECURITY DEFINER privileges\n * - Includes proper permission checks\n * - Automatically populates audit fields (granted_by, timestamps)\n * - Complies with Row-Level Security policies\n * \n * @param params - Role grant parameters\n * @returns Promise resolving to operation result with role ID\n */\n const grantEventAppRole = useCallback(async (\n params: GrantEventAppRoleParams\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { data, error: rpcError } = await supabase.rpc('grant_event_app_role', {\n p_user_id: params.user_id,\n p_organisation_id: params.organisation_id,\n p_event_id: params.event_id,\n p_app_id: params.app_id,\n p_role: params.role,\n p_granted_by: params.granted_by || user?.id || undefined,\n p_valid_from: params.valid_from,\n p_valid_to: params.valid_to\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to grant role');\n }\n\n if (!data) {\n return {\n success: false,\n error: 'Failed to grant role - no role ID returned'\n };\n }\n\n return {\n success: true,\n message: 'Role granted successfully',\n roleId: data as UUID\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id]);\n\n /**\n * Revoke an event app role by role ID (alternative method)\n * \n * This fetches the role by ID first to get the required context (role name, event_id, app_id),\n * then uses the unified `rbac_role_revoke` function to revoke it.\n * \n * @param roleId - The role ID to revoke\n * @returns Promise resolving to operation result\n */\n const revokeRoleById = useCallback(async (\n roleId: UUID\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n // First, fetch the role by ID to get the required context\n const { data: roleData, error: fetchError } = await supabase\n .from('rbac_event_app_roles')\n .select('user_id, role, event_id, app_id')\n .eq('id', roleId)\n .single();\n\n if (fetchError || !roleData) {\n throw new Error(fetchError?.message || 'Role not found');\n }\n\n // Construct context_id in the format required by rbac_role_revoke: \"event_id:app_id\"\n const contextId = `${roleData.event_id}:${roleData.app_id}`;\n\n // Now call rbac_role_revoke with the required parameters\n const { data, error: rpcError } = await supabase.rpc('rbac_role_revoke', {\n p_user_id: roleData.user_id,\n p_role_type: 'event_app',\n p_role_name: roleData.role,\n p_context_id: contextId,\n p_revoked_by: user?.id || undefined\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to revoke role');\n }\n\n // rbac_role_revoke returns a table with success, message, revoked_count, error_code\n const result = Array.isArray(data) && data.length > 0 ? data[0] : null;\n\n return {\n success: result?.success === true,\n message: result?.message || undefined,\n error: result?.success === false ? (result?.message || result?.error_code || 'Unknown error') : undefined\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id, supabase]);\n\n return {\n revokeEventAppRole,\n grantEventAppRole,\n revokeRoleById,\n isLoading,\n error\n };\n}\n\n"],"mappings":"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","names":["useEffect","useState","useState","useEffect","error","useState","useEffect","useCallback","useMemo","useRef","useState","useRef","useEffect","useCallback","logger","useMemo","useMemo","error","useMemo","useState","useCallback"]}
@@ -57,7 +57,7 @@ import {
57
57
  useFileReferenceForRecord,
58
58
  useFilesByCategory,
59
59
  usePublicPageContext as usePublicPageContext2
60
- } from "./chunk-Q4NUEMNV.js";
60
+ } from "./chunk-OX3NRR4N.js";
61
61
  import {
62
62
  Alert,
63
63
  AlertDescription,
@@ -98,8 +98,8 @@ import {
98
98
  TooltipProvider,
99
99
  TooltipRoot,
100
100
  TooltipTrigger
101
- } from "./chunk-23YYDN24.js";
102
- import "./chunk-GFSYFEVZ.js";
101
+ } from "./chunk-4AJ2ZWQW.js";
102
+ import "./chunk-PKYRTRZA.js";
103
103
  import "./chunk-BVYWGZVV.js";
104
104
  import "./chunk-SBVILCCA.js";
105
105
  import {
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ import {
30
30
  withAccessLevelGuard,
31
31
  withPermissionGuard,
32
32
  withRoleGuard
33
- } from "./chunk-DDB2M4XO.js";
33
+ } from "./chunk-AXFP27TJ.js";
34
34
  import {
35
35
  init_useInactivityTracker,
36
36
  useInactivityTracker
@@ -96,7 +96,7 @@ import {
96
96
  useFileReferenceForRecord,
97
97
  useFilesByCategory,
98
98
  usePublicPageContext as usePublicPageContext2
99
- } from "./chunk-Q4NUEMNV.js";
99
+ } from "./chunk-OX3NRR4N.js";
100
100
  import {
101
101
  Alert,
102
102
  AlertDescription,
@@ -146,7 +146,7 @@ import {
146
146
  max,
147
147
  min,
148
148
  sum
149
- } from "./chunk-23YYDN24.js";
149
+ } from "./chunk-4AJ2ZWQW.js";
150
150
  import {
151
151
  useAccessLevel,
152
152
  useCachedPermissions,
@@ -159,7 +159,7 @@ import {
159
159
  useResolvedScope,
160
160
  useResourcePermissions,
161
161
  useRoleManagement
162
- } from "./chunk-GFSYFEVZ.js";
162
+ } from "./chunk-PKYRTRZA.js";
163
163
  import {
164
164
  CACHE_PATTERNS,
165
165
  RBACCache,
@@ -1286,13 +1286,20 @@ declare function useResourcePermissions(resource: string, options?: UseResourceP
1286
1286
  * Hook to get user's permissions in a scope
1287
1287
  *
1288
1288
  * @param userId - User ID
1289
- * @param scope - Scope for permission checking
1289
+ * @param organisationId - Organisation ID
1290
+ * @param eventId - Event ID (optional)
1291
+ * @param appId - Application ID (optional)
1290
1292
  * @returns Permission state and methods
1291
1293
  *
1292
1294
  * @example
1293
1295
  * ```tsx
1294
1296
  * function MyComponent() {
1295
- * const { permissions, isLoading, error } = usePermissions(userId, scope);
1297
+ * const { permissions, isLoading, error } = usePermissions(
1298
+ * userId,
1299
+ * organisationId,
1300
+ * eventId,
1301
+ * appId
1302
+ * );
1296
1303
  *
1297
1304
  * if (isLoading) return <div>Loading...</div>;
1298
1305
  * if (error) return <div>Error: {error.message}</div>;
@@ -1306,7 +1313,7 @@ declare function useResourcePermissions(resource: string, options?: UseResourceP
1306
1313
  * }
1307
1314
  * ```
1308
1315
  */
1309
- declare function usePermissions(userId: UUID, scope: Scope): {
1316
+ declare function usePermissions(userId: UUID, organisationId: string | undefined, eventId: string | undefined, appId: string | undefined): {
1310
1317
  permissions: PermissionMap;
1311
1318
  isLoading: boolean;
1312
1319
  error: Error | null;
@@ -30,7 +30,7 @@ import {
30
30
  withAccessLevelGuard,
31
31
  withPermissionGuard,
32
32
  withRoleGuard
33
- } from "../chunk-DDB2M4XO.js";
33
+ } from "../chunk-AXFP27TJ.js";
34
34
  import {
35
35
  useAccessLevel,
36
36
  useCachedPermissions,
@@ -43,7 +43,7 @@ import {
43
43
  useResolvedScope,
44
44
  useResourcePermissions,
45
45
  useRoleManagement
46
- } from "../chunk-GFSYFEVZ.js";
46
+ } from "../chunk-PKYRTRZA.js";
47
47
  import {
48
48
  CACHE_PATTERNS,
49
49
  RBACCache,
package/dist/utils.js CHANGED
@@ -614,7 +614,7 @@ function createLazyComponent(importFn, componentName, options = {}) {
614
614
  return WrappedComponent;
615
615
  }
616
616
  var LazyDataTable = createLazyComponent(
617
- () => import("./DataTable-HAATGNJY.js").then((module) => ({ default: module.DataTable })),
617
+ () => import("./DataTable-QMER2BPR.js").then((module) => ({ default: module.DataTable })),
618
618
  "DataTable"
619
619
  );
620
620
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / ColumnFactory
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / ColumnFactory
2
2
 
3
3
  # Class: ColumnFactory\<TData\>
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / ErrorBoundary
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / ErrorBoundary
2
2
 
3
3
  # Class: ErrorBoundary
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / InvalidScopeError
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / InvalidScopeError
2
2
 
3
3
  # Class: InvalidScopeError
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / MissingUserContextError
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / MissingUserContextError
2
2
 
3
3
  # Class: MissingUserContextError
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / OrganisationContextRequiredError
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / OrganisationContextRequiredError
2
2
 
3
3
  # Class: OrganisationContextRequiredError
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / PermissionDeniedError
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / PermissionDeniedError
2
2
 
3
3
  # Class: PermissionDeniedError
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / PublicErrorBoundary
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / PublicErrorBoundary
2
2
 
3
3
  # Class: PublicErrorBoundary
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / RBACAuditManager
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / RBACAuditManager
2
2
 
3
3
  # Class: RBACAuditManager
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / RBACCache
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / RBACCache
2
2
 
3
3
  # Class: RBACCache
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / RBACEngine
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / RBACEngine
2
2
 
3
3
  # Class: RBACEngine
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / RBACError
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / RBACError
2
2
 
3
3
  # Class: RBACError
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / RBACNotInitializedError
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / RBACNotInitializedError
2
2
 
3
3
  # Class: RBACNotInitializedError
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / SecureSupabaseClient
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / SecureSupabaseClient
2
2
 
3
3
  # Class: SecureSupabaseClient
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / StorageUtils
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / StorageUtils
2
2
 
3
3
  # Class: StorageUtils
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / FileCategory
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / FileCategory
2
2
 
3
3
  # Enumeration: FileCategory
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / AggregateConfig
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / AggregateConfig
2
2
 
3
3
  # Interface: AggregateConfig
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / BadgeProps
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / BadgeProps
2
2
 
3
3
  # Interface: BadgeProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / ButtonProps
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / ButtonProps
2
2
 
3
3
  # Interface: ButtonProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / CalendarProps
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / CalendarProps
2
2
 
3
3
  # Interface: CalendarProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / CardProps
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / CardProps
2
2
 
3
3
  # Interface: CardProps
4
4
 
@@ -1,4 +1,4 @@
1
- [@jmruthers/pace-core - v0.5.165](../README.md) / [Exports](../modules.md) / ColorPalette
1
+ [@jmruthers/pace-core - v0.5.167](../README.md) / [Exports](../modules.md) / ColorPalette
2
2
 
3
3
  # Interface: ColorPalette
4
4