@jmruthers/pace-core 0.6.1 → 0.6.3

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 (549) hide show
  1. package/CHANGELOG.md +88 -10
  2. package/cursor-rules/00-pace-core-compliance.mdc +46 -87
  3. package/cursor-rules/01-standards-compliance.mdc +16 -47
  4. package/cursor-rules/02-project-structure.mdc +4 -4
  5. package/cursor-rules/03-solid-principles.mdc +45 -164
  6. package/cursor-rules/04-testing-standards.mdc +22 -69
  7. package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
  8. package/cursor-rules/06-code-quality.mdc +42 -125
  9. package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
  10. package/cursor-rules/08-markup-quality.mdc +452 -0
  11. package/cursor-rules/CHANGELOG.md +18 -0
  12. package/cursor-rules/README.md +2 -1
  13. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-Cb34EQs3.d.ts} +63 -1
  14. package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
  15. package/dist/{DataTable-DQ7RSOHE.js → DataTable-THFPBKTP.js} +12 -10
  16. package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DEMpysFR.d.ts} +394 -171
  17. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +30 -8
  18. package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
  19. package/dist/{api-N774RPUA.js → api-IAGWF3ZG.js} +10 -10
  20. package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
  21. package/dist/{chunk-JBKQ3SAO.js → chunk-2T2IG7T7.js} +107 -57
  22. package/dist/chunk-2T2IG7T7.js.map +1 -0
  23. package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
  24. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  25. package/dist/{chunk-3XTALGJF.js → chunk-6Z7LTB3D.js} +69 -240
  26. package/dist/chunk-6Z7LTB3D.js.map +1 -0
  27. package/dist/{chunk-4ZC4GX36.js → chunk-CNCQDFLN.js} +199 -46
  28. package/dist/chunk-CNCQDFLN.js.map +1 -0
  29. package/dist/chunk-DGUM43GV.js +11 -0
  30. package/dist/{chunk-BYFSK72L.js → chunk-DWUBLJJM.js} +361 -187
  31. package/dist/chunk-DWUBLJJM.js.map +1 -0
  32. package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
  33. package/dist/chunk-FFQEQTNW.js.map +1 -0
  34. package/dist/chunk-FMUCXFII.js +76 -0
  35. package/dist/chunk-FMUCXFII.js.map +1 -0
  36. package/dist/{chunk-4N5C5XZU.js → chunk-HFZBI76P.js} +4 -4
  37. package/dist/chunk-HFZBI76P.js.map +1 -0
  38. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  39. package/dist/chunk-L4OXEN46.js.map +1 -0
  40. package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
  41. package/dist/chunk-M43Y4SSO.js.map +1 -0
  42. package/dist/{chunk-I7PSE6JW.js → chunk-M7MPQISP.js} +3 -76
  43. package/dist/chunk-M7MPQISP.js.map +1 -0
  44. package/dist/chunk-PQBSKX33.js +7793 -0
  45. package/dist/chunk-PQBSKX33.js.map +1 -0
  46. package/dist/chunk-QRPVRXYT.js +226 -0
  47. package/dist/chunk-QRPVRXYT.js.map +1 -0
  48. package/dist/{chunk-KNC55RTG.js → chunk-RWEBCB47.js} +194 -416
  49. package/dist/chunk-RWEBCB47.js.map +1 -0
  50. package/dist/{chunk-XM25TVIE.js → chunk-YDQHOZNA.js} +843 -388
  51. package/dist/chunk-YDQHOZNA.js.map +1 -0
  52. package/dist/{chunk-GLK6VM3F.js → chunk-ZNIWI3UC.js} +739 -737
  53. package/dist/chunk-ZNIWI3UC.js.map +1 -0
  54. package/dist/components.d.ts +5 -5
  55. package/dist/components.js +18 -16
  56. package/dist/components.js.map +1 -1
  57. package/dist/contextValidator-3JNZKUTX.js +9 -0
  58. package/dist/contextValidator-3JNZKUTX.js.map +1 -0
  59. package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
  60. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  61. package/dist/hooks.d.ts +55 -122
  62. package/dist/hooks.js +10 -13
  63. package/dist/hooks.js.map +1 -1
  64. package/dist/index.d.ts +60 -13
  65. package/dist/index.js +30 -25
  66. package/dist/index.js.map +1 -1
  67. package/dist/providers.d.ts +21 -3
  68. package/dist/providers.js +4 -3
  69. package/dist/rbac/index.d.ts +210 -139
  70. package/dist/rbac/index.js +17 -13
  71. package/dist/styles/index.js +1 -1
  72. package/dist/theming/runtime.d.ts +1 -13
  73. package/dist/theming/runtime.js +2 -2
  74. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  75. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  76. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  77. package/dist/types.d.ts +2 -2
  78. package/dist/types.js +1 -1
  79. package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +38 -17
  80. package/dist/utils.d.ts +4 -5
  81. package/dist/utils.js +17 -19
  82. package/dist/utils.js.map +1 -1
  83. package/docs/api/README.md +21 -17
  84. package/docs/api/modules.md +4191 -2967
  85. package/docs/architecture/database-schema-requirements.md +161 -0
  86. package/docs/components/context-selector.md +126 -0
  87. package/docs/core-concepts/rbac-system.md +3 -3
  88. package/docs/documentation-index.md +2 -4
  89. package/docs/getting-started/cursor-rules.md +2 -1
  90. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  91. package/docs/migration/MIGRATION_GUIDE.md +2 -24
  92. package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
  93. package/docs/migration/README.md +52 -6
  94. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  95. package/docs/migration/database-changes-december-2025.md +3 -3
  96. package/docs/pace-mint-fix-auto-selection.md +218 -0
  97. package/docs/pace-mint-rbac-setup.md +391 -0
  98. package/docs/rbac/event-based-apps.md +1 -1
  99. package/docs/rbac/getting-started.md +1 -1
  100. package/docs/rbac/quick-start.md +1 -1
  101. package/docs/rbac/secure-client-protection.md +330 -0
  102. package/docs/standards/README.md +1 -0
  103. package/package.json +4 -3
  104. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  105. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  106. package/scripts/audit/core/checks/bundle.cjs +142 -0
  107. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +784 -685
  108. package/scripts/audit/core/checks/config.cjs +54 -0
  109. package/scripts/audit/core/checks/coverage.cjs +84 -0
  110. package/scripts/audit/core/checks/dependencies.cjs +985 -0
  111. package/scripts/audit/core/checks/documentation.cjs +268 -0
  112. package/scripts/audit/core/checks/environment.cjs +116 -0
  113. package/scripts/audit/core/checks/error-handling.cjs +340 -0
  114. package/scripts/audit/core/checks/forms.cjs +172 -0
  115. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  116. package/scripts/audit/core/checks/hooks.cjs +334 -0
  117. package/scripts/audit/core/checks/imports.cjs +244 -0
  118. package/scripts/audit/core/checks/performance.cjs +325 -0
  119. package/scripts/audit/core/checks/routes.cjs +117 -0
  120. package/scripts/audit/core/checks/state.cjs +130 -0
  121. package/scripts/audit/core/checks/structure.cjs +65 -0
  122. package/scripts/audit/core/checks/style.cjs +584 -0
  123. package/scripts/audit/core/checks/testing.cjs +122 -0
  124. package/scripts/audit/core/checks/typescript.cjs +61 -0
  125. package/scripts/audit/core/scanner.cjs +199 -0
  126. package/scripts/audit/core/utils.cjs +137 -0
  127. package/scripts/audit/index.cjs +223 -0
  128. package/scripts/audit/reporters/console.cjs +151 -0
  129. package/scripts/audit/reporters/json.cjs +54 -0
  130. package/scripts/audit/reporters/markdown.cjs +124 -0
  131. package/scripts/audit-consuming-app.cjs +61 -936
  132. package/scripts/build-docs/build-decision.js +240 -0
  133. package/scripts/build-docs/cache-utils.js +105 -0
  134. package/scripts/build-docs/content-normalization.js +150 -0
  135. package/scripts/build-docs/file-utils.js +105 -0
  136. package/scripts/build-docs/git-utils.js +86 -0
  137. package/scripts/build-docs/hash-utils.js +116 -0
  138. package/scripts/build-docs/typedoc-runner.js +220 -0
  139. package/scripts/build-docs-incremental.js +77 -913
  140. package/scripts/utils/command-runner.js +16 -11
  141. package/scripts/validate-formats.js +61 -56
  142. package/scripts/validate-master.js +74 -69
  143. package/scripts/validate-pre-publish.js +70 -65
  144. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  145. package/src/components/Alert/Alert.test.tsx +12 -18
  146. package/src/components/Alert/Alert.tsx +5 -7
  147. package/src/components/Avatar/Avatar.test.tsx +4 -4
  148. package/src/components/Badge/Badge.tsx +14 -0
  149. package/src/components/Button/Button.tsx +22 -0
  150. package/src/components/Calendar/Calendar.tsx +8 -2
  151. package/src/components/Card/Card.tsx +4 -0
  152. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  153. package/src/components/Checkbox/Checkbox.tsx +2 -2
  154. package/src/components/ContextSelector/ContextSelector.tsx +384 -0
  155. package/src/components/ContextSelector/index.ts +3 -0
  156. package/src/components/DataTable/DataTable.tsx +38 -4
  157. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  158. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
  159. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  160. package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
  161. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  162. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  163. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  164. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  165. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  166. package/src/components/DataTable/components/DataTableCore.tsx +196 -554
  167. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  168. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  169. package/src/components/DataTable/components/DataTableModals.tsx +8 -0
  170. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  171. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  172. package/src/components/DataTable/components/EditFields.tsx +307 -0
  173. package/src/components/DataTable/components/EditableRow.tsx +8 -0
  174. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  175. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  176. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  177. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  178. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  179. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  180. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  181. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  182. package/src/components/DataTable/components/UnifiedTableBody.tsx +63 -851
  183. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  184. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  185. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  186. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  187. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  188. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  189. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  190. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  191. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  192. package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
  193. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  194. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  195. package/src/components/DataTable/hooks/useDataTablePermissions.ts +127 -33
  196. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  197. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  198. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  199. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  200. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  201. package/src/components/DataTable/styles.ts +6 -6
  202. package/src/components/DataTable/types.ts +6 -10
  203. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  204. package/src/components/DataTable/utils/debugTools.ts +18 -113
  205. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  206. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  207. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  208. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  209. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  210. package/src/components/Dialog/Dialog.tsx +31 -3
  211. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  212. package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
  213. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  214. package/src/components/ErrorBoundary/index.ts +27 -2
  215. package/src/components/FileDisplay/FileDisplay.tsx +74 -28
  216. package/src/components/FileUpload/FileUpload.tsx +22 -2
  217. package/src/components/Footer/Footer.test.tsx +16 -16
  218. package/src/components/Footer/Footer.tsx +14 -11
  219. package/src/components/Form/Form.tsx +1 -0
  220. package/src/components/Header/Header.test.tsx +43 -73
  221. package/src/components/Header/Header.tsx +59 -49
  222. package/src/components/Input/Input.test.tsx +2 -2
  223. package/src/components/Input/Input.tsx +8 -4
  224. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  225. package/src/components/LoginForm/LoginForm.tsx +4 -0
  226. package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
  227. package/src/components/NavigationMenu/types.ts +56 -0
  228. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  229. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
  230. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
  231. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
  232. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +13 -11
  233. package/src/components/PaceAppLayout/PaceAppLayout.tsx +167 -44
  234. package/src/components/PaceAppLayout/README.md +14 -17
  235. package/src/components/PaceAppLayout/test-setup.tsx +3 -4
  236. package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
  237. package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
  238. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  239. package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
  240. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  241. package/src/components/Select/Select.tsx +80 -434
  242. package/src/components/Select/context.ts +23 -0
  243. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  244. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  245. package/src/components/Select/hooks/useSelectState.ts +104 -0
  246. package/src/components/Select/index.ts +9 -1
  247. package/src/components/Select/types.ts +123 -0
  248. package/src/components/Select/utils/text.ts +26 -0
  249. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
  250. package/src/components/Switch/Switch.tsx +4 -4
  251. package/src/components/Tabs/Tabs.tsx +1 -1
  252. package/src/components/Toast/Toast.tsx +4 -0
  253. package/src/components/Tooltip/Tooltip.tsx +2 -2
  254. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  255. package/src/components/UserMenu/UserMenu.tsx +21 -18
  256. package/src/components/index.ts +7 -7
  257. package/src/eslint-rules/pace-core-compliance.cjs +106 -0
  258. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  259. package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
  260. package/src/hooks/index.ts +1 -2
  261. package/src/hooks/public/usePublicEvent.ts +4 -0
  262. package/src/hooks/public/usePublicEventLogo.ts +4 -0
  263. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  264. package/src/hooks/public/usePublicRouteParams.ts +4 -0
  265. package/src/hooks/services/useAuth.ts +32 -0
  266. package/src/hooks/services/useCurrentEvent.ts +6 -0
  267. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  268. package/src/hooks/useAppConfig.ts +15 -30
  269. package/src/hooks/useDebounce.ts +9 -0
  270. package/src/hooks/useEventTheme.ts +6 -0
  271. package/src/hooks/useFileDisplay.ts +81 -50
  272. package/src/hooks/useFileReference.ts +25 -7
  273. package/src/hooks/useFileUrl.ts +11 -1
  274. package/src/hooks/useFocusManagement.ts +14 -0
  275. package/src/hooks/useFocusTrap.ts +3 -0
  276. package/src/hooks/useInactivityTracker.ts +3 -0
  277. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  278. package/src/hooks/useOrganisationPermissions.ts +4 -0
  279. package/src/hooks/useOrganisationSecurity.ts +4 -0
  280. package/src/hooks/usePerformanceMonitor.ts +4 -0
  281. package/src/hooks/usePermissionCache.ts +7 -0
  282. package/src/hooks/useQueryCache.ts +12 -1
  283. package/src/hooks/useSessionRestoration.ts +4 -0
  284. package/src/hooks/useStorage.ts +4 -0
  285. package/src/hooks/useToast.ts +1 -1
  286. package/src/index.ts +6 -6
  287. package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
  288. package/src/providers/services/AuthServiceProvider.tsx +35 -7
  289. package/src/providers/services/EventServiceProvider.tsx +51 -5
  290. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  291. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  292. package/src/providers/services/UnifiedAuthProvider.tsx +126 -134
  293. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
  294. package/src/rbac/README.md +1 -1
  295. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
  296. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  297. package/src/rbac/adapters.tsx +12 -3
  298. package/src/rbac/api.test.ts +59 -51
  299. package/src/rbac/api.ts +246 -167
  300. package/src/rbac/components/NavigationProvider.tsx +4 -1
  301. package/src/rbac/components/PagePermissionGuard.tsx +185 -17
  302. package/src/rbac/components/RoleBasedRouter.tsx +5 -1
  303. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  304. package/src/rbac/components/SecureDataProvider.tsx +20 -5
  305. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  306. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  307. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  308. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  309. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  310. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  311. package/src/rbac/engine.ts +38 -14
  312. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
  313. package/src/rbac/hooks/permissions/index.ts +7 -0
  314. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  315. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  316. package/src/rbac/hooks/permissions/useCan.ts +377 -0
  317. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  318. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  319. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  320. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  321. package/src/rbac/hooks/useCan.test.ts +64 -66
  322. package/src/rbac/hooks/usePermissions.ts +14 -995
  323. package/src/rbac/hooks/useRBAC.test.ts +1 -5
  324. package/src/rbac/hooks/useRBAC.ts +36 -37
  325. package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
  326. package/src/rbac/hooks/useResolvedScope.ts +35 -40
  327. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  328. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  329. package/src/rbac/hooks/useSecureSupabase.ts +27 -7
  330. package/src/rbac/index.ts +7 -0
  331. package/src/rbac/permissions.ts +0 -30
  332. package/src/rbac/secureClient.test.ts +22 -18
  333. package/src/rbac/secureClient.ts +294 -68
  334. package/src/rbac/security.ts +0 -17
  335. package/src/rbac/types.ts +9 -0
  336. package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
  337. package/src/rbac/utils/clientSecurity.ts +93 -0
  338. package/src/rbac/utils/contextValidator.ts +77 -168
  339. package/src/services/AuthService.ts +39 -7
  340. package/src/services/EventService.ts +186 -54
  341. package/src/services/OrganisationService.ts +81 -14
  342. package/src/services/__tests__/EventService.test.ts +1 -2
  343. package/src/services/base/BaseService.ts +3 -0
  344. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  345. package/src/theming/parseEventColours.ts +5 -19
  346. package/src/types/vitest-globals.d.ts +51 -26
  347. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  348. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  349. package/src/utils/__tests__/index.unit.test.ts +2 -2
  350. package/src/utils/audit/audit.ts +0 -3
  351. package/src/utils/core/cn.ts +1 -1
  352. package/src/utils/dynamic/dynamicUtils.ts +7 -4
  353. package/src/utils/file-reference/index.ts +53 -1
  354. package/src/utils/formatting/formatting.ts +8 -18
  355. package/src/utils/index.ts +0 -1
  356. package/dist/chunk-3QRJFVBR.js.map +0 -1
  357. package/dist/chunk-3XTALGJF.js.map +0 -1
  358. package/dist/chunk-4N5C5XZU.js.map +0 -1
  359. package/dist/chunk-4ZC4GX36.js.map +0 -1
  360. package/dist/chunk-7D4SUZUM.js +0 -38
  361. package/dist/chunk-BYFSK72L.js.map +0 -1
  362. package/dist/chunk-EXUD6RNJ.js +0 -451
  363. package/dist/chunk-EXUD6RNJ.js.map +0 -1
  364. package/dist/chunk-GLK6VM3F.js.map +0 -1
  365. package/dist/chunk-I7PSE6JW.js.map +0 -1
  366. package/dist/chunk-JBKQ3SAO.js.map +0 -1
  367. package/dist/chunk-KNC55RTG.js.map +0 -1
  368. package/dist/chunk-LXQLPRQ2.js.map +0 -1
  369. package/dist/chunk-R77UEZ4E.js.map +0 -1
  370. package/dist/chunk-SQGMNID3.js.map +0 -1
  371. package/dist/chunk-T33XF5ZC.js +0 -12922
  372. package/dist/chunk-T33XF5ZC.js.map +0 -1
  373. package/dist/chunk-XM25TVIE.js.map +0 -1
  374. package/docs/api/classes/ColumnFactory.md +0 -243
  375. package/docs/api/classes/ErrorBoundary.md +0 -144
  376. package/docs/api/classes/InvalidScopeError.md +0 -73
  377. package/docs/api/classes/Logger.md +0 -178
  378. package/docs/api/classes/MissingUserContextError.md +0 -66
  379. package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
  380. package/docs/api/classes/PermissionDeniedError.md +0 -73
  381. package/docs/api/classes/RBACAuditManager.md +0 -297
  382. package/docs/api/classes/RBACCache.md +0 -322
  383. package/docs/api/classes/RBACEngine.md +0 -171
  384. package/docs/api/classes/RBACError.md +0 -76
  385. package/docs/api/classes/RBACNotInitializedError.md +0 -66
  386. package/docs/api/classes/SecureSupabaseClient.md +0 -160
  387. package/docs/api/classes/StorageUtils.md +0 -328
  388. package/docs/api/enums/FileCategory.md +0 -184
  389. package/docs/api/enums/LogLevel.md +0 -54
  390. package/docs/api/enums/RBACErrorCode.md +0 -228
  391. package/docs/api/enums/RPCFunction.md +0 -118
  392. package/docs/api/interfaces/AddressFieldProps.md +0 -241
  393. package/docs/api/interfaces/AddressFieldRef.md +0 -94
  394. package/docs/api/interfaces/AggregateConfig.md +0 -43
  395. package/docs/api/interfaces/AutocompleteOptions.md +0 -75
  396. package/docs/api/interfaces/AvatarProps.md +0 -128
  397. package/docs/api/interfaces/BadgeProps.md +0 -27
  398. package/docs/api/interfaces/ButtonProps.md +0 -53
  399. package/docs/api/interfaces/CalendarProps.md +0 -70
  400. package/docs/api/interfaces/CardProps.md +0 -66
  401. package/docs/api/interfaces/ColorPalette.md +0 -7
  402. package/docs/api/interfaces/ColorShade.md +0 -66
  403. package/docs/api/interfaces/ComplianceResult.md +0 -30
  404. package/docs/api/interfaces/DataAccessRecord.md +0 -96
  405. package/docs/api/interfaces/DataRecord.md +0 -11
  406. package/docs/api/interfaces/DataTableAction.md +0 -249
  407. package/docs/api/interfaces/DataTableColumn.md +0 -504
  408. package/docs/api/interfaces/DataTableProps.md +0 -625
  409. package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
  410. package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
  411. package/docs/api/interfaces/DatabaseIssue.md +0 -41
  412. package/docs/api/interfaces/EmptyStateConfig.md +0 -61
  413. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
  414. package/docs/api/interfaces/EventAppRoleData.md +0 -71
  415. package/docs/api/interfaces/ExportColumn.md +0 -90
  416. package/docs/api/interfaces/ExportOptions.md +0 -126
  417. package/docs/api/interfaces/FileDisplayProps.md +0 -249
  418. package/docs/api/interfaces/FileMetadata.md +0 -129
  419. package/docs/api/interfaces/FileReference.md +0 -118
  420. package/docs/api/interfaces/FileSizeLimits.md +0 -7
  421. package/docs/api/interfaces/FileUploadOptions.md +0 -139
  422. package/docs/api/interfaces/FileUploadProps.md +0 -293
  423. package/docs/api/interfaces/FooterProps.md +0 -105
  424. package/docs/api/interfaces/FormFieldProps.md +0 -166
  425. package/docs/api/interfaces/FormProps.md +0 -113
  426. package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
  427. package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
  428. package/docs/api/interfaces/InputProps.md +0 -53
  429. package/docs/api/interfaces/LabelProps.md +0 -107
  430. package/docs/api/interfaces/LoggerConfig.md +0 -62
  431. package/docs/api/interfaces/LoginFormProps.md +0 -184
  432. package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
  433. package/docs/api/interfaces/NavigationContextType.md +0 -164
  434. package/docs/api/interfaces/NavigationGuardProps.md +0 -139
  435. package/docs/api/interfaces/NavigationItem.md +0 -120
  436. package/docs/api/interfaces/NavigationMenuProps.md +0 -221
  437. package/docs/api/interfaces/NavigationProviderProps.md +0 -117
  438. package/docs/api/interfaces/Organisation.md +0 -140
  439. package/docs/api/interfaces/OrganisationContextType.md +0 -388
  440. package/docs/api/interfaces/OrganisationMembership.md +0 -140
  441. package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
  442. package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
  443. package/docs/api/interfaces/PaceAppLayoutProps.md +0 -406
  444. package/docs/api/interfaces/PaceLoginPageProps.md +0 -47
  445. package/docs/api/interfaces/PageAccessRecord.md +0 -85
  446. package/docs/api/interfaces/PagePermissionContextType.md +0 -140
  447. package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
  448. package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
  449. package/docs/api/interfaces/PaletteData.md +0 -41
  450. package/docs/api/interfaces/ParsedAddress.md +0 -120
  451. package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
  452. package/docs/api/interfaces/ProgressProps.md +0 -42
  453. package/docs/api/interfaces/ProtectedRouteProps.md +0 -97
  454. package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
  455. package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
  456. package/docs/api/interfaces/PublicPageLayoutProps.md +0 -198
  457. package/docs/api/interfaces/QuickFix.md +0 -52
  458. package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
  459. package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
  460. package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
  461. package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
  462. package/docs/api/interfaces/RBACConfig.md +0 -133
  463. package/docs/api/interfaces/RBACContext.md +0 -52
  464. package/docs/api/interfaces/RBACLogger.md +0 -112
  465. package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
  466. package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
  467. package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
  468. package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
  469. package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
  470. package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
  471. package/docs/api/interfaces/RBACResult.md +0 -58
  472. package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
  473. package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
  474. package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
  475. package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
  476. package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
  477. package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
  478. package/docs/api/interfaces/RBACRolesListParams.md +0 -52
  479. package/docs/api/interfaces/RBACRolesListResult.md +0 -74
  480. package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
  481. package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
  482. package/docs/api/interfaces/ResourcePermissions.md +0 -155
  483. package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
  484. package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
  485. package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
  486. package/docs/api/interfaces/RoleManagementResult.md +0 -52
  487. package/docs/api/interfaces/RouteAccessRecord.md +0 -107
  488. package/docs/api/interfaces/RouteConfig.md +0 -134
  489. package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
  490. package/docs/api/interfaces/SecureDataContextType.md +0 -168
  491. package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
  492. package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
  493. package/docs/api/interfaces/SetupIssue.md +0 -41
  494. package/docs/api/interfaces/StorageConfig.md +0 -41
  495. package/docs/api/interfaces/StorageFileInfo.md +0 -74
  496. package/docs/api/interfaces/StorageFileMetadata.md +0 -151
  497. package/docs/api/interfaces/StorageListOptions.md +0 -99
  498. package/docs/api/interfaces/StorageListResult.md +0 -41
  499. package/docs/api/interfaces/StorageUploadOptions.md +0 -101
  500. package/docs/api/interfaces/StorageUploadResult.md +0 -63
  501. package/docs/api/interfaces/StorageUrlOptions.md +0 -60
  502. package/docs/api/interfaces/StyleImport.md +0 -19
  503. package/docs/api/interfaces/SwitchProps.md +0 -34
  504. package/docs/api/interfaces/TabsContentProps.md +0 -9
  505. package/docs/api/interfaces/TabsListProps.md +0 -9
  506. package/docs/api/interfaces/TabsProps.md +0 -9
  507. package/docs/api/interfaces/TabsTriggerProps.md +0 -50
  508. package/docs/api/interfaces/TextareaProps.md +0 -53
  509. package/docs/api/interfaces/ToastActionElement.md +0 -9
  510. package/docs/api/interfaces/ToastProps.md +0 -9
  511. package/docs/api/interfaces/UnifiedAuthContextType.md +0 -820
  512. package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -171
  513. package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
  514. package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
  515. package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -136
  516. package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
  517. package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
  518. package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -81
  519. package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
  520. package/docs/api/interfaces/UsePublicEventReturn.md +0 -68
  521. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
  522. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -120
  523. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -94
  524. package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
  525. package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
  526. package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
  527. package/docs/api/interfaces/UserEventAccess.md +0 -118
  528. package/docs/api/interfaces/UserMenuProps.md +0 -86
  529. package/docs/api/interfaces/UserProfile.md +0 -63
  530. package/docs/migration/quick-migration-guide.md +0 -356
  531. package/docs/migration/service-architecture.md +0 -281
  532. package/src/components/EventSelector/EventSelector.test.tsx +0 -720
  533. package/src/components/EventSelector/EventSelector.tsx +0 -420
  534. package/src/components/EventSelector/index.ts +0 -3
  535. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
  536. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -324
  537. package/src/components/OrganisationSelector/index.ts +0 -9
  538. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  539. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  540. package/src/hooks/useSecureDataAccess.ts +0 -681
  541. /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-THFPBKTP.js.map} +0 -0
  542. /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
  543. /package/dist/{api-N774RPUA.js.map → api-IAGWF3ZG.js.map} +0 -0
  544. /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
  545. /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
  546. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  547. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  548. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  549. /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
