@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
@@ -427,7 +427,7 @@ const { data: files } = await supabase
427
427
 
428
428
  **Migration:** `20251205211120_migrate_profiles_to_person_scoped.sql` + related migrations
429
429
  **Date:** 2025-12-05
430
- **Documentation:** See [Person-Scoped Profiles Migration Guide](./person-scoped-profiles-migration-guide.md)
430
+ **Documentation:** See [Person-Scoped Profiles Migration Guide](./V0.5.190_person-scoped-profiles-migration-guide.md)
431
431
 
432
432
  ### Quick Summary
433
433
 
@@ -501,7 +501,7 @@ const { data } = await supabase
501
501
  - [ ] Update `pace_contact` queries to use `person_id` instead of `member_id`
502
502
  - [ ] Update profile creation logic (no `organisation_id` needed)
503
503
  - [ ] Review profile access patterns (person-scoped, not org-scoped)
504
- - [ ] See [Person-Scoped Profiles Migration Guide](./person-scoped-profiles-migration-guide.md) for detailed checklist
504
+ - [ ] See [Person-Scoped Profiles Migration Guide](./V0.5.190_person-scoped-profiles-migration-guide.md) for detailed checklist
505
505
 
506
506
  ### Phase 3: Testing
507
507
 
@@ -708,7 +708,7 @@ All migration files are located in:
708
708
 
709
709
  ### Documentation
710
710
 
711
- - [Person-Scoped Profiles Migration Guide](./person-scoped-profiles-migration-guide.md)
711
+ - [Person-Scoped Profiles Migration Guide](./V0.5.190_person-scoped-profiles-migration-guide.md)
712
712
  - [API & RPC Standard](../standards/02-api-and-rpc-standard.md)
713
713
  - [Database Schema Requirements](../architecture/database-schema-requirements.md)
714
714
 
@@ -0,0 +1,218 @@
1
+ # Fix for Auto-Selection Issue in pace-mint AppLayout
2
+
3
+ ## Problem
4
+
5
+ When a user switches from an event to an organisation in the context selector, pace-mint's AppLayout component is auto-selecting the event again, even though the user explicitly cleared it. This causes the selector to show the event instead of the organisation.
6
+
7
+ ## Root Cause
8
+
9
+ pace-mint's AppLayout has its own auto-selection logic that runs whenever:
10
+ - Events are loaded/refreshed
11
+ - `selectedEvent` becomes `null`
12
+ - There's only one event available
13
+
14
+ This logic doesn't distinguish between:
15
+ - **Initial load** (should auto-select)
16
+ - **User explicitly cleared event** (should NOT auto-select)
17
+
18
+ ## Solution
19
+
20
+ Update pace-mint's AppLayout component to track when the user explicitly clears the event selection, and skip auto-selection in those cases.
21
+
22
+ ## Implementation Steps
23
+
24
+ ### Step 1: Track Previous Event Selection
25
+
26
+ Add a ref to track the previous `selectedEvent` value to detect when the user explicitly clears it:
27
+
28
+ ```tsx
29
+ import { useRef, useEffect } from 'react';
30
+ import { useEvents } from '@jmruthers/pace-core';
31
+
32
+ function AppLayout() {
33
+ const { events, selectedEvent, setSelectedEvent } = useEvents();
34
+
35
+ // Track previous selectedEvent to detect explicit clears
36
+ const previousSelectedEventRef = useRef<Event | null>(null);
37
+ const userExplicitlyClearedRef = useRef<boolean>(false);
38
+
39
+ // Detect when user explicitly clears event (was set, now null)
40
+ useEffect(() => {
41
+ const wasSet = previousSelectedEventRef.current !== null;
42
+ const isNowNull = selectedEvent === null;
43
+
44
+ if (wasSet && isNowNull) {
45
+ // User explicitly cleared the event
46
+ userExplicitlyClearedRef.current = true;
47
+ } else if (selectedEvent !== null) {
48
+ // Event is selected, reset the flag
49
+ userExplicitlyClearedRef.current = false;
50
+ }
51
+
52
+ previousSelectedEventRef.current = selectedEvent;
53
+ }, [selectedEvent]);
54
+
55
+ // ... rest of component
56
+ }
57
+ ```
58
+
59
+ ### Step 2: Update Auto-Selection Logic
60
+
61
+ Modify your auto-selection logic to check the flag before auto-selecting:
62
+
63
+ ```tsx
64
+ // OLD CODE (problematic):
65
+ useEffect(() => {
66
+ if (!selectedEvent && events.length === 1 && !isLoading) {
67
+ // Auto-select single event
68
+ setSelectedEvent(events[0]);
69
+ }
70
+ }, [selectedEvent, events, isLoading, setSelectedEvent]);
71
+
72
+ // NEW CODE (fixed):
73
+ useEffect(() => {
74
+ // Only auto-select if:
75
+ // 1. No event is currently selected
76
+ // 2. There's exactly one event available
77
+ // 3. Events are not loading
78
+ // 4. User did NOT explicitly clear the event
79
+ if (
80
+ !selectedEvent &&
81
+ events.length === 1 &&
82
+ !isLoading &&
83
+ !userExplicitlyClearedRef.current
84
+ ) {
85
+ // Auto-select single event
86
+ setSelectedEvent(events[0]);
87
+ }
88
+ }, [selectedEvent, events, isLoading, setSelectedEvent]);
89
+ ```
90
+
91
+ ### Step 3: Reset Flag on Initial Load (Optional)
92
+
93
+ If you want to allow auto-selection on initial page load but prevent it after user actions, you can track whether this is the initial load:
94
+
95
+ ```tsx
96
+ const isInitialLoadRef = useRef<boolean>(true);
97
+
98
+ useEffect(() => {
99
+ // After first render, mark initial load as complete
100
+ if (isInitialLoadRef.current && events.length > 0) {
101
+ isInitialLoadRef.current = false;
102
+ }
103
+ }, [events]);
104
+
105
+ // Then in auto-selection logic:
106
+ useEffect(() => {
107
+ if (
108
+ !selectedEvent &&
109
+ events.length === 1 &&
110
+ !isLoading &&
111
+ (!userExplicitlyClearedRef.current || isInitialLoadRef.current)
112
+ ) {
113
+ setSelectedEvent(events[0]);
114
+ }
115
+ }, [selectedEvent, events, isLoading, setSelectedEvent]);
116
+ ```
117
+
118
+ ## Complete Example
119
+
120
+ Here's a complete example of the fix:
121
+
122
+ ```tsx
123
+ import { useRef, useEffect } from 'react';
124
+ import { useEvents } from '@jmruthers/pace-core';
125
+ import type { Event } from '@jmruthers/pace-core';
126
+
127
+ function AppLayout() {
128
+ const { events, selectedEvent, setSelectedEvent, isLoading } = useEvents();
129
+
130
+ // Track previous selectedEvent to detect explicit clears
131
+ const previousSelectedEventRef = useRef<Event | null>(null);
132
+ const userExplicitlyClearedRef = useRef<boolean>(false);
133
+ const isInitialLoadRef = useRef<boolean>(true);
134
+
135
+ // Detect when user explicitly clears event
136
+ useEffect(() => {
137
+ const wasSet = previousSelectedEventRef.current !== null;
138
+ const isNowNull = selectedEvent === null;
139
+
140
+ if (wasSet && isNowNull) {
141
+ // User explicitly cleared the event (e.g., switched to organisation)
142
+ userExplicitlyClearedRef.current = true;
143
+ console.log('[AppLayout] User explicitly cleared event - preventing auto-selection');
144
+ } else if (selectedEvent !== null) {
145
+ // Event is selected, reset the flag
146
+ userExplicitlyClearedRef.current = false;
147
+ }
148
+
149
+ previousSelectedEventRef.current = selectedEvent;
150
+ }, [selectedEvent]);
151
+
152
+ // Mark initial load as complete after events are loaded
153
+ useEffect(() => {
154
+ if (isInitialLoadRef.current && events.length > 0 && !isLoading) {
155
+ isInitialLoadRef.current = false;
156
+ }
157
+ }, [events, isLoading]);
158
+
159
+ // Auto-select single event ONLY if:
160
+ // - No event is selected
161
+ // - Exactly one event available
162
+ // - Not loading
163
+ // - User didn't explicitly clear it (or it's initial load)
164
+ useEffect(() => {
165
+ const shouldAutoSelect =
166
+ !selectedEvent &&
167
+ events.length === 1 &&
168
+ !isLoading &&
169
+ (!userExplicitlyClearedRef.current || isInitialLoadRef.current);
170
+
171
+ if (shouldAutoSelect) {
172
+ console.log('[AppLayout] Auto-selecting single available event');
173
+ setSelectedEvent(events[0]);
174
+ } else if (!selectedEvent && events.length === 1 && userExplicitlyClearedRef.current) {
175
+ console.log('[AppLayout] Auto-select skipped - user explicitly cleared event');
176
+ }
177
+ }, [selectedEvent, events, isLoading, setSelectedEvent]);
178
+
179
+ // ... rest of your component
180
+ }
181
+ ```
182
+
183
+ ## Testing
184
+
185
+ After implementing the fix, test the following scenarios:
186
+
187
+ 1. **Initial Load**:
188
+ - ✅ Should auto-select if only one event is available
189
+
190
+ 2. **Switch Event to Organisation**:
191
+ - ✅ Should NOT auto-select event after switching to organisation
192
+ - ✅ Selector should show the organisation, not the event
193
+
194
+ 3. **Switch Organisation to Event**:
195
+ - ✅ Should select the event when user explicitly selects it
196
+ - ✅ Selector should show the event
197
+
198
+ 4. **Manual Event Selection**:
199
+ - ✅ Should select the event when user clicks on it
200
+ - ✅ Should not interfere with manual selections
201
+
202
+ ## Additional Notes
203
+
204
+ - pace-core's EventService already has internal logic to prevent auto-selection when `userClearedEventRef` is set
205
+ - This fix adds an additional layer of protection at the application level
206
+ - The ref-based approach ensures the flag persists across re-renders
207
+ - The initial load flag allows auto-selection on first page load while preventing it after user actions
208
+
209
+ ## Related pace-core Changes
210
+
211
+ The following changes were made in pace-core to support this fix:
212
+
213
+ 1. **ContextSelector** - Now prioritizes event selection over organisation when both are selected
214
+ 2. **Header Component** - Clears event selection when switching to organisation
215
+ 3. **EventService** - Preserves `userClearedEventRef` flag when organisation changes
216
+
217
+ These changes ensure that pace-core respects user intent, but consuming apps (like pace-mint) should also implement the above fix to prevent their own auto-selection logic from interfering.
218
+
@@ -0,0 +1,391 @@
1
+ ---
2
+ lastUpdated: 2025-12-31T00:00:00+11:00
3
+ version: 0.6.4
4
+ reviewedBy: pace-mint-implementation-guide
5
+ ---
6
+
7
+ # pace-mint RBAC Setup Guide
8
+
9
+ This guide explains how to configure pace-mint to support both organisation-based and event-based pages using pace-core's RBAC system.
10
+
11
+ ## Overview
12
+
13
+ pace-mint is a **hybrid app** that supports both:
14
+ - **Organisation-based pages**: Financial management at the organisation level
15
+ - **Event-based pages**: Budget and budget variables for specific events
16
+
17
+ Users can have:
18
+ - Organisation roles (org_admin, leader, member, etc.) for organisation-based pages
19
+ - Event-app roles (planner, participant, etc.) for event-based pages
20
+ - Both types of roles simultaneously
21
+
22
+ ## Architecture
23
+
24
+ ### Two-Stream Navigation
25
+
26
+ pace-mint uses a **two-stream architecture**:
27
+
28
+ 1. **Landing/Dashboard Page**: User selects their mode
29
+ - "Organisation Financial Management" → Organisation stream
30
+ - "Event Financial Management" → Event stream
31
+
32
+ 2. **Each Stream**: Has its own navigation showing only relevant pages
33
+ - Organisation stream: Shows only organisation-scoped pages
34
+ - Event stream: Shows only event-scoped pages
35
+
36
+ ### Page Scoping
37
+
38
+ Each page in pace-mint is marked with a `scope_type`:
39
+
40
+ - `'event'`: Page requires event context (e.g., "budget", "budget-variables")
41
+ - `'organisation'`: Page requires organisation context (e.g., "dashboard", "reports")
42
+ - `'both'`: Page can be accessed in either context (rare, but possible)
43
+
44
+ **Note**: All pages must have `scope_type` set explicitly. There is no inheritance from app-level configuration.
45
+
46
+ ## Database Setup
47
+
48
+ ### 1. Ensure pace-mint App Configuration
49
+
50
+ ```sql
51
+ -- Verify pace-mint app exists and is configured correctly
52
+ SELECT id, name, display_name, is_active
53
+ FROM rbac_apps
54
+ WHERE name = 'MINT';
55
+
56
+ -- Note: App-level scope configuration (requires_event) has been removed.
57
+ -- All scope is now configured at the page level via scope_type.
58
+ ```
59
+
60
+ ### 2. Create/Update Pages with scope_type
61
+
62
+ For each page in pace-mint, set the `scope_type`:
63
+
64
+ ```sql
65
+ -- Example: Budget page is event-based
66
+ UPDATE rbac_app_pages
67
+ SET scope_type = 'event'
68
+ WHERE app_id = (SELECT id FROM rbac_apps WHERE name = 'MINT')
69
+ AND page_name = 'budget';
70
+
71
+ -- Example: Dashboard page is organisation-based (or NULL to inherit)
72
+ UPDATE rbac_app_pages
73
+ SET scope_type = 'organisation'
74
+ WHERE app_id = (SELECT id FROM rbac_apps WHERE name = 'MINT')
75
+ AND page_name = 'dashboard';
76
+
77
+ -- Example: Budget variables page is event-based
78
+ UPDATE rbac_app_pages
79
+ SET scope_type = 'event'
80
+ WHERE app_id = (SELECT id FROM rbac_apps WHERE name = 'MINT')
81
+ AND page_name = 'budget-variables';
82
+ ```
83
+
84
+ **Important**:
85
+ - All pages must have `scope_type` set explicitly (no NULL values allowed)
86
+ - Set `scope_type` to match the page's context requirements
87
+ - There is no inheritance - each page declares its own scope
88
+
89
+ ### 3. Configure Page Permissions
90
+
91
+ Set up permissions for each page based on its scope:
92
+
93
+ #### Event-Based Pages (e.g., "budget")
94
+
95
+ ```sql
96
+ -- Get the page ID
97
+ WITH mint_app AS (
98
+ SELECT id FROM rbac_apps WHERE name = 'MINT'
99
+ ),
100
+ budget_page AS (
101
+ SELECT ap.id as page_id
102
+ FROM rbac_app_pages ap
103
+ JOIN mint_app ma ON ap.app_id = ma.id
104
+ WHERE ap.page_name = 'budget'
105
+ )
106
+ -- Insert permissions for event-based roles
107
+ INSERT INTO rbac_page_permissions (app_page_id, operation, role_name, allowed, organisation_id)
108
+ SELECT
109
+ bp.page_id,
110
+ op.operation,
111
+ role.role_name,
112
+ CASE
113
+ WHEN role.role_name = 'planner' THEN true
114
+ WHEN role.role_name = 'participant' AND op.operation = 'read' THEN true
115
+ ELSE false
116
+ END,
117
+ '00000000-0000-0000-0000-000000000000'::uuid -- ⚠️ REPLACE with actual organisation ID
118
+ FROM budget_page bp
119
+ CROSS JOIN (SELECT unnest(ARRAY['read', 'create', 'update', 'delete']) as operation) op
120
+ CROSS JOIN (SELECT unnest(ARRAY['planner', 'participant', 'viewer']) as role_name) role;
121
+ ```
122
+
123
+ #### Organisation-Based Pages (e.g., "dashboard")
124
+
125
+ ```sql
126
+ -- Get the page ID
127
+ WITH mint_app AS (
128
+ SELECT id FROM rbac_apps WHERE name = 'MINT'
129
+ ),
130
+ dashboard_page AS (
131
+ SELECT ap.id as page_id
132
+ FROM rbac_app_pages ap
133
+ JOIN mint_app ma ON ap.app_id = ma.id
134
+ WHERE ap.page_name = 'dashboard'
135
+ )
136
+ -- Insert permissions for organisation-based roles
137
+ INSERT INTO rbac_page_permissions (app_page_id, operation, role_name, allowed, organisation_id)
138
+ SELECT
139
+ dp.page_id,
140
+ op.operation,
141
+ role.role_name,
142
+ CASE
143
+ WHEN role.role_name = 'org_admin' THEN true
144
+ WHEN role.role_name = 'leader' AND op.operation != 'delete' THEN true
145
+ WHEN role.role_name = 'member' AND op.operation = 'read' THEN true
146
+ ELSE false
147
+ END,
148
+ '00000000-0000-0000-0000-000000000000'::uuid -- ⚠️ REPLACE with actual organisation ID
149
+ FROM dashboard_page dp
150
+ CROSS JOIN (SELECT unnest(ARRAY['read', 'create', 'update', 'delete']) as operation) op
151
+ CROSS JOIN (SELECT unnest(ARRAY['org_admin', 'leader', 'member', 'supporter']) as role_name) role;
152
+ ```
153
+
154
+ ## pace-core Components
155
+
156
+ ### 1. Context Selector Component
157
+
158
+ pace-core provides a unified `ContextSelector` component that intelligently shows all organisations and events a user can access:
159
+
160
+ ```tsx
161
+ import { ContextSelector } from '@jmruthers/pace-core';
162
+
163
+ <ContextSelector
164
+ onOrganisationSelect={(org) => {
165
+ // Switch to organisation stream
166
+ setSelectedOrganisation(org);
167
+ setSelectedEvent(null);
168
+ navigate('/dashboard'); // Navigate to org-based dashboard
169
+ }}
170
+ onEventSelect={(event) => {
171
+ // Switch to event stream
172
+ setSelectedEvent(event);
173
+ setSelectedOrganisation(null);
174
+ navigate('/budget'); // Navigate to event-based budget page
175
+ }}
176
+ />
177
+ ```
178
+
179
+ The `ContextSelector` automatically shows:
180
+ - All organisations the user has access to (via organisation roles)
181
+ - All events the user has access to (via event-app roles or organisation membership)
182
+ - Everything for super admins
183
+
184
+ ### 2. Navigation Filtering
185
+
186
+ pace-core's navigation automatically filters pages based on:
187
+ - Current context (event selected vs organisation selected)
188
+ - Page `scope_type`
189
+ - User's permissions
190
+
191
+ You don't need to manually filter - pace-core handles this automatically.
192
+
193
+ ### 3. Permission Checking
194
+
195
+ pace-core automatically handles permission checking based on page scope:
196
+
197
+ - **Event-based pages**: Checks event-app roles for the selected event
198
+ - **Organisation-based pages**: Checks organisation roles for the selected organisation
199
+ - **'both' pages**: Checks both scopes and returns the union (higher permissions win)
200
+
201
+ ## Implementation Steps
202
+
203
+ ### Step 1: Database Configuration
204
+
205
+ 1. Run the pace-core migration that adds `scope_type` to `rbac_app_pages`
206
+ 2. Update all pace-mint pages with appropriate `scope_type` values
207
+ 3. Configure page permissions for each scope type
208
+
209
+ ### Step 2: Landing Page
210
+
211
+ Create a landing page where users choose their mode:
212
+
213
+ ```tsx
214
+ function MintLandingPage() {
215
+ const { selectedOrganisation, selectedEvent } = useUnifiedAuth();
216
+ const navigate = useNavigate();
217
+
218
+ const handleOrgMode = () => {
219
+ // Ensure organisation is selected
220
+ if (!selectedOrganisation) {
221
+ // Show organisation selector
222
+ return;
223
+ }
224
+ navigate('/dashboard');
225
+ };
226
+
227
+ const handleEventMode = () => {
228
+ // Ensure event is selected
229
+ if (!selectedEvent) {
230
+ // Show event selector
231
+ return;
232
+ }
233
+ navigate('/budget');
234
+ };
235
+
236
+ return (
237
+ <main>
238
+ <section>
239
+ <h1>pace-mint</h1>
240
+ <button onClick={handleOrgMode}>
241
+ Organisation Financial Management
242
+ </button>
243
+ <button onClick={handleEventMode}>
244
+ Event Financial Management
245
+ </button>
246
+ </section>
247
+ </main>
248
+ );
249
+ }
250
+ ```
251
+
252
+ ### Step 3: Navigation Configuration
253
+
254
+ Configure navigation items with proper page names:
255
+
256
+ ```tsx
257
+ const orgNavItems = [
258
+ { label: 'Dashboard', pageName: 'dashboard', href: '/dashboard' },
259
+ { label: 'Reports', pageName: 'reports', href: '/reports' },
260
+ // ... other org-based pages
261
+ ];
262
+
263
+ const eventNavItems = [
264
+ { label: 'Budget', pageName: 'budget', href: '/budget' },
265
+ { label: 'Budget Variables', pageName: 'budget-variables', href: '/budget-variables' },
266
+ // ... other event-based pages
267
+ ];
268
+ ```
269
+
270
+ ### Step 4: Use Context Selector in Header
271
+
272
+ The `PaceAppLayout` component includes the unified `ContextSelector` by default:
273
+
274
+ ```tsx
275
+ import { PaceAppLayout } from '@jmruthers/pace-core';
276
+
277
+ function App() {
278
+ const { selectedOrganisation, selectedEvent, setSelectedOrganisation, setSelectedEvent } = useUnifiedAuth();
279
+ const navigate = useNavigate();
280
+
281
+ return (
282
+ <PaceAppLayout
283
+ appName="MINT"
284
+ navItems={/* your nav items */}
285
+ showContextSelector={true} // Default: shows unified selector
286
+ />
287
+ );
288
+ }
289
+ ```
290
+
291
+ The `ContextSelector` is automatically integrated into the header and handles organisation/event selection. You can also use it directly if you need custom behavior:
292
+
293
+ ```tsx
294
+ import { ContextSelector } from '@jmruthers/pace-core';
295
+
296
+ <ContextSelector
297
+ onOrganisationSelect={(org) => {
298
+ setSelectedOrganisation(org);
299
+ setSelectedEvent(null);
300
+ navigate('/dashboard');
301
+ }}
302
+ onEventSelect={(event) => {
303
+ setSelectedEvent(event);
304
+ setSelectedOrganisation(null);
305
+ navigate('/budget');
306
+ }}
307
+ />
308
+ ```
309
+
310
+ ### Step 5: Page Protection
311
+
312
+ Use `PagePermissionGuard` for all pages:
313
+
314
+ ```tsx
315
+ import { PagePermissionGuard } from '@jmruthers/pace-core/rbac';
316
+
317
+ function BudgetPage() {
318
+ return (
319
+ <PagePermissionGuard pageName="budget" operation="read">
320
+ {/* Budget page content */}
321
+ </PagePermissionGuard>
322
+ );
323
+ }
324
+ ```
325
+
326
+ ## Permission Precedence
327
+
328
+ When a user has both organisation and event permissions for a 'both' page:
329
+
330
+ 1. **Union of permissions**: User gets permissions from both scopes
331
+ 2. **Higher order wins**: More permissive permission takes precedence
332
+ - Access levels: `super` > `admin` > `user` > `none`
333
+ - Permission values: `true` > `false`
334
+
335
+ Example:
336
+ - User has `read:budget` at org level (org_admin)
337
+ - User has `write:budget` at event level (planner)
338
+ - Result: User gets `write:budget` (higher permission wins)
339
+
340
+ ## Super Admin Behavior
341
+
342
+ Super admins:
343
+ - See **all events** in the hybrid selector
344
+ - See **all organisations** in the hybrid selector
345
+ - Can access **all pages** regardless of scope
346
+ - Bypass all permission checks
347
+
348
+ ## Testing Checklist
349
+
350
+ - [ ] All pages have correct `scope_type` set in database
351
+ - [ ] Event-based pages only accessible when event is selected
352
+ - [ ] Organisation-based pages only accessible when organisation is selected
353
+ - [ ] Navigation filters correctly based on current context
354
+ - [ ] Context selector shows all accessible orgs and events
355
+ - [ ] Permission checks work correctly for each scope type
356
+ - [ ] 'both' pages return union of permissions
357
+ - [ ] Super admins can access all pages
358
+ - [ ] Users with both org and event roles see appropriate pages
359
+
360
+ ## Troubleshooting
361
+
362
+ ### Pages not showing in navigation
363
+
364
+ 1. Check page `scope_type` matches current context
365
+ 2. Verify user has permissions for the page
366
+ 3. Ensure page exists in `rbac_app_pages` table
367
+ 4. Check page permissions are configured in `rbac_page_permissions`
368
+
369
+ ### Permission denied errors
370
+
371
+ 1. Verify user has the required role for the page
372
+ 2. Check page `scope_type` matches selected context
373
+ 3. Ensure permissions are configured for the correct organisation/event
374
+ 4. Verify user's roles are active (valid_from/valid_to dates)
375
+
376
+ ### Context selector not showing items
377
+
378
+ 1. Check user has organisation memberships or event-app roles
379
+ 2. Verify organisations/events are active
380
+ 3. Check RLS policies allow user to see the items
381
+ 4. Ensure super admin status if testing as super admin
382
+
383
+ ## Related Documentation
384
+
385
+ - [RBAC Scope Migration Guide](../migration/RBAC_SCOPE_MIGRATION.md) - Complete migration guide
386
+ - [Context Selector Component](../components/context-selector.md) - Unified selector documentation
387
+ - [RBAC System Overview](../rbac/README.md)
388
+ - [Event-Based Apps](../rbac/event-based-apps.md)
389
+ - [Navigation Filtering](../components/NavigationMenu.md)
390
+ - [Page Permission Guard](../rbac/components/PagePermissionGuard.md)
391
+
@@ -973,7 +973,7 @@ VALUES (
973
973
  - **[API Reference](./api-reference.md)** - Complete API documentation
974
974
  - **[Examples](./examples.md)** - More complex usage patterns
975
975
  - **[Troubleshooting](./troubleshooting.md)** - Advanced troubleshooting
976
- - **[Migration Guide](../migration/rbac-migration.md)** - If migrating from legacy RBAC
976
+ - **[Migration Guide](../migration/V0.4.0_rbac-migration.md)** - If migrating from legacy RBAC
977
977
 
978
978
  ## 🆘 Still Having Issues?
979
979
 
@@ -843,7 +843,7 @@ function AdaptiveComponent({ appId }) {
843
843
  - [Examples](./examples.md) - More complex examples
844
844
  - [Event-Based Apps](./event-based-apps.md) - Guide for event-based applications
845
845
  - [Troubleshooting](./troubleshooting.md) - Common issues and solutions
846
- - [Migration Guide](../migration/rbac-migration.md) - Migrating from legacy RBAC
846
+ - [Migration Guide](../migration/V0.4.0_rbac-migration.md) - Migrating from legacy RBAC
847
847
 
848
848
  ## ♿ Accessibility
849
849
 
@@ -740,7 +740,7 @@ The system will automatically resolve the organisation ID from the event context
740
740
  - **[API Reference](./api-reference.md)** - Complete API documentation
741
741
  - **[Examples](./examples.md)** - More complex usage patterns
742
742
  - **[Troubleshooting](./troubleshooting.md)** - Advanced troubleshooting
743
- - **[Migration Guide](../migration/rbac-migration.md)** - If migrating from legacy RBAC
743
+ - **[Migration Guide](../migration/V0.4.0_rbac-migration.md)** - If migrating from legacy RBAC
744
744
 
745
745
  ## 🆘 Still Having Issues?
746
746