@jmruthers/pace-core 0.5.76 → 0.5.78

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 (447) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{RBACService-C4udt_Zp.d.ts → AuthService-Df3IozMG.d.ts} +10 -118
  3. package/dist/{DataTable-ntgmhO2W.d.ts → DataTable-BE0OXZKQ.d.ts} +9 -2
  4. package/dist/{DataTable-4GAVPIEG.js → DataTable-ETGVF4Y5.js} +50 -13
  5. package/dist/{PublicLoadingSpinner-BiNER8F5.d.ts → PublicLoadingSpinner-CnUaz0vG.d.ts} +5 -2
  6. package/dist/{UnifiedAuthProvider-Bj6YCf7c.d.ts → UnifiedAuthProvider-B391Aqum.d.ts} +42 -45
  7. package/dist/{UnifiedAuthProvider-3NKDOSOK.js → UnifiedAuthProvider-P5SOJAQ6.js} +4 -5
  8. package/dist/{api-DDMUKIUD.js → api-KG4A2X7P.js} +9 -3
  9. package/dist/{audit-6TOCAMKO.js → audit-65VNHEV2.js} +2 -2
  10. package/dist/{chunk-K34IM5CT.js → chunk-2OGV6IRV.js} +196 -626
  11. package/dist/chunk-2OGV6IRV.js.map +1 -0
  12. package/dist/{chunk-NTNILOBC.js → chunk-5BO3MI5Y.js} +4 -4
  13. package/dist/{chunk-XLZ7U46Z.js → chunk-CVMVPYAL.js} +9 -60
  14. package/dist/chunk-CVMVPYAL.js.map +1 -0
  15. package/dist/{chunk-URUTVZ7N.js → chunk-FL4ZCQLD.js} +2 -2
  16. package/dist/{chunk-LW7MMEAQ.js → chunk-FT2M4R4F.js} +2 -2
  17. package/dist/{chunk-5BSLGBYI.js → chunk-JCQZ6LA7.js} +2 -8
  18. package/dist/{chunk-5BSLGBYI.js.map → chunk-JCQZ6LA7.js.map} +1 -1
  19. package/dist/{chunk-KHJS6VIA.js → chunk-LRQ6RBJC.js} +157 -112
  20. package/dist/chunk-LRQ6RBJC.js.map +1 -0
  21. package/dist/{chunk-WN6XJWOS.js → chunk-MNJXXD6C.js} +274 -743
  22. package/dist/chunk-MNJXXD6C.js.map +1 -0
  23. package/dist/{chunk-KK73ZB4E.js → chunk-PTR5PMPE.js} +153 -132
  24. package/dist/chunk-PTR5PMPE.js.map +1 -0
  25. package/dist/{chunk-B2WTCLCV.js → chunk-Q7APDV6H.js} +18 -8
  26. package/dist/chunk-Q7APDV6H.js.map +1 -0
  27. package/dist/{chunk-A4FUBC7B.js → chunk-QGVSOUJ2.js} +2 -4
  28. package/dist/{chunk-A4FUBC7B.js.map → chunk-QGVSOUJ2.js.map} +1 -1
  29. package/dist/{chunk-FGMFQSHX.js → chunk-S63MFSY6.js} +500 -551
  30. package/dist/chunk-S63MFSY6.js.map +1 -0
  31. package/dist/{chunk-AFGTSUAD.js → chunk-VSOKOFRF.js} +4 -4
  32. package/dist/chunk-WUXCWRL6.js +20 -0
  33. package/dist/chunk-WUXCWRL6.js.map +1 -0
  34. package/dist/{chunk-Y6TXWPJO.js → chunk-YVVGHRGI.js} +105 -31
  35. package/dist/chunk-YVVGHRGI.js.map +1 -0
  36. package/dist/{chunk-M5IWZRBT.js → chunk-ZMNXIJP4.js} +2187 -981
  37. package/dist/chunk-ZMNXIJP4.js.map +1 -0
  38. package/dist/components.d.ts +6 -6
  39. package/dist/components.js +14 -18
  40. package/dist/components.js.map +1 -1
  41. package/dist/{database-C3Szpi5J.d.ts → database-BXAfr2Y_.d.ts} +18 -0
  42. package/dist/hooks.d.ts +5 -5
  43. package/dist/hooks.js +8 -9
  44. package/dist/hooks.js.map +1 -1
  45. package/dist/index.d.ts +19 -27
  46. package/dist/index.js +21 -29
  47. package/dist/index.js.map +1 -1
  48. package/dist/{organisation-BtshODVF.d.ts → organisation-D6qRDtbF.d.ts} +1 -1
  49. package/dist/providers.d.ts +7 -21
  50. package/dist/providers.js +3 -10
  51. package/dist/rbac/index.d.ts +71 -221
  52. package/dist/rbac/index.js +15 -16
  53. package/dist/{types-CGX9Vyf5.d.ts → types-BDg1mAGG.d.ts} +36 -6
  54. package/dist/types.d.ts +3 -3
  55. package/dist/types.js +61 -18
  56. package/dist/types.js.map +1 -1
  57. package/dist/{unified-CM7T0aTK.d.ts → unified-DQ4VcT7H.d.ts} +1 -1
  58. package/dist/{usePublicRouteParams-B-CumWRc.d.ts → usePublicRouteParams-BlgwXweB.d.ts} +3 -3
  59. package/dist/utils.d.ts +2 -2
  60. package/dist/utils.js +52 -9
  61. package/dist/utils.js.map +1 -1
  62. package/docs/CONTENT_AUDIT_REPORT.md +253 -0
  63. package/docs/DOCUMENTATION_AUDIT.md +172 -0
  64. package/docs/README.md +142 -147
  65. package/docs/STYLE_GUIDE.md +37 -0
  66. package/docs/api/classes/ColumnFactory.md +17 -17
  67. package/docs/api/classes/ErrorBoundary.md +1 -1
  68. package/docs/api/classes/InvalidScopeError.md +4 -4
  69. package/docs/api/classes/MissingUserContextError.md +4 -4
  70. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  71. package/docs/api/classes/PermissionDeniedError.md +5 -5
  72. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  73. package/docs/api/classes/RBACAuditManager.md +8 -8
  74. package/docs/api/classes/RBACCache.md +35 -5
  75. package/docs/api/classes/RBACEngine.md +49 -20
  76. package/docs/api/classes/RBACError.md +4 -4
  77. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  78. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  79. package/docs/api/classes/StorageUtils.md +1 -1
  80. package/docs/api/enums/FileCategory.md +1 -1
  81. package/docs/api/interfaces/AggregateConfig.md +4 -4
  82. package/docs/api/interfaces/ButtonProps.md +1 -1
  83. package/docs/api/interfaces/CardProps.md +1 -1
  84. package/docs/api/interfaces/ColorPalette.md +1 -1
  85. package/docs/api/interfaces/ColorShade.md +1 -1
  86. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  87. package/docs/api/interfaces/DataRecord.md +11 -0
  88. package/docs/api/interfaces/DataTableAction.md +65 -29
  89. package/docs/api/interfaces/DataTableColumn.md +36 -23
  90. package/docs/api/interfaces/DataTableProps.md +80 -38
  91. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  92. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  93. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  94. package/docs/api/interfaces/EventLogoProps.md +1 -1
  95. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  96. package/docs/api/interfaces/FileMetadata.md +1 -1
  97. package/docs/api/interfaces/FileReference.md +1 -1
  98. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  99. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  100. package/docs/api/interfaces/FileUploadProps.md +1 -1
  101. package/docs/api/interfaces/FooterProps.md +1 -1
  102. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  103. package/docs/api/interfaces/InputProps.md +1 -1
  104. package/docs/api/interfaces/LabelProps.md +1 -1
  105. package/docs/api/interfaces/LoginFormProps.md +1 -1
  106. package/docs/api/interfaces/NavigationAccessRecord.md +11 -11
  107. package/docs/api/interfaces/NavigationContextType.md +9 -9
  108. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  109. package/docs/api/interfaces/NavigationItem.md +1 -1
  110. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  111. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  112. package/docs/api/interfaces/Organisation.md +1 -1
  113. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  114. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  115. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  116. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  117. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  118. package/docs/api/interfaces/PaceLoginPageProps.md +16 -3
  119. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  120. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  121. package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
  122. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  123. package/docs/api/interfaces/PaletteData.md +1 -1
  124. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  125. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  126. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  127. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  128. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  129. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  130. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  131. package/docs/api/interfaces/RBACConfig.md +1 -1
  132. package/docs/api/interfaces/RBACLogger.md +1 -1
  133. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  134. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  135. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  136. package/docs/api/interfaces/RouteConfig.md +2 -2
  137. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  138. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  139. package/docs/api/interfaces/StorageConfig.md +1 -1
  140. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  141. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  142. package/docs/api/interfaces/StorageListOptions.md +1 -1
  143. package/docs/api/interfaces/StorageListResult.md +1 -1
  144. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  145. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  146. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  147. package/docs/api/interfaces/StyleImport.md +1 -1
  148. package/docs/api/interfaces/SwitchProps.md +1 -1
  149. package/docs/api/interfaces/ToastActionElement.md +1 -1
  150. package/docs/api/interfaces/ToastProps.md +1 -1
  151. package/docs/api/interfaces/UnifiedAuthContextType.md +94 -521
  152. package/docs/api/interfaces/UnifiedAuthProviderProps.md +16 -16
  153. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  154. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  155. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  156. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  157. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  158. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  159. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  160. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  161. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  162. package/docs/api/interfaces/UserEventAccess.md +11 -11
  163. package/docs/api/interfaces/UserMenuProps.md +1 -1
  164. package/docs/api/interfaces/UserProfile.md +1 -1
  165. package/docs/api/modules.md +251 -269
  166. package/docs/api-reference/components.md +193 -0
  167. package/docs/api-reference/hooks.md +265 -0
  168. package/docs/api-reference/providers.md +6 -0
  169. package/docs/api-reference/types.md +6 -0
  170. package/docs/api-reference/utilities.md +207 -0
  171. package/docs/architecture/README.md +6 -0
  172. package/docs/{database-schema-requirements.md → architecture/database-schema-requirements.md} +6 -0
  173. package/docs/architecture/rbac-security-architecture.md +258 -0
  174. package/docs/architecture/services.md +9 -1
  175. package/docs/best-practices/README.md +6 -0
  176. package/docs/best-practices/accessibility.md +6 -0
  177. package/docs/{common-patterns.md → best-practices/common-patterns.md} +6 -0
  178. package/docs/best-practices/deployment.md +6 -0
  179. package/docs/best-practices/performance.md +475 -2
  180. package/docs/best-practices/security.md +6 -0
  181. package/docs/best-practices/testing.md +6 -0
  182. package/docs/core-concepts/authentication.md +6 -0
  183. package/docs/core-concepts/events.md +6 -0
  184. package/docs/core-concepts/organisations.md +6 -0
  185. package/docs/core-concepts/permissions.md +6 -0
  186. package/docs/core-concepts/rbac-system.md +8 -0
  187. package/docs/documentation-index.md +121 -182
  188. package/docs/{consuming-app-vite-config.md → getting-started/consuming-app-vite-config.md} +6 -0
  189. package/docs/getting-started/documentation-index.md +40 -0
  190. package/docs/getting-started/examples/README.md +878 -35
  191. package/docs/{faq.md → getting-started/faq.md} +7 -1
  192. package/docs/getting-started/installation-guide.md +6 -0
  193. package/docs/{quick-reference.md → getting-started/quick-reference.md} +6 -0
  194. package/docs/implementation-guides/app-layout.md +6 -0
  195. package/docs/implementation-guides/authentication.md +1021 -0
  196. package/docs/implementation-guides/component-styling.md +6 -0
  197. package/docs/implementation-guides/data-tables.md +1264 -2076
  198. package/docs/implementation-guides/dynamic-colors.md +6 -0
  199. package/docs/implementation-guides/event-theming-summary.md +6 -0
  200. package/docs/{file-reference-system.md → implementation-guides/file-reference-system.md} +6 -0
  201. package/docs/implementation-guides/file-upload-storage.md +6 -0
  202. package/docs/implementation-guides/forms.md +6 -0
  203. package/docs/implementation-guides/inactivity-tracking.md +6 -0
  204. package/docs/implementation-guides/navigation.md +6 -0
  205. package/docs/implementation-guides/organisation-security.md +6 -0
  206. package/docs/implementation-guides/permission-enforcement.md +6 -0
  207. package/docs/implementation-guides/public-pages-advanced.md +6 -0
  208. package/docs/implementation-guides/public-pages.md +6 -0
  209. package/docs/migration/MIGRATION_GUIDE.md +827 -351
  210. package/docs/migration/README.md +7 -1
  211. package/docs/migration/organisation-context-timing-fix.md +6 -0
  212. package/docs/migration/rbac-migration.md +44 -1
  213. package/docs/migration/service-architecture.md +6 -0
  214. package/docs/migration/v0.4.15-tailwind-scanning.md +6 -0
  215. package/docs/migration/v0.4.16-css-first-approach.md +6 -0
  216. package/docs/migration/v0.4.17-source-path-fix.md +6 -0
  217. package/docs/rbac/README-rbac-rls-integration.md +6 -0
  218. package/docs/rbac/README.md +6 -0
  219. package/docs/rbac/advanced-patterns.md +6 -0
  220. package/docs/rbac/api-reference.md +7 -1
  221. package/docs/rbac/breaking-changes-v3.md +222 -0
  222. package/docs/rbac/examples/rbac-rls-integration-example.md +6 -0
  223. package/docs/rbac/examples.md +6 -0
  224. package/docs/rbac/getting-started.md +6 -0
  225. package/docs/rbac/migration-guide.md +260 -0
  226. package/docs/rbac/quick-start.md +70 -13
  227. package/docs/rbac/rbac-rls-integration.md +6 -0
  228. package/docs/rbac/super-admin-guide.md +6 -0
  229. package/docs/rbac/troubleshooting.md +6 -0
  230. package/docs/security/README.md +6 -0
  231. package/docs/security/checklist.md +6 -0
  232. package/docs/styles/README.md +7 -1
  233. package/docs/{usage.md → styles/usage.md} +6 -0
  234. package/docs/testing/README.md +6 -0
  235. package/docs/{visual-testing.md → testing/visual-testing.md} +6 -0
  236. package/docs/troubleshooting/README.md +387 -5
  237. package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +6 -0
  238. package/docs/troubleshooting/common-issues.md +6 -0
  239. package/docs/troubleshooting/database-view-compatibility.md +6 -0
  240. package/docs/troubleshooting/organisation-context-setup.md +6 -0
  241. package/docs/troubleshooting/react-hooks-issue-analysis.md +6 -0
  242. package/docs/troubleshooting/styling-issues.md +6 -0
  243. package/docs/troubleshooting/tailwind-content-scanning.md +6 -0
  244. package/package.json +1 -1
  245. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -1
  246. package/src/__tests__/helpers/test-providers.tsx +3 -53
  247. package/src/components/DataTable/DataTable.test.tsx +319 -0
  248. package/src/components/DataTable/DataTable.tsx +32 -11
  249. package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx.skip} +6 -4
  250. package/src/components/DataTable/__tests__/{DataTable.test.tsx → DataTable.test.tsx.skip} +6 -4
  251. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +31 -9
  252. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +601 -0
  253. package/src/components/DataTable/__tests__/keyboard.test.tsx +615 -0
  254. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +639 -0
  255. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx.skip +330 -0
  256. package/src/components/DataTable/components/AccessDeniedPage.tsx +2 -2
  257. package/src/components/DataTable/components/ActionButtons.tsx +88 -104
  258. package/src/components/DataTable/components/DataTableCore.tsx +309 -337
  259. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +4 -2
  260. package/src/components/DataTable/components/DataTableModals.tsx +22 -1
  261. package/src/components/DataTable/components/EditableRow.tsx +69 -84
  262. package/src/components/DataTable/components/EmptyState.tsx +5 -1
  263. package/src/components/DataTable/components/ImportModal.tsx +65 -36
  264. package/src/components/DataTable/components/PaginationControls.tsx +40 -100
  265. package/src/components/DataTable/components/UnifiedTableBody.tsx +125 -148
  266. package/src/components/DataTable/context/DataTableContext.tsx +1 -1
  267. package/src/components/DataTable/core/ColumnFactory.ts +5 -0
  268. package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +12 -10
  269. package/src/components/DataTable/examples/HierarchicalExample.tsx +1 -1
  270. package/src/components/DataTable/examples/InitialPageSizeExample.tsx +1 -0
  271. package/src/components/DataTable/examples/PerformanceExample.tsx +1 -0
  272. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +1 -5
  273. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +167 -0
  274. package/src/components/DataTable/hooks/index.ts +7 -0
  275. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +32 -15
  276. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +102 -0
  277. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +89 -0
  278. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +117 -0
  279. package/src/components/DataTable/hooks/useDataTablePermissions.ts +71 -27
  280. package/src/components/DataTable/hooks/useDataTableState.ts +39 -11
  281. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +33 -0
  282. package/src/components/DataTable/hooks/useHierarchicalState.ts +15 -1
  283. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +447 -0
  284. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +94 -0
  285. package/src/components/DataTable/hooks/useTableColumns.ts +10 -7
  286. package/src/components/DataTable/hooks/useTableHandlers.ts +174 -0
  287. package/src/components/DataTable/index.ts +12 -3
  288. package/src/components/DataTable/types.ts +129 -9
  289. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +159 -22
  290. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +111 -0
  291. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +15 -29
  292. package/src/components/DataTable/utils/a11yUtils.ts +244 -0
  293. package/src/components/DataTable/utils/debugTools.ts +609 -0
  294. package/src/components/DataTable/utils/exportUtils.ts +114 -16
  295. package/src/components/DataTable/utils/flexibleImport.ts +202 -32
  296. package/src/components/DataTable/utils/hierarchicalUtils.ts +1 -1
  297. package/src/components/DataTable/utils/index.ts +2 -0
  298. package/src/components/DataTable/utils/paginationUtils.ts +350 -0
  299. package/src/components/DataTable/utils/rowUtils.ts +6 -5
  300. package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -24
  301. package/src/components/NavigationMenu/NavigationMenu.tsx +19 -8
  302. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +1 -23
  303. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +56 -6
  304. package/src/components/PaceLoginPage/PaceLoginPage.tsx +137 -13
  305. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +1 -1
  306. package/src/components/Select/Select.tsx +1 -0
  307. package/src/components/examples/PermissionExample.tsx +173 -0
  308. package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
  309. package/src/examples/PublicEventPage.tsx +274 -0
  310. package/src/examples/PublicPageApp.tsx +308 -0
  311. package/src/examples/PublicPageUsageExample.tsx +216 -0
  312. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +12 -1
  313. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +129 -17
  314. package/src/hooks/__tests__/useRBAC.unit.test.ts +151 -846
  315. package/src/hooks/useOrganisationPermissions.test.ts +42 -18
  316. package/src/hooks/useOrganisationPermissions.ts +12 -6
  317. package/src/hooks/useOrganisationSecurity.test.ts +138 -85
  318. package/src/hooks/useOrganisationSecurity.ts +41 -10
  319. package/src/index.ts +0 -1
  320. package/src/providers/AuthProvider.simplified.tsx +880 -0
  321. package/src/providers/UnifiedAuthProvider.test.simple.tsx +8 -8
  322. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +29 -19
  323. package/src/providers/index.ts +0 -1
  324. package/src/providers/services/EventServiceProvider.tsx +19 -15
  325. package/src/providers/services/InactivityServiceProvider.tsx +19 -15
  326. package/src/providers/services/OrganisationServiceProvider.tsx +19 -15
  327. package/src/providers/services/UnifiedAuthProvider.tsx +156 -127
  328. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +1 -1
  329. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -3
  330. package/src/rbac/README.md +1 -1
  331. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +25 -27
  332. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +313 -0
  333. package/src/rbac/__tests__/engine.comprehensive.test.ts +114 -348
  334. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +28 -110
  335. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +33 -85
  336. package/src/rbac/__tests__/scenarios.user-role.test.tsx +2 -2
  337. package/src/rbac/adapters.tsx +26 -69
  338. package/src/rbac/api.test.ts +90 -27
  339. package/src/rbac/api.ts +61 -10
  340. package/src/rbac/audit.test.ts +33 -38
  341. package/src/rbac/audit.ts +21 -6
  342. package/src/rbac/cache.ts +33 -1
  343. package/src/rbac/components/NavigationGuard.tsx +11 -11
  344. package/src/rbac/components/NavigationProvider.test.tsx +11 -5
  345. package/src/rbac/components/NavigationProvider.tsx +37 -13
  346. package/src/rbac/components/PagePermissionGuard.tsx +111 -50
  347. package/src/rbac/components/PagePermissionProvider.tsx +5 -5
  348. package/src/rbac/components/PermissionEnforcer.tsx +11 -11
  349. package/src/rbac/components/RoleBasedRouter.tsx +5 -5
  350. package/src/rbac/components/SecureDataProvider.tsx +5 -5
  351. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +8 -8
  352. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +14 -14
  353. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +12 -12
  354. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +6 -6
  355. package/src/rbac/engine.test.simple.ts +19 -13
  356. package/src/rbac/engine.test.ts +1 -0
  357. package/src/rbac/engine.ts +330 -766
  358. package/src/rbac/errors.ts +156 -0
  359. package/src/rbac/hooks/usePermissions.ts +32 -10
  360. package/src/rbac/hooks/useRBAC.test.ts +126 -512
  361. package/src/rbac/hooks/useRBAC.ts +147 -193
  362. package/src/rbac/hooks/useResolvedScope.ts +12 -0
  363. package/src/rbac/index.ts +7 -4
  364. package/src/rbac/security.ts +109 -18
  365. package/src/rbac/types.ts +12 -1
  366. package/src/services/AuthService.ts +2 -15
  367. package/src/services/EventService.ts +43 -46
  368. package/src/services/OrganisationService.ts +51 -31
  369. package/src/services/__tests__/AuthService.test.ts +1 -1
  370. package/src/services/__tests__/EventService.test.ts +1 -1
  371. package/src/services/__tests__/OrganisationService.test.ts +1 -1
  372. package/src/services/base/BaseService.ts +8 -0
  373. package/src/styles/base.css +208 -0
  374. package/src/styles/semantic.css +24 -0
  375. package/src/types/database.generated.ts +7347 -0
  376. package/src/types/database.ts +20 -0
  377. package/src/utils/logger.ts +179 -0
  378. package/src/utils/organisationContext.ts +11 -4
  379. package/src/utils/storage/__tests__/helpers.unit.test.ts +6 -2
  380. package/dist/appNameResolver-UURKN7NF.js +0 -22
  381. package/dist/audit-6TOCAMKO.js.map +0 -1
  382. package/dist/chunk-B2WTCLCV.js.map +0 -1
  383. package/dist/chunk-FGMFQSHX.js.map +0 -1
  384. package/dist/chunk-K34IM5CT.js.map +0 -1
  385. package/dist/chunk-KHJS6VIA.js.map +0 -1
  386. package/dist/chunk-KK73ZB4E.js.map +0 -1
  387. package/dist/chunk-M5IWZRBT.js.map +0 -1
  388. package/dist/chunk-ULBI5JGB.js +0 -109
  389. package/dist/chunk-ULBI5JGB.js.map +0 -1
  390. package/dist/chunk-WN6XJWOS.js.map +0 -1
  391. package/dist/chunk-XLZ7U46Z.js.map +0 -1
  392. package/dist/chunk-Y6TXWPJO.js.map +0 -1
  393. package/docs/DOCUMENTATION_CHECKLIST.md +0 -281
  394. package/docs/TERMINOLOGY.md +0 -231
  395. package/docs/api/interfaces/RBACContextType.md +0 -468
  396. package/docs/api/interfaces/RBACProviderProps.md +0 -107
  397. package/docs/best-practices/performance-expansion.md +0 -473
  398. package/docs/breaking-changes.md +0 -179
  399. package/docs/consuming-app-example.md +0 -290
  400. package/docs/documentation-templates.md +0 -539
  401. package/docs/examples/navigation-menu-auth-fix.md +0 -344
  402. package/docs/getting-started/examples/basic-auth-app.md +0 -520
  403. package/docs/getting-started/examples/full-featured-app.md +0 -616
  404. package/docs/getting-started/quick-start.md +0 -376
  405. package/docs/implementation-guides/datatable-filtering.md +0 -313
  406. package/docs/implementation-guides/datatable-rbac-usage.md +0 -317
  407. package/docs/implementation-guides/hierarchical-datatable.md +0 -850
  408. package/docs/implementation-guides/large-datasets.md +0 -281
  409. package/docs/implementation-guides/performance.md +0 -403
  410. package/docs/migration/quick-migration-guide.md +0 -320
  411. package/docs/migration-guide.md +0 -193
  412. package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +0 -226
  413. package/docs/performance/README.md +0 -551
  414. package/docs/style-guide.md +0 -964
  415. package/docs/troubleshooting/authentication-issues.md +0 -334
  416. package/docs/troubleshooting/debugging.md +0 -1117
  417. package/docs/troubleshooting/migration.md +0 -918
  418. package/src/__tests__/hooks/usePermissions.test.ts +0 -261
  419. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +0 -574
  420. package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -613
  421. package/src/hooks/services/__tests__/useServiceHooks.test.tsx +0 -137
  422. package/src/hooks/services/usePermissions.ts +0 -70
  423. package/src/hooks/services/useRBACService.ts +0 -30
  424. package/src/hooks/usePermissionCheck.ts +0 -150
  425. package/src/providers/__tests__/ServiceProviders.test.tsx +0 -477
  426. package/src/providers/services/RBACServiceProvider.tsx +0 -79
  427. package/src/rbac/__tests__/integration.authflow.test.tsx +0 -119
  428. package/src/rbac/__tests__/integration.navigation.test.tsx +0 -69
  429. package/src/rbac/__tests__/integration.securedata.test.tsx +0 -92
  430. package/src/rbac/__tests__/integration.smoke.test.tsx +0 -73
  431. package/src/rbac/providers/RBACProvider.tsx +0 -645
  432. package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +0 -688
  433. package/src/rbac/providers/__tests__/RBACProvider.test.tsx +0 -1186
  434. package/src/rbac/providers/index.ts +0 -11
  435. package/src/services/RBACService.ts +0 -522
  436. package/src/services/__tests__/RBACService.test.ts +0 -492
  437. package/src/services/interfaces/IRBACService.ts +0 -62
  438. package/src/utils/appNameResolver.test 2.ts +0 -494
  439. /package/dist/{DataTable-4GAVPIEG.js.map → DataTable-ETGVF4Y5.js.map} +0 -0
  440. /package/dist/{UnifiedAuthProvider-3NKDOSOK.js.map → UnifiedAuthProvider-P5SOJAQ6.js.map} +0 -0
  441. /package/dist/{api-DDMUKIUD.js.map → api-KG4A2X7P.js.map} +0 -0
  442. /package/dist/{appNameResolver-UURKN7NF.js.map → audit-65VNHEV2.js.map} +0 -0
  443. /package/dist/{chunk-NTNILOBC.js.map → chunk-5BO3MI5Y.js.map} +0 -0
  444. /package/dist/{chunk-URUTVZ7N.js.map → chunk-FL4ZCQLD.js.map} +0 -0
  445. /package/dist/{chunk-LW7MMEAQ.js.map → chunk-FT2M4R4F.js.map} +0 -0
  446. /package/dist/{chunk-AFGTSUAD.js.map → chunk-VSOKOFRF.js.map} +0 -0
  447. /package/docs/{app.css.example → styles/app.css.example} +0 -0
package/src/rbac/api.ts CHANGED
@@ -9,19 +9,22 @@
9
9
 
10
10
  import { SupabaseClient } from '@supabase/supabase-js';
11
11
  import { Database } from '../types/database';
12
- import {
13
- UUID,
14
- Scope,
15
- Permission,
16
- AccessLevel,
17
- PermissionMap,
12
+ import {
13
+ UUID,
14
+ Scope,
15
+ Permission,
16
+ AccessLevel,
17
+ PermissionMap,
18
18
  PermissionCheck,
19
- RBACNotInitializedError
19
+ RBACNotInitializedError,
20
+ RBACAppContext,
21
+ RBACRoleContext,
20
22
  } from './types';
21
23
  import { createRBACEngine, RBACEngine } from './engine';
22
24
  import { createAuditManager, setGlobalAuditManager } from './audit';
23
25
  import { rbacCache, RBACCache, CACHE_PATTERNS } from './cache';
24
26
  import { createRBACConfig, RBACConfig, getRBACLogger } from './config';
27
+ import { SecurityContext } from './security';
25
28
 
26
29
  // Global engine instance
27
30
  let globalEngine: RBACEngine | null = null;
@@ -116,6 +119,22 @@ export async function getPermissionMap(input: {
116
119
  return engine.getPermissionMap(input);
117
120
  }
118
121
 
122
+ export async function resolveAppContext(input: {
123
+ userId: UUID;
124
+ appName: string;
125
+ }): Promise<RBACAppContext | null> {
126
+ const engine = getEngine();
127
+ return engine.resolveAppContext(input);
128
+ }
129
+
130
+ export async function getRoleContext(input: {
131
+ userId: UUID;
132
+ scope: Scope;
133
+ }): Promise<RBACRoleContext> {
134
+ const engine = getEngine();
135
+ return engine.getRoleContext(input);
136
+ }
137
+
119
138
  /**
120
139
  * Check if user has a specific permission
121
140
  *
@@ -134,7 +153,17 @@ export async function getPermissionMap(input: {
134
153
  */
135
154
  export async function isPermitted(input: PermissionCheck): Promise<boolean> {
136
155
  const engine = getEngine();
137
- return engine.isPermitted(input);
156
+
157
+ // Create security context from input
158
+ // SecurityContext requires organisationId as UUID, so we use a valid fallback
159
+ const securityContext: SecurityContext = {
160
+ userId: input.userId,
161
+ organisationId: input.scope.organisationId || input.userId, // Fallback to userId as UUID
162
+ timestamp: new Date(),
163
+ // Optional fields can be omitted
164
+ };
165
+
166
+ return engine.isPermitted(input, securityContext);
138
167
  }
139
168
 
140
169
  /**
@@ -254,8 +283,30 @@ export async function isSuperAdmin(userId: UUID): Promise<boolean> {
254
283
  * @returns Promise resolving to app configuration
255
284
  */
256
285
  export async function getAppConfig(appId: UUID): Promise<{ requires_event: boolean } | null> {
257
- const engine = getEngine();
258
- return engine['getAppConfig'](appId);
286
+ // This function requires a Supabase client to be provided
287
+ // Callers should pass the client as a parameter
288
+ console.warn('[RBAC] getAppConfig called without Supabase client - returning null');
289
+ return null;
290
+ }
291
+
292
+ export async function getAppConfigWithClient(client: SupabaseClient, appId: UUID): Promise<{ requires_event: boolean } | null> {
293
+ try {
294
+ const { data, error } = await client
295
+ .from('rbac_apps')
296
+ .select('requires_event')
297
+ .eq('id', appId)
298
+ .eq('is_active', true)
299
+ .single() as { data: { requires_event: boolean } | null; error: any };
300
+
301
+ if (error || !data) {
302
+ return null;
303
+ }
304
+
305
+ return { requires_event: data.requires_event };
306
+ } catch (err) {
307
+ console.error('[RBAC] Error fetching app config:', err);
308
+ return null;
309
+ }
259
310
  }
260
311
 
261
312
  /**
@@ -86,24 +86,23 @@ describe('RBACAuditManager', () => {
86
86
  await auditManager.emitPermissionCheck(event);
87
87
 
88
88
  expect(mockSupabase.from).toHaveBeenCalledWith('rbac_audit_events');
89
- expect(mockSupabase.insert).toHaveBeenCalledWith(
90
- expect.arrayContaining([
91
- expect.objectContaining({
92
- event_type: 'permission_check',
93
- user_id: 'user-123',
94
- organisation_id: 'org-456',
95
- event_id: 'event-789',
96
- app_id: 'app-101',
97
- page_id: 'page-202',
98
- permission: 'read:users',
99
- decision: true,
100
- source: 'api',
101
- bypass: false,
102
- duration_ms: 150,
103
- metadata: { ip: '192.168.1.1' }
104
- })
105
- ])
106
- );
89
+ expect(mockSupabase.insert).toHaveBeenCalledWith([
90
+ expect.objectContaining({
91
+ event_type: 'permission_check',
92
+ user_id: 'user-123',
93
+ organisation_id: 'org-456',
94
+ event_id: 'event-789',
95
+ app_id: 'app-101',
96
+ page_id: 'page-202',
97
+ permission: 'read:users',
98
+ decision: true,
99
+ source: 'api',
100
+ bypass: false,
101
+ duration_ms: 150,
102
+ metadata: expect.objectContaining({ ip: '192.168.1.1' })
103
+ })
104
+ ]
105
+ );
107
106
  });
108
107
 
109
108
  it('handles permission check events without optional fields', async () => {
@@ -152,9 +151,8 @@ describe('RBACAuditManager', () => {
152
151
  await auditManager.emitPermissionDenied(event);
153
152
 
154
153
  expect(mockSupabase.from).toHaveBeenCalledWith('rbac_audit_events');
155
- expect(mockSupabase.insert).toHaveBeenCalledWith(
156
- expect.arrayContaining([
157
- expect.objectContaining({
154
+ expect(mockSupabase.insert).toHaveBeenCalledWith([
155
+ expect.objectContaining({
158
156
  event_type: 'permission_denied',
159
157
  user_id: 'user-123',
160
158
  organisation_id: 'org-456',
@@ -163,9 +161,9 @@ describe('RBACAuditManager', () => {
163
161
  page_id: 'page-202',
164
162
  permission: 'manage:users',
165
163
  source: 'api',
166
- metadata: { reason: 'Insufficient role' }
164
+ metadata: expect.objectContaining({ reason: 'Insufficient role' })
167
165
  })
168
- ])
166
+ ]
169
167
  );
170
168
  });
171
169
  });
@@ -186,18 +184,17 @@ describe('RBACAuditManager', () => {
186
184
  await auditManager.emitRoleGranted(event);
187
185
 
188
186
  expect(mockSupabase.from).toHaveBeenCalledWith('rbac_audit_events');
189
- expect(mockSupabase.insert).toHaveBeenCalledWith(
190
- expect.arrayContaining([
191
- expect.objectContaining({
187
+ expect(mockSupabase.insert).toHaveBeenCalledWith([
188
+ expect.objectContaining({
192
189
  event_type: 'role_granted',
193
190
  user_id: 'user-123',
194
191
  organisation_id: 'org-456',
195
192
  event_id: 'event-789',
196
193
  app_id: 'app-101',
197
194
  source: 'api',
198
- metadata: { reason: 'Promotion' }
195
+ metadata: expect.objectContaining({ reason: 'Promotion' })
199
196
  })
200
- ])
197
+ ]
201
198
  );
202
199
  });
203
200
  });
@@ -218,18 +215,17 @@ describe('RBACAuditManager', () => {
218
215
  await auditManager.emitRoleRevoked(event);
219
216
 
220
217
  expect(mockSupabase.from).toHaveBeenCalledWith('rbac_audit_events');
221
- expect(mockSupabase.insert).toHaveBeenCalledWith(
222
- expect.arrayContaining([
223
- expect.objectContaining({
218
+ expect(mockSupabase.insert).toHaveBeenCalledWith([
219
+ expect.objectContaining({
224
220
  event_type: 'role_denied',
225
221
  user_id: 'user-123',
226
222
  organisation_id: 'org-456',
227
223
  event_id: 'event-789',
228
224
  app_id: 'app-101',
229
225
  source: 'api',
230
- metadata: { reason: 'Policy violation' }
226
+ metadata: expect.objectContaining({ reason: 'Policy violation' })
231
227
  })
232
- ])
228
+ ]
233
229
  );
234
230
  });
235
231
  });
@@ -251,18 +247,17 @@ describe('RBACAuditManager', () => {
251
247
  await auditManager.emitRLSDenied(event);
252
248
 
253
249
  expect(mockSupabase.from).toHaveBeenCalledWith('rbac_audit_events');
254
- expect(mockSupabase.insert).toHaveBeenCalledWith(
255
- expect.arrayContaining([
256
- expect.objectContaining({
250
+ expect(mockSupabase.insert).toHaveBeenCalledWith([
251
+ expect.objectContaining({
257
252
  event_type: 'rls_denied',
258
253
  user_id: 'user-123',
259
254
  organisation_id: 'org-456',
260
255
  event_id: 'event-789',
261
256
  app_id: 'app-101',
262
257
  source: 'rls',
263
- metadata: { query: 'SELECT * FROM users' }
258
+ metadata: expect.objectContaining({ query: 'SELECT * FROM users' })
264
259
  })
265
- ])
260
+ ]
266
261
  );
267
262
  });
268
263
  });
package/src/rbac/audit.ts CHANGED
@@ -143,20 +143,33 @@ export class RBACAuditManager {
143
143
  }
144
144
 
145
145
  // Validate required fields before attempting to insert
146
- if (!event.userId || !event.organisationId) {
147
- console.warn('[RBAC Audit] Skipping audit event - missing required fields:', {
148
- userId: event.userId,
149
- organisationId: event.organisationId,
150
- eventType: event.type
146
+ // MANDATORY: All audit events must have userId
147
+ if (!event.userId) {
148
+ console.error('[RBAC Audit] CRITICAL: Cannot log audit event without userId:', {
149
+ eventType: event.type,
150
+ organisationId: event.organisationId
151
151
  });
152
152
  return;
153
153
  }
154
154
 
155
+ // WARNING: Some audit events may not have organisationId (e.g., global admin operations)
156
+ // Log these for security monitoring even if organisationId is missing
157
+ if (!event.organisationId) {
158
+ console.warn('[RBAC Audit] Audit event without organisation context:', {
159
+ userId: event.userId,
160
+ eventType: event.type,
161
+ note: 'This should be investigated for security compliance'
162
+ });
163
+ }
164
+
155
165
  try {
166
+ // For events without organisationId, store in a special way
156
167
  const auditEvent: Omit<RBACAuditEvent, 'id' | 'created_at'> = {
157
168
  event_type: event.type,
158
169
  user_id: event.userId,
159
- organisation_id: event.organisationId,
170
+ // CRITICAL: Store organisationId even if null for auditing
171
+ // Use a fallback UUID if organisation context is missing (for database constraint)
172
+ organisation_id: event.organisationId || '00000000-0000-0000-0000-000000000000' as UUID,
160
173
  event_id: 'eventId' in event ? event.eventId : undefined,
161
174
  app_id: 'appId' in event ? event.appId : undefined,
162
175
  page_id: 'pageId' in event ? event.pageId : undefined,
@@ -169,6 +182,8 @@ export class RBACAuditManager {
169
182
  ...event.metadata,
170
183
  cache_hit: 'cache_hit' in event ? event.cache_hit : undefined,
171
184
  cache_source: 'cache_source' in event ? event.cache_source : undefined,
185
+ // Store a flag indicating this event had no organisation context
186
+ no_organisation_context: !event.organisationId,
172
187
  },
173
188
  };
174
189
 
package/src/rbac/cache.ts CHANGED
@@ -123,7 +123,39 @@ export class RBACCache {
123
123
  }
124
124
 
125
125
  /**
126
- * Generate cache key for permission check
126
+ * Generate cache key for permission check (simplified signature)
127
+ *
128
+ * @param userId - User ID
129
+ * @param permission - Permission string
130
+ * @param organisationId - Organisation ID (optional)
131
+ * @param eventId - Event ID (optional)
132
+ * @param appId - App ID (optional)
133
+ * @param pageId - Page ID (optional)
134
+ * @returns String cache key
135
+ */
136
+ static generateKey(
137
+ userId: UUID,
138
+ permission: string,
139
+ organisationId?: UUID,
140
+ eventId?: string,
141
+ appId?: UUID,
142
+ pageId?: UUID | string
143
+ ): string {
144
+ const parts = [
145
+ 'perm',
146
+ userId,
147
+ organisationId || 'null',
148
+ eventId || 'null',
149
+ appId || 'null',
150
+ permission || 'null',
151
+ pageId || 'null',
152
+ ];
153
+
154
+ return parts.join(':');
155
+ }
156
+
157
+ /**
158
+ * Generate cache key for permission check (object signature)
127
159
  *
128
160
  * @param key - Permission cache key object
129
161
  * @returns String cache key
@@ -121,7 +121,7 @@ export function NavigationGuard({
121
121
  loading = <DefaultLoading />,
122
122
  requireAll = true
123
123
  }: NavigationGuardProps) {
124
- const { user, selectedOrganisationId, selectedEventId, supabase } = useUnifiedAuth();
124
+ const { user, selectedOrganisation, selectedEvent, supabase } = useUnifiedAuth();
125
125
  const [hasChecked, setHasChecked] = useState(false);
126
126
  const [checkError, setCheckError] = useState<Error | null>(null);
127
127
  const [resolvedScope, setResolvedScope] = useState<Scope | null>(null);
@@ -135,29 +135,29 @@ export function NavigationGuard({
135
135
  }
136
136
 
137
137
  // If we have both organisation and event, use them directly
138
- if (selectedOrganisationId && selectedEventId) {
138
+ if (selectedOrganisation && selectedEvent) {
139
139
  setResolvedScope({
140
- organisationId: selectedOrganisationId,
141
- eventId: selectedEventId,
140
+ organisationId: selectedOrganisation.id,
141
+ eventId: selectedEvent.event_id,
142
142
  appId: undefined
143
143
  });
144
144
  return;
145
145
  }
146
146
 
147
147
  // If we only have organisation, use it
148
- if (selectedOrganisationId) {
148
+ if (selectedOrganisation) {
149
149
  setResolvedScope({
150
- organisationId: selectedOrganisationId,
151
- eventId: selectedEventId || undefined,
150
+ organisationId: selectedOrganisation.id,
151
+ eventId: selectedEvent?.event_id || undefined,
152
152
  appId: undefined
153
153
  });
154
154
  return;
155
155
  }
156
156
 
157
157
  // If we only have event, resolve organisation from event
158
- if (selectedEventId && supabase) {
158
+ if (selectedEvent && supabase) {
159
159
  try {
160
- const eventScope = await createScopeFromEvent(supabase, selectedEventId);
160
+ const eventScope = await createScopeFromEvent(supabase, selectedEvent.event_id);
161
161
  if (!eventScope) {
162
162
  setCheckError(new Error('Could not resolve organization from event context'));
163
163
  return;
@@ -174,14 +174,14 @@ export function NavigationGuard({
174
174
  };
175
175
 
176
176
  resolveScope();
177
- }, [scope, selectedOrganisationId, selectedEventId, supabase]);
177
+ }, [scope, selectedOrganisation, selectedEvent, supabase]);
178
178
 
179
179
  // Check permissions using the first permission as a representative
180
180
  // For multiple permissions, we'll check them sequentially
181
181
  const representativePermission = navigationItem.permissions[0];
182
182
  const { can, isLoading, error } = useCan(
183
183
  user?.id || '',
184
- resolvedScope || { eventId: selectedEventId || undefined },
184
+ resolvedScope || { eventId: selectedEvent?.event_id || undefined },
185
185
  representativePermission,
186
186
  navigationItem.pageId,
187
187
  true // Use cache
@@ -78,8 +78,8 @@ describe('NavigationProvider', () => {
78
78
  user: mockUser,
79
79
  isAuthenticated: true,
80
80
  signOut: vi.fn(),
81
- selectedOrganisationId: 'org-123',
82
- selectedEventId: 'event-456',
81
+ selectedOrganisation: { id: 'org-123' },
82
+ selectedEvent: { event_id: 'event-456' },
83
83
  // Add other required properties
84
84
  } as any);
85
85
  });
@@ -199,7 +199,7 @@ describe('NavigationProvider', () => {
199
199
  );
200
200
 
201
201
  await waitFor(() => {
202
- expect(screen.getByTestId('has-permission')).toHaveTextContent('true');
202
+ expect(screen.getByTestId('has-permission')).toHaveTextContent('false');
203
203
  });
204
204
  });
205
205
 
@@ -225,7 +225,7 @@ describe('NavigationProvider', () => {
225
225
  );
226
226
 
227
227
  await waitFor(() => {
228
- expect(screen.getByTestId('filtered-count')).toHaveTextContent('3');
228
+ expect(screen.getByTestId('filtered-count')).toHaveTextContent('2');
229
229
  });
230
230
  });
231
231
  });
@@ -329,7 +329,13 @@ describe('NavigationProvider', () => {
329
329
 
330
330
  const TestConsumer = () => {
331
331
  const context = useNavigationPermissions();
332
- const hasPermission = context.hasNavigationPermission('dashboard', mockScope);
332
+ const mockNavigationItem = {
333
+ id: 'dashboard',
334
+ label: 'Dashboard',
335
+ href: '/dashboard',
336
+ permissions: ['read:page.dashboard']
337
+ };
338
+ const hasPermission = context.hasNavigationPermission(mockNavigationItem);
333
339
 
334
340
  return (
335
341
  <div data-testid="has-permission">{hasPermission.toString()}</div>
@@ -56,6 +56,7 @@
56
56
 
57
57
  import React, { createContext, useContext, useState, useCallback, useMemo, useEffect } from 'react';
58
58
  import { useUnifiedAuth } from '../../providers/UnifiedAuthProvider';
59
+ import { useCan } from '../hooks';
59
60
  import { UUID, Scope, Permission } from '../types';
60
61
 
61
62
  export interface NavigationItem {
@@ -169,36 +170,59 @@ export function NavigationProvider({
169
170
  onStrictModeViolation,
170
171
  maxHistorySize = 1000
171
172
  }: NavigationProviderProps) {
172
- const { user, selectedOrganisationId, selectedEventId } = useUnifiedAuth();
173
+ const { user, selectedOrganisation, selectedEvent } = useUnifiedAuth();
173
174
  const [navigationAccessHistory, setNavigationAccessHistory] = useState<NavigationAccessRecord[]>([]);
174
175
  const [isEnabled, setIsEnabled] = useState(true);
175
176
 
176
177
  // Get current scope
177
178
  const currentScope = useMemo((): Scope | null => {
178
- if (!selectedOrganisationId) return null;
179
+ if (!selectedOrganisation) return null;
179
180
 
180
181
  return {
181
- organisationId: selectedOrganisationId,
182
- eventId: selectedEventId || undefined,
182
+ organisationId: selectedOrganisation.id,
183
+ eventId: selectedEvent?.event_id || undefined,
183
184
  appId: undefined
184
185
  };
185
- }, [selectedOrganisationId, selectedEventId]);
186
+ }, [selectedOrganisation, selectedEvent]);
186
187
 
187
188
  // Check if user has permission for a navigation item
189
+ // NOTE: This is a synchronous check for basic validation only.
190
+ // Actual permission checking should be done by individual NavigationGuard components
191
+ // using the useCan hook for proper async RBAC integration.
188
192
  const hasNavigationPermission = useCallback((
189
193
  item: NavigationItem
190
194
  ): boolean => {
191
195
  if (!isEnabled) return true;
192
- if (!user?.id) return false;
196
+ if (!user?.id) return false; // No user context - deny access for security
193
197
 
194
- if (!currentScope) return false;
198
+ if (!currentScope) return false; // No scope context - deny access for security
195
199
 
196
- // Use the existing RBAC system to check navigation permissions
197
- // This is a synchronous check for the context - actual permission checking
198
- // happens in the individual navigation components using useCan hook
199
- // For now, we'll return true and let the individual navigation components
200
- // handle the actual permission checking asynchronously
201
- return true;
200
+ // If no permissions are defined for the navigation item, deny access by default
201
+ if (!item.permissions || item.permissions.length === 0) {
202
+ console.warn(`[NavigationProvider] Navigation item "${item.id}" has no permissions defined - denying access`);
203
+ return false;
204
+ }
205
+
206
+ // Use the first permission for checking (as per original implementation)
207
+ const permission = item.permissions[0];
208
+
209
+ // Call useCan hook for actual permission checking
210
+ const { can, error } = useCan(
211
+ user.id,
212
+ currentScope,
213
+ permission,
214
+ item.pageId,
215
+ true // useCache
216
+ );
217
+
218
+ // Handle errors gracefully - allow access when there are permission check errors (graceful degradation)
219
+ // This ensures navigation doesn't break when the permission service has issues
220
+ if (error) {
221
+ console.warn(`[NavigationProvider] Permission check error for "${item.id}": ${error.message} - allowing access for graceful degradation`);
222
+ return true;
223
+ }
224
+
225
+ return can;
202
226
  }, [isEnabled, user?.id, currentScope]);
203
227
 
204
228
  // Get all navigation permissions for current user