@@ -141,17 +141,87 @@ const PagePermissionGuardComponent = ({
141
141
  const { user, selectedOrganisation, selectedEvent, supabase, appId: contextAppId, appName } = useUnifiedAuth();
142
142
 
143
143
  const [hasChecked, setHasChecked] = useState(false);
144
+ const hasLoggedSuperAdminRef = useRef(false);
145
+
146
+ // Determine the page ID for permission checking
147
+ const effectivePageId = useMemo((): string => {
148
+ return pageId || pageName;
149
+ }, [pageId, pageName]);
150
+
151
+ // Check super admin status directly - don't rely on useRBAC which might not be loaded yet
152
+ // This ensures super admins get immediate access without waiting for RBAC context to load
153
+ const [isSuperAdmin, setIsSuperAdmin] = useState<boolean | null>(null);
154
+
155
+ useEffect(() => {
156
+ if (!user?.id) {
157
+ setIsSuperAdmin(false);
158
+ return;
159
+ }
160
+
161
+ let cancelled = false;
162
+ const checkSuperAdmin = async () => {
163
+ const startTime = Date.now();
164
+ try {
165
+ const { isSuperAdmin: checkSuperAdmin } = await import('../api');
166
+
167
+ // Add timeout to prevent hanging
168
+ const timeoutPromise = new Promise<boolean>((_, reject) => {
169
+ setTimeout(() => reject(new Error('Super admin check timeout')), 10000);
170
+ });
171
+
172
+ const isSuper = await Promise.race([
173
+ checkSuperAdmin(user.id),
174
+ timeoutPromise
175
+ ]);
176
+
177
+ const elapsed = Date.now() - startTime;
178
+
179
+ if (!cancelled) {
180
+ setIsSuperAdmin(isSuper);
181
+ if (process.env.NODE_ENV === 'development') {
182
+ console.log('[PagePermissionGuard] Super admin check completed', {
183
+ userId: user.id,
184
+ isSuperAdmin: isSuper,
185
+ elapsedMs: elapsed
186
+ });
187
+ }
188
+ }
189
+ } catch (err) {
190
+ const elapsed = Date.now() - startTime;
191
+ if (!cancelled) {
192
+ console.error('[PagePermissionGuard] Error checking super admin', {
193
+ error: err,
194
+ userId: user.id,
195
+ elapsedMs: elapsed
196
+ });
197
+ // On timeout or error, assume not super admin (fail secure)
198
+ // But log it so we can debug
199
+ setIsSuperAdmin(false);
200
+ }
201
+ }
202
+ };
203
+
204
+ checkSuperAdmin();
205
+ return () => {
206
+ cancelled = true;
207
+ };
208
+ }, [user?.id]);
144
209
 
145
210
  // Use useResolvedScope hook for consistent scope resolution
146
211
  // For event-required apps: selectedOrganisation is null, org derived from event
147
212
  // For org-required apps: selectedOrganisation is available, event optional
148
213
  // For page-level permissions, PORTAL app allows both contexts to be optional
214
+ // NOTE: Super admins bypass scope requirements, but we still need to call the hook
149
215
  const { resolvedScope: hookResolvedScope, isLoading: scopeLoading, error: scopeError } = useResolvedScope({
150
216
  supabase,
151
217
  selectedOrganisationId: selectedOrganisation?.id || null,
152
218
  selectedEventId: selectedEvent?.event_id || null
153
219
  });
154
220
 
221
+ // For super admins, we can use a minimal scope since they bypass all checks
222
+ // This prevents scope resolution from blocking super admin access
223
+ const shouldBypassScopeForSuperAdmin = isSuperAdmin === true;
224
+
155
225
  // Use provided scope if available, otherwise use resolved scope
156
226
  // For PORTAL app page-level permissions, allow scope without org/event
157
227
  // Ensure appId is available for PORTAL/ADMIN (use contextAppId as fallback)
@@ -171,11 +241,6 @@ const PagePermissionGuardComponent = ({
171
241
  } : null)));
172
242
  const checkError = scopeError;
173
243
 
174
- // Determine the page ID for permission checking
175
- const effectivePageId = useMemo((): string => {
176
- return pageId || pageName;
177
- }, [pageId, pageName]);
178
-
179
244
  // Build the permission string
180
245
  const permission = useMemo((): Permission => {
181
246
  return `${operation}:page.${pageName}` as Permission;
@@ -225,22 +290,44 @@ const PagePermissionGuardComponent = ({
225
290
  return newScope;
226
291
  }, [effectiveScope, appName, contextAppId, selectedEvent?.event_id]);
227
292
 
293
+ // For super admins, use a minimal valid scope since they bypass all checks
294
+ // This prevents scope validation from blocking super admin access
295
+ const scopeForPermissionCheck = shouldBypassScopeForSuperAdmin && !stableScope?.organisationId
296
+ ? {
297
+ organisationId: undefined,
298
+ appId: contextAppId || undefined,
299
+ eventId: selectedEvent?.event_id || undefined
300
+ }
301
+ : stableScope;
302
+
303
+ // CRITICAL: If super admin is confirmed, skip useCan entirely to avoid any blocking
304
+ // Super admins have access to everything, so no need to check permissions
305
+ const shouldSkipPermissionCheck = isSuperAdmin === true;
306
+
228
307
  // Check if user has permission - only call useCan when we have a resolved scope with valid organisationId
229
308
  // If resolvedScope is null or has no organisationId, useCan will keep isLoading=true
230
309
  // Pass appName to useCan so it can be passed to isPermitted for PORTAL/ADMIN special case
310
+ // Pass precomputed super admin status to avoid duplicate checks in useCan
311
+ // For super admins, we can skip the check entirely, but still call useCan with a dummy scope to avoid hook rule violations
231
312
  const { can, isLoading: canIsLoading, error: canError } = useCan(
232
313
  user?.id || '',
233
- stableScope,
314
+ shouldSkipPermissionCheck ? { organisationId: undefined, appId: contextAppId || undefined, eventId: undefined } : scopeForPermissionCheck,
234
315
  permission,
235
316
  effectivePageId,
236
317
  true, // Use cache
318
+ isSuperAdmin, // precomputedSuperAdmin - null if checking, true/false if checked
237
319
  appName // Pass appName for PORTAL/ADMIN special case
238
320
  );
239
321
 
322
+ // Override can for super admins - they always have access
323
+ const effectiveCan = shouldSkipPermissionCheck ? true : can;
324
+ const effectiveIsLoading = shouldSkipPermissionCheck ? false : canIsLoading;
325
+
240
326
 
241
327
  // Combine loading states - we're loading if scope resolution or permission check is loading
242
328
  // For page-level permissions, PORTAL/ADMIN apps allow undefined organisationId
243
- const isLoading = scopeLoading || canIsLoading;
329
+ // Super admins bypass scope resolution - don't wait for it
330
+ const isLoading = shouldBypassScopeForSuperAdmin ? effectiveIsLoading : (scopeLoading || effectiveIsLoading);
244
331
  const error = checkError || canError;
245
332
 
246
333
  // Handle permission check completion
@@ -248,13 +335,13 @@ const PagePermissionGuardComponent = ({
248
335
  if (!isLoading && !error) {
249
336
  setHasChecked(true);
250
337
 
251
- if (!can && onDenied) {
338
+ if (!effectiveCan && onDenied) {
252
339
  onDenied(pageName, operation);
253
340
  }
254
341
  } else if (error) {
255
342
  setHasChecked(true);
256
343
  }
257
- }, [can, isLoading, error, pageName, operation, onDenied]);
344
+ }, [effectiveCan, isLoading, error, pageName, operation, onDenied]);
258
345
 
259
346
  // Log page access attempt for audit
260
347
  useEffect(() => {
@@ -265,16 +352,17 @@ const PagePermissionGuardComponent = ({
265
352
  operation,
266
353
  userId: user?.id,
267
354
  scope: effectiveScope,
268
- allowed: can,
355
+ allowed: effectiveCan,
356
+ isSuperAdmin,
269
357
  timestamp: new Date().toISOString()
270
358
  });
271
359
  }
272
- }, [auditLog, hasChecked, isLoading, pageName, operation, user?.id, effectiveScope, can]);
360
+ }, [auditLog, hasChecked, isLoading, pageName, operation, user?.id, effectiveScope, effectiveCan, isSuperAdmin]);
273
361
 
274
362
 
275
363
  // Handle strict mode violations
276
364
  useEffect(() => {
277
- if (strictMode && hasChecked && !isLoading && !can) {
365
+ if (strictMode && hasChecked && !isLoading && !effectiveCan && !shouldBypassScopeForSuperAdmin) {
278
366
  const logger = getRBACLogger();
279
367
  logger.error(`STRICT MODE VIOLATION: User attempted to access protected page without permission`, {
280
368
  pageName,
@@ -286,20 +374,22 @@ const PagePermissionGuardComponent = ({
286
374
  scopeValid: allowsOptionalContexts ? true : (effectiveScope !== null), // PORTAL/ADMIN allow scope without org/event
287
375
  checkError,
288
376
  canError,
377
+ isSuperAdmin,
289
378
  timestamp: new Date().toISOString()
290
379
  });
291
380
  }
292
- }, [strictMode, hasChecked, isLoading, can, pageName, operation, effectivePageId, user?.id, effectiveScope, allowsOptionalContexts, checkError, canError]);
381
+ }, [strictMode, hasChecked, isLoading, effectiveCan, shouldBypassScopeForSuperAdmin, pageName, operation, effectivePageId, user?.id, effectiveScope, allowsOptionalContexts, checkError, canError, isSuperAdmin]);
293
382
 
294
383
  // Calculate the actual render state - FIXED: Proper state calculation
295
384
  // Add defensive checks to ensure we have valid state
296
385
  // For page-level permissions, PORTAL/ADMIN apps allow scope without org/event
297
- const hasValidScopeForPagePermissions = allowsOptionalContexts ? true : (effectiveScope !== null);
386
+ // Super admins bypass scope validation
387
+ const hasValidScopeForPagePermissions = shouldBypassScopeForSuperAdmin ? true : (allowsOptionalContexts ? true : (effectiveScope !== null));
298
388
  const hasValidUser = user && user.id;
299
389
  const isPermissionCheckComplete = hasChecked && !isLoading;
300
390
 
301
- const shouldShowAccessDenied = isPermissionCheckComplete && hasValidScopeForPagePermissions && hasValidUser && !checkError && !can;
302
- const shouldShowContent = isPermissionCheckComplete && hasValidScopeForPagePermissions && hasValidUser && !checkError && can;
391
+ const shouldShowAccessDenied = isPermissionCheckComplete && hasValidScopeForPagePermissions && hasValidUser && !checkError && !effectiveCan;
392
+ const shouldShowContent = isPermissionCheckComplete && hasValidScopeForPagePermissions && hasValidUser && !checkError && effectiveCan;
303
393
 
304
394
  // Create a key to force re-render when scope or permission state changes
305
395
  const scopeKey = effectiveScope ? `${effectiveScope.organisationId}-${effectiveScope.eventId}-${effectiveScope.appId}` : 'no-scope';
@@ -307,9 +397,87 @@ const PagePermissionGuardComponent = ({
307
397
 
308
398
 
309
399
 
400
+ // Debug logging for permission check state (only log when state actually changes, not on every render)
401
+ const lastLogStateRef = useRef<string>('');
402
+ useEffect(() => {
403
+ if (process.env.NODE_ENV === 'development') {
404
+ const currentState = JSON.stringify({
405
+ pageName,
406
+ userId: user?.id,
407
+ isSuperAdmin,
408
+ isLoading,
409
+ scopeLoading,
410
+ canIsLoading,
411
+ hasChecked,
412
+ hasValidUser,
413
+ effectiveCan
414
+ });
415
+ // Only log if state actually changed
416
+ if (currentState !== lastLogStateRef.current) {
417
+ lastLogStateRef.current = currentState;
418
+ console.log('[PagePermissionGuard] Permission check state', {
419
+ pageName,
420
+ userId: user?.id,
421
+ isSuperAdmin,
422
+ isLoading,
423
+ scopeLoading,
424
+ canIsLoading,
425
+ hasChecked,
426
+ hasValidUser,
427
+ effectiveCan,
428
+ stableScope,
429
+ effectiveScope
430
+ });
431
+ }
432
+ }
433
+ }, [pageName, user?.id, isSuperAdmin, isLoading, scopeLoading, canIsLoading, hasChecked, hasValidUser, effectiveCan, stableScope, effectiveScope]);
434
+
310
435
  // Show loading state - if we're still loading or don't have valid state
311
436
  // For page-level permissions, we don't require organisation context, so only check for user and loading state
312
- if (isLoading || !hasValidUser || !hasChecked) {
437
+ // Add timeout warning for debugging
438
+ useEffect(() => {
439
+ if (isLoading && isSuperAdmin === null && hasValidUser) {
440
+ const timeout = setTimeout(() => {
441
+ console.warn('[PagePermissionGuard] Permission check taking longer than expected', {
442
+ pageName,
443
+ userId: user?.id,
444
+ isSuperAdmin,
445
+ scopeLoading,
446
+ canIsLoading,
447
+ hasChecked,
448
+ stableScope,
449
+ effectiveScope,
450
+ appName
451
+ });
452
+ }, 5000);
453
+ return () => clearTimeout(timeout);
454
+ }
455
+ }, [isLoading, isSuperAdmin, hasValidUser, pageName, user?.id, scopeLoading, canIsLoading, hasChecked, stableScope, effectiveScope, appName]);
456
+
457
+ // CRITICAL: Super admins bypass all checks - show content immediately once confirmed
458
+ // This must be checked AFTER all hooks are called to avoid React hooks rule violations
459
+ // Log super admin access only once when it's first confirmed (not on every render)
460
+ useEffect(() => {
461
+ if (isSuperAdmin === true && hasValidUser && !hasLoggedSuperAdminRef.current && process.env.NODE_ENV === 'development') {
462
+ hasLoggedSuperAdminRef.current = true;
463
+ console.log('[PagePermissionGuard] Super admin access granted - bypassing all checks', {
464
+ pageName,
465
+ userId: user?.id,
466
+ operation
467
+ });
468
+ }
469
+ // Reset log flag if super admin status changes back to false/null
470
+ if (isSuperAdmin !== true) {
471
+ hasLoggedSuperAdminRef.current = false;
472
+ }
473
+ }, [isSuperAdmin, hasValidUser, pageName, user?.id, operation]);
474
+
475
+ if (isSuperAdmin === true && hasValidUser) {
476
+ // Super admin confirmed - grant access immediately
477
+ return <>{children}</>;
478
+ }
479
+
480
+ if (isLoading || !hasValidUser || !hasChecked || isSuperAdmin === null) {
313
481
  return loading || <div>Checking permissions...</div>;
314
482
  }
315
483
 
@@ -231,11 +231,15 @@ export function RoleBasedRouter({
231
231
  }, [user?.id, currentScope, routes]);
232
232
 
233
233
  // Use useCan hook for actual permission checking
234
+ // Pass null for super admin status (not checked yet - hook will check if needed)
234
235
  const { can: canAccessCurrentRoute, isLoading: permissionLoading } = useCan(
235
236
  user?.id || '',
236
237
  currentScope || { organisationId: '', eventId: undefined, appId: undefined },
237
238
  currentRouteConfig?.permissions?.[0] || 'read:page',
238
- currentRouteConfig?.pageId
239
+ currentRouteConfig?.pageId,
240
+ true, // useCache
241
+ null, // precomputedSuperAdmin - not checked yet
242
+ undefined // appName
239
243
  );
240
244
 
241
245
  // Check if route is public
@@ -12,7 +12,7 @@ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
12
12
  import React, { ReactNode } from 'react';
13
13
  import { SecureDataProvider, useSecureData } from './SecureDataProvider';
14
14
  import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
15
- import { useSecureDataAccess } from '../../hooks/useSecureDataAccess';
15
+ // useSecureDataAccess has been removed - SecureDataProvider now uses useSecureSupabase internally
16
16
  import { UUID, Scope, Permission } from '../types';
17
17
 
18
18
  // Mock the auth provider
@@ -23,8 +23,77 @@ vi.mock('../../providers/services/UnifiedAuthProvider', () => ({
23
23
  }));
24
24
 
25
25
  // Mock the secure data access hook
26
- vi.mock('../../hooks/useSecureDataAccess', () => ({
27
- useSecureDataAccess: vi.fn()
26
+ // useSecureDataAccess has been removed - no longer needed
27
+
28
+ // Mock useResolvedScope
29
+ const mockUseResolvedScopeFn = vi.fn();
30
+ vi.mock('../../hooks/useResolvedScope', () => ({
31
+ useResolvedScope: () => mockUseResolvedScopeFn(),
32
+ }));
33
+
34
+ // Mock useOrganisations to prevent provider requirement
35
+ vi.mock('../../hooks/useOrganisations', () => ({
36
+ useOrganisations: vi.fn(() => ({
37
+ organisations: [],
38
+ isLoading: false,
39
+ error: null,
40
+ refetch: vi.fn(),
41
+ selectedOrganisation: {
42
+ id: 'org-123',
43
+ name: 'Test Org',
44
+ display_name: 'Test Organisation',
45
+ description: 'Test',
46
+ subscription_tier: 'basic',
47
+ settings: {},
48
+ is_active: true,
49
+ created_at: '2023-01-01T00:00:00Z',
50
+ updated_at: '2023-01-01T00:00:00Z'
51
+ }
52
+ }))
53
+ }));
54
+
55
+ // Mock useEvents
56
+ vi.mock('../../hooks/useEvents', () => ({
57
+ useEvents: vi.fn(() => ({
58
+ events: [],
59
+ isLoading: false,
60
+ error: null,
61
+ refetch: vi.fn(),
62
+ selectedEvent: {
63
+ id: 'event-456',
64
+ event_id: 'event-456',
65
+ event_name: 'Test Event',
66
+ event_date: '2023-01-01T00:00:00Z',
67
+ event_venue: 'Test Venue',
68
+ event_participants: 100,
69
+ event_colours: '#FF0000',
70
+ event_logo: '',
71
+ organisation_id: 'org-123' as any,
72
+ is_visible: true,
73
+ created_at: '2023-01-01T00:00:00Z',
74
+ updated_at: '2023-01-01T00:00:00Z'
75
+ },
76
+ eventLoading: false
77
+ }))
78
+ }));
79
+
80
+ // Mock useOrganisationSecurity
81
+ vi.mock('../../hooks/useOrganisationSecurity', () => ({
82
+ useOrganisationSecurity: vi.fn(() => ({
83
+ superAdminContext: {
84
+ isSuperAdmin: false,
85
+ isLoading: false
86
+ },
87
+ organisationSecurity: {
88
+ canAccessOrganisation: vi.fn(() => true),
89
+ canAccessEvent: vi.fn(() => true)
90
+ }
91
+ }))
92
+ }));
93
+
94
+ // Mock useSecureSupabase
95
+ vi.mock('../hooks/useSecureSupabase', () => ({
96
+ useSecureSupabase: vi.fn((supabase) => supabase)
28
97
  }));
29
98
 
30
99
  // Mock the RBAC hooks
@@ -55,7 +124,7 @@ const TestComponent = ({ children }: { children: ReactNode }) => (
55
124
  );
56
125
 
57
126
  describe('SecureDataProvider', () => {
58
- const mockUseSecureDataAccess = vi.mocked(useSecureDataAccess);
127
+ // useSecureDataAccess has been removed
59
128
  const mockUseCan = vi.mocked(useCan);
60
129
 
61
130
  beforeEach(() => {
@@ -67,12 +136,7 @@ describe('SecureDataProvider', () => {
67
136
  // Add other required properties
68
137
  } as any);
69
138
 
70
- mockUseSecureDataAccess.mockReturnValue({
71
- isDataAccessAllowed: vi.fn().mockReturnValue(true),
72
- getDataAccessPermissions: vi.fn().mockReturnValue({}),
73
- validateDataAccess: vi.fn().mockReturnValue(true),
74
- // Add other required properties
75
- } as any);
139
+ // useSecureDataAccess has been removed - SecureDataProvider handles this internally
76
140
  });
77
141
 
78
142
  describe('Provider Initialization', () => {
@@ -160,12 +224,7 @@ describe('SecureDataProvider', () => {
160
224
  });
161
225
 
162
226
  it('denies data access when user lacks permissions', async () => {
163
- mockUseSecureDataAccess.mockReturnValue({
164
- isDataAccessAllowed: vi.fn().mockReturnValue(false),
165
- getDataAccessPermissions: vi.fn().mockReturnValue({}),
166
- validateDataAccess: vi.fn().mockReturnValue(false),
167
- // Add other required properties
168
- } as any);
227
+ // useSecureDataAccess has been removed - SecureDataProvider handles this internally
169
228
 
170
229
  const TestConsumer = () => {
171
230
  const context = useSecureData();
@@ -216,12 +275,7 @@ describe('SecureDataProvider', () => {
216
275
  'events': ['read', 'update']
217
276
  };
218
277
 
219
- mockUseSecureDataAccess.mockReturnValue({
220
- isDataAccessAllowed: vi.fn().mockReturnValue(true),
221
- getDataAccessPermissions: vi.fn().mockReturnValue(mockPermissions),
222
- validateDataAccess: vi.fn().mockReturnValue(true),
223
- // Add other required properties
224
- } as any);
278
+ // useSecureDataAccess has been removed - SecureDataProvider handles this internally
225
279
 
226
280
  const TestConsumer = () => {
227
281
  const context = useSecureData();
@@ -309,13 +363,7 @@ describe('SecureDataProvider', () => {
309
363
 
310
364
  it('calls onStrictModeViolation callback when strict mode is violated', async () => {
311
365
  const onStrictModeViolation = vi.fn();
312
- mockUseSecureDataAccess.mockReturnValue({
313
- isDataAccessAllowed: vi.fn().mockReturnValue(false),
314
- getDataAccessPermissions: vi.fn().mockReturnValue({}),
315
- validateDataAccess: vi.fn().mockReturnValue(false),
316
- // Add other required properties
317
- } as any);
318
-
366
+ // useSecureDataAccess has been removed - SecureDataProvider handles this internally
319
367
  const TestConsumer = () => {
320
368
  const context = useSecureData();
321
369
  context.isDataAccessAllowed('admin_users', 'delete', mockScope);
@@ -340,14 +388,7 @@ describe('SecureDataProvider', () => {
340
388
 
341
389
  describe('Error Handling', () => {
342
390
  it('handles data access validation errors gracefully', async () => {
343
- mockUseSecureDataAccess.mockReturnValue({
344
- isDataAccessAllowed: vi.fn().mockImplementation(() => {
345
- throw new Error('Data access validation failed');
346
- }),
347
- getDataAccessPermissions: vi.fn().mockReturnValue({}),
348
- validateDataAccess: vi.fn().mockReturnValue(false),
349
- // Add other required properties
350
- } as any);
391
+ // useSecureDataAccess has been removed - SecureDataProvider handles this internally
351
392
 
352
393
  const TestConsumer = () => {
353
394
  const context = useSecureData();
@@ -462,7 +503,7 @@ describe('SecureDataProvider', () => {
462
503
  });
463
504
 
464
505
  describe('Integration with useSecureDataAccess', () => {
465
- it('integrates with useSecureDataAccess hook', () => {
506
+ it('integrates with secure data access system', () => {
466
507
  const TestConsumer = () => {
467
508
  const context = useSecureData();
468
509
  context.isDataAccessAllowed(mockTable, mockOperation, mockScope);
@@ -476,19 +517,12 @@ describe('SecureDataProvider', () => {
476
517
  </SecureDataProvider>
477
518
  );
478
519
 
479
- expect(mockUseSecureDataAccess).toHaveBeenCalled();
520
+ // useSecureDataAccess has been removed - this test verifies the component works
521
+ // expect(mockUseSecureDataAccess).toHaveBeenCalled();
480
522
  });
481
523
 
482
524
  it('passes through secure data access functionality', () => {
483
- const mockSecureDataAccess = {
484
- isDataAccessAllowed: vi.fn().mockReturnValue(true),
485
- getDataAccessPermissions: vi.fn().mockReturnValue({}),
486
- validateDataAccess: vi.fn().mockReturnValue(true),
487
- // Add other required properties
488
- };
489
-
490
- mockUseSecureDataAccess.mockReturnValue(mockSecureDataAccess as any);
491
-
525
+ // useSecureDataAccess has been removed - SecureDataProvider handles this internally
492
526
  const TestConsumer = () => {
493
527
  const context = useSecureData();
494
528
  context.isDataAccessAllowed(mockTable, mockOperation, mockScope);
@@ -502,7 +536,8 @@ describe('SecureDataProvider', () => {
502
536
  </SecureDataProvider>
503
537
  );
504
538
 
505
- expect(mockSecureDataAccess.isDataAccessAllowed).not.toHaveBeenCalled();
539
+ // Component should render without errors
540
+ expect(screen.getByText('Test')).toBeInTheDocument();
506
541
  });
507
542
  });
508
543
  });
@@ -52,13 +52,14 @@
52
52
  * @dependencies
53
53
  * - React 19+ - Context and hooks
54
54
  * - useUnifiedAuth - Authentication context
55
- * - useSecureDataAccess - Secure data access hook
55
+ * - useSecureSupabase - Secure Supabase client hook
56
56
  * - RBAC types - Type definitions
57
57
  */
58
58
 
59
59
  import React, { createContext, useContext, useState, useCallback, useMemo, useEffect } from 'react';
60
60
  import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
61
- import { useSecureDataAccess } from '../../hooks/useSecureDataAccess';
61
+ import { useSecureSupabase } from '../hooks/useSecureSupabase';
62
+ import { useOrganisationSecurity } from '../../hooks/useOrganisationSecurity';
62
63
  import { useResolvedScope } from '../hooks/useResolvedScope';
63
64
  import { UUID, Scope, Permission } from '../types';
64
65
  import { getRBACLogger } from '../config';
@@ -144,7 +145,8 @@ export function SecureDataProvider({
144
145
  enforceRLS = true
145
146
  }: SecureDataProviderProps) {
146
147
  const { user, selectedOrganisation, selectedEvent, supabase } = useUnifiedAuth();
147
- const { validateContext } = useSecureDataAccess();
148
+ const secureSupabase = useSecureSupabase(supabase);
149
+ const { superAdminContext } = useOrganisationSecurity();
148
150
  const [dataAccessHistory, setDataAccessHistory] = useState<DataAccessRecord[]>([]);
149
151
  const [isEnabled, setIsEnabled] = useState(true);
150
152
 
@@ -154,6 +156,19 @@ export function SecureDataProvider({
154
156
  selectedOrganisationId: selectedOrganisation?.id || null,
155
157
  selectedEventId: selectedEvent?.event_id || null
156
158
  });
159
+
160
+ // Validate context - similar to useSecureDataAccess.validateContext
161
+ const validateContext = useCallback((): void => {
162
+ if (!secureSupabase) {
163
+ throw new Error('No Supabase client available');
164
+ }
165
+ if (!user) {
166
+ throw new Error('User must be authenticated');
167
+ }
168
+ if (!superAdminContext.isSuperAdmin && !resolvedScope?.organisationId) {
169
+ throw new Error('Organisation context is required for data access');
170
+ }
171
+ }, [secureSupabase, user, superAdminContext.isSuperAdmin, resolvedScope?.organisationId]);
157
172
 
158
173
  // Get current scope from resolved scope
159
174
  // For event-required apps: org is derived from event
@@ -174,10 +189,10 @@ export function SecureDataProvider({
174
189
 
175
190
  // Use the existing RBAC system to check data access permissions
176
191
  // This is a synchronous check for the context - actual permission checking
177
- // happens in the useSecureDataAccess hook using the RBAC engine
192
+ // happens using the RBAC engine via useSecureSupabase
178
193
  const permission = `${operation}:data.${table}` as Permission;
179
194
 
180
- // For now, we'll return true and let the useSecureDataAccess hook
195
+ // For now, we'll return true and let the RBAC system
181
196
  // handle the actual permission checking asynchronously
182
197
  // This context is mainly for tracking and audit purposes
183
198
  return true;