@jmruthers/pace-core 0.6.1 → 0.6.2

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 (502) hide show
  1. package/CHANGELOG.md +43 -10
  2. package/cursor-rules/00-pace-core-compliance.mdc +18 -91
  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-BPvc3Ka0.d.ts} +54 -0
  14. package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
  15. package/dist/{DataTable-DQ7RSOHE.js → DataTable-TPTKCX4D.js} +10 -9
  16. package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +356 -111
  17. package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  18. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  19. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  20. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  21. package/dist/chunk-24UVZUZG.js.map +1 -0
  22. package/dist/{chunk-4N5C5XZU.js → chunk-2UOI2FG5.js} +4 -4
  23. package/dist/chunk-2UOI2FG5.js.map +1 -0
  24. package/dist/{chunk-T33XF5ZC.js → chunk-3XC4CPTD.js} +4317 -3963
  25. package/dist/chunk-3XC4CPTD.js.map +1 -0
  26. package/dist/{chunk-4ZC4GX36.js → chunk-6J4GEEJR.js} +172 -45
  27. package/dist/chunk-6J4GEEJR.js.map +1 -0
  28. package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
  29. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  30. package/dist/{chunk-BYFSK72L.js → chunk-EHMR7VYL.js} +4 -4
  31. package/dist/chunk-EHMR7VYL.js.map +1 -0
  32. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  33. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  34. package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
  35. package/dist/chunk-FFQEQTNW.js.map +1 -0
  36. package/dist/chunk-FMUCXFII.js +76 -0
  37. package/dist/chunk-FMUCXFII.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-3XTALGJF.js → chunk-MMZ7JXPU.js} +60 -223
  43. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  44. package/dist/{chunk-GLK6VM3F.js → chunk-NECFR5MM.js} +254 -170
  45. package/dist/chunk-NECFR5MM.js.map +1 -0
  46. package/dist/{chunk-JBKQ3SAO.js → chunk-SFZUDBL5.js} +40 -4
  47. package/dist/chunk-SFZUDBL5.js.map +1 -0
  48. package/dist/{chunk-XM25TVIE.js → chunk-XWQCNGTQ.js} +724 -363
  49. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  50. package/dist/components.d.ts +5 -5
  51. package/dist/components.js +14 -11
  52. package/dist/components.js.map +1 -1
  53. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  54. package/dist/hooks.d.ts +55 -122
  55. package/dist/hooks.js +8 -12
  56. package/dist/hooks.js.map +1 -1
  57. package/dist/index.d.ts +60 -13
  58. package/dist/index.js +19 -19
  59. package/dist/index.js.map +1 -1
  60. package/dist/providers.d.ts +21 -3
  61. package/dist/providers.js +2 -2
  62. package/dist/rbac/index.d.ts +145 -114
  63. package/dist/rbac/index.js +8 -11
  64. package/dist/theming/runtime.d.ts +1 -13
  65. package/dist/theming/runtime.js +1 -1
  66. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  67. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  68. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  69. package/dist/types.d.ts +2 -2
  70. package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +31 -1
  71. package/dist/utils.d.ts +4 -5
  72. package/dist/utils.js +14 -14
  73. package/dist/utils.js.map +1 -1
  74. package/docs/api/README.md +7 -1
  75. package/docs/api/classes/ColumnFactory.md +8 -8
  76. package/docs/api/classes/InvalidScopeError.md +4 -4
  77. package/docs/api/classes/Logger.md +1 -1
  78. package/docs/api/classes/MissingUserContextError.md +4 -4
  79. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  80. package/docs/api/classes/PermissionDeniedError.md +4 -4
  81. package/docs/api/classes/RBACAuditManager.md +1 -1
  82. package/docs/api/classes/RBACCache.md +1 -1
  83. package/docs/api/classes/RBACEngine.md +1 -1
  84. package/docs/api/classes/RBACError.md +4 -4
  85. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  86. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  87. package/docs/api/classes/StorageUtils.md +1 -1
  88. package/docs/api/enums/FileCategory.md +1 -1
  89. package/docs/api/enums/LogLevel.md +1 -1
  90. package/docs/api/enums/RBACErrorCode.md +1 -1
  91. package/docs/api/enums/RPCFunction.md +1 -1
  92. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  93. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  94. package/docs/api/interfaces/AggregateConfig.md +4 -4
  95. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  96. package/docs/api/interfaces/AvatarProps.md +1 -1
  97. package/docs/api/interfaces/BadgeProps.md +9 -2
  98. package/docs/api/interfaces/ButtonProps.md +7 -4
  99. package/docs/api/interfaces/CalendarProps.md +8 -5
  100. package/docs/api/interfaces/CardProps.md +8 -5
  101. package/docs/api/interfaces/ColorPalette.md +1 -1
  102. package/docs/api/interfaces/ColorShade.md +1 -1
  103. package/docs/api/interfaces/ComplianceResult.md +1 -1
  104. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  105. package/docs/api/interfaces/DataRecord.md +1 -1
  106. package/docs/api/interfaces/DataTableAction.md +24 -21
  107. package/docs/api/interfaces/DataTableColumn.md +31 -31
  108. package/docs/api/interfaces/DataTableProps.md +1 -1
  109. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  110. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  111. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  112. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  113. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  114. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  115. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  116. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  117. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  118. package/docs/api/interfaces/ExportColumn.md +1 -1
  119. package/docs/api/interfaces/ExportOptions.md +8 -8
  120. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  121. package/docs/api/interfaces/FileMetadata.md +1 -1
  122. package/docs/api/interfaces/FileReference.md +1 -1
  123. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  124. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  125. package/docs/api/interfaces/FileUploadProps.md +26 -23
  126. package/docs/api/interfaces/FooterProps.md +10 -8
  127. package/docs/api/interfaces/FormFieldProps.md +10 -10
  128. package/docs/api/interfaces/FormProps.md +1 -1
  129. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  130. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  131. package/docs/api/interfaces/InputProps.md +7 -4
  132. package/docs/api/interfaces/LabelProps.md +1 -1
  133. package/docs/api/interfaces/LoggerConfig.md +1 -1
  134. package/docs/api/interfaces/LoginFormProps.md +14 -11
  135. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  136. package/docs/api/interfaces/NavigationContextType.md +1 -1
  137. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  138. package/docs/api/interfaces/NavigationItem.md +11 -11
  139. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  140. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  141. package/docs/api/interfaces/Organisation.md +1 -1
  142. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  143. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  144. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  145. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  146. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  147. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  148. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  149. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  150. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  151. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  152. package/docs/api/interfaces/PaletteData.md +1 -1
  153. package/docs/api/interfaces/ParsedAddress.md +1 -1
  154. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  155. package/docs/api/interfaces/ProgressProps.md +1 -1
  156. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  157. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  158. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  159. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  160. package/docs/api/interfaces/QuickFix.md +1 -1
  161. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  162. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  163. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  164. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  165. package/docs/api/interfaces/RBACConfig.md +1 -1
  166. package/docs/api/interfaces/RBACContext.md +1 -1
  167. package/docs/api/interfaces/RBACLogger.md +1 -1
  168. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  169. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  170. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  171. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  172. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  173. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  174. package/docs/api/interfaces/RBACResult.md +1 -1
  175. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  176. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  177. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  178. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  181. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  182. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  183. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  184. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  185. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  186. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  187. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  188. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  189. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  190. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  191. package/docs/api/interfaces/RouteConfig.md +1 -1
  192. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  193. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  194. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  195. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  196. package/docs/api/interfaces/SetupIssue.md +1 -1
  197. package/docs/api/interfaces/StorageConfig.md +1 -1
  198. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  199. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  200. package/docs/api/interfaces/StorageListOptions.md +1 -1
  201. package/docs/api/interfaces/StorageListResult.md +1 -1
  202. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  203. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  204. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  205. package/docs/api/interfaces/StyleImport.md +1 -1
  206. package/docs/api/interfaces/SwitchProps.md +1 -1
  207. package/docs/api/interfaces/TabsContentProps.md +1 -1
  208. package/docs/api/interfaces/TabsListProps.md +1 -1
  209. package/docs/api/interfaces/TabsProps.md +1 -1
  210. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  211. package/docs/api/interfaces/TextareaProps.md +1 -1
  212. package/docs/api/interfaces/ToastActionElement.md +4 -1
  213. package/docs/api/interfaces/ToastProps.md +1 -1
  214. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  215. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  216. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  217. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  218. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  219. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  220. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  221. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  222. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  223. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  224. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  225. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  226. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  227. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  228. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  229. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  230. package/docs/api/interfaces/UserEventAccess.md +14 -11
  231. package/docs/api/interfaces/UserMenuProps.md +8 -6
  232. package/docs/api/interfaces/UserProfile.md +1 -1
  233. package/docs/api/modules.md +575 -634
  234. package/docs/architecture/database-schema-requirements.md +161 -0
  235. package/docs/core-concepts/rbac-system.md +3 -3
  236. package/docs/documentation-index.md +2 -4
  237. package/docs/getting-started/cursor-rules.md +2 -1
  238. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  239. package/docs/migration/MIGRATION_GUIDE.md +2 -24
  240. package/docs/migration/README.md +52 -6
  241. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  242. package/docs/migration/database-changes-december-2025.md +3 -3
  243. package/docs/rbac/event-based-apps.md +1 -1
  244. package/docs/rbac/getting-started.md +1 -1
  245. package/docs/rbac/quick-start.md +1 -1
  246. package/docs/standards/README.md +1 -0
  247. package/package.json +2 -1
  248. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  249. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  250. package/scripts/audit/core/checks/bundle.cjs +142 -0
  251. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +714 -687
  252. package/scripts/audit/core/checks/config.cjs +54 -0
  253. package/scripts/audit/core/checks/coverage.cjs +84 -0
  254. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  255. package/scripts/audit/core/checks/documentation.cjs +203 -0
  256. package/scripts/audit/core/checks/environment.cjs +128 -0
  257. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  258. package/scripts/audit/core/checks/forms.cjs +172 -0
  259. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  260. package/scripts/audit/core/checks/hooks.cjs +334 -0
  261. package/scripts/audit/core/checks/imports.cjs +244 -0
  262. package/scripts/audit/core/checks/performance.cjs +325 -0
  263. package/scripts/audit/core/checks/routes.cjs +117 -0
  264. package/scripts/audit/core/checks/state.cjs +130 -0
  265. package/scripts/audit/core/checks/structure.cjs +65 -0
  266. package/scripts/audit/core/checks/style.cjs +584 -0
  267. package/scripts/audit/core/checks/testing.cjs +122 -0
  268. package/scripts/audit/core/checks/typescript.cjs +61 -0
  269. package/scripts/audit/core/scanner.cjs +199 -0
  270. package/scripts/audit/core/utils.cjs +137 -0
  271. package/scripts/audit/index.cjs +223 -0
  272. package/scripts/audit/reporters/console.cjs +151 -0
  273. package/scripts/audit/reporters/json.cjs +54 -0
  274. package/scripts/audit/reporters/markdown.cjs +124 -0
  275. package/scripts/audit-consuming-app.cjs +61 -936
  276. package/scripts/build-docs/build-decision.js +240 -0
  277. package/scripts/build-docs/cache-utils.js +105 -0
  278. package/scripts/build-docs/content-normalization.js +150 -0
  279. package/scripts/build-docs/file-utils.js +105 -0
  280. package/scripts/build-docs/git-utils.js +86 -0
  281. package/scripts/build-docs/hash-utils.js +116 -0
  282. package/scripts/build-docs/typedoc-runner.js +220 -0
  283. package/scripts/build-docs-incremental.js +77 -913
  284. package/scripts/utils/command-runner.js +16 -11
  285. package/scripts/validate-formats.js +61 -56
  286. package/scripts/validate-master.js +74 -69
  287. package/scripts/validate-pre-publish.js +70 -65
  288. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  289. package/src/components/Alert/Alert.test.tsx +12 -18
  290. package/src/components/Alert/Alert.tsx +5 -7
  291. package/src/components/Avatar/Avatar.test.tsx +4 -4
  292. package/src/components/Badge/Badge.tsx +14 -0
  293. package/src/components/Button/Button.tsx +22 -0
  294. package/src/components/Calendar/Calendar.tsx +8 -2
  295. package/src/components/Card/Card.tsx +4 -0
  296. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  297. package/src/components/Checkbox/Checkbox.tsx +2 -2
  298. package/src/components/DataTable/DataTable.tsx +38 -4
  299. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  300. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
  301. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  302. package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
  303. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  304. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  305. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  306. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  307. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  308. package/src/components/DataTable/components/DataTableCore.tsx +196 -554
  309. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  310. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  311. package/src/components/DataTable/components/DataTableModals.tsx +8 -0
  312. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  313. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  314. package/src/components/DataTable/components/EditFields.tsx +307 -0
  315. package/src/components/DataTable/components/EditableRow.tsx +8 -0
  316. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  317. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  318. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  319. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  320. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  321. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  322. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  323. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  324. package/src/components/DataTable/components/UnifiedTableBody.tsx +61 -849
  325. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  326. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  327. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  328. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  329. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  330. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  331. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  332. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  333. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  334. package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
  335. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  336. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  337. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  338. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  339. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  340. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  341. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  342. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  343. package/src/components/DataTable/styles.ts +6 -6
  344. package/src/components/DataTable/types.ts +6 -10
  345. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  346. package/src/components/DataTable/utils/debugTools.ts +18 -113
  347. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  348. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  349. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  350. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  351. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  352. package/src/components/Dialog/Dialog.tsx +2 -2
  353. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  354. package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
  355. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  356. package/src/components/ErrorBoundary/index.ts +27 -2
  357. package/src/components/EventSelector/EventSelector.tsx +3 -0
  358. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  359. package/src/components/FileUpload/FileUpload.tsx +22 -2
  360. package/src/components/Footer/Footer.test.tsx +16 -16
  361. package/src/components/Footer/Footer.tsx +14 -11
  362. package/src/components/Form/Form.tsx +1 -0
  363. package/src/components/Header/Header.tsx +21 -10
  364. package/src/components/Input/Input.test.tsx +2 -2
  365. package/src/components/Input/Input.tsx +8 -4
  366. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  367. package/src/components/LoginForm/LoginForm.tsx +4 -0
  368. package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
  369. package/src/components/NavigationMenu/types.ts +56 -0
  370. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  371. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  372. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -2
  373. package/src/components/PaceAppLayout/PaceAppLayout.tsx +32 -11
  374. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  375. package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
  376. package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
  377. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  378. package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
  379. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  380. package/src/components/Select/Select.tsx +80 -434
  381. package/src/components/Select/context.ts +23 -0
  382. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  383. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  384. package/src/components/Select/hooks/useSelectState.ts +104 -0
  385. package/src/components/Select/index.ts +9 -1
  386. package/src/components/Select/types.ts +123 -0
  387. package/src/components/Select/utils/text.ts +26 -0
  388. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
  389. package/src/components/Switch/Switch.tsx +4 -4
  390. package/src/components/Tabs/Tabs.tsx +1 -1
  391. package/src/components/Toast/Toast.tsx +4 -0
  392. package/src/components/Tooltip/Tooltip.tsx +2 -2
  393. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  394. package/src/components/UserMenu/UserMenu.tsx +21 -18
  395. package/src/components/index.ts +2 -2
  396. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  397. package/src/hooks/index.ts +1 -2
  398. package/src/hooks/public/usePublicEvent.ts +4 -0
  399. package/src/hooks/public/usePublicEventLogo.ts +4 -0
  400. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  401. package/src/hooks/public/usePublicRouteParams.ts +4 -0
  402. package/src/hooks/services/useAuth.ts +32 -0
  403. package/src/hooks/services/useCurrentEvent.ts +6 -0
  404. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  405. package/src/hooks/useDebounce.ts +9 -0
  406. package/src/hooks/useEventTheme.ts +6 -0
  407. package/src/hooks/useFileDisplay.ts +4 -0
  408. package/src/hooks/useFileReference.ts +25 -7
  409. package/src/hooks/useFileUrl.ts +11 -1
  410. package/src/hooks/useFocusManagement.ts +14 -0
  411. package/src/hooks/useFocusTrap.ts +3 -0
  412. package/src/hooks/useInactivityTracker.ts +3 -0
  413. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  414. package/src/hooks/useOrganisationPermissions.ts +4 -0
  415. package/src/hooks/useOrganisationSecurity.ts +4 -0
  416. package/src/hooks/usePerformanceMonitor.ts +4 -0
  417. package/src/hooks/usePermissionCache.ts +7 -0
  418. package/src/hooks/useQueryCache.ts +12 -1
  419. package/src/hooks/useSessionRestoration.ts +4 -0
  420. package/src/hooks/useStorage.ts +4 -0
  421. package/src/hooks/useToast.ts +1 -1
  422. package/src/index.ts +2 -1
  423. package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
  424. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  425. package/src/providers/services/EventServiceProvider.tsx +18 -0
  426. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  427. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  428. package/src/providers/services/UnifiedAuthProvider.tsx +36 -0
  429. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
  430. package/src/rbac/README.md +1 -1
  431. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +2 -2
  432. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  433. package/src/rbac/adapters.tsx +14 -5
  434. package/src/rbac/api.ts +100 -67
  435. package/src/rbac/components/NavigationProvider.tsx +4 -1
  436. package/src/rbac/components/PagePermissionGuard.tsx +157 -17
  437. package/src/rbac/components/RoleBasedRouter.tsx +5 -1
  438. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  439. package/src/rbac/components/SecureDataProvider.tsx +20 -5
  440. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  441. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  442. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  443. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  444. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  445. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  446. package/src/rbac/engine.ts +38 -14
  447. package/src/rbac/hooks/permissions/index.ts +7 -0
  448. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  449. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  450. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  451. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  452. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  453. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  454. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  455. package/src/rbac/hooks/useCan.test.ts +71 -64
  456. package/src/rbac/hooks/usePermissions.ts +14 -995
  457. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  458. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  459. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  460. package/src/rbac/permissions.ts +0 -30
  461. package/src/rbac/secureClient.ts +200 -61
  462. package/src/rbac/types.ts +8 -0
  463. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  464. package/src/theming/parseEventColours.ts +5 -19
  465. package/src/types/vitest-globals.d.ts +51 -26
  466. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  467. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  468. package/src/utils/__tests__/index.unit.test.ts +2 -2
  469. package/src/utils/audit/audit.ts +0 -3
  470. package/src/utils/core/cn.ts +1 -1
  471. package/src/utils/file-reference/index.ts +53 -1
  472. package/src/utils/formatting/formatting.ts +8 -18
  473. package/src/utils/index.ts +0 -1
  474. package/dist/chunk-3QRJFVBR.js.map +0 -1
  475. package/dist/chunk-3XTALGJF.js.map +0 -1
  476. package/dist/chunk-4N5C5XZU.js.map +0 -1
  477. package/dist/chunk-4ZC4GX36.js.map +0 -1
  478. package/dist/chunk-BYFSK72L.js.map +0 -1
  479. package/dist/chunk-EXUD6RNJ.js +0 -451
  480. package/dist/chunk-EXUD6RNJ.js.map +0 -1
  481. package/dist/chunk-GLK6VM3F.js.map +0 -1
  482. package/dist/chunk-I7PSE6JW.js.map +0 -1
  483. package/dist/chunk-JBKQ3SAO.js.map +0 -1
  484. package/dist/chunk-KNC55RTG.js.map +0 -1
  485. package/dist/chunk-LXQLPRQ2.js.map +0 -1
  486. package/dist/chunk-R77UEZ4E.js.map +0 -1
  487. package/dist/chunk-SQGMNID3.js.map +0 -1
  488. package/dist/chunk-T33XF5ZC.js.map +0 -1
  489. package/dist/chunk-XM25TVIE.js.map +0 -1
  490. package/docs/api/classes/ErrorBoundary.md +0 -144
  491. package/docs/migration/quick-migration-guide.md +0 -356
  492. package/docs/migration/service-architecture.md +0 -281
  493. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  494. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  495. package/src/hooks/useSecureDataAccess.ts +0 -681
  496. /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  497. /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  498. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  499. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  500. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  501. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  502. /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
@@ -0,0 +1,390 @@
1
+ import * as React from "react";
2
+ import { useUnifiedAuth } from "../../providers/services/UnifiedAuthProvider";
3
+ import { useRBAC } from "../../rbac/hooks/useRBAC";
4
+ import { useResolvedScope } from "../../rbac/hooks";
5
+ import { usePermissions } from "../../rbac/hooks/usePermissions";
6
+ import type {
7
+ Permission,
8
+ AccessLevel as RBACAccessLevel,
9
+ UUID,
10
+ PermissionMap,
11
+ } from "../../rbac/types";
12
+ import { logger } from "../../utils/core/logger";
13
+ import type { NavigationItem } from "./types";
14
+
15
+ /**
16
+ * Options for the useNavigationFiltering hook.
17
+ */
18
+ interface UseNavigationFilteringOptions {
19
+ items: NavigationItem[];
20
+ itemsPreFiltered?: boolean;
21
+ auditLog?: boolean;
22
+ }
23
+
24
+ /**
25
+ * Return value of the useNavigationFiltering hook.
26
+ */
27
+ interface UseNavigationFilteringResult {
28
+ authContext: ReturnType<typeof useUnifiedAuth> | null;
29
+ rbacContext: ReturnType<typeof useRBAC> | null;
30
+ filteredItems: NavigationItem[];
31
+ permissionMap: PermissionMap;
32
+ hasAnyPermission: ((permissions: Permission[]) => boolean) | null;
33
+ }
34
+
35
+ /**
36
+ * Hook for filtering navigation items based on RBAC permissions.
37
+ * Filters navigation items to show only those the user has access to.
38
+ *
39
+ * @param options - Navigation filtering configuration
40
+ * @returns Filtered navigation items and permission context
41
+ */
42
+ export function useNavigationFiltering({
43
+ items,
44
+ itemsPreFiltered = false,
45
+ auditLog = true,
46
+ }: UseNavigationFilteringOptions): UseNavigationFilteringResult {
47
+ const [resolvedAppId, setResolvedAppId] = React.useState<string | undefined>(undefined);
48
+ const previousFilteredItemsRef = React.useRef<NavigationItem[]>([]);
49
+
50
+ let authContext = null;
51
+ try {
52
+ authContext = useUnifiedAuth();
53
+ } catch (error) {
54
+ logger.warn(
55
+ "NavigationMenu",
56
+ "useUnifiedAuth not available, running in unauthenticated mode",
57
+ );
58
+ }
59
+
60
+ let rbacContext = null;
61
+ try {
62
+ rbacContext = useRBAC();
63
+ } catch (error) {
64
+ logger.warn(
65
+ "NavigationMenu",
66
+ "useRBAC not available, permission filtering disabled",
67
+ );
68
+ }
69
+
70
+ const eventLoadingRaw = authContext?.eventLoading;
71
+ const eventLoading = eventLoadingRaw ?? false;
72
+ const selectedEvent = authContext?.selectedEvent || null;
73
+ const orgContextReady =
74
+ authContext?.isContextReady ?? (authContext?.selectedOrganisation?.id ? true : false);
75
+
76
+ const { supabase } = authContext || {};
77
+ const { selectedOrganisation } = authContext || {};
78
+ const { resolvedScope, isLoading: scopeLoading, error: scopeError } = useResolvedScope({
79
+ supabase: itemsPreFiltered ? null : supabase || null,
80
+ selectedOrganisationId: itemsPreFiltered ? null : selectedOrganisation?.id || null,
81
+ selectedEventId: itemsPreFiltered ? null : selectedEvent?.event_id || null,
82
+ });
83
+
84
+ React.useEffect(() => {
85
+ if (
86
+ !scopeLoading &&
87
+ !resolvedScope?.appId &&
88
+ selectedOrganisation?.id &&
89
+ authContext?.appName &&
90
+ authContext?.user?.id &&
91
+ !resolvedAppId
92
+ ) {
93
+ if (!authContext.user || !authContext.appName) {
94
+ return;
95
+ }
96
+ const userId = authContext.user.id;
97
+ const appName = authContext.appName;
98
+ import("../../rbac/api")
99
+ .then(({ resolveAppContext }) => {
100
+ resolveAppContext({
101
+ userId,
102
+ appName,
103
+ })
104
+ .then((result) => {
105
+ if (result?.appId) {
106
+ setResolvedAppId(result.appId);
107
+ }
108
+ })
109
+ .catch(() => {});
110
+ })
111
+ .catch(() => {});
112
+ }
113
+ }, [
114
+ scopeLoading,
115
+ resolvedScope?.appId,
116
+ selectedOrganisation?.id,
117
+ authContext?.appName,
118
+ authContext?.user?.id,
119
+ resolvedAppId,
120
+ ]);
121
+
122
+ const effectiveScope = React.useMemo(() => {
123
+ if (resolvedScope?.organisationId) {
124
+ return resolvedScope;
125
+ }
126
+
127
+ if (selectedOrganisation?.id) {
128
+ const fallbackScope = {
129
+ organisationId: selectedOrganisation.id,
130
+ eventId: selectedEvent?.event_id || undefined,
131
+ appId: resolvedAppId,
132
+ };
133
+ return fallbackScope;
134
+ }
135
+
136
+ return null;
137
+ }, [resolvedScope, selectedOrganisation?.id, selectedEvent?.event_id, resolvedAppId]);
138
+
139
+ const scopeKey = effectiveScope
140
+ ? `${effectiveScope.organisationId || ""}-${effectiveScope.eventId || ""}-${
141
+ effectiveScope.appId || ""
142
+ }`
143
+ : "empty";
144
+
145
+ const stableScope = React.useMemo(() => {
146
+ if (effectiveScope?.organisationId) {
147
+ return {
148
+ organisationId: effectiveScope.organisationId,
149
+ eventId: effectiveScope.eventId,
150
+ appId: effectiveScope.appId,
151
+ };
152
+ }
153
+ return {
154
+ organisationId: "",
155
+ eventId: undefined,
156
+ appId: undefined,
157
+ };
158
+ }, [scopeKey, effectiveScope]);
159
+
160
+ const userId = (authContext?.user?.id || "") as UUID;
161
+ const {
162
+ permissions: permissionMap,
163
+ hasAnyPermission,
164
+ isLoading: permissionsLoading,
165
+ error: permissionsError,
166
+ } = usePermissions(
167
+ itemsPreFiltered ? (null as any) : userId,
168
+ itemsPreFiltered ? undefined : stableScope.organisationId,
169
+ itemsPreFiltered ? undefined : stableScope.eventId,
170
+ itemsPreFiltered ? undefined : stableScope.appId,
171
+ );
172
+
173
+ const filteredItems = React.useMemo(() => {
174
+ if (itemsPreFiltered && items && items.length > 0) {
175
+ const visibleItems = (items || []).filter((item) => !item.meta?.hidden);
176
+ previousFilteredItemsRef.current = visibleItems;
177
+ return visibleItems;
178
+ }
179
+
180
+ const isOrgContextReady = orgContextReady && selectedOrganisation?.id;
181
+ const isEventContextReady = eventLoadingRaw === undefined ? true : !eventLoading;
182
+ const hasValidContext = isOrgContextReady && isEventContextReady;
183
+ const shouldWaitForScope = scopeLoading || !hasValidContext;
184
+ const shouldRetryAfterError = scopeError && hasValidContext && !scopeLoading;
185
+
186
+ if (items && items.length > 0 && selectedOrganisation?.id) {
187
+ const visibleItems = (items || []).filter((item) => !item.meta?.hidden);
188
+ previousFilteredItemsRef.current = visibleItems;
189
+ return visibleItems;
190
+ }
191
+
192
+ if (!authContext || !rbacContext || (shouldWaitForScope && !shouldRetryAfterError)) {
193
+ return [];
194
+ }
195
+
196
+ if (permissionsLoading) {
197
+ if (previousFilteredItemsRef.current.length > 0) {
198
+ return previousFilteredItemsRef.current;
199
+ }
200
+ if (items && items.length > 0 && stableScope.organisationId) {
201
+ return (items || []).filter((item) => !item.meta?.hidden);
202
+ }
203
+ return [];
204
+ }
205
+
206
+ if (permissionsError) {
207
+ logger.warn(
208
+ "NavigationMenu",
209
+ "Permission check error - showing no items for security",
210
+ {
211
+ permissionsError: permissionsError?.message,
212
+ },
213
+ );
214
+ return [];
215
+ }
216
+
217
+ if (!permissionMap || Object.keys(permissionMap).length === 0) {
218
+ if (stableScope.organisationId && items && items.length > 0) {
219
+ return (items || []).filter((item) => !item.meta?.hidden);
220
+ }
221
+
222
+ if (stableScope.organisationId) {
223
+ logger.warn("NavigationMenu", "Permission map is empty and no items provided - showing nothing", {
224
+ permissionMapSize: 0,
225
+ organisationId: stableScope.organisationId,
226
+ eventId: stableScope.eventId,
227
+ appId: stableScope.appId,
228
+ });
229
+ }
230
+ return [];
231
+ }
232
+
233
+ const getPageIdFromHref = (href?: string): string | null => {
234
+ if (!href) return null;
235
+ const path = href.split("?")[0].split("#")[0].replace(/^\//, "");
236
+ return path || "home";
237
+ };
238
+
239
+ const hasItemPermission = (item: NavigationItem): boolean => {
240
+ if (item.permissions && item.permissions.length > 0 && !item.href) {
241
+ const permissions = item.permissions
242
+ .filter((p): p is string => typeof p === "string")
243
+ .map((p) => p as Permission);
244
+
245
+ if (permissions.length > 0) {
246
+ const hasPermission = hasAnyPermission?.(permissions);
247
+ if (!hasPermission) {
248
+ return false;
249
+ }
250
+ }
251
+ }
252
+
253
+ if (item.roles && item.roles.length > 0) {
254
+ const hasRole = item.roles.some((role) => {
255
+ if (typeof role !== "string") return true;
256
+
257
+ switch (role.toLowerCase()) {
258
+ case "super_admin":
259
+ case "super admin":
260
+ return rbacContext.isSuperAdmin;
261
+ case "org_admin":
262
+ case "org admin":
263
+ case "admin":
264
+ return rbacContext.isOrgAdmin || rbacContext.isSuperAdmin;
265
+ case "event_admin":
266
+ case "event admin":
267
+ return rbacContext.isEventAdmin || rbacContext.isSuperAdmin;
268
+ default:
269
+ return (
270
+ rbacContext.organisationRole === role ||
271
+ rbacContext.eventAppRole === role ||
272
+ rbacContext.isSuperAdmin
273
+ );
274
+ }
275
+ });
276
+ if (!hasRole) {
277
+ return false;
278
+ }
279
+ }
280
+
281
+ if (item.accessLevel) {
282
+ if (typeof item.accessLevel === "string") {
283
+ const accessLevel = item.accessLevel.toLowerCase() as RBACAccessLevel;
284
+ const userEventRole = rbacContext.eventAppRole;
285
+
286
+ if (!rbacContext.isSuperAdmin) {
287
+ const roleToAccessLevel: Record<string, RBACAccessLevel> = {
288
+ viewer: "viewer",
289
+ participant: "participant",
290
+ planner: "planner",
291
+ event_admin: "admin",
292
+ };
293
+ const userAccessLevel = userEventRole ? roleToAccessLevel[userEventRole] || "viewer" : null;
294
+
295
+ const levelHierarchy: Record<RBACAccessLevel, number> = {
296
+ viewer: 1,
297
+ participant: 2,
298
+ planner: 3,
299
+ admin: 4,
300
+ super: 5,
301
+ };
302
+ const requiredLevel = levelHierarchy[accessLevel] || 0;
303
+ const userLevel = userAccessLevel ? levelHierarchy[userAccessLevel] || 0 : 0;
304
+
305
+ if (userLevel < requiredLevel) {
306
+ return false;
307
+ }
308
+ }
309
+ }
310
+ }
311
+
312
+ if (item.href) {
313
+ const pageId = item.pageId || getPageIdFromHref(item.href);
314
+ if (pageId) {
315
+ const pagePermission: Permission = `read:page.${pageId}` as Permission;
316
+
317
+ const isSuperAdmin = permissionMap["*"] === true;
318
+ const hasPagePermission = permissionMap[pagePermission] === true;
319
+ const finalHasPermission = isSuperAdmin || hasPagePermission;
320
+
321
+ if (!finalHasPermission) {
322
+ return false;
323
+ }
324
+ }
325
+ }
326
+
327
+ return true;
328
+ };
329
+
330
+ const filterItem = (item: NavigationItem): NavigationItem | null => {
331
+ if (item.meta?.hidden) return null;
332
+
333
+ if (!hasItemPermission(item)) return null;
334
+
335
+ let filteredChildren: NavigationItem[] | undefined;
336
+ if (item.children && item.children.length > 0) {
337
+ filteredChildren = item.children
338
+ .map((child) => filterItem(child))
339
+ .filter((child): child is NavigationItem => child !== null);
340
+
341
+ if (filteredChildren.length === 0 && !item.href) {
342
+ return null;
343
+ }
344
+ }
345
+
346
+ return {
347
+ ...item,
348
+ children: filteredChildren,
349
+ };
350
+ };
351
+
352
+ const filtered = (items || [])
353
+ .map((item) => filterItem(item))
354
+ .filter((item): item is NavigationItem => item !== null);
355
+
356
+ previousFilteredItemsRef.current = filtered;
357
+
358
+ return filtered;
359
+ }, [
360
+ items,
361
+ itemsPreFiltered,
362
+ authContext,
363
+ rbacContext,
364
+ permissionMap,
365
+ hasAnyPermission,
366
+ scopeLoading,
367
+ scopeError,
368
+ permissionsLoading,
369
+ resolvedScope,
370
+ effectiveScope,
371
+ auditLog,
372
+ eventLoadingRaw,
373
+ eventLoading,
374
+ selectedEvent,
375
+ orgContextReady,
376
+ selectedOrganisation?.id,
377
+ permissionsError,
378
+ stableScope.organisationId,
379
+ stableScope.eventId,
380
+ stableScope.appId,
381
+ ]);
382
+
383
+ return {
384
+ authContext,
385
+ rbacContext,
386
+ filteredItems,
387
+ permissionMap,
388
+ hasAnyPermission: hasAnyPermission || null,
389
+ };
390
+ }
@@ -63,6 +63,9 @@ import { useOrganisations } from '../../hooks/useOrganisations';
63
63
  import type { Organisation } from '../../types/organisation';
64
64
  import { logger } from '../../utils/core/logger';
65
65
 
66
+ /**
67
+ * Props for the OrganisationSelector component.
68
+ */
66
69
  export interface OrganisationSelectorProps {
67
70
  /** Placeholder text for the dropdown */
68
71
  placeholder?: string;
@@ -889,7 +889,7 @@ describe('PaceAppLayout Component', () => {
889
889
  );
890
890
 
891
891
  await waitFor(() => {
892
- // useCan is called with userId, scope, permission, pageId, useCache, appName
892
+ // useCan is called with userId, scope, permission, pageId, useCache, precomputedSuperAdmin, appName
893
893
  expect(mockUseCan).toHaveBeenCalledWith(
894
894
  'user-123',
895
895
  expect.objectContaining({
@@ -900,6 +900,7 @@ describe('PaceAppLayout Component', () => {
900
900
  'update:page.dashboard-page',
901
901
  'dashboard-page',
902
902
  true,
903
+ expect.any(Boolean), // precomputedSuperAdmin - can be false, null, or undefined during checks
903
904
  'Test App'
904
905
  );
905
906
  }, { timeout: 2000 });
@@ -921,7 +922,7 @@ describe('PaceAppLayout Component', () => {
921
922
  );
922
923
 
923
924
  await waitFor(() => {
924
- // useCan is called with userId, scope, permission, pageId, useCache, appName
925
+ // useCan is called with userId, scope, permission, pageId, useCache, precomputedSuperAdmin, appName
925
926
  // Uses defaultPermission "create" since /dashboard is not in routePermissions
926
927
  expect(mockUseCan).toHaveBeenCalledWith(
927
928
  'user-123',
@@ -933,6 +934,7 @@ describe('PaceAppLayout Component', () => {
933
934
  'create:page.dashboard',
934
935
  'dashboard',
935
936
  true,
937
+ expect.any(Boolean), // precomputedSuperAdmin - can be false, null, or undefined during checks
936
938
  'Test App'
937
939
  );
938
940
  }, { timeout: 2000 });
@@ -121,6 +121,10 @@ import type { PasswordChangeFormError } from '../PasswordChange/PasswordChangeFo
121
121
  // Define Operation type locally since old RBAC types are removed
122
122
  type Operation = 'read' | 'create' | 'update' | 'delete' | 'manage';
123
123
 
124
+ /**
125
+ * Props for the PaceAppLayout component.
126
+ * Configures the application layout including navigation, header, and footer.
127
+ */
124
128
  export interface PaceAppLayoutProps {
125
129
  /** The name of the application to be displayed in the header. */
126
130
  appName: string;
@@ -524,12 +528,15 @@ export function PaceAppLayout({
524
528
  // Pass appName to useCan so it can be passed to isPermitted for PORTAL/ADMIN special case
525
529
  // Only check permissions if enforcePermissions is true and we have a valid permission string
526
530
  const shouldCheckPermission = enforcePermissions && !!currentPermission && !!currentPageId;
531
+ // Pass super admin status to avoid duplicate check - use null if still checking, false/true if known
532
+ const superAdminStatus = isSuperAdminFromRBAC ? true : (isCheckingSuperAdminDirect ? null : isSuperAdminDirect);
527
533
  const { can: canFromHook, isLoading: isCheckingPermission, error: permissionError } = useCan(
528
534
  user?.id || '',
529
535
  scope,
530
536
  shouldCheckPermission ? currentPermission : ('' as Permission),
531
537
  shouldCheckPermission ? currentPageId : '',
532
538
  true, // useCache
539
+ superAdminStatus, // Pass super admin status to avoid duplicate check
533
540
  appName // Pass appName for PORTAL/ADMIN special case
534
541
  );
535
542
 
@@ -817,25 +824,39 @@ export function PaceAppLayout({
817
824
  }, [roleBasedRouting, routeConfig, location.pathname, strictMode, user?.id, fallbackRoute, scope, navigate, auditLog, onRouteAccessDenied, onRouteStrictModeViolation]);
818
825
 
819
826
  const handleSignOut = async () => {
820
- await signOut();
827
+ try {
828
+ await signOut();
829
+ } catch (error) {
830
+ logger.error('PaceAppLayout', 'Failed to sign out', { error: error instanceof Error ? error.message : String(error) });
831
+ }
821
832
  };
822
833
 
823
834
  const handleChangePassword = async (newPassword: string, confirmPassword: string) => {
824
- // The form component in UserMenu already checks for matching passwords
825
- const result = await updatePassword(newPassword);
826
- if (result?.error) {
827
- // The form will display the error message
828
- logger.error('PaceAppLayout', 'Failed to change password', { error: result.error.message });
829
- // Convert AuthError to PasswordChangeFormError
835
+ try {
836
+ // The form component in UserMenu already checks for matching passwords
837
+ const result = await updatePassword(newPassword);
838
+ if (result?.error) {
839
+ // The form will display the error message
840
+ logger.error('PaceAppLayout', 'Failed to change password', { error: result.error.message });
841
+ // Convert AuthError to PasswordChangeFormError
842
+ return {
843
+ error: {
844
+ message: result.error.message,
845
+ code: result.error.name || 'PASSWORD_UPDATE_ERROR'
846
+ }
847
+ };
848
+ }
849
+ // The form will handle closing the modal on success
850
+ return {};
851
+ } catch (error) {
852
+ logger.error('PaceAppLayout', 'Failed to change password', { error: error instanceof Error ? error.message : String(error) });
830
853
  return {
831
854
  error: {
832
- message: result.error.message,
833
- code: result.error.name || 'PASSWORD_UPDATE_ERROR'
855
+ message: error instanceof Error ? error.message : 'An unexpected error occurred',
856
+ code: 'PASSWORD_UPDATE_ERROR'
834
857
  }
835
858
  };
836
859
  }
837
- // The form will handle closing the modal on success
838
- return {};
839
860
  };
840
861
 
841
862
  // CRITICAL: Wait for organisation context to be ready before proceeding
@@ -1,3 +1,4 @@
1
+ /// <reference types="vitest/globals" />
1
2
  /**
2
3
  * @file Shared Test Setup for PaceAppLayout Tests
3
4
  * @package @jmruthers/pace-core
@@ -10,8 +11,6 @@
10
11
  * provides the shared mock data and reset functions that can be imported and used
11
12
  * in the vi.mock() factory functions.
12
13
  */
13
-
14
- import { vi } from 'vitest';
15
14
  import React from 'react';
16
15
 
17
16
  // === MOCK DATA ===
@@ -131,6 +131,9 @@ import { clearPalette } from '../../theming/runtime';
131
131
  import { EventServiceContext } from '../../providers/services/EventServiceProvider';
132
132
  import { logger } from '../../utils/core/logger';
133
133
 
134
+ /**
135
+ * Props for the PaceLoginPage component.
136
+ */
134
137
  export interface PaceLoginPageProps {
135
138
  /** The name of the application to be displayed on the login form. */
136
139
  appName: string;
@@ -103,16 +103,25 @@ import { Input } from '../Input/Input';
103
103
  import { Label } from '../Label';
104
104
  import { cn } from '../../utils/core/cn';
105
105
 
106
+ /**
107
+ * Form values for password change.
108
+ */
106
109
  export interface PasswordChangeFormValues {
107
110
  newPassword: string;
108
111
  confirmPassword: string;
109
112
  }
110
113
 
114
+ /**
115
+ * Error structure for password change form.
116
+ */
111
117
  export interface PasswordChangeFormError {
112
118
  message?: string;
113
119
  code?: string;
114
120
  }
115
121
 
122
+ /**
123
+ * Props for the PasswordChangeForm component.
124
+ */
116
125
  export interface PasswordChangeFormProps {
117
126
  onSubmit: (values: PasswordChangeFormValues) => Promise<{ error?: PasswordChangeFormError }>;
118
127
  className?: string;
@@ -79,6 +79,9 @@ import { Alert, AlertDescription, AlertTitle } from '../Alert/Alert';
79
79
  import { logger } from '../../utils/core/logger';
80
80
  import { usePreventTabReload } from '../../hooks/usePreventTabReload';
81
81
 
82
+ /**
83
+ * Props for the ProtectedRoute component.
84
+ */
82
85
  export interface ProtectedRouteProps {
83
86
  /**
84
87
  * Whether an event is required for routes inside this component.
@@ -88,14 +91,6 @@ export interface ProtectedRouteProps {
88
91
  */
89
92
  requireEvent?: boolean;
90
93
 
91
- /**
92
- * Whether super admins can bypass event requirement.
93
- * Note: This feature requires additional RBAC setup. For simple bypass, set requireEvent={false} instead.
94
- * @default false
95
- * @deprecated Use requireEvent={false} for routes that don't need events
96
- */
97
- allowSuperAdminBypass?: boolean;
98
-
99
94
  /**
100
95
  * Custom component to render when no events are available.
101
96
  * If not provided, a default message is shown.
@@ -133,7 +128,6 @@ export interface ProtectedRouteProps {
133
128
  */
134
129
  export function ProtectedRoute({
135
130
  requireEvent = false,
136
- allowSuperAdminBypass = false,
137
131
  noEventsFallback,
138
132
  loadingFallback,
139
133
  loginPath = '/login'
@@ -35,9 +35,9 @@
35
35
  * refetch={refetch}
36
36
  * >
37
37
  * <h1>Event Details</h1>
38
- * <div className="content">
38
+ * <main className="content">
39
39
  * Your public page content
40
- * </div>
40
+ * </main>
41
41
  * </PublicPageLayout>
42
42
  * );
43
43
  * }
@@ -90,8 +90,6 @@ export interface PublicPageLayoutProps {
90
90
  refetch?: () => Promise<void> | void;
91
91
  /** Whether to show the footer (default: true) */
92
92
  showFooter?: boolean;
93
- /** @deprecated Custom CSS classes for the layout - no longer used as wrapper div was removed */
94
- className?: string;
95
93
  /** Custom error fallback component */
96
94
  errorFallback?: React.ComponentType<{ error: Error; retry: () => void }>;
97
95
  /** Custom loading fallback component */
@@ -294,7 +292,6 @@ export function PublicPageLayout({
294
292
  error = null,
295
293
  refetch,
296
294
  showFooter = true,
297
- className = '',
298
295
  errorFallback: ErrorFallback,
299
296
  loadingFallback: LoadingFallback,
300
297
  customHeader,
@@ -50,6 +50,10 @@ interface PublicPageContextType {
50
50
  };
51
51
  }
52
52
 
53
+ /**
54
+ * Context for public pages.
55
+ * Provides isolated context for public pages without authentication requirements.
56
+ */
53
57
  export const PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);
54
58
 
55
59
  export interface PublicPageProviderProps {