@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
@@ -121,15 +121,21 @@ import type { PasswordChangeFormError } from '../PasswordChange/PasswordChangeFo
121
121
  // Define Operation type locally since old RBAC types are removed
122
122
  type Operation = 'read' | 'create' | 'update' | 'delete' | 'manage';
123
123
 
124
+ /**
125
+ * Props for the PaceAppLayout component.
126
+ * Configures the application layout including navigation, header, and footer.
127
+ */
124
128
  export interface PaceAppLayoutProps {
125
129
  /** The name of the application to be displayed in the header. */
126
130
  appName: string;
127
131
  /** Optional navigation items for the header menu. If not provided, uses default navigation. */
128
132
  navItems?: NavigationItem[];
129
- /** Show/hide event selector in the header */
130
- showEventSelector?: boolean;
131
- /** Show/hide organisation selector in the header */
132
- showOrgSelector?: boolean;
133
+ /** Show/hide unified context selector (shows all accessible orgs and events) - default: true */
134
+ showContextSelector?: boolean;
135
+ /** Show organisations in context selector - default: true */
136
+ showOrganisations?: boolean;
137
+ /** Show events in context selector - default: true */
138
+ showEvents?: boolean;
133
139
  /** Custom actions to display in the header (between event selector and user menu) */
134
140
  headerActions?: React.ReactNode;
135
141
  /** Custom logo component (overrides default logo) */
@@ -345,8 +351,9 @@ export interface PaceAppLayoutProps {
345
351
  export function PaceAppLayout({
346
352
  appName,
347
353
  navItems,
348
- showEventSelector,
349
- showOrgSelector,
354
+ showContextSelector = true,
355
+ showOrganisations = true,
356
+ showEvents = true,
350
357
  headerActions,
351
358
  customLogo,
352
359
  logoHref = '/dashboard',
@@ -524,12 +531,15 @@ export function PaceAppLayout({
524
531
  // Pass appName to useCan so it can be passed to isPermitted for PORTAL/ADMIN special case
525
532
  // Only check permissions if enforcePermissions is true and we have a valid permission string
526
533
  const shouldCheckPermission = enforcePermissions && !!currentPermission && !!currentPageId;
534
+ // Pass super admin status to avoid duplicate check - use null if still checking, false/true if known
535
+ const superAdminStatus = isSuperAdminFromRBAC ? true : (isCheckingSuperAdminDirect ? null : isSuperAdminDirect);
527
536
  const { can: canFromHook, isLoading: isCheckingPermission, error: permissionError } = useCan(
528
537
  user?.id || '',
529
538
  scope,
530
539
  shouldCheckPermission ? currentPermission : ('' as Permission),
531
540
  shouldCheckPermission ? currentPageId : '',
532
541
  true, // useCache
542
+ superAdminStatus, // Pass super admin status to avoid duplicate check
533
543
  appName // Pass appName for PORTAL/ADMIN special case
534
544
  );
535
545
 
@@ -607,9 +617,9 @@ export function PaceAppLayout({
607
617
  const hasOrganisationContext = currentScope.organisationId;
608
618
  const hasUser = !!user?.id;
609
619
 
610
- // BUG FIX: When showOrgSelector is enabled, show navigation optimistically while waiting
611
- // for organisation selection. Only hide navigation if user is not loaded.
612
- // This prevents navigation from disappearing after initial render while waiting for org selection.
620
+ // Show navigation optimistically while waiting for context selection.
621
+ // Only hide navigation if user is not loaded.
622
+ // This prevents navigation from disappearing after initial render while waiting for context.
613
623
  if (!hasUser) {
614
624
  // User not loaded yet - show all items until user is ready
615
625
  if (isMounted) {
@@ -618,9 +628,9 @@ export function PaceAppLayout({
618
628
  return;
619
629
  }
620
630
 
621
- // If no organisation context yet, show items optimistically if org selector is enabled
622
- // This allows users to see navigation while they select an organisation
623
- // Only proceed with permission filtering once organisation is selected
631
+ // If no organisation context yet, show items optimistically
632
+ // This allows users to see navigation while they select a context
633
+ // Only proceed with permission filtering once context is selected
624
634
  if (!hasOrganisationContext) {
625
635
  if (isMounted) {
626
636
  // Show items optimistically when org selector is enabled, otherwise show all items
@@ -675,32 +685,130 @@ export function PaceAppLayout({
675
685
  scope: permissionScope,
676
686
  });
677
687
 
678
- // Filter items using the permission map (synchronous, no rate limit issues)
679
- const filtered = baseMenuItems.map((item) => {
680
- if (!item.href) return { item, hasAccess: true };
681
-
682
- // Extract page ID from href: remove leading slash, fallback to 'dashboard' for root
683
- // This matches database page names in rbac_app_pages
684
- const pageId = pageIdMapping[item.href] || (item.href === '/' ? 'dashboard' : item.href.slice(1)) || 'dashboard';
685
- const permission = routePermissions[item.href] || defaultPermission;
686
- const fullPermission: Permission = permission.includes(':')
687
- ? (permission as Permission)
688
- : (pageId ? `${permission}:page.${pageId}` : permission) as Permission;
689
-
690
- // Check permission map (super admin check already handled in getPermissionMap)
691
- const hasAccess = permissionMap['*'] === true || permissionMap[fullPermission] === true;
692
-
693
- return { item, hasAccess };
694
- });
688
+ // Filter items using the permission map and scope type
689
+ // First, get scope types for all pages (batch if possible, or individual)
690
+ const { getPageScopeType } = await import('../../rbac/api');
691
+ const effectiveAppId = currentScope.appId || resolvedAppId;
692
+ const effectiveAppName = appName;
693
+
694
+ // Determine current context type for scope filtering
695
+ const hasEventContext = !!currentScope.eventId;
696
+ const hasOrgContext = !!currentScope.organisationId;
697
+
698
+ // Filter items using both permission map and scope type
699
+ const filtered = await Promise.all(
700
+ baseMenuItems.map(async (item) => {
701
+ if (!item.href) return { item, hasAccess: true, matchesScope: true, scopeCheckError: false };
702
+
703
+ // Extract page ID from href: remove leading slash, fallback to 'dashboard' for root
704
+ // This matches database page names in rbac_app_pages
705
+ const pageId = pageIdMapping[item.href] || (item.href === '/' ? 'dashboard' : item.href.slice(1)) || 'dashboard';
706
+ const permission = routePermissions[item.href] || defaultPermission;
707
+ const fullPermission: Permission = permission.includes(':')
708
+ ? (permission as Permission)
709
+ : (pageId ? `${permission}:page.${pageId}` : permission) as Permission;
710
+
711
+ // Check permission map (super admin check already handled in getPermissionMap)
712
+ const hasAccess = permissionMap['*'] === true || permissionMap[fullPermission] === true;
713
+
714
+ // Check scope type compatibility
715
+ let matchesScope = true;
716
+ let scopeCheckError = false;
717
+ if (effectiveAppId || effectiveAppName) {
718
+ try {
719
+ const pageScopeType = await getPageScopeType(
720
+ pageId,
721
+ effectiveAppId,
722
+ effectiveAppName
723
+ );
724
+
725
+ // Filter based on current context:
726
+ // - If event is selected: show only 'event' or 'both' pages
727
+ // - If only org is selected: show only 'organisation' or 'both' pages
728
+ if (hasEventContext) {
729
+ // Event context: show 'event' or 'both' pages only
730
+ matchesScope = pageScopeType === 'event' || pageScopeType === 'both';
731
+ } else if (hasOrgContext) {
732
+ // Organisation context only: show 'organisation' or 'both' pages only
733
+ matchesScope = pageScopeType === 'organisation' || pageScopeType === 'both';
734
+ } else {
735
+ // No context: show all pages (shouldn't happen, but safe fallback)
736
+ matchesScope = true;
737
+ }
738
+ } catch (error) {
739
+ // If we can't get scope type, allow the page (graceful degradation)
740
+ // This prevents navigation from disappearing if there's a database issue
741
+ scopeCheckError = true;
742
+ logger.warn('PaceAppLayout', 'Failed to get page scope type for navigation filtering', {
743
+ pageId,
744
+ href: item.href,
745
+ contextType: hasEventContext ? 'event' : (hasOrgContext ? 'organisation' : 'none'),
746
+ error: error instanceof Error ? error.message : String(error),
747
+ note: 'Allowing page to prevent navigation from disappearing - this may indicate missing scope_type in database'
748
+ });
749
+ matchesScope = true; // Default to allowing if we can't check
750
+ }
751
+ } else {
752
+ // No appId/appName available - can't check scope, allow the page
753
+ // This happens during initial load before app context is resolved
754
+ matchesScope = true;
755
+ }
756
+
757
+ return { item, hasAccess, matchesScope, scopeCheckError };
758
+ })
759
+ );
695
760
 
696
761
  if (!isMounted) return;
697
762
 
698
763
  const accessibleItems = filtered
699
- .filter(({ hasAccess }) => hasAccess)
764
+ .filter(({ hasAccess, matchesScope }) => hasAccess && matchesScope)
700
765
  .map(({ item }) => item);
701
766
 
767
+ // If all items were filtered out, check if it's due to scope filtering
768
+ // This can happen if:
769
+ // 1. All pages are scoped for the other context type (expected behavior)
770
+ // 2. Scope type lookup failed for all pages (should fallback to showing items with permissions)
771
+ if (accessibleItems.length === 0 && filtered.length > 0) {
772
+ const itemsWithPermissions = filtered.filter(({ hasAccess }) => hasAccess);
773
+ const itemsFilteredByScope = filtered.filter(({ hasAccess, matchesScope }) => hasAccess && !matchesScope);
774
+ const itemsWithScopeErrors = filtered.filter(({ hasAccess, scopeCheckError }) => hasAccess && scopeCheckError);
775
+
776
+ // If scope checking failed for ALL items with permissions, fall back to showing them
777
+ // This prevents navigation from disappearing due to database/API issues
778
+ if (itemsWithPermissions.length > 0 &&
779
+ itemsWithScopeErrors.length === itemsWithPermissions.length) {
780
+ logger.warn('PaceAppLayout', 'Scope checking failed for all items - falling back to permission-only filtering', {
781
+ contextType: hasEventContext ? 'event' : (hasOrgContext ? 'organisation' : 'none'),
782
+ totalItems: baseMenuItems.length,
783
+ itemsWithPermissions: itemsWithPermissions.length,
784
+ scopeCheckErrorCount: itemsWithScopeErrors.length,
785
+ note: 'Showing items based on permissions only - scope filtering disabled due to errors. This may indicate database issues or missing scope_type values.'
786
+ });
787
+
788
+ // Fall back to showing items that have permissions (ignore scope check)
789
+ const fallbackItems = filtered
790
+ .filter(({ hasAccess }) => hasAccess)
791
+ .map(({ item }) => item);
792
+
793
+ if (isMounted && fallbackItems.length > 0) {
794
+ setFilteredMenuItems(fallbackItems);
795
+ return;
796
+ }
797
+ } else if (itemsWithPermissions.length > 0 && itemsFilteredByScope.length === itemsWithPermissions.length) {
798
+ // All items were filtered by scope (not errors) - this is expected if all pages are scoped for other context
799
+ logger.info('PaceAppLayout', 'All navigation items filtered out by scope type', {
800
+ contextType: hasEventContext ? 'event' : (hasOrgContext ? 'organisation' : 'none'),
801
+ totalItems: baseMenuItems.length,
802
+ itemsWithPermissions: itemsWithPermissions.length,
803
+ itemsFilteredByScope: itemsFilteredByScope.length,
804
+ note: 'All pages appear to be scoped for a different context type - this is expected behavior. Navigation will be empty until user selects the appropriate context.'
805
+ });
806
+ }
807
+ }
808
+
702
809
  // SECURITY: Never show all items if permission check fails - this would be a security risk
703
810
  // If all items are filtered out, it means the user doesn't have permission to see any navigation
811
+ // OR all pages are scoped for a different context type
704
812
  // This is the correct behavior - better to show nothing than show unauthorized items
705
813
  setFilteredMenuItems(accessibleItems);
706
814
  } catch (error) {
@@ -718,7 +826,7 @@ export function PaceAppLayout({
718
826
  return () => {
719
827
  isMounted = false;
720
828
  };
721
- }, [baseMenuItems, pageIdMapping, routePermissions, defaultPermission, can, user?.id, scope, scopeLoading, contextAppId, resolvedScope?.appId, selectedOrganisation?.id]);
829
+ }, [baseMenuItems, pageIdMapping, routePermissions, defaultPermission, can, user?.id, scope, scopeLoading, contextAppId, resolvedScope?.appId, selectedOrganisation?.id, selectedEvent?.event_id, appName]);
722
830
 
723
831
  // NEW: Phase 2 - Enhanced Routing Features
724
832
  // Check route access for role-based routing
@@ -817,25 +925,39 @@ export function PaceAppLayout({
817
925
  }, [roleBasedRouting, routeConfig, location.pathname, strictMode, user?.id, fallbackRoute, scope, navigate, auditLog, onRouteAccessDenied, onRouteStrictModeViolation]);
818
926
 
819
927
  const handleSignOut = async () => {
820
- await signOut();
928
+ try {
929
+ await signOut();
930
+ } catch (error) {
931
+ logger.error('PaceAppLayout', 'Failed to sign out', { error: error instanceof Error ? error.message : String(error) });
932
+ }
821
933
  };
822
934
 
823
935
  const handleChangePassword = async (newPassword: string, confirmPassword: string) => {
824
- // The form component in UserMenu already checks for matching passwords
825
- const result = await updatePassword(newPassword);
826
- if (result?.error) {
827
- // The form will display the error message
828
- logger.error('PaceAppLayout', 'Failed to change password', { error: result.error.message });
829
- // Convert AuthError to PasswordChangeFormError
936
+ try {
937
+ // The form component in UserMenu already checks for matching passwords
938
+ const result = await updatePassword(newPassword);
939
+ if (result?.error) {
940
+ // The form will display the error message
941
+ logger.error('PaceAppLayout', 'Failed to change password', { error: result.error.message });
942
+ // Convert AuthError to PasswordChangeFormError
943
+ return {
944
+ error: {
945
+ message: result.error.message,
946
+ code: result.error.name || 'PASSWORD_UPDATE_ERROR'
947
+ }
948
+ };
949
+ }
950
+ // The form will handle closing the modal on success
951
+ return {};
952
+ } catch (error) {
953
+ logger.error('PaceAppLayout', 'Failed to change password', { error: error instanceof Error ? error.message : String(error) });
830
954
  return {
831
955
  error: {
832
- message: result.error.message,
833
- code: result.error.name || 'PASSWORD_UPDATE_ERROR'
956
+ message: error instanceof Error ? error.message : 'An unexpected error occurred',
957
+ code: 'PASSWORD_UPDATE_ERROR'
834
958
  }
835
959
  };
836
960
  }
837
- // The form will handle closing the modal on success
838
- return {};
839
961
  };
840
962
 
841
963
  // CRITICAL: Wait for organisation context to be ready before proceeding
@@ -953,8 +1075,9 @@ export function PaceAppLayout({
953
1075
  navigate(item.href);
954
1076
  }
955
1077
  }}
956
- showEventSelector={showEventSelector}
957
- showOrgSelector={showOrgSelector}
1078
+ showContextSelector={showContextSelector}
1079
+ showOrganisations={showOrganisations}
1080
+ showEvents={showEvents}
958
1081
  showUserMenu={showUserMenu}
959
1082
  className={headerClassName || "sticky top-0 z-[40] w-full"}
960
1083
  />
@@ -27,8 +27,7 @@ A comprehensive application layout component that provides a consistent structur
27
27
  |------|------|---------|-------------|
28
28
  | `appName` | `string` | required | The name of the application to be displayed in the header |
29
29
  | `navItems` | `NavigationItem[]` | optional | Navigation items for the header menu. If not provided, uses default navigation |
30
- | `showEventSelector` | `boolean` | `true` | Show/hide event selector in the header |
31
- | `showOrgSelector` | `boolean` | `false` | Show/hide organisation selector in the header |
30
+ | `showContextSelector` | `boolean` | `true` | Show/hide unified context selector (organisations and events) in the header |
32
31
  | `headerActions` | `React.ReactNode` | optional | Custom actions to display in the header (between event selector and user menu) |
33
32
  | `customLogo` | `React.ReactNode` | optional | Custom logo component (overrides default logo) |
34
33
  | `logoHref` | `string` | `'/dashboard'` | URL to navigate to when logo is clicked (e.g., '/dashboard', '/home') |
@@ -162,7 +161,7 @@ function TeamApp() {
162
161
  <PaceAppLayout
163
162
  appName="TEAM"
164
163
  navItems={teamNavItems}
165
- showEventSelector={false} // Hide event selector
164
+ showContextSelector={false} // Hide context selector
166
165
  />
167
166
  }>
168
167
  <Route index element={<ProfilePage />} />
@@ -231,26 +230,24 @@ function App() {
231
230
 
232
231
  ## When to Use Each Feature
233
232
 
234
- ### Show Event Selector (`showEventSelector={true}`)
233
+ ### Show Context Selector (`showContextSelector={true}`) - Default
235
234
  - Event management applications
236
235
  - Apps with multi-tenant event contexts
237
- - Applications where users need to switch between events
236
+ - Multi-organisation applications
237
+ - Applications where users need to switch between events or organisations
238
238
  - Event planning tools
239
239
  - Event registration systems
240
+ - Hybrid apps (like pace-mint) that support both event and organisation contexts
241
+ - Organisation management tools
242
+ - Multi-tenant applications with organisation context
243
+ - Apps requiring organisation-scoped data access
240
244
 
241
- ### Hide Event Selector (`showEventSelector={false}`)
245
+ ### Hide Context Selector (`showContextSelector={false}`)
242
246
  - Personal profile management apps (like TEAM)
243
247
  - User account settings applications
244
- - Non-event specific tools
248
+ - Apps that don't require event or organisation context
245
249
  - Single-tenant applications
246
- - Administrative dashboards that don't depend on events
247
-
248
- ### Show Organisation Selector (`showOrgSelector={true}`)
249
- - Multi-organisation applications
250
- - Apps where users need to switch between organisations
251
- - Organisation management tools
252
- - Multi-tenant applications with organisation context
253
- - Apps requiring organisation-scoped data access
250
+ - Administrative dashboards that don't depend on events or organisations
254
251
 
255
252
  ### Custom Header Components
256
253
  - Applications requiring custom branding
@@ -267,7 +264,7 @@ function App() {
267
264
 
268
265
  ## Integration with UnifiedAuthProvider
269
266
 
270
- The PaceAppLayout works seamlessly with the UnifiedAuthProvider. When `showEventSelector={false}`, the provider will:
267
+ The PaceAppLayout works seamlessly with the UnifiedAuthProvider. When `showContextSelector={false}`, the provider will:
271
268
 
272
269
  - Still handle user authentication
273
270
  - Provide user profile access
@@ -285,7 +282,7 @@ function App() {
285
282
  <Route path="/" element={
286
283
  <PaceAppLayout
287
284
  appName="TEAM"
288
- showEventSelector={false} // No events needed
285
+ showContextSelector={false} // No context selection needed
289
286
  />
290
287
  }>
291
288
  <Route index element={<ProfilePage />} />
@@ -1,3 +1,4 @@
1
+ /// <reference types="vitest/globals" />
1
2
  /**
2
3
  * @file Shared Test Setup for PaceAppLayout Tests
3
4
  * @package @jmruthers/pace-core
@@ -10,8 +11,6 @@
10
11
  * provides the shared mock data and reset functions that can be imported and used
11
12
  * in the vi.mock() factory functions.
12
13
  */
13
-
14
- import { vi } from 'vitest';
15
14
  import React from 'react';
16
15
 
17
16
  // === MOCK DATA ===
@@ -118,7 +117,7 @@ export const createMockHeader = () => ({
118
117
  onChangePassword,
119
118
  currentPath,
120
119
  onNavigate,
121
- showEventSelector,
120
+ showContextSelector,
122
121
  showUserMenu,
123
122
  className
124
123
  }: any) => (
@@ -157,7 +156,7 @@ export const createMockHeader = () => ({
157
156
  Navigate
158
157
  </button>
159
158
  <div data-testid="current-path">{currentPath}</div>
160
- <div data-testid="show-event-selector">{showEventSelector !== false ? 'true' : 'false'}</div>
159
+ <div data-testid="show-context-selector">{showContextSelector !== false ? 'true' : 'false'}</div>
161
160
  </header>
162
161
  )
163
162
  });
@@ -131,6 +131,9 @@ import { clearPalette } from '../../theming/runtime';
131
131
  import { EventServiceContext } from '../../providers/services/EventServiceProvider';
132
132
  import { logger } from '../../utils/core/logger';
133
133
 
134
+ /**
135
+ * Props for the PaceLoginPage component.
136
+ */
134
137
  export interface PaceLoginPageProps {
135
138
  /** The name of the application to be displayed on the login form. */
136
139
  appName: string;
@@ -103,16 +103,25 @@ import { Input } from '../Input/Input';
103
103
  import { Label } from '../Label';
104
104
  import { cn } from '../../utils/core/cn';
105
105
 
106
+ /**
107
+ * Form values for password change.
108
+ */
106
109
  export interface PasswordChangeFormValues {
107
110
  newPassword: string;
108
111
  confirmPassword: string;
109
112
  }
110
113
 
114
+ /**
115
+ * Error structure for password change form.
116
+ */
111
117
  export interface PasswordChangeFormError {
112
118
  message?: string;
113
119
  code?: string;
114
120
  }
115
121
 
122
+ /**
123
+ * Props for the PasswordChangeForm component.
124
+ */
116
125
  export interface PasswordChangeFormProps {
117
126
  onSubmit: (values: PasswordChangeFormValues) => Promise<{ error?: PasswordChangeFormError }>;
118
127
  className?: string;
@@ -79,6 +79,9 @@ import { Alert, AlertDescription, AlertTitle } from '../Alert/Alert';
79
79
  import { logger } from '../../utils/core/logger';
80
80
  import { usePreventTabReload } from '../../hooks/usePreventTabReload';
81
81
 
82
+ /**
83
+ * Props for the ProtectedRoute component.
84
+ */
82
85
  export interface ProtectedRouteProps {
83
86
  /**
84
87
  * Whether an event is required for routes inside this component.
@@ -88,14 +91,6 @@ export interface ProtectedRouteProps {
88
91
  */
89
92
  requireEvent?: boolean;
90
93
 
91
- /**
92
- * Whether super admins can bypass event requirement.
93
- * Note: This feature requires additional RBAC setup. For simple bypass, set requireEvent={false} instead.
94
- * @default false
95
- * @deprecated Use requireEvent={false} for routes that don't need events
96
- */
97
- allowSuperAdminBypass?: boolean;
98
-
99
94
  /**
100
95
  * Custom component to render when no events are available.
101
96
  * If not provided, a default message is shown.
@@ -133,7 +128,6 @@ export interface ProtectedRouteProps {
133
128
  */
134
129
  export function ProtectedRoute({
135
130
  requireEvent = false,
136
- allowSuperAdminBypass = false,
137
131
  noEventsFallback,
138
132
  loadingFallback,
139
133
  loginPath = '/login'
@@ -35,9 +35,9 @@
35
35
  * refetch={refetch}
36
36
  * >
37
37
  * <h1>Event Details</h1>
38
- * <div className="content">
38
+ * <main className="content">
39
39
  * Your public page content
40
- * </div>
40
+ * </main>
41
41
  * </PublicPageLayout>
42
42
  * );
43
43
  * }
@@ -90,8 +90,6 @@ export interface PublicPageLayoutProps {
90
90
  refetch?: () => Promise<void> | void;
91
91
  /** Whether to show the footer (default: true) */
92
92
  showFooter?: boolean;
93
- /** @deprecated Custom CSS classes for the layout - no longer used as wrapper div was removed */
94
- className?: string;
95
93
  /** Custom error fallback component */
96
94
  errorFallback?: React.ComponentType<{ error: Error; retry: () => void }>;
97
95
  /** Custom loading fallback component */
@@ -294,7 +292,6 @@ export function PublicPageLayout({
294
292
  error = null,
295
293
  refetch,
296
294
  showFooter = true,
297
- className = '',
298
295
  errorFallback: ErrorFallback,
299
296
  loadingFallback: LoadingFallback,
300
297
  customHeader,
@@ -50,6 +50,10 @@ interface PublicPageContextType {
50
50
  };
51
51
  }
52
52
 
53
+ /**
54
+ * Context for public pages.
55
+ * Provides isolated context for public pages without authentication requirements.
56
+ */
53
57
  export const PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);
54
58
 
55
59
  export interface PublicPageProviderProps {