@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
@@ -53,6 +53,16 @@ import { Tooltip } from '../Tooltip';
53
53
  // BASE BUTTON COMPONENT
54
54
  // ============================================================================
55
55
 
56
+ /**
57
+ * Button component props
58
+ * Extends standard HTML button attributes with button-specific styling and behavior options.
59
+ *
60
+ * @interface ButtonProps
61
+ */
62
+ /**
63
+ * Props for the Button component.
64
+ * Extends standard button HTML attributes.
65
+ */
56
66
  export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
57
67
  /** Visual variant of the button */
58
68
  variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
@@ -127,6 +137,12 @@ Button.displayName = 'Button';
127
137
  // BUTTON GROUP COMPONENT
128
138
  // ============================================================================
129
139
 
140
+ /**
141
+ * ButtonGroup component props
142
+ * Configuration for grouping multiple buttons with consistent styling and spacing.
143
+ *
144
+ * @interface ButtonGroupProps
145
+ */
130
146
  export type ButtonGroupProps = {
131
147
  /** Child elements to be rendered in the group */
132
148
  children: React.ReactNode;
@@ -216,6 +232,12 @@ export function ButtonGroup({
216
232
  // ICON BUTTON COMPONENT
217
233
  // ============================================================================
218
234
 
235
+ /**
236
+ * IconButton component props
237
+ * Extends ButtonProps but requires an icon and aria-label instead of children.
238
+ *
239
+ * @interface IconButtonProps
240
+ */
219
241
  export type IconButtonProps = Omit<ButtonProps, 'children'> & {
220
242
  /** The icon element to display in the button */
221
243
  icon: React.ReactNode;
@@ -89,6 +89,10 @@ type RootProps = {
89
89
  // CALENDAR COMPONENT
90
90
  // ============================================================================
91
91
 
92
+ /**
93
+ * Props for the Calendar component.
94
+ * Extends DayPickerProps with pace-core specific customizations.
95
+ */
92
96
  export interface CalendarProps extends Omit<DayPickerProps, 'className' | 'classNames' | 'styles' | 'onSelect'> {
93
97
  /**
94
98
  * Additional CSS classes to apply to the calendar table
@@ -416,8 +420,10 @@ const Calendar = React.forwardRef<HTMLTableElement, CalendarProps>(
416
420
  </>
417
421
  );
418
422
  }
419
-
420
- return child;
423
+
424
+ return React.isValidElement(child)
425
+ ? React.cloneElement(child, { key: child.key ?? `calendar-child-${index}` })
426
+ : child;
421
427
  })}
422
428
  </>
423
429
  );
@@ -65,6 +65,10 @@ import * as React from "react"
65
65
  import { useNavigate } from "react-router-dom"
66
66
  import { cn } from "../../utils/core/cn"
67
67
 
68
+ /**
69
+ * Props for the Card component.
70
+ * Extends standard HTML attributes for semantic HTML elements.
71
+ */
68
72
  export interface CardProps extends React.HTMLAttributes<HTMLElement> {
69
73
  /** Visual variant of the card */
70
74
  variant?: 'default' | 'outline' | 'ghost';
@@ -235,10 +235,10 @@ describe('Checkbox Component', () => {
235
235
 
236
236
  it('supports aria-labelledby', () => {
237
237
  renderWithProviders(
238
- <div>
238
+ <>
239
239
  <label id="terms-label">Accept terms and conditions</label>
240
240
  <Checkbox aria-labelledby="terms-label" />
241
- </div>
241
+ </>
242
242
  );
243
243
  const checkbox = screen.getByRole('checkbox', { name: 'Accept terms and conditions' });
244
244
  expect(checkbox).toBeInTheDocument();
@@ -246,10 +246,10 @@ describe('Checkbox Component', () => {
246
246
 
247
247
  it('supports aria-describedby', () => {
248
248
  renderWithProviders(
249
- <div>
249
+ <>
250
250
  <Checkbox aria-describedby="terms-help" />
251
- <div id="terms-help">Please read the terms carefully</div>
252
- </div>
251
+ <span id="terms-help">Please read the terms carefully</span>
252
+ </>
253
253
  );
254
254
  const checkbox = screen.getByRole('checkbox');
255
255
  expect(checkbox).toHaveAttribute('aria-describedby', 'terms-help');
@@ -319,10 +319,10 @@ describe('Checkbox Component', () => {
319
319
  describe('Form Integration', () => {
320
320
  it('works with form labels', () => {
321
321
  renderWithProviders(
322
- <div>
322
+ <>
323
323
  <label htmlFor="terms-checkbox">Accept terms and conditions</label>
324
324
  <Checkbox id="terms-checkbox" />
325
- </div>
325
+ </>
326
326
  );
327
327
 
328
328
  const checkbox = screen.getByRole('checkbox');
@@ -388,11 +388,11 @@ describe('Checkbox Component', () => {
388
388
  describe('Integration', () => {
389
389
  it('works with multiple checkboxes', () => {
390
390
  renderWithProviders(
391
- <div>
391
+ <>
392
392
  <Checkbox id="option1" />
393
393
  <Checkbox id="option2" />
394
394
  <Checkbox id="option3" />
395
- </div>
395
+ </>
396
396
  );
397
397
 
398
398
  expect(screen.getAllByRole('checkbox')).toHaveLength(3);
@@ -420,13 +420,13 @@ describe('Checkbox Component', () => {
420
420
  const TestComponent = () => {
421
421
  const [checked, setChecked] = React.useState(false);
422
422
  return (
423
- <div>
423
+ <>
424
424
  <Checkbox
425
425
  checked={checked}
426
426
  onCheckedChange={setChecked}
427
427
  />
428
428
  <span data-testid="status">{checked ? 'checked' : 'unchecked'}</span>
429
- </div>
429
+ </>
430
430
  );
431
431
  };
432
432
 
@@ -470,7 +470,7 @@ describe('Checkbox Component', () => {
470
470
  ));
471
471
 
472
472
  const startTime = performance.now();
473
- renderWithProviders(<div>{checkboxes}</div>);
473
+ renderWithProviders(<>{checkboxes}</>);
474
474
  const endTime = performance.now();
475
475
 
476
476
  // Performance test: verify rendering completes in reasonable time
@@ -21,10 +21,10 @@
21
21
  * <Checkbox />
22
22
  *
23
23
  * // With label
24
- * <div className="flex items-center space-x-2">
24
+ * <>
25
25
  * <Checkbox id="terms" />
26
26
  * <label htmlFor="terms">Accept terms and conditions</label>
27
- * </div>
27
+ * </>
28
28
  *
29
29
  * // Disabled state
30
30
  * <Checkbox disabled />
@@ -0,0 +1,384 @@
1
+ /**
2
+ * @file Context Selector Component
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/ContextSelector
5
+ * @since 0.6.4
6
+ *
7
+ * A unified intelligent selector component that shows all organisations and events
8
+ * a user can access based on their roles and permissions. This is the single
9
+ * selector for all apps - it intelligently determines what to show based on:
10
+ * - User's organisation roles
11
+ * - User's event-app roles
12
+ * - Super admin status (shows all)
13
+ *
14
+ * Features:
15
+ * - Intelligent display of accessible organisations and events
16
+ * - Grouped display (Organisations / Events sections)
17
+ * - Visual distinction between orgs and events
18
+ * - Super admin support (shows all orgs and events)
19
+ * - Loading states and error handling
20
+ * - Accessible interface design
21
+ * - Integration with OrganisationProvider and EventService
22
+ * - Automatically shows superset of what user can access
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * <ContextSelector
27
+ * onOrganisationSelect={(org) => {
28
+ * switchOrganisation(org.id);
29
+ * }}
30
+ * onEventSelect={(event) => {
31
+ * setSelectedEvent(event);
32
+ * }}
33
+ * />
34
+ * ```
35
+ *
36
+ * @accessibility
37
+ * - WCAG 2.1 AA compliant
38
+ * - Keyboard navigation support
39
+ * - Screen reader friendly
40
+ * - Focus management
41
+ * - ARIA labels and descriptions
42
+ * - High contrast support
43
+ *
44
+ * @security
45
+ * - Only shows organisations/events user has access to
46
+ * - Super admins see all items
47
+ * - Validates access before selection
48
+ */
49
+
50
+ import React, { useMemo } from 'react';
51
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectGroup, SelectLabel, SelectSeparator } from '../Select';
52
+ import { Alert, AlertDescription } from '../Alert/Alert';
53
+ import { Button } from '../Button/Button';
54
+ import { LoadingSpinner } from '../LoadingSpinner/LoadingSpinner';
55
+ import { RefreshCw, AlertCircle, Building2, Calendar } from 'lucide-react';
56
+ import { useOrganisations } from '../../hooks/useOrganisations';
57
+ import { useEvents } from '../../hooks/useEvents';
58
+ import { useRBAC } from '../../rbac/hooks/useRBAC';
59
+ import type { Organisation } from '../../types/organisation';
60
+ import type { Event } from '../../types/event';
61
+ import { logger } from '../../utils/core/logger';
62
+
63
+ /**
64
+ * Props for the ContextSelector component.
65
+ */
66
+ export interface ContextSelectorProps {
67
+ /** Placeholder text for the dropdown */
68
+ placeholder?: string;
69
+ /** Additional CSS classes */
70
+ className?: string;
71
+ /** Callback fired when an organisation is selected */
72
+ onOrganisationSelect?: (org: Organisation) => void;
73
+ /** Callback fired when an event is selected */
74
+ onEventSelect?: (event: Event) => void;
75
+ /** Show friendly message when no items available */
76
+ showNoItemsMessage?: boolean;
77
+ /** Show retry button on errors */
78
+ showRetryButton?: boolean;
79
+ /** Compact display mode */
80
+ compact?: boolean;
81
+ /** Disabled state */
82
+ disabled?: boolean;
83
+ /** Show organisations section (default: true) */
84
+ showOrganisations?: boolean;
85
+ /** Show events section (default: true) */
86
+ showEvents?: boolean;
87
+ }
88
+
89
+ /**
90
+ * ContextSelector component for selecting organisations or events
91
+ *
92
+ * This is the unified intelligent selector that shows all organisations and events
93
+ * a user can access. It automatically determines what to show based on the user's
94
+ * roles and permissions - no need to configure separate org/event selectors.
95
+ *
96
+ * The selector intelligently shows:
97
+ * - All organisations the user has access to (via organisation roles)
98
+ * - All events the user has access to (via event-app roles or organisation membership)
99
+ * - Everything for super admins
100
+ *
101
+ * @component
102
+ * @example
103
+ * <ContextSelector
104
+ * onOrganisationSelect={(org) => switchOrganisation(org)}
105
+ * onEventSelect={(event) => setSelectedEvent(event)}
106
+ * />
107
+ */
108
+ export function ContextSelector({
109
+ placeholder = "Select organisation or event",
110
+ className,
111
+ onOrganisationSelect,
112
+ onEventSelect,
113
+ showNoItemsMessage = true,
114
+ showRetryButton = true,
115
+ compact = false,
116
+ disabled = false,
117
+ showOrganisations = true,
118
+ showEvents = true
119
+ }: ContextSelectorProps) {
120
+ const {
121
+ organisations,
122
+ selectedOrganisation,
123
+ isLoading: orgLoading,
124
+ error: orgError,
125
+ refreshOrganisations
126
+ } = useOrganisations();
127
+
128
+ const {
129
+ events,
130
+ selectedEvent,
131
+ isLoading: eventLoading,
132
+ error: eventError,
133
+ refreshEvents
134
+ } = useEvents();
135
+
136
+ const { isSuperAdmin } = useRBAC();
137
+
138
+ const isLoading = (showOrganisations && orgLoading) || (showEvents && eventLoading);
139
+ const hasError = (showOrganisations && orgError) || (showEvents && eventError);
140
+ const hasItems =
141
+ (showOrganisations && (organisations?.length || 0) > 0) ||
142
+ (showEvents && (events?.length || 0) > 0);
143
+
144
+ // Determine current selection value
145
+ // Priority: Event selection takes precedence over organisation selection
146
+ // When an event is selected, show the event (even if an org is also selected)
147
+ // The organisation remains in context (derived from event) but selector shows the active selection
148
+ const currentValue = useMemo(() => {
149
+ if (showEvents && selectedEvent) {
150
+ return `event:${selectedEvent.event_id || selectedEvent.id}`;
151
+ }
152
+ if (showOrganisations && selectedOrganisation) {
153
+ return `org:${selectedOrganisation.id}`;
154
+ }
155
+ return '';
156
+ }, [showOrganisations, showEvents, selectedOrganisation?.id, selectedEvent]);
157
+
158
+ const handleValueChange = (value: string) => {
159
+ if (disabled || isLoading) return;
160
+
161
+ if (value.startsWith('org:') && showOrganisations) {
162
+ const orgId = value.replace('org:', '');
163
+ const org = organisations?.find(o => o.id === orgId);
164
+ if (org && onOrganisationSelect) {
165
+ onOrganisationSelect(org);
166
+ }
167
+ } else if (value.startsWith('event:') && showEvents) {
168
+ const eventId = value.replace('event:', '');
169
+ const event = events?.find(e => (e.event_id || e.id) === eventId);
170
+ if (event && onEventSelect) {
171
+ onEventSelect(event);
172
+ }
173
+ }
174
+ };
175
+
176
+ const handleRetry = async () => {
177
+ try {
178
+ if (showOrganisations && orgError) {
179
+ await refreshOrganisations();
180
+ }
181
+ if (showEvents && eventError) {
182
+ await refreshEvents();
183
+ }
184
+ } catch (error) {
185
+ logger.error('ContextSelector', 'Failed to refresh:', error);
186
+ }
187
+ };
188
+
189
+ // Loading state - only show loading if we don't have any items yet
190
+ // This prevents the selector from being stuck in loading when data exists
191
+ if (isLoading && !hasItems) {
192
+ const loadingText = compact ? "Loading..." :
193
+ showOrganisations && showEvents ? "Loading organisations and events..." :
194
+ showOrganisations ? "Loading organisations..." :
195
+ "Loading events...";
196
+ return (
197
+ <div className={`flex items-center gap-2 ${className}`}>
198
+ <LoadingSpinner size="sm" />
199
+ <span className="text-sm text-muted-foreground">
200
+ {loadingText}
201
+ </span>
202
+ </div>
203
+ );
204
+ }
205
+
206
+ // Error state
207
+ if (hasError) {
208
+ const errorMessages = [];
209
+ if (showOrganisations && orgError) {
210
+ errorMessages.push(`Failed to load organisations: ${orgError.message}`);
211
+ }
212
+ if (showEvents && eventError) {
213
+ errorMessages.push(`Failed to load events: ${eventError.message}`);
214
+ }
215
+ return (
216
+ <div className={`space-y-2 ${className}`}>
217
+ <Alert variant="destructive">
218
+ <AlertCircle className="size-4" />
219
+ <AlertDescription>
220
+ {errorMessages.join(' ')}
221
+ </AlertDescription>
222
+ </Alert>
223
+ {showRetryButton && (
224
+ <Button
225
+ variant="outline"
226
+ size="sm"
227
+ onClick={handleRetry}
228
+ disabled={isLoading}
229
+ className="w-full"
230
+ >
231
+ <RefreshCw className={`size-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} />
232
+ Retry
233
+ </Button>
234
+ )}
235
+ </div>
236
+ );
237
+ }
238
+
239
+ // No items available
240
+ if (!hasItems) {
241
+ if (showNoItemsMessage) {
242
+ const noItemsText =
243
+ showOrganisations && showEvents ? "No organisations or events available. Please contact your administrator." :
244
+ showOrganisations ? "No organisations available. Please contact your administrator." :
245
+ "No events available. Please contact your administrator.";
246
+ return (
247
+ <div className={`space-y-2 ${className}`}>
248
+ <Alert>
249
+ <AlertCircle className="size-4" />
250
+ <AlertDescription>
251
+ {noItemsText}
252
+ </AlertDescription>
253
+ </Alert>
254
+ {showRetryButton && (
255
+ <Button
256
+ variant="outline"
257
+ size="sm"
258
+ onClick={handleRetry}
259
+ disabled={isLoading}
260
+ className="w-full"
261
+ >
262
+ <RefreshCw className={`size-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} />
263
+ Check Again
264
+ </Button>
265
+ )}
266
+ </div>
267
+ );
268
+ }
269
+ return null;
270
+ }
271
+
272
+ // Format display value
273
+ // Priority: Event selection takes precedence over organisation selection (matches currentValue)
274
+ const displayValue = useMemo(() => {
275
+ if (showEvents && selectedEvent) {
276
+ return (
277
+ <div className="flex items-center gap-2">
278
+ <Calendar className="size-4 flex-shrink-0" />
279
+ <span className="truncate">{selectedEvent.event_name}</span>
280
+ </div>
281
+ );
282
+ }
283
+ if (showOrganisations && selectedOrganisation) {
284
+ return (
285
+ <div className="flex items-center gap-2">
286
+ <Building2 className="size-4 flex-shrink-0" />
287
+ <span className="truncate">{selectedOrganisation.display_name}</span>
288
+ </div>
289
+ );
290
+ }
291
+ return null;
292
+ }, [showOrganisations, showEvents, selectedOrganisation, selectedEvent]);
293
+
294
+ // Determine placeholder text based on what's shown
295
+ const effectivePlaceholder = useMemo(() => {
296
+ if (placeholder !== "Select organisation or event") {
297
+ return placeholder;
298
+ }
299
+ if (showOrganisations && showEvents) {
300
+ return "Select organisation or event";
301
+ }
302
+ if (showOrganisations) {
303
+ return "Select organisation";
304
+ }
305
+ if (showEvents) {
306
+ return "Select event";
307
+ }
308
+ return placeholder;
309
+ }, [placeholder, showOrganisations, showEvents]);
310
+
311
+ return (
312
+ <div className={className} data-testid="context-selector">
313
+ <Select
314
+ value={currentValue}
315
+ onValueChange={handleValueChange}
316
+ disabled={disabled || isLoading}
317
+ >
318
+ <SelectTrigger
319
+ className="text-left"
320
+ variant="outline"
321
+ >
322
+ <SelectValue placeholder={effectivePlaceholder}>
323
+ {displayValue}
324
+ </SelectValue>
325
+ </SelectTrigger>
326
+ <SelectContent>
327
+ {/* Organisations Section */}
328
+ {showOrganisations && organisations && organisations.length > 0 && (
329
+ <>
330
+ <SelectGroup>
331
+ <SelectLabel>Organisations</SelectLabel>
332
+ {organisations.map((org) => (
333
+ <SelectItem
334
+ key={org.id}
335
+ value={`org:${org.id}`}
336
+ >
337
+ <div className="flex items-center gap-2">
338
+ <Building2 className="size-4" />
339
+ <div className="flex flex-col">
340
+ <span className="font-medium">{org.display_name}</span>
341
+ {!compact && org.description && (
342
+ <span className="text-xs text-muted-foreground truncate max-w-40">
343
+ {org.description}
344
+ </span>
345
+ )}
346
+ </div>
347
+ </div>
348
+ </SelectItem>
349
+ ))}
350
+ </SelectGroup>
351
+ {showEvents && events && events.length > 0 && <SelectSeparator />}
352
+ </>
353
+ )}
354
+
355
+ {/* Events Section */}
356
+ {showEvents && events && events.length > 0 && (
357
+ <SelectGroup>
358
+ <SelectLabel>Events</SelectLabel>
359
+ {events.map((event) => (
360
+ <SelectItem
361
+ key={event.event_id || event.id}
362
+ value={`event:${event.event_id || event.id}`}
363
+ >
364
+ <div className="flex items-center gap-2">
365
+ <Calendar className="size-4" />
366
+ <div className="flex flex-col">
367
+ <span className="font-medium">{event.event_name}</span>
368
+ {!compact && event.event_date && (
369
+ <span className="text-xs text-muted-foreground">
370
+ {new Date(event.event_date).toLocaleDateString()}
371
+ </span>
372
+ )}
373
+ </div>
374
+ </div>
375
+ </SelectItem>
376
+ ))}
377
+ </SelectGroup>
378
+ )}
379
+ </SelectContent>
380
+ </Select>
381
+ </div>
382
+ );
383
+ }
384
+
@@ -0,0 +1,3 @@
1
+ export { ContextSelector } from './ContextSelector';
2
+ export type { ContextSelectorProps } from './ContextSelector';
3
+
@@ -462,6 +462,38 @@ export interface DataTableProps<TData extends DataRecord> {
462
462
  *
463
463
  * Features are configured through the unified `features` prop for maximum flexibility.
464
464
  */
465
+ /**
466
+ * Enhanced DataTable component with performance optimizations and comprehensive features.
467
+ *
468
+ * This is the main entry point for the DataTable component. It wraps DataTableCore
469
+ * and provides feature normalization and validation.
470
+ *
471
+ * @template TData - The type of data records in the table
472
+ * @param props - DataTable configuration including data, columns, features, and RBAC
473
+ * @returns The rendered DataTable component
474
+ *
475
+ * @example
476
+ * ```tsx
477
+ * <DataTable
478
+ * data={users}
479
+ * columns={userColumns}
480
+ * rbac={{ pageId: 'user-management' }}
481
+ * features={{
482
+ * search: true,
483
+ * pagination: true,
484
+ * sorting: true
485
+ * }}
486
+ * />
487
+ * ```
488
+ */
489
+ /**
490
+ * Comprehensive, feature-rich data table component built on top of TanStack Table.
491
+ * Provides advanced data management capabilities with a clean, accessible interface.
492
+ *
493
+ * @template TData - The type of data records in the table
494
+ * @param props - DataTable configuration and props
495
+ * @returns The rendered DataTable component
496
+ */
465
497
  export function DataTable<TData extends DataRecord>(props: DataTableProps<TData>) {
466
498
  const logger = createLogger('DataTable');
467
499
  const { features, ...rest } = props;
@@ -485,11 +517,13 @@ export function DataTable<TData extends DataRecord>(props: DataTableProps<TData>
485
517
  // ============================================================================
486
518
 
487
519
  /**
488
- * DataTable component with React.memo for performance optimization
520
+ * DataTable component export
521
+ *
522
+ * Note: React.memo removed for React 19 compatibility. React 19's automatic
523
+ * memoization (React Compiler) handles optimization automatically, and manual
524
+ * memoization can interfere with prop updates in React 19.
489
525
  */
490
- const DataTableComponent = React.memo(DataTable) as typeof DataTable;
491
-
492
- export { DataTableComponent as default };
526
+ export { DataTable as default };
493
527
 
494
528
  // Re-export for convenience
495
529
  export { DataTable as EnhancedDataTable };
@@ -1,3 +1,4 @@
1
+ /// <reference types="vitest/globals" />
1
2
  /**
2
3
  * @file DataTableCore Test Setup
3
4
  * @package @jmruthers/pace-core
@@ -7,8 +8,6 @@
7
8
  * Shared test setup and mocks for DataTableCore component tests.
8
9
  * This file is imported by all DataTableCore test files to ensure consistent mocking.
9
10
  */
10
-
11
- import { vi } from 'vitest';
12
11
  import React from 'react';
13
12
 
14
13
  // Mock the RBAC hooks
@@ -132,7 +131,7 @@ vi.mock('../hooks/useColumnOrderPersistence', () => ({
132
131
  // Mock the column factory
133
132
  vi.mock('../core/ColumnFactory', () => ({
134
133
  ColumnFactory: {
135
- createColumns: vi.fn((columns) => columns),
134
+ createColumns: vi.fn((columns: unknown) => columns),
136
135
  createActionColumn: vi.fn(() => ({
137
136
  id: 'actions',
138
137
  header: 'Actions',
@@ -144,11 +143,11 @@ vi.mock('../core/ColumnFactory', () => ({
144
143
  // Mock the hierarchical utilities
145
144
  vi.mock('../utils/hierarchicalUtils', () => ({
146
145
  validateHierarchicalData: vi.fn(() => true),
147
- sortHierarchicalData: vi.fn((data) => data),
146
+ sortHierarchicalData: vi.fn((data: unknown) => data),
148
147
  }));
149
148
 
150
149
  vi.mock('../utils/hierarchicalSorting', () => ({
151
- sortHierarchicalData: vi.fn((data) => data),
150
+ sortHierarchicalData: vi.fn((data: unknown) => data),
152
151
  }));
153
152
 
154
153
  // Create a mock implementation that can be overridden
@@ -184,7 +183,7 @@ vi.mock('../hooks/useDataTablePermissions', () => ({
184
183
  }));
185
184
 
186
185
  vi.mock('../hooks/useTableColumns', () => ({
187
- useTableColumns: vi.fn(({ columns }) => ({
186
+ useTableColumns: vi.fn(({ columns }: { columns?: unknown[] }) => ({
188
187
  enhancedColumns: columns || []
189
188
  }))
190
189
  }));