@jmruthers/pace-core 0.5.110 → 0.5.112

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 (236) hide show
  1. package/dist/{AuthService-DrHrvXNZ.d.ts → AuthService-CVgsgtaZ.d.ts} +8 -0
  2. package/dist/{DataTable-D3BK2FCN.js → DataTable-3D3BUZDV.js} +8 -8
  3. package/dist/{UnifiedAuthProvider-A7I23UCN.js → UnifiedAuthProvider-KZZUO27W.js} +3 -3
  4. package/dist/{api-PIE4JRFS.js → api-QPMBZZUZ.js} +5 -3
  5. package/dist/{audit-65VNHEV2.js → audit-H4YJJF7R.js} +2 -2
  6. package/dist/{chunk-Q7APDV6H.js → chunk-3OGQLOJM.js} +23 -7
  7. package/dist/chunk-3OGQLOJM.js.map +1 -0
  8. package/dist/{chunk-EYSXQ756.js → chunk-7H75SHXZ.js} +2 -2
  9. package/dist/{chunk-D6MEKC27.js → chunk-BUN7NMV7.js} +2 -2
  10. package/dist/{chunk-AWK2FAUN.js → chunk-C5RN4TE5.js} +7 -7
  11. package/dist/{chunk-3J5N2T2N.js → chunk-EKVVTPIF.js} +183 -127
  12. package/dist/chunk-EKVVTPIF.js.map +1 -0
  13. package/dist/{chunk-2W4WKJVF.js → chunk-F6QB26OS.js} +290 -255
  14. package/dist/chunk-F6QB26OS.js.map +1 -0
  15. package/dist/{chunk-HADXAZT3.js → chunk-I7JC7PTJ.js} +54 -92
  16. package/dist/chunk-I7JC7PTJ.js.map +1 -0
  17. package/dist/{chunk-EZ64QG2I.js → chunk-L36JW4KV.js} +2 -2
  18. package/dist/{chunk-7GBEBJLR.js → chunk-MNSGWRPB.js} +45 -37
  19. package/dist/chunk-MNSGWRPB.js.map +1 -0
  20. package/dist/{chunk-YFMENCR4.js → chunk-NEONKMTU.js} +3 -3
  21. package/dist/{chunk-AUXS7XSO.js → chunk-OO3V7W4H.js} +35 -11
  22. package/dist/chunk-OO3V7W4H.js.map +1 -0
  23. package/dist/{chunk-XRSP3H52.js → chunk-TAJRS6YB.js} +57 -23
  24. package/dist/chunk-TAJRS6YB.js.map +1 -0
  25. package/dist/{chunk-HGZSO43Y.js → chunk-WMPZY26G.js} +8 -4
  26. package/dist/{chunk-HGZSO43Y.js.map → chunk-WMPZY26G.js.map} +1 -1
  27. package/dist/components.js +10 -10
  28. package/dist/hooks.d.ts +11 -1
  29. package/dist/hooks.js +9 -7
  30. package/dist/hooks.js.map +1 -1
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +13 -13
  33. package/dist/providers.d.ts +2 -2
  34. package/dist/providers.js +2 -2
  35. package/dist/rbac/index.d.ts +13 -8
  36. package/dist/rbac/index.js +9 -9
  37. package/dist/utils.js +1 -1
  38. package/docs/api/classes/ColumnFactory.md +1 -1
  39. package/docs/api/classes/ErrorBoundary.md +1 -1
  40. package/docs/api/classes/InvalidScopeError.md +4 -4
  41. package/docs/api/classes/MissingUserContextError.md +4 -4
  42. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  43. package/docs/api/classes/PermissionDeniedError.md +4 -4
  44. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  45. package/docs/api/classes/RBACAuditManager.md +8 -8
  46. package/docs/api/classes/RBACCache.md +8 -8
  47. package/docs/api/classes/RBACEngine.md +4 -4
  48. package/docs/api/classes/RBACError.md +4 -4
  49. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  50. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  51. package/docs/api/classes/StorageUtils.md +1 -1
  52. package/docs/api/enums/FileCategory.md +1 -1
  53. package/docs/api/interfaces/AggregateConfig.md +1 -1
  54. package/docs/api/interfaces/ButtonProps.md +1 -1
  55. package/docs/api/interfaces/CardProps.md +1 -1
  56. package/docs/api/interfaces/ColorPalette.md +1 -1
  57. package/docs/api/interfaces/ColorShade.md +1 -1
  58. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  59. package/docs/api/interfaces/DataRecord.md +1 -1
  60. package/docs/api/interfaces/DataTableAction.md +1 -1
  61. package/docs/api/interfaces/DataTableColumn.md +1 -1
  62. package/docs/api/interfaces/DataTableProps.md +1 -1
  63. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  64. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  65. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  66. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  67. package/docs/api/interfaces/FileMetadata.md +1 -1
  68. package/docs/api/interfaces/FileReference.md +1 -1
  69. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  70. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  71. package/docs/api/interfaces/FileUploadProps.md +1 -1
  72. package/docs/api/interfaces/FooterProps.md +1 -1
  73. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  74. package/docs/api/interfaces/InputProps.md +1 -1
  75. package/docs/api/interfaces/LabelProps.md +1 -1
  76. package/docs/api/interfaces/LoginFormProps.md +1 -1
  77. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  78. package/docs/api/interfaces/NavigationContextType.md +1 -1
  79. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  80. package/docs/api/interfaces/NavigationItem.md +1 -1
  81. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  82. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  83. package/docs/api/interfaces/Organisation.md +1 -1
  84. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  85. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  86. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  87. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  88. package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
  89. package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
  90. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  91. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  92. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  93. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  94. package/docs/api/interfaces/PaletteData.md +1 -1
  95. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  96. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  97. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  98. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  99. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  100. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  101. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  102. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  103. package/docs/api/interfaces/RBACConfig.md +1 -1
  104. package/docs/api/interfaces/RBACLogger.md +1 -1
  105. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  106. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  107. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  108. package/docs/api/interfaces/RouteConfig.md +19 -6
  109. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  110. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  111. package/docs/api/interfaces/StorageConfig.md +1 -1
  112. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  113. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  114. package/docs/api/interfaces/StorageListOptions.md +1 -1
  115. package/docs/api/interfaces/StorageListResult.md +1 -1
  116. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  117. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  118. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  119. package/docs/api/interfaces/StyleImport.md +1 -1
  120. package/docs/api/interfaces/SwitchProps.md +1 -1
  121. package/docs/api/interfaces/ToastActionElement.md +1 -1
  122. package/docs/api/interfaces/ToastProps.md +1 -1
  123. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  124. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  125. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  126. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  127. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  128. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  129. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  130. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  131. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  132. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  133. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  134. package/docs/api/interfaces/UserEventAccess.md +1 -1
  135. package/docs/api/interfaces/UserMenuProps.md +1 -1
  136. package/docs/api/interfaces/UserProfile.md +1 -1
  137. package/docs/api/modules.md +36 -36
  138. package/docs/api-reference/hooks.md +8 -4
  139. package/docs/architecture/rpc-function-standards.md +3 -1
  140. package/docs/best-practices/common-patterns.md +3 -3
  141. package/docs/best-practices/deployment.md +10 -4
  142. package/docs/best-practices/performance.md +11 -3
  143. package/docs/core-concepts/organisations.md +8 -8
  144. package/docs/core-concepts/permissions.md +133 -72
  145. package/docs/migration/rbac-migration.md +65 -66
  146. package/docs/rbac/advanced-patterns.md +15 -22
  147. package/docs/rbac/examples.md +12 -12
  148. package/docs/rbac/getting-started.md +3 -3
  149. package/docs/rbac/troubleshooting.md +2 -1
  150. package/package.json +1 -1
  151. package/src/components/DataTable/DataTable.test.tsx +405 -154
  152. package/src/components/DataTable/components/DataTableCore.tsx +6 -1
  153. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +913 -0
  154. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +609 -0
  155. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +434 -0
  156. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +120 -0
  157. package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +519 -0
  158. package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +316 -0
  159. package/src/components/DataTable/examples/__tests__/InitialPageSizeExample.test.tsx +211 -0
  160. package/src/components/EventSelector/EventSelector.tsx +32 -2
  161. package/src/components/FileUpload/FileUpload.tsx +2 -8
  162. package/src/components/NavigationMenu/NavigationMenu.test.tsx +56 -8
  163. package/src/components/NavigationMenu/NavigationMenu.tsx +75 -12
  164. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +193 -63
  165. package/src/components/PaceAppLayout/PaceAppLayout.tsx +102 -135
  166. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +41 -2
  167. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +61 -6
  168. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +71 -21
  169. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +113 -41
  170. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +155 -45
  171. package/src/components/PaceLoginPage/PaceLoginPage.tsx +30 -1
  172. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +63 -5
  173. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +156 -72
  174. package/src/hooks/__tests__/useRBAC.unit.test.ts +4 -38
  175. package/src/hooks/index.ts +1 -1
  176. package/src/hooks/useFileDisplay.ts +51 -0
  177. package/src/hooks/usePermissionCache.test.ts +112 -68
  178. package/src/hooks/usePermissionCache.ts +55 -15
  179. package/src/rbac/README.md +81 -39
  180. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +3 -3
  181. package/src/rbac/__tests__/engine.comprehensive.test.ts +15 -6
  182. package/src/rbac/__tests__/rbac-core.test.tsx +1 -1
  183. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +57 -4
  184. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +3 -2
  185. package/src/rbac/adapters.tsx +4 -4
  186. package/src/rbac/api.test.ts +37 -13
  187. package/src/rbac/api.ts +25 -8
  188. package/src/rbac/audit-enhanced.ts +14 -2
  189. package/src/rbac/audit.test.ts +18 -8
  190. package/src/rbac/audit.ts +25 -6
  191. package/src/rbac/cache.test.ts +12 -0
  192. package/src/rbac/cache.ts +29 -9
  193. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +1 -1
  194. package/src/rbac/components/NavigationGuard.tsx +14 -14
  195. package/src/rbac/components/NavigationProvider.test.tsx +1 -1
  196. package/src/rbac/components/PagePermissionGuard.tsx +4 -3
  197. package/src/rbac/components/PagePermissionProvider.test.tsx +1 -1
  198. package/src/rbac/components/PermissionEnforcer.tsx +19 -15
  199. package/src/rbac/components/RoleBasedRouter.tsx +16 -9
  200. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +123 -107
  201. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +1 -1
  202. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +121 -103
  203. package/src/rbac/docs/event-based-apps.md +6 -6
  204. package/src/rbac/engine.ts +12 -2
  205. package/src/rbac/hooks/useCan.test.ts +29 -2
  206. package/src/rbac/hooks/usePermissions.test.ts +25 -25
  207. package/src/rbac/hooks/usePermissions.ts +65 -25
  208. package/src/rbac/hooks/useRBAC.simple.test.ts +1 -8
  209. package/src/rbac/hooks/useRBAC.test.ts +3 -40
  210. package/src/rbac/hooks/useRBAC.ts +0 -55
  211. package/src/rbac/hooks/useResolvedScope.ts +23 -31
  212. package/src/rbac/permissions.test.ts +11 -7
  213. package/src/rbac/security.test.ts +2 -2
  214. package/src/rbac/security.ts +22 -7
  215. package/src/rbac/types.test.ts +2 -2
  216. package/src/rbac/types.ts +1 -2
  217. package/src/services/EventService.ts +42 -13
  218. package/src/services/__tests__/EventService.test.ts +25 -4
  219. package/src/services/interfaces/IEventService.ts +1 -0
  220. package/src/utils/file-reference.ts +9 -0
  221. package/dist/chunk-2W4WKJVF.js.map +0 -1
  222. package/dist/chunk-3J5N2T2N.js.map +0 -1
  223. package/dist/chunk-7GBEBJLR.js.map +0 -1
  224. package/dist/chunk-AUXS7XSO.js.map +0 -1
  225. package/dist/chunk-HADXAZT3.js.map +0 -1
  226. package/dist/chunk-Q7APDV6H.js.map +0 -1
  227. package/dist/chunk-XRSP3H52.js.map +0 -1
  228. /package/dist/{DataTable-D3BK2FCN.js.map → DataTable-3D3BUZDV.js.map} +0 -0
  229. /package/dist/{UnifiedAuthProvider-A7I23UCN.js.map → UnifiedAuthProvider-KZZUO27W.js.map} +0 -0
  230. /package/dist/{api-PIE4JRFS.js.map → api-QPMBZZUZ.js.map} +0 -0
  231. /package/dist/{audit-65VNHEV2.js.map → audit-H4YJJF7R.js.map} +0 -0
  232. /package/dist/{chunk-EYSXQ756.js.map → chunk-7H75SHXZ.js.map} +0 -0
  233. /package/dist/{chunk-D6MEKC27.js.map → chunk-BUN7NMV7.js.map} +0 -0
  234. /package/dist/{chunk-AWK2FAUN.js.map → chunk-C5RN4TE5.js.map} +0 -0
  235. /package/dist/{chunk-EZ64QG2I.js.map → chunk-L36JW4KV.js.map} +0 -0
  236. /package/dist/{chunk-YFMENCR4.js.map → chunk-NEONKMTU.js.map} +0 -0
@@ -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"],"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 const { user, session, appName } = useUnifiedAuth();\n const { selectedOrganisation } = useOrganisations();\n\n let selectedEvent: { event_id: string } | null = null;\n try {\n const eventsContext = useEvents();\n selectedEvent = eventsContext.selectedEvent;\n } catch (error) {\n logger.debug('[useRBAC] Event provider not available, continuing without event context', error);\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 if (!user || !session) {\n resetState();\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n let appId: UUID | undefined;\n if (appName) {\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 }\n\n const scope: Scope = {\n organisationId: selectedOrganisation?.id,\n eventId: selectedEvent?.event_id || undefined,\n appId,\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 } 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?.event_id, selectedOrganisation?.id, session, user]);\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 loadRBACContext();\n }, [loadRBACContext]);\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/appNameResolver';\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 console.error(`[useResolvedScope] App \"${appName}\" exists but is inactive (is_active: ${inactiveApp.is_active})`);\n } else {\n console.error(`[useResolvedScope] App \"${appName}\" not found in rbac_apps table`);\n }\n } else if (app) {\n appId = app.id;\n }\n } catch (error) {\n console.error('[useResolvedScope] 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 console.error('[useResolvedScope] 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 console.error('[useResolvedScope] 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 console.error('[useResolvedScope] 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 { 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';\n\n/**\n * Hook to get user's permissions in a scope\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @returns Permission state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { permissions, isLoading, error } = usePermissions(userId, scope);\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(userId: UUID, scope: Scope) {\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\n // Add timeout for missing organisation context (3 seconds)\n useEffect(() => {\n if (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.organisationId.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 }, [scope.organisationId, error]);\n\n useEffect(() => {\n const fetchPermissions = 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 // Wait for organisation context to resolve\n // IMPORTANT: Don't clear existing permissions here - keep them until we have new ones\n if (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.organisationId.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 // 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 console.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, scope.organisationId, scope.eventId, scope.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 (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.organisationId.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 // 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 console.error('[usePermissions] 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, scope.organisationId, scope.eventId, scope.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 // Add timeout for missing organisation context (3 seconds)\n useEffect(() => {\n if (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.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 }, [scope.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\n const scopeKey = `${scope.organisationId}-${scope.eventId}-${scope.appId}`;\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 // Don't check permissions if scope is invalid (e.g., organisationId is null/empty)\n // Wait for organisation context to resolve\n if (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.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 const result = useCache \n ? await isPermittedCached({ userId, scope, permission, pageId })\n : await isPermitted({ userId, scope, permission, pageId });\n \n setCan(result);\n } catch (err) {\n console.error('[useCan] 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, scope.organisationId, scope.eventId, scope.appId, permission, pageId, useCache]);\n\n const refetch = useCallback(async () => {\n if (!userId) {\n setCan(false);\n setIsLoading(false);\n return;\n }\n\n // Don't check permissions if scope is invalid (e.g., organisationId is null/empty)\n if (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.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 const result = useCache \n ? await isPermittedCached({ userId, scope, permission, pageId })\n : await isPermitted({ userId, scope, 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, scope.organisationId, scope.eventId, scope.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"],"mappings":"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","names":["error","useEffect","useState","useState","useEffect","error","useState","useEffect","useCallback","useMemo","useRef","useState","useRef","useEffect","useCallback","useMemo"]}
@@ -3,7 +3,7 @@ import {
3
3
  init_useOrganisationService,
4
4
  useEventService2 as useEventService,
5
5
  useOrganisationService2 as useOrganisationService
6
- } from "./chunk-AUXS7XSO.js";
6
+ } from "./chunk-OO3V7W4H.js";
7
7
  import {
8
8
  __esm
9
9
  } from "./chunk-PLDDJCW6.js";
@@ -57,4 +57,4 @@ export {
57
57
  init_useOrganisations,
58
58
  useEvents
59
59
  };
60
- //# sourceMappingURL=chunk-EZ64QG2I.js.map
60
+ //# sourceMappingURL=chunk-L36JW4KV.js.map
@@ -1,23 +1,24 @@
1
1
  import {
2
2
  createScopeFromEvent,
3
3
  useAccessLevel,
4
- useCan
5
- } from "./chunk-HADXAZT3.js";
4
+ useCan,
5
+ useMultiplePermissions
6
+ } from "./chunk-I7JC7PTJ.js";
6
7
  import {
7
8
  OrganisationContextRequiredError,
8
9
  RBACCache,
9
10
  getRBACLogger,
10
11
  rbacCache
11
- } from "./chunk-XRSP3H52.js";
12
+ } from "./chunk-TAJRS6YB.js";
12
13
  import {
13
14
  useSecureDataAccess
14
- } from "./chunk-YFMENCR4.js";
15
+ } from "./chunk-NEONKMTU.js";
15
16
  import {
16
17
  init_UnifiedAuthProvider
17
- } from "./chunk-EYSXQ756.js";
18
+ } from "./chunk-7H75SHXZ.js";
18
19
  import {
19
20
  useUnifiedAuth
20
- } from "./chunk-AUXS7XSO.js";
21
+ } from "./chunk-OO3V7W4H.js";
21
22
  import {
22
23
  getCurrentAppName
23
24
  } from "./chunk-JCQZ6LA7.js";
@@ -460,7 +461,7 @@ var PagePermissionGuardComponent = ({
460
461
  eventId: resolvedScope.eventId || void 0
461
462
  };
462
463
  }
463
- return { organisationId: "", appId: void 0, eventId: void 0 };
464
+ return { organisationId: void 0, appId: void 0, eventId: void 0 };
464
465
  }, [resolvedScope]);
465
466
  const { can, isLoading: canIsLoading, error: canError } = useCan(
466
467
  user?.id || "",
@@ -694,7 +695,6 @@ function PermissionEnforcer({
694
695
  const { user, selectedOrganisation, selectedEvent, supabase } = useUnifiedAuth();
695
696
  const [hasChecked, setHasChecked] = useState4(false);
696
697
  const [checkError, setCheckError] = useState4(null);
697
- const [permissionResults, setPermissionResults] = useState4({});
698
698
  const [resolvedScope, setResolvedScope] = useState4(null);
699
699
  useEffect4(() => {
700
700
  const resolveScope = async () => {
@@ -735,19 +735,24 @@ function PermissionEnforcer({
735
735
  };
736
736
  resolveScope();
737
737
  }, [scope, selectedOrganisation, selectedEvent, supabase]);
738
- const representativePermission = permissions[0];
739
- const { can, isLoading, error } = useCan(
738
+ const { results: permissionResults, isLoading, error } = useMultiplePermissions(
740
739
  user?.id || "",
741
740
  resolvedScope || { eventId: selectedEvent?.event_id || void 0 },
742
- representativePermission,
743
- void 0,
741
+ permissions,
744
742
  true
745
743
  // Use cache
746
744
  );
747
745
  const hasRequiredPermissions = useMemo4(() => {
748
746
  if (permissions.length === 0) return true;
749
- return can;
750
- }, [permissions, can]);
747
+ if (!permissionResults || Object.keys(permissionResults).length === 0) {
748
+ return false;
749
+ }
750
+ if (requireAll) {
751
+ return Object.values(permissionResults).every((result) => result === true);
752
+ } else {
753
+ return Object.values(permissionResults).some((result) => result === true);
754
+ }
755
+ }, [permissions, permissionResults, requireAll]);
751
756
  useEffect4(() => {
752
757
  if (!isLoading && !error) {
753
758
  setHasChecked(true);
@@ -865,9 +870,10 @@ function RoleBasedRouter({
865
870
  currentRouteConfig?.permissions?.[0] || "read:page",
866
871
  currentRouteConfig?.pageId
867
872
  );
873
+ const isPublicRoute = currentRouteConfig?.public === true;
868
874
  const hasPermissions = currentRouteConfig?.permissions && currentRouteConfig.permissions.length > 0;
869
- const finalCanAccess = hasPermissions ? canAccessCurrentRoute : false;
870
- const finalLoading = hasPermissions ? permissionLoading : false;
875
+ const finalCanAccess = isPublicRoute ? true : hasPermissions ? canAccessCurrentRoute : false;
876
+ const finalLoading = isPublicRoute ? false : hasPermissions ? permissionLoading : false;
871
877
  const getAccessibleRoutes = useCallback5(() => {
872
878
  if (!user?.id || !currentScope) return [];
873
879
  return routes.filter((route) => canAccessRoute(route.path));
@@ -930,10 +936,10 @@ function RoleBasedRouter({
930
936
  }
931
937
  const allowed = finalCanAccess;
932
938
  recordRouteAccess(currentPath, allowed, currentRouteConfig);
933
- if (!allowed) {
939
+ if (!allowed && !isPublicRoute) {
934
940
  navigate(fallbackRoute, { replace: true });
935
941
  }
936
- }, [location.pathname, currentRouteConfig, canAccessCurrentRoute, recordRouteAccess, strictMode, user?.id, currentScope, onStrictModeViolation, navigate, fallbackRoute]);
942
+ }, [location.pathname, currentRouteConfig, canAccessCurrentRoute, recordRouteAccess, strictMode, user?.id, currentScope, onStrictModeViolation, navigate, fallbackRoute, isPublicRoute]);
937
943
  const contextValue = useMemo5(() => ({
938
944
  getAccessibleRoutes,
939
945
  canAccessRoute,
@@ -951,13 +957,13 @@ function RoleBasedRouter({
951
957
  strictMode,
952
958
  auditLog
953
959
  ]);
954
- if (finalLoading) {
960
+ if (finalLoading && !isPublicRoute) {
955
961
  return /* @__PURE__ */ jsx5("div", { className: "flex items-center justify-center min-h-screen", children: /* @__PURE__ */ jsxs3("div", { className: "text-center", children: [
956
962
  /* @__PURE__ */ jsx5("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-main-600 mx-auto mb-4" }),
957
963
  /* @__PURE__ */ jsx5("p", { className: "text-sec-600", children: "Checking permissions..." })
958
964
  ] }) });
959
965
  }
960
- if (currentRouteConfig && !finalCanAccess) {
966
+ if (currentRouteConfig && !finalCanAccess && !isPublicRoute) {
961
967
  return /* @__PURE__ */ jsx5(
962
968
  UnauthorizedComponent,
963
969
  {
@@ -1178,19 +1184,21 @@ function NavigationGuard({
1178
1184
  };
1179
1185
  resolveScope();
1180
1186
  }, [scope, selectedOrganisation, selectedEvent, supabase]);
1181
- const representativePermission = navigationItem.permissions[0];
1182
- const { can, isLoading, error } = useCan(
1187
+ const { results: permissionResults, isLoading, error } = useMultiplePermissions(
1183
1188
  user?.id || "",
1184
1189
  resolvedScope || { eventId: selectedEvent?.event_id || void 0 },
1185
- representativePermission,
1186
- navigationItem.pageId,
1190
+ navigationItem.permissions || [],
1187
1191
  true
1188
1192
  // Use cache
1189
1193
  );
1190
1194
  const hasRequiredPermissions = useMemo7(() => {
1191
- if (navigationItem.permissions.length === 0) return true;
1192
- return can;
1193
- }, [navigationItem.permissions, can]);
1195
+ if (!navigationItem.permissions || navigationItem.permissions.length === 0) return true;
1196
+ if (requireAll) {
1197
+ return Object.values(permissionResults).every((result) => result === true);
1198
+ } else {
1199
+ return Object.values(permissionResults).some((result) => result === true);
1200
+ }
1201
+ }, [navigationItem.permissions, permissionResults, requireAll]);
1194
1202
  useEffect7(() => {
1195
1203
  if (!isLoading && !error) {
1196
1204
  setHasChecked(true);
@@ -1531,7 +1539,7 @@ function withPermissionGuard(config, handler) {
1531
1539
  if (!userId || !organisationId) {
1532
1540
  throw new Error("User context required for permission check");
1533
1541
  }
1534
- const { isPermitted: isPermitted2 } = await import("./api-PIE4JRFS.js");
1542
+ const { isPermitted: isPermitted2 } = await import("./api-QPMBZZUZ.js");
1535
1543
  const hasPermission2 = await isPermitted2({
1536
1544
  userId,
1537
1545
  scope: { organisationId, eventId, appId },
@@ -1554,7 +1562,7 @@ function withAccessLevelGuard(minLevel, handler) {
1554
1562
  if (!userId || !organisationId) {
1555
1563
  throw new Error("User context required for access level check");
1556
1564
  }
1557
- const { getAccessLevel: getAccessLevel2 } = await import("./api-PIE4JRFS.js");
1565
+ const { getAccessLevel: getAccessLevel2 } = await import("./api-QPMBZZUZ.js");
1558
1566
  const accessLevel = await getAccessLevel2({
1559
1567
  userId,
1560
1568
  scope: { organisationId, eventId, appId }
@@ -1579,11 +1587,11 @@ function withRoleGuard(config, handler) {
1579
1587
  throw new Error("User context required for role check");
1580
1588
  }
1581
1589
  if (config.globalRoles && config.globalRoles.length > 0) {
1582
- const { isSuperAdmin } = await import("./api-PIE4JRFS.js");
1590
+ const { isSuperAdmin } = await import("./api-QPMBZZUZ.js");
1583
1591
  const isSuper = await isSuperAdmin(userId);
1584
1592
  if (isSuper) {
1585
1593
  if (organisationId) {
1586
- const { emitAuditEvent: emitAuditEvent2 } = await import("./audit-65VNHEV2.js");
1594
+ const { emitAuditEvent: emitAuditEvent2 } = await import("./audit-H4YJJF7R.js");
1587
1595
  await emitAuditEvent2({
1588
1596
  type: "permission_check",
1589
1597
  userId,
@@ -1605,21 +1613,21 @@ function withRoleGuard(config, handler) {
1605
1613
  }
1606
1614
  }
1607
1615
  if (config.organisationRoles && config.organisationRoles.length > 0) {
1608
- const { isOrganisationAdmin } = await import("./api-PIE4JRFS.js");
1616
+ const { isOrganisationAdmin } = await import("./api-QPMBZZUZ.js");
1609
1617
  const isOrgAdmin = await isOrganisationAdmin(userId, organisationId);
1610
1618
  if (!isOrgAdmin && config.requireAll !== false) {
1611
1619
  throw new Error(`Organisation admin role required`);
1612
1620
  }
1613
1621
  }
1614
1622
  if (config.eventAppRoles && config.eventAppRoles.length > 0 && eventId && appId) {
1615
- const { isEventAdmin } = await import("./api-PIE4JRFS.js");
1623
+ const { isEventAdmin } = await import("./api-QPMBZZUZ.js");
1616
1624
  const isEventAdminUser = await isEventAdmin(userId, { organisationId, eventId, appId });
1617
1625
  if (!isEventAdminUser && config.requireAll !== false) {
1618
1626
  throw new Error(`Event admin role required`);
1619
1627
  }
1620
1628
  }
1621
1629
  if (organisationId) {
1622
- const { emitAuditEvent: emitAuditEvent2 } = await import("./audit-65VNHEV2.js");
1630
+ const { emitAuditEvent: emitAuditEvent2 } = await import("./audit-H4YJJF7R.js");
1623
1631
  await emitAuditEvent2({
1624
1632
  type: "permission_check",
1625
1633
  userId,
@@ -1652,7 +1660,7 @@ function createRBACMiddleware(config) {
1652
1660
  );
1653
1661
  if (protectedRoute) {
1654
1662
  try {
1655
- const { isPermitted: isPermitted2 } = await import("./api-PIE4JRFS.js");
1663
+ const { isPermitted: isPermitted2 } = await import("./api-QPMBZZUZ.js");
1656
1664
  const hasPermission2 = await isPermitted2({
1657
1665
  userId,
1658
1666
  scope: { organisationId },
@@ -1679,7 +1687,7 @@ function createRBACExpressMiddleware(config) {
1679
1687
  return res.status(401).json({ error: "User context required" });
1680
1688
  }
1681
1689
  try {
1682
- const { isPermitted: isPermitted2 } = await import("./api-PIE4JRFS.js");
1690
+ const { isPermitted: isPermitted2 } = await import("./api-QPMBZZUZ.js");
1683
1691
  const hasPermission2 = await isPermitted2({
1684
1692
  userId,
1685
1693
  scope: { organisationId, eventId, appId },
@@ -1852,4 +1860,4 @@ export {
1852
1860
  getPermissionsForRole,
1853
1861
  ALL_PERMISSIONS
1854
1862
  };
1855
- //# sourceMappingURL=chunk-7GBEBJLR.js.map
1863
+ //# sourceMappingURL=chunk-MNSGWRPB.js.map