@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
@@ -1,688 +0,0 @@
1
- /**
2
- * @file RBACProvider Integration Tests
3
- * @package @jmruthers/pace-core
4
- * @module Providers/RBACProvider
5
- * @since 1.0.0
6
- *
7
- * Integration tests for RBACProvider covering real provider behavior,
8
- * organisation context, permission refresh, and event access loading.
9
- * These tests DO NOT mock useRBAC but test the actual provider implementation.
10
- */
11
-
12
- import { render, screen, waitFor, act, renderHook } from '@testing-library/react';
13
- import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
14
- import { RBACProvider, useRBAC } from '../RBACProvider';
15
- import type { User, Session } from '@supabase/supabase-js';
16
- import { AccessLevel } from '../../../types/unified';
17
-
18
- import { createMockSupabaseClient } from '../../../__tests__/helpers/supabaseMock';
19
-
20
- describe('[integration] RBACProvider', () => {
21
- let mockSupabase: ReturnType<typeof createMockSupabaseClient>;
22
- let mockUser: User;
23
- let mockSession: Session;
24
-
25
- beforeEach(() => {
26
- vi.clearAllMocks();
27
-
28
- mockSupabase = createMockSupabaseClient({
29
- data: [{ id: 'app-123' }],
30
- error: null
31
- });
32
-
33
- mockUser = {
34
- id: 'user-123',
35
- email: 'test@example.com',
36
- user_metadata: { globalRole: null },
37
- app_metadata: {},
38
- aud: 'authenticated',
39
- created_at: '2024-01-01T00:00:00Z',
40
- } as User;
41
-
42
- mockSession = {
43
- access_token: 'token-123',
44
- refresh_token: 'refresh-123',
45
- expires_at: 1234567890,
46
- expires_in: 3600,
47
- token_type: 'bearer',
48
- user: mockUser,
49
- } as Session;
50
-
51
- // Mock RPC calls
52
- mockSupabase.rpc.mockImplementation((functionName) => {
53
- if (functionName === 'get_app_config') {
54
- return Promise.resolve({
55
- data: [{ requires_event: true }],
56
- error: null
57
- });
58
- }
59
- if (functionName === 'rbac_permissions_get') {
60
- return Promise.resolve({
61
- data: [
62
- { permission_type: 'event_app_access', role_name: 'planner' },
63
- { permission_type: 'organisation_access', role_name: 'org_member' }
64
- ],
65
- error: null
66
- });
67
- }
68
- return Promise.resolve({ data: null, error: null });
69
- });
70
- });
71
-
72
- afterEach(() => {
73
- vi.clearAllMocks();
74
- localStorage.clear();
75
- });
76
-
77
- // Test component that uses RBAC
78
- const TestComponent = () => {
79
- const {
80
- permissions,
81
- roles,
82
- accessLevel,
83
- rbacLoading,
84
- rbacError,
85
- selectedEventId,
86
- selectedOrganisationId,
87
- hasPermission,
88
- hasAnyPermission,
89
- hasAllPermissions,
90
- hasRole,
91
- hasAccessLevel,
92
- canAccess,
93
- } = useRBAC();
94
-
95
- return (
96
- <div data-testid="rbac-context">
97
- <div data-testid="permissions">{JSON.stringify(permissions)}</div>
98
- <div data-testid="roles">{roles.join(', ')}</div>
99
- <div data-testid="access-level">{accessLevel}</div>
100
- <div data-testid="loading">{rbacLoading ? 'Loading' : 'Not Loading'}</div>
101
- <div data-testid="error">{rbacError?.message || 'No error'}</div>
102
- <div data-testid="selected-event">{selectedEventId || 'None'}</div>
103
- <div data-testid="selected-org">{selectedOrganisationId || 'None'}</div>
104
- <div data-testid="has-read-users">{hasPermission('read:users') ? 'Yes' : 'No'}</div>
105
- <div data-testid="has-any-permission">{hasAnyPermission(['read:users', 'create:users']) ? 'Yes' : 'No'}</div>
106
- <div data-testid="has-all-permissions">{hasAllPermissions(['read:users']) ? 'Yes' : 'No'}</div>
107
- <div data-testid="has-role-planner">{hasRole('planner') ? 'Yes' : 'No'}</div>
108
- <div data-testid="has-access-level">{hasAccessLevel(AccessLevel.PLANNER) ? 'Yes' : 'No'}</div>
109
- <div data-testid="can-access-users-read">{canAccess('users', 'read') ? 'Yes' : 'No'}</div>
110
- </div>
111
- );
112
- };
113
-
114
- describe('Organisation Context Integration', () => {
115
- it('initializes with no organisation context', () => {
116
- render(
117
- <RBACProvider
118
- supabaseClient={mockSupabase as any}
119
- user={null}
120
- session={null}
121
- appName="test-app"
122
- >
123
- <TestComponent />
124
- </RBACProvider>
125
- );
126
-
127
- expect(screen.getByTestId('selected-org')).toHaveTextContent('None');
128
- });
129
-
130
- it('requires organisation context when specified', () => {
131
- render(
132
- <RBACProvider
133
- supabaseClient={mockSupabase as any}
134
- user={null}
135
- session={null}
136
- appName="test-app"
137
- requireOrganisationContext={true}
138
- >
139
- <TestComponent />
140
- </RBACProvider>
141
- );
142
-
143
- expect(screen.getByTestId('selected-org')).toHaveTextContent('None');
144
- });
145
-
146
- it('handles organisation context availability', async () => {
147
- const { result } = renderHook(() => useRBAC(), {
148
- wrapper: ({ children }) => (
149
- <RBACProvider
150
- supabaseClient={mockSupabase as any}
151
- user={mockUser}
152
- session={mockSession}
153
- appName="test-app"
154
- >
155
- {children}
156
- </RBACProvider>
157
- ),
158
- });
159
-
160
- await waitFor(() => {
161
- expect(result.current.requireOrganisationContext).toBeDefined();
162
- });
163
- });
164
- });
165
-
166
- describe('Permission Refresh Logic', () => {
167
- it('loads app configuration on mount', async () => {
168
- render(
169
- <RBACProvider
170
- supabaseClient={mockSupabase as any}
171
- user={mockUser}
172
- session={mockSession}
173
- appName="test-app"
174
- >
175
- <TestComponent />
176
- </RBACProvider>
177
- );
178
-
179
- await waitFor(() => {
180
- expect(mockSupabase.rpc).toHaveBeenCalledWith('get_app_config', expect.any(Object));
181
- });
182
- });
183
-
184
- it('refreshes permissions when event changes', async () => {
185
- const { rerender } = render(
186
- <RBACProvider
187
- supabaseClient={mockSupabase as any}
188
- user={mockUser}
189
- session={mockSession}
190
- appName="test-app"
191
- >
192
- <TestComponent />
193
- </RBACProvider>
194
- );
195
-
196
- await waitFor(() => {
197
- expect(mockSupabase.rpc).toHaveBeenCalled();
198
- });
199
-
200
- // Rerender with different event (simulated by changing a prop)
201
- rerender(
202
- <RBACProvider
203
- supabaseClient={mockSupabase as any}
204
- user={mockUser}
205
- session={mockSession}
206
- appName="test-app-2"
207
- >
208
- <TestComponent />
209
- </RBACProvider>
210
- );
211
-
212
- await waitFor(() => {
213
- expect(mockSupabase.rpc).toHaveBeenCalledTimes(2);
214
- });
215
- });
216
-
217
- it('handles permission refresh errors gracefully', async () => {
218
- // Setup an error scenario - app config fails but provider continues
219
- const { result } = renderHook(() => useRBAC(), {
220
- wrapper: ({ children }) => (
221
- <RBACProvider
222
- supabaseClient={mockSupabase as any}
223
- user={mockUser}
224
- session={mockSession}
225
- appName="test-app"
226
- >
227
- {children}
228
- </RBACProvider>
229
- ),
230
- });
231
-
232
- // Provider should handle errors gracefully
233
- await waitFor(() => {
234
- expect(result.current.rbacError).toBeDefined();
235
- }, { timeout: 3000 });
236
- });
237
-
238
- it('clears permissions when app requires events but no event is selected', async () => {
239
- render(
240
- <RBACProvider
241
- supabaseClient={mockSupabase as any}
242
- user={mockUser}
243
- session={mockSession}
244
- appName="test-app"
245
- >
246
- <TestComponent />
247
- </RBACProvider>
248
- );
249
-
250
- await waitFor(() => {
251
- expect(screen.getByTestId('selected-event')).toHaveTextContent('None');
252
- });
253
- });
254
- });
255
-
256
- describe('Event Access Loading', () => {
257
- it('loads user event access on mount', async () => {
258
- mockSupabase.from().select.mockResolvedValue({
259
- data: [
260
- {
261
- event_id: 'event-123',
262
- role: 'planner',
263
- granted_at: '2024-01-01T00:00:00Z'
264
- }
265
- ],
266
- error: null
267
- });
268
-
269
- render(
270
- <RBACProvider
271
- supabaseClient={mockSupabase as any}
272
- user={mockUser}
273
- session={mockSession}
274
- appName="test-app"
275
- >
276
- <TestComponent />
277
- </RBACProvider>
278
- );
279
-
280
- await waitFor(() => {
281
- expect(mockSupabase.from).toHaveBeenCalled();
282
- });
283
- });
284
-
285
- it('handles event access loading errors', async () => {
286
- mockSupabase.from().select.mockResolvedValue({
287
- data: null,
288
- error: new Error('Failed to load event access')
289
- });
290
-
291
- render(
292
- <RBACProvider
293
- supabaseClient={mockSupabase as any}
294
- user={mockUser}
295
- session={mockSession}
296
- appName="test-app"
297
- >
298
- <TestComponent />
299
- </RBACProvider>
300
- );
301
-
302
- await waitFor(() => {
303
- expect(mockSupabase.from).toHaveBeenCalled();
304
- });
305
- });
306
-
307
- it('loads event access when user and session are available', async () => {
308
- mockSupabase.from().select.mockResolvedValue({
309
- data: [],
310
- error: null
311
- });
312
-
313
- const { result } = renderHook(() => useRBAC(), {
314
- wrapper: ({ children }) => (
315
- <RBACProvider
316
- supabaseClient={mockSupabase as any}
317
- user={mockUser}
318
- session={mockSession}
319
- appName="test-app"
320
- >
321
- {children}
322
- </RBACProvider>
323
- ),
324
- });
325
-
326
- await waitFor(() => {
327
- expect(result.current.userEventAccess).toBeDefined();
328
- });
329
- });
330
-
331
- it('clears event access when user signs out', async () => {
332
- const { rerender } = render(
333
- <RBACProvider
334
- supabaseClient={mockSupabase as any}
335
- user={mockUser}
336
- session={mockSession}
337
- appName="test-app"
338
- >
339
- <TestComponent />
340
- </RBACProvider>
341
- );
342
-
343
- await waitFor(() => {
344
- expect(mockSupabase.from).toHaveBeenCalled();
345
- });
346
-
347
- // Simulate sign out
348
- rerender(
349
- <RBACProvider
350
- supabaseClient={mockSupabase as any}
351
- user={null}
352
- session={null}
353
- appName="test-app"
354
- >
355
- <TestComponent />
356
- </RBACProvider>
357
- );
358
-
359
- await waitFor(() => {
360
- expect(screen.getByTestId('selected-event')).toHaveTextContent('None');
361
- });
362
- });
363
- });
364
-
365
- describe('Permission Validation Methods', () => {
366
- it('hasPermission returns correct boolean based on permissions', async () => {
367
- const { result } = renderHook(() => useRBAC(), {
368
- wrapper: ({ children }) => (
369
- <RBACProvider
370
- supabaseClient={mockSupabase as any}
371
- user={mockUser}
372
- session={mockSession}
373
- appName="test-app"
374
- >
375
- {children}
376
- </RBACProvider>
377
- ),
378
- });
379
-
380
- await waitFor(() => {
381
- expect(result.current.rbacLoading).toBe(false);
382
- });
383
-
384
- // Verify permission checking works (may be empty initially)
385
- expect(typeof result.current.hasPermission('read:users')).toBe('boolean');
386
- expect(typeof result.current.hasPermission('delete:users')).toBe('boolean');
387
- });
388
-
389
- it('hasAnyPermission checks if user has any of the specified permissions', async () => {
390
- const { result } = renderHook(() => useRBAC(), {
391
- wrapper: ({ children }) => (
392
- <RBACProvider
393
- supabaseClient={mockSupabase as any}
394
- user={mockUser}
395
- session={mockSession}
396
- appName="test-app"
397
- >
398
- {children}
399
- </RBACProvider>
400
- ),
401
- });
402
-
403
- await waitFor(() => {
404
- expect(result.current.rbacLoading).toBe(false);
405
- });
406
-
407
- // Test method exists and returns boolean
408
- expect(typeof result.current.hasAnyPermission(['read:users', 'write:users'])).toBe('boolean');
409
- expect(typeof result.current.hasAnyPermission(['delete:users', 'update:users'])).toBe('boolean');
410
- });
411
-
412
- it('hasAllPermissions checks if user has all specified permissions', async () => {
413
- const { result } = renderHook(() => useRBAC(), {
414
- wrapper: ({ children }) => (
415
- <RBACProvider
416
- supabaseClient={mockSupabase as any}
417
- user={mockUser}
418
- session={mockSession}
419
- appName="test-app"
420
- >
421
- {children}
422
- </RBACProvider>
423
- ),
424
- });
425
-
426
- await waitFor(() => {
427
- expect(result.current.rbacLoading).toBe(false);
428
- });
429
-
430
- // Test method exists and returns boolean
431
- expect(typeof result.current.hasAllPermissions(['read:users', 'create:users'])).toBe('boolean');
432
- expect(typeof result.current.hasAllPermissions(['read:users', 'delete:users'])).toBe('boolean');
433
- });
434
-
435
- it('canAccess checks resource:action permissions', async () => {
436
- const { result } = renderHook(() => useRBAC(), {
437
- wrapper: ({ children }) => (
438
- <RBACProvider
439
- supabaseClient={mockSupabase as any}
440
- user={mockUser}
441
- session={mockSession}
442
- appName="test-app"
443
- >
444
- {children}
445
- </RBACProvider>
446
- ),
447
- });
448
-
449
- await waitFor(() => {
450
- expect(result.current.rbacLoading).toBe(false);
451
- });
452
-
453
- // Test method exists and returns boolean
454
- expect(typeof result.current.canAccess('users', 'read')).toBe('boolean');
455
- expect(typeof result.current.canAccess('users', 'delete')).toBe('boolean');
456
- });
457
- });
458
-
459
- describe('Super Admin Handling', () => {
460
- it('grants all permissions to super admin from user metadata', async () => {
461
- const superAdminUser = {
462
- ...mockUser,
463
- user_metadata: { globalRole: 'super_admin' }
464
- };
465
-
466
- const { result } = renderHook(() => useRBAC(), {
467
- wrapper: ({ children }) => (
468
- <RBACProvider
469
- supabaseClient={mockSupabase as any}
470
- user={superAdminUser}
471
- session={mockSession}
472
- appName="test-app"
473
- >
474
- {children}
475
- </RBACProvider>
476
- ),
477
- });
478
-
479
- await waitFor(() => {
480
- expect(result.current.rbacLoading).toBe(false);
481
- });
482
-
483
- // Super admin should be detected
484
- expect(result.current.hasRole('super_admin')).toBe(true);
485
- // Should have admin permissions (not necessarily SUPER level)
486
- expect(result.current.hasPermission('admin:create')).toBe(true);
487
- expect(result.current.hasPermission('admin:read')).toBe(true);
488
- expect(result.current.hasPermission('admin:update')).toBe(true);
489
- expect(result.current.hasPermission('admin:delete')).toBe(true);
490
- // Access level may be ADMIN, not SUPER
491
- expect([AccessLevel.ADMIN, AccessLevel.SUPER]).toContain(result.current.accessLevel);
492
- });
493
-
494
- it('checks super admin status from database', async () => {
495
- const { result } = renderHook(() => useRBAC(), {
496
- wrapper: ({ children }) => (
497
- <RBACProvider
498
- supabaseClient={mockSupabase as any}
499
- user={mockUser}
500
- session={mockSession}
501
- appName="test-app"
502
- >
503
- {children}
504
- </RBACProvider>
505
- ),
506
- });
507
-
508
- // Provider should check for super admin status on init
509
- await waitFor(() => {
510
- expect(result.current.rbacLoading).toBe(false);
511
- });
512
-
513
- // Should have attempted to check global roles
514
- expect(mockSupabase.from).toHaveBeenCalled();
515
- });
516
- });
517
-
518
- describe('State Persistence', () => {
519
- it('persists selected event to localStorage', async () => {
520
- // Simulate localStorage persistence
521
- const setItemSpy = vi.spyOn(Storage.prototype, 'setItem');
522
-
523
- const { result } = renderHook(() => useRBAC(), {
524
- wrapper: ({ children }) => (
525
- <RBACProvider
526
- supabaseClient={mockSupabase as any}
527
- user={mockUser}
528
- session={mockSession}
529
- appName="test-app"
530
- persistState={true}
531
- >
532
- {children}
533
- </RBACProvider>
534
- ),
535
- });
536
-
537
- // Provider initializes with persistence enabled
538
- await waitFor(() => {
539
- expect(result.current.rbacLoading).toBe(false);
540
- });
541
-
542
- // localStorage may not be called until state changes
543
- // This test validates persistence is configured
544
- expect(typeof result.current.setSelectedEventId).toBe('function');
545
- });
546
-
547
- it('does not persist when persistState is false', async () => {
548
- const setItemSpy = vi.spyOn(Storage.prototype, 'setItem');
549
-
550
- render(
551
- <RBACProvider
552
- supabaseClient={mockSupabase as any}
553
- user={mockUser}
554
- session={mockSession}
555
- appName="test-app"
556
- persistState={false}
557
- >
558
- <TestComponent />
559
- </RBACProvider>
560
- );
561
-
562
- // Should not call setItem excessively
563
- await waitFor(() => {
564
- expect(setItemSpy).not.toHaveBeenCalledWith(
565
- 'pace-core-selected-event',
566
- expect.any(String)
567
- );
568
- });
569
- });
570
- });
571
-
572
- describe('Error Recovery', () => {
573
- it('recovers from app config load error', async () => {
574
- mockSupabase.rpc.mockImplementation((functionName) => {
575
- if (functionName === 'get_app_config') {
576
- return Promise.resolve({
577
- data: null,
578
- error: new Error('Failed to load config')
579
- });
580
- }
581
- return Promise.resolve({ data: null, error: null });
582
- });
583
-
584
- const { result } = renderHook(() => useRBAC(), {
585
- wrapper: ({ children }) => (
586
- <RBACProvider
587
- supabaseClient={mockSupabase as any}
588
- user={mockUser}
589
- session={mockSession}
590
- appName="test-app"
591
- >
592
- {children}
593
- </RBACProvider>
594
- ),
595
- });
596
-
597
- await waitFor(() => {
598
- expect(result.current.rbacError).toBeDefined();
599
- });
600
- });
601
-
602
- it('allows permission refresh after error', async () => {
603
- // First call fails
604
- mockSupabase.rpc.mockRejectedValueOnce(new Error('Network error'));
605
-
606
- const { result } = renderHook(() => useRBAC(), {
607
- wrapper: ({ children }) => (
608
- <RBACProvider
609
- supabaseClient={mockSupabase as any}
610
- user={mockUser}
611
- session={mockSession}
612
- appName="test-app"
613
- >
614
- {children}
615
- </RBACProvider>
616
- ),
617
- });
618
-
619
- await waitFor(() => {
620
- expect(result.current.rbacError).toBeDefined();
621
- });
622
-
623
- // Retry succeeds
624
- mockSupabase.rpc.mockResolvedValueOnce({
625
- data: [{ permission_type: 'read:users', role_name: 'planner' }],
626
- error: null
627
- });
628
-
629
- await act(async () => {
630
- await result.current.refreshPermissions('event-123');
631
- });
632
-
633
- await waitFor(() => {
634
- expect(result.current.rbacError).toBeNull();
635
- });
636
- });
637
- });
638
-
639
- describe('Loading States', () => {
640
- it('shows loading state during initial permission fetch', async () => {
641
- // Set up a delayed response
642
- mockSupabase.rpc.mockImplementation(() =>
643
- new Promise((resolve) => setTimeout(() => resolve({ data: [], error: null }), 100))
644
- );
645
-
646
- const { result } = renderHook(() => useRBAC(), {
647
- wrapper: ({ children }) => (
648
- <RBACProvider
649
- supabaseClient={mockSupabase as any}
650
- user={mockUser}
651
- session={mockSession}
652
- appName="test-app"
653
- >
654
- {children}
655
- </RBACProvider>
656
- ),
657
- });
658
-
659
- // Loading state should be active initially or briefly
660
- expect(result.current.rbacLoading || !result.current.rbacLoading).toBeDefined();
661
-
662
- // Eventually should complete
663
- await waitFor(() => {
664
- expect(result.current.rbacLoading).toBe(false);
665
- }, { timeout: 200 });
666
- });
667
-
668
- it('updates loading state when permissions are fetched', async () => {
669
- const { result } = renderHook(() => useRBAC(), {
670
- wrapper: ({ children }) => (
671
- <RBACProvider
672
- supabaseClient={mockSupabase as any}
673
- user={mockUser}
674
- session={mockSession}
675
- appName="test-app"
676
- >
677
- {children}
678
- </RBACProvider>
679
- ),
680
- });
681
-
682
- await waitFor(() => {
683
- expect(result.current.rbacLoading).toBe(false);
684
- });
685
- });
686
- });
687
- });
688
-