@jmruthers/pace-core 0.6.5 → 0.6.7

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 (473) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +5 -403
  3. package/audit-tool/00-dependencies.cjs +394 -0
  4. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  5. package/audit-tool/audits/02-project-structure.cjs +255 -0
  6. package/audit-tool/audits/03-architecture.cjs +196 -0
  7. package/audit-tool/audits/04-code-quality.cjs +149 -0
  8. package/audit-tool/audits/05-styling.cjs +224 -0
  9. package/audit-tool/audits/06-security-rbac.cjs +544 -0
  10. package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
  11. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  12. package/audit-tool/audits/09-operations.cjs +208 -0
  13. package/audit-tool/index.cjs +291 -0
  14. package/audit-tool/utils/code-utils.cjs +218 -0
  15. package/audit-tool/utils/file-utils.cjs +230 -0
  16. package/audit-tool/utils/report-utils.cjs +241 -0
  17. package/core-usage-manifest.json +93 -0
  18. package/cursor-rules/00-standards-overview.mdc +156 -0
  19. package/cursor-rules/01-pace-core-compliance.mdc +586 -0
  20. package/cursor-rules/02-project-structure.mdc +42 -4
  21. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
  22. package/cursor-rules/04-code-quality.mdc +419 -0
  23. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
  24. package/cursor-rules/06-security-rbac.mdc +518 -0
  25. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  26. package/cursor-rules/08-testing-documentation.mdc +324 -0
  27. package/cursor-rules/09-operations.mdc +365 -0
  28. package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
  29. package/dist/DataTable-7PMH7XN7.js +15 -0
  30. package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  31. package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
  32. package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
  33. package/dist/api-Y4MQWOFW.js +4 -0
  34. package/dist/audit-MYQXYZFU.js +3 -0
  35. package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
  36. package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
  37. package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
  38. package/dist/chunk-6F3IILHI.js +62 -0
  39. package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
  40. package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
  41. package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
  42. package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
  43. package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
  44. package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
  45. package/dist/chunk-C7NSAPTL.js +1 -0
  46. package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
  47. package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
  48. package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
  49. package/dist/chunk-GHYHJTYV.js +994 -0
  50. package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
  51. package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
  52. package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
  53. package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
  54. package/dist/chunk-MBADTM7L.js +64 -0
  55. package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
  56. package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
  57. package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
  58. package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
  59. package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
  60. package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
  61. package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
  62. package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
  63. package/dist/components.d.ts +7 -5
  64. package/dist/components.js +46 -257
  65. package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
  66. package/dist/eslint-rules/index.cjs +35 -0
  67. package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
  68. package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
  69. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  70. package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
  71. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
  72. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  73. package/dist/eslint-rules/utils/helpers.cjs +42 -0
  74. package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
  75. package/dist/hooks.d.ts +6 -6
  76. package/dist/hooks.js +62 -172
  77. package/dist/icons/index.d.ts +1 -0
  78. package/dist/icons/index.js +1 -0
  79. package/dist/index.d.ts +12 -11
  80. package/dist/index.js +67 -660
  81. package/dist/providers.d.ts +2 -2
  82. package/dist/providers.js +8 -35
  83. package/dist/rbac/eslint-rules.d.ts +46 -44
  84. package/dist/rbac/eslint-rules.js +7 -4
  85. package/dist/rbac/index.d.ts +109 -586
  86. package/dist/rbac/index.js +14 -207
  87. package/dist/styles/index.js +2 -12
  88. package/dist/theming/runtime.d.ts +14 -1
  89. package/dist/theming/runtime.js +3 -19
  90. package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
  91. package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
  92. package/dist/types-t9H8qKRw.d.ts +55 -0
  93. package/dist/types.d.ts +1 -1
  94. package/dist/types.js +7 -94
  95. package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
  96. package/dist/utils.d.ts +24 -117
  97. package/dist/utils.js +54 -392
  98. package/docs/README.md +17 -7
  99. package/docs/api/README.md +4 -402
  100. package/docs/api/modules.md +301 -871
  101. package/docs/api-reference/components.md +21 -21
  102. package/docs/api-reference/deprecated.md +31 -6
  103. package/docs/api-reference/hooks.md +80 -80
  104. package/docs/api-reference/rpc-functions.md +78 -3
  105. package/docs/api-reference/types.md +1 -1
  106. package/docs/api-reference/utilities.md +1 -1
  107. package/docs/architecture/README.md +1 -1
  108. package/docs/core-concepts/events.md +3 -3
  109. package/docs/core-concepts/organisations.md +6 -6
  110. package/docs/core-concepts/permissions.md +6 -6
  111. package/docs/documentation-index.md +12 -18
  112. package/docs/getting-started/cursor-rules.md +3 -23
  113. package/docs/getting-started/dependencies.md +650 -0
  114. package/docs/getting-started/documentation-index.md +1 -1
  115. package/docs/getting-started/examples/README.md +4 -4
  116. package/docs/getting-started/examples/full-featured-app.md +1 -1
  117. package/docs/getting-started/faq.md +2 -2
  118. package/docs/getting-started/installation-guide.md +20 -7
  119. package/docs/getting-started/quick-reference.md +4 -4
  120. package/docs/getting-started/quick-start.md +23 -12
  121. package/docs/implementation-guides/authentication.md +15 -15
  122. package/docs/implementation-guides/component-styling.md +1 -1
  123. package/docs/implementation-guides/data-tables.md +126 -33
  124. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  125. package/docs/implementation-guides/dynamic-colors.md +3 -3
  126. package/docs/implementation-guides/file-upload-storage.md +2 -2
  127. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  128. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  129. package/docs/implementation-guides/large-datasets.md +3 -2
  130. package/docs/implementation-guides/organisation-security.md +2 -2
  131. package/docs/implementation-guides/performance.md +2 -2
  132. package/docs/implementation-guides/permission-enforcement.md +5 -1
  133. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  134. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  135. package/docs/rbac/MIGRATION_GUIDE.md +819 -0
  136. package/docs/rbac/RBAC_CONTRACT.md +724 -0
  137. package/docs/rbac/README.md +17 -8
  138. package/docs/rbac/advanced-patterns.md +6 -6
  139. package/docs/rbac/api-reference.md +20 -20
  140. package/docs/rbac/edge-functions-guide.md +376 -0
  141. package/docs/rbac/event-based-apps.md +3 -3
  142. package/docs/rbac/examples.md +41 -41
  143. package/docs/rbac/getting-started.md +37 -37
  144. package/docs/rbac/performance.md +1 -1
  145. package/docs/rbac/quick-start.md +52 -52
  146. package/docs/rbac/secure-client-protection.md +1 -35
  147. package/docs/rbac/troubleshooting.md +1 -1
  148. package/docs/security/README.md +5 -5
  149. package/docs/standards/0-standards-overview.md +220 -0
  150. package/docs/standards/1-pace-core-compliance-standards.md +986 -0
  151. package/docs/standards/2-project-structure-standards.md +949 -0
  152. package/docs/standards/3-architecture-standards.md +606 -0
  153. package/docs/standards/4-code-quality-standards.md +728 -0
  154. package/docs/standards/5-styling-standards.md +348 -0
  155. package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
  156. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  157. package/docs/standards/8-testing-documentation-standards.md +401 -0
  158. package/docs/standards/9-operations-standards.md +1102 -0
  159. package/docs/standards/README.md +185 -57
  160. package/docs/troubleshooting/README.md +4 -4
  161. package/docs/troubleshooting/common-issues.md +2 -2
  162. package/docs/troubleshooting/debugging.md +9 -9
  163. package/docs/troubleshooting/migration.md +4 -4
  164. package/docs/troubleshooting/organisation-context-setup.md +42 -19
  165. package/eslint-config-pace-core.cjs +33 -6
  166. package/package.json +35 -23
  167. package/scripts/install-cursor-rules.cjs +25 -6
  168. package/scripts/install-eslint-config.cjs +284 -0
  169. package/src/__tests__/fixtures/supabase.ts +1 -1
  170. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
  171. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
  172. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
  173. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  174. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
  175. package/src/__tests__/helpers/component-test-utils.tsx +1 -1
  176. package/src/__tests__/helpers/supabaseMock.ts +2 -2
  177. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  178. package/src/__tests__/public-recipe-view.test.ts +38 -9
  179. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  180. package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
  181. package/src/__tests__/templates/component.test.template.tsx +18 -15
  182. package/src/components/Button/Button.tsx +5 -1
  183. package/src/components/Calendar/Calendar.tsx +201 -47
  184. package/src/components/ContextSelector/ContextSelector.tsx +106 -119
  185. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  186. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
  187. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
  188. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  189. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  190. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  191. package/src/components/DataTable/components/DataTableCore.tsx +186 -13
  192. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  193. package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
  194. package/src/components/DataTable/components/EditFields.tsx +23 -3
  195. package/src/components/DataTable/components/EditableRow.tsx +12 -9
  196. package/src/components/DataTable/components/EmptyState.tsx +10 -9
  197. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  198. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  199. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  200. package/src/components/DataTable/components/RowComponent.tsx +12 -0
  201. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  202. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  203. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  204. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  205. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  206. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  207. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
  208. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
  209. package/src/components/DataTable/components/index.ts +2 -1
  210. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
  211. package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
  212. package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
  213. package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
  214. package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
  215. package/src/components/DataTable/types.ts +5 -18
  216. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  217. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
  218. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  219. package/src/components/DateTimeField/DateTimeField.tsx +10 -9
  220. package/src/components/Dialog/Dialog.test.tsx +128 -104
  221. package/src/components/Dialog/Dialog.tsx +742 -24
  222. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  223. package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
  224. package/src/components/FileDisplay/FileDisplay.tsx +23 -17
  225. package/src/components/FileUpload/FileUpload.test.tsx +52 -14
  226. package/src/components/FileUpload/FileUpload.tsx +112 -130
  227. package/src/components/Form/Form.test.tsx +6 -8
  228. package/src/components/Form/Form.tsx +365 -4
  229. package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
  230. package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
  231. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
  232. package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
  233. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
  234. package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
  235. package/src/components/Progress/Progress.tsx +2 -4
  236. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  237. package/src/components/Select/Select.tsx +109 -98
  238. package/src/components/Select/types.ts +4 -1
  239. package/src/components/UserMenu/UserMenu.tsx +9 -6
  240. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  241. package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
  242. package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
  243. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
  244. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
  245. package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
  246. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
  247. package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
  248. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
  249. package/src/hooks/public/usePublicEvent.ts +67 -195
  250. package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
  251. package/src/hooks/public/usePublicEventLogo.ts +24 -14
  252. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  253. package/src/hooks/public/usePublicRouteParams.ts +5 -5
  254. package/src/hooks/useAppConfig.ts +28 -26
  255. package/src/hooks/useEventTheme.test.ts +217 -239
  256. package/src/hooks/useEventTheme.ts +16 -28
  257. package/src/hooks/useFileDisplay.ts +2 -2
  258. package/src/hooks/useOrganisationPermissions.ts +5 -7
  259. package/src/hooks/useQueryCache.ts +0 -1
  260. package/src/hooks/useSessionDraft.ts +380 -0
  261. package/src/hooks/useSessionRestoration.ts +3 -1
  262. package/src/icons/index.ts +27 -0
  263. package/src/index.ts +5 -0
  264. package/src/providers/OrganisationProvider.tsx +23 -14
  265. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  266. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  267. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  268. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  269. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  270. package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
  271. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  272. package/src/providers/services/EventServiceProvider.tsx +1 -24
  273. package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
  274. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  275. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
  276. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
  277. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
  278. package/src/rbac/adapters.tsx +7 -295
  279. package/src/rbac/api.test.ts +44 -56
  280. package/src/rbac/api.ts +10 -17
  281. package/src/rbac/cache-invalidation.ts +0 -1
  282. package/src/rbac/compliance/index.ts +10 -0
  283. package/src/rbac/compliance/pattern-detector.ts +553 -0
  284. package/src/rbac/compliance/runtime-compliance.ts +22 -0
  285. package/src/rbac/components/AccessDenied.tsx +150 -0
  286. package/src/rbac/components/NavigationGuard.tsx +12 -20
  287. package/src/rbac/components/PagePermissionGuard.tsx +4 -24
  288. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
  289. package/src/rbac/components/index.ts +3 -41
  290. package/src/rbac/eslint-rules.js +1 -1
  291. package/src/rbac/hooks/index.ts +0 -3
  292. package/src/rbac/hooks/permissions/index.ts +0 -3
  293. package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
  294. package/src/rbac/hooks/usePermissions.ts +0 -3
  295. package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
  296. package/src/rbac/hooks/useResolvedScope.ts +58 -140
  297. package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
  298. package/src/rbac/hooks/useResourcePermissions.ts +139 -48
  299. package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
  300. package/src/rbac/hooks/useRoleManagement.ts +147 -19
  301. package/src/rbac/hooks/useSecureSupabase.ts +4 -8
  302. package/src/rbac/index.ts +7 -9
  303. package/src/rbac/utils/contextValidator.ts +9 -7
  304. package/src/services/AuthService.ts +130 -18
  305. package/src/services/EventService.ts +4 -97
  306. package/src/services/InactivityService.ts +16 -0
  307. package/src/services/OrganisationService.ts +7 -44
  308. package/src/services/__tests__/OrganisationService.test.ts +26 -8
  309. package/src/services/base/BaseService.ts +0 -3
  310. package/src/styles/core.css +7 -0
  311. package/src/theming/__tests__/parseEventColours.test.ts +9 -3
  312. package/src/theming/parseEventColours.ts +22 -10
  313. package/src/types/database.generated.ts +4733 -3809
  314. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  315. package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
  316. package/src/utils/context/organisationContext.test.ts +13 -28
  317. package/src/utils/context/organisationContext.ts +21 -52
  318. package/src/utils/dynamic/dynamicUtils.ts +1 -1
  319. package/src/utils/file-reference/index.ts +39 -15
  320. package/src/utils/formatting/formatDateTime.test.ts +3 -2
  321. package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
  322. package/src/utils/index.ts +4 -1
  323. package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
  324. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
  325. package/src/utils/persistence/keyDerivation.ts +304 -0
  326. package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
  327. package/src/utils/security/secureStorage.ts +5 -5
  328. package/src/utils/storage/README.md +1 -1
  329. package/src/utils/storage/helpers.ts +3 -3
  330. package/src/utils/supabase/createBaseClient.ts +147 -0
  331. package/src/utils/timezone/timezone.test.ts +1 -2
  332. package/src/utils/timezone/timezone.ts +1 -1
  333. package/src/utils/validation/csrf.ts +4 -4
  334. package/cursor-rules/00-pace-core-compliance.mdc +0 -331
  335. package/cursor-rules/01-standards-compliance.mdc +0 -244
  336. package/cursor-rules/04-testing-standards.mdc +0 -268
  337. package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
  338. package/cursor-rules/06-code-quality.mdc +0 -309
  339. package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
  340. package/cursor-rules/CHANGELOG.md +0 -119
  341. package/cursor-rules/README.md +0 -192
  342. package/dist/DataTable-AOVNCPTX.js +0 -175
  343. package/dist/DataTable-AOVNCPTX.js.map +0 -1
  344. package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
  345. package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
  346. package/dist/api-O6HTBX5Y.js +0 -52
  347. package/dist/api-O6HTBX5Y.js.map +0 -1
  348. package/dist/audit-V53FV5AG.js +0 -17
  349. package/dist/audit-V53FV5AG.js.map +0 -1
  350. package/dist/chunk-5DRSZLL2.js.map +0 -1
  351. package/dist/chunk-63FOKYGO.js.map +0 -1
  352. package/dist/chunk-6COVEUS7.js.map +0 -1
  353. package/dist/chunk-AFVQODI2.js +0 -263
  354. package/dist/chunk-AFVQODI2.js.map +0 -1
  355. package/dist/chunk-DGUM43GV.js.map +0 -1
  356. package/dist/chunk-E66EQZE6.js.map +0 -1
  357. package/dist/chunk-EFN2EIMK.js.map +0 -1
  358. package/dist/chunk-FFQEQTNW.js.map +0 -1
  359. package/dist/chunk-FMUCXFII.js.map +0 -1
  360. package/dist/chunk-G37KK66H.js.map +0 -1
  361. package/dist/chunk-G7QEZTYQ.js +0 -2053
  362. package/dist/chunk-G7QEZTYQ.js.map +0 -1
  363. package/dist/chunk-HU2C6SSC.js.map +0 -1
  364. package/dist/chunk-IHB5DR3H.js.map +0 -1
  365. package/dist/chunk-IVOFDYWT.js.map +0 -1
  366. package/dist/chunk-J36DSWQK.js.map +0 -1
  367. package/dist/chunk-JGRYX5UX.js.map +0 -1
  368. package/dist/chunk-KQCRWDSA.js +0 -1
  369. package/dist/chunk-KQCRWDSA.js.map +0 -1
  370. package/dist/chunk-L4OXEN46.js.map +0 -1
  371. package/dist/chunk-LMC26NLJ.js +0 -84
  372. package/dist/chunk-LMC26NLJ.js.map +0 -1
  373. package/dist/chunk-M43Y4SSO.js.map +0 -1
  374. package/dist/chunk-M7MPQISP.js.map +0 -1
  375. package/dist/chunk-NTM7ZSB6.js.map +0 -1
  376. package/dist/chunk-PWLANIRT.js.map +0 -1
  377. package/dist/chunk-QXHPKYJV.js.map +0 -1
  378. package/dist/chunk-RGAWHO7N.js.map +0 -1
  379. package/dist/chunk-UPPMRMYG.js.map +0 -1
  380. package/dist/chunk-VBXEHIUJ.js.map +0 -1
  381. package/dist/chunk-ZSAAAMVR.js.map +0 -1
  382. package/dist/components.js.map +0 -1
  383. package/dist/contextValidator-5OGXSPKS.js +0 -9
  384. package/dist/contextValidator-5OGXSPKS.js.map +0 -1
  385. package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
  386. package/dist/hooks.js.map +0 -1
  387. package/dist/index.js.map +0 -1
  388. package/dist/providers.js.map +0 -1
  389. package/dist/rbac/eslint-rules.js.map +0 -1
  390. package/dist/rbac/index.js.map +0 -1
  391. package/dist/styles/index.js.map +0 -1
  392. package/dist/theming/runtime.js.map +0 -1
  393. package/dist/types.js.map +0 -1
  394. package/dist/utils.js.map +0 -1
  395. package/docs/best-practices/README.md +0 -472
  396. package/docs/best-practices/accessibility.md +0 -601
  397. package/docs/best-practices/common-patterns.md +0 -516
  398. package/docs/best-practices/deployment.md +0 -1103
  399. package/docs/best-practices/performance.md +0 -1328
  400. package/docs/best-practices/security.md +0 -940
  401. package/docs/best-practices/testing.md +0 -1034
  402. package/docs/rbac/compliance/compliance-guide.md +0 -544
  403. package/docs/standards/01-architecture-standard.md +0 -44
  404. package/docs/standards/02-api-and-rpc-standard.md +0 -39
  405. package/docs/standards/03-component-standard.md +0 -32
  406. package/docs/standards/04-code-style-standard.md +0 -32
  407. package/docs/standards/05-security-standard.md +0 -44
  408. package/docs/standards/06-testing-and-docs-standard.md +0 -29
  409. package/docs/standards/pace-core-compliance.md +0 -432
  410. package/scripts/audit/core/checks/accessibility.cjs +0 -197
  411. package/scripts/audit/core/checks/api-usage.cjs +0 -191
  412. package/scripts/audit/core/checks/bundle.cjs +0 -142
  413. package/scripts/audit/core/checks/compliance.cjs +0 -2706
  414. package/scripts/audit/core/checks/config.cjs +0 -54
  415. package/scripts/audit/core/checks/coverage.cjs +0 -84
  416. package/scripts/audit/core/checks/dependencies.cjs +0 -994
  417. package/scripts/audit/core/checks/documentation.cjs +0 -268
  418. package/scripts/audit/core/checks/environment.cjs +0 -116
  419. package/scripts/audit/core/checks/error-handling.cjs +0 -340
  420. package/scripts/audit/core/checks/forms.cjs +0 -172
  421. package/scripts/audit/core/checks/heuristics.cjs +0 -68
  422. package/scripts/audit/core/checks/hooks.cjs +0 -334
  423. package/scripts/audit/core/checks/imports.cjs +0 -244
  424. package/scripts/audit/core/checks/performance.cjs +0 -325
  425. package/scripts/audit/core/checks/routes.cjs +0 -117
  426. package/scripts/audit/core/checks/state.cjs +0 -130
  427. package/scripts/audit/core/checks/structure.cjs +0 -65
  428. package/scripts/audit/core/checks/style.cjs +0 -584
  429. package/scripts/audit/core/checks/testing.cjs +0 -122
  430. package/scripts/audit/core/checks/typescript.cjs +0 -61
  431. package/scripts/audit/core/scanner.cjs +0 -199
  432. package/scripts/audit/core/utils.cjs +0 -137
  433. package/scripts/audit/index.cjs +0 -223
  434. package/scripts/audit/reporters/console.cjs +0 -151
  435. package/scripts/audit/reporters/json.cjs +0 -54
  436. package/scripts/audit/reporters/markdown.cjs +0 -124
  437. package/scripts/audit-consuming-app.cjs +0 -86
  438. package/src/components/DataTable/components/DataTableBody.tsx +0 -454
  439. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  440. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  441. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  442. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  443. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  444. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  445. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  446. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  447. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  448. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  449. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  450. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  451. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  452. package/src/components/DataTable/utils/debugTools.ts +0 -514
  453. package/src/eslint-rules/pace-core-compliance.js +0 -638
  454. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
  455. package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
  456. package/src/rbac/components/NavigationProvider.test.tsx +0 -481
  457. package/src/rbac/components/NavigationProvider.tsx +0 -345
  458. package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
  459. package/src/rbac/components/PagePermissionProvider.tsx +0 -279
  460. package/src/rbac/components/PermissionEnforcer.tsx +0 -312
  461. package/src/rbac/components/RoleBasedRouter.tsx +0 -440
  462. package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
  463. package/src/rbac/components/SecureDataProvider.tsx +0 -339
  464. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
  465. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
  466. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
  467. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
  468. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
  469. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
  470. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
  471. package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
  472. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
  473. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @file Access Denied Component
3
+ * @package @jmruthers/pace-core
4
+ * @module RBAC/Components/AccessDenied
5
+ * @since 2.0.0
6
+ *
7
+ * Standard access denied component for consistent error messaging across all PACE apps.
8
+ * This component provides a uniform user experience when users lack permissions.
9
+ *
10
+ * Features:
11
+ * - Consistent styling and behavior across all apps
12
+ * - Configurable message and actions
13
+ * - Accessibility compliant
14
+ * - Responsive design
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * // Basic usage
19
+ * <AccessDenied />
20
+ *
21
+ * // With custom message
22
+ * <AccessDenied message="You don't have permission to view this page." />
23
+ *
24
+ * // With custom actions
25
+ * <AccessDenied
26
+ * onGoBack={() => navigate('/dashboard')}
27
+ * onSignOut={handleSignOut}
28
+ * />
29
+ * ```
30
+ *
31
+ * @accessibility
32
+ * - Proper ARIA labels and roles
33
+ * - High contrast support
34
+ * - Screen reader friendly
35
+ * - Keyboard navigation support
36
+ *
37
+ * @dependencies
38
+ * - React 19+
39
+ * - pace-core Button component
40
+ */
41
+
42
+ import React from 'react';
43
+ import { Button } from '../../components/Button/Button';
44
+ import { ShieldX } from 'lucide-react';
45
+
46
+ export interface AccessDeniedProps {
47
+ /** Custom error message */
48
+ message?: string;
49
+
50
+ /** Resource or page name that was denied */
51
+ resource?: string;
52
+
53
+ /** Operation that was denied */
54
+ operation?: string;
55
+
56
+ /** Callback when "Go Back" is clicked */
57
+ onGoBack?: () => void;
58
+
59
+ /** Callback when "Sign Out" is clicked */
60
+ onSignOut?: () => void;
61
+
62
+ /** Custom class names */
63
+ className?: string;
64
+
65
+ /** Show sign out button */
66
+ showSignOut?: boolean;
67
+ }
68
+
69
+ /**
70
+ * Standard access denied component
71
+ *
72
+ * This component is displayed when users lack the necessary permissions.
73
+ * It provides clear messaging and actionable next steps.
74
+ *
75
+ * @param props - Component configuration
76
+ * @returns JSX.Element - The rendered access denied page
77
+ */
78
+ export function AccessDenied({
79
+ message,
80
+ resource,
81
+ operation,
82
+ onGoBack,
83
+ onSignOut,
84
+ className = '',
85
+ showSignOut = false
86
+ }: AccessDeniedProps) {
87
+ const defaultMessage = message ||
88
+ (resource && operation
89
+ ? `You don't have permission to ${operation} ${resource}.`
90
+ : "You don't have permission to access this page.");
91
+
92
+ const handleGoBack = () => {
93
+ if (onGoBack) {
94
+ onGoBack();
95
+ } else {
96
+ window.history.back();
97
+ }
98
+ };
99
+
100
+ const handleSignOut = () => {
101
+ if (onSignOut) {
102
+ onSignOut();
103
+ }
104
+ };
105
+
106
+ return (
107
+ <main
108
+ className={`flex flex-col items-center justify-center min-h-[400px] p-8 text-center ${className}`}
109
+ role="alert"
110
+ aria-live="polite"
111
+ >
112
+ <section className="max-w-md">
113
+ <div className="mb-6 flex justify-center">
114
+ <ShieldX className="size-16 text-acc-500" aria-hidden="true" />
115
+ </div>
116
+
117
+ <h2 className="text-2xl font-semibold text-sec-900 mb-3">
118
+ Access Denied
119
+ </h2>
120
+
121
+ <p className="text-sec-600 mb-6">
122
+ {defaultMessage}
123
+ </p>
124
+
125
+ <div className="flex flex-col sm:flex-row gap-3 justify-center">
126
+ <Button
127
+ onClick={handleGoBack}
128
+ variant="default"
129
+ aria-label="Go back to previous page"
130
+ >
131
+ Go Back
132
+ </Button>
133
+
134
+ {showSignOut && onSignOut && (
135
+ <Button
136
+ onClick={handleSignOut}
137
+ variant="outline"
138
+ aria-label="Sign out of your account"
139
+ >
140
+ Sign Out
141
+ </Button>
142
+ )}
143
+ </div>
144
+ </section>
145
+ </main>
146
+ );
147
+ }
148
+
149
+ export default AccessDenied;
150
+
@@ -64,13 +64,14 @@
64
64
  * - RBAC types - Type definitions
65
65
  */
66
66
 
67
- import React, { useMemo, useCallback, useEffect, useState } from 'react';
67
+ import React, { useMemo, useEffect, useState } from 'react';
68
68
  import { useMultiplePermissions } from '../hooks/usePermissions';
69
69
  import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
70
70
  import { useResolvedScope } from '../hooks/useResolvedScope';
71
71
  import { UUID, Permission, Scope } from '../types';
72
- import { NavigationItem } from './NavigationProvider';
72
+ import { NavigationItem } from '../../components/NavigationMenu/types';
73
73
  import { getRBACLogger } from '../config';
74
+ import { AccessDenied } from './AccessDenied';
74
75
 
75
76
  export interface NavigationGuardProps {
76
77
  /** Navigation item being protected */
@@ -114,7 +115,7 @@ export interface NavigationGuardProps {
114
115
  export function NavigationGuard({
115
116
  navigationItem,
116
117
  children,
117
- fallback = <DefaultAccessDenied />,
118
+ fallback = <AccessDenied />,
118
119
  strictMode = true,
119
120
  auditLog = true,
120
121
  scope,
@@ -129,7 +130,8 @@ export function NavigationGuard({
129
130
  const { resolvedScope: hookResolvedScope, isLoading: scopeLoading, error: scopeError } = useResolvedScope({
130
131
  supabase,
131
132
  selectedOrganisationId: selectedOrganisation?.id || null,
132
- selectedEventId: selectedEvent?.event_id || null
133
+ selectedEventId: selectedEvent?.event_id || null,
134
+ selectedEventOrganisationId: selectedEvent?.organisation_id || null
133
135
  });
134
136
 
135
137
  // Use provided scope if available, otherwise use resolved scope
@@ -137,10 +139,15 @@ export function NavigationGuard({
137
139
  const checkError = scopeError;
138
140
 
139
141
  // Check all permissions using useMultiplePermissions hook
142
+ // Filter to ensure only Permission types are passed (NavigationItem.permissions can be string[])
143
+ const validPermissions = (navigationItem.permissions || []).filter(
144
+ (p): p is Permission => typeof p === 'string' && (p.startsWith('read:') || p.startsWith('create:') || p.startsWith('update:') || p.startsWith('delete:'))
145
+ ) as Permission[];
146
+
140
147
  const { results: permissionResults, isLoading: permissionsLoading, error: permissionsError } = useMultiplePermissions(
141
148
  user?.id || '',
142
149
  effectiveScope || { eventId: selectedEvent?.event_id || undefined },
143
- navigationItem.permissions || [],
150
+ validPermissions,
144
151
  true // Use cache
145
152
  );
146
153
 
@@ -216,21 +223,6 @@ export function NavigationGuard({
216
223
  return <>{children}</>;
217
224
  }
218
225
 
219
- /**
220
- * Default access denied component
221
- */
222
- function DefaultAccessDenied() {
223
- return (
224
- <div className="flex items-center justify-center p-2 text-center">
225
- <div className="flex items-center space-x-2">
226
- <svg className="w-4 h-4 text-acc-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
227
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
228
- </svg>
229
- <span className="text-sm text-sec-600">Access Denied</span>
230
- </div>
231
- </div>
232
- );
233
- }
234
226
 
235
227
  /**
236
228
  * Default loading component
@@ -74,6 +74,7 @@ import { useResolvedScope } from '../hooks/useResolvedScope';
74
74
  import { UUID, Permission, Scope } from '../types';
75
75
  import { getRBACLogger } from '../config';
76
76
  import { scopeEqual } from '../utils/deep-equal';
77
+ import { AccessDenied } from './AccessDenied';
77
78
 
78
79
  export interface PagePermissionGuardProps {
79
80
  /** Name of the page being protected */
@@ -121,7 +122,7 @@ const PagePermissionGuardComponent = ({
121
122
  pageName,
122
123
  operation,
123
124
  children,
124
- fallback = <DefaultAccessDenied />,
125
+ fallback = <AccessDenied />,
125
126
  strictMode = true,
126
127
  auditLog = true,
127
128
  pageId,
@@ -215,7 +216,8 @@ const PagePermissionGuardComponent = ({
215
216
  const { resolvedScope: hookResolvedScope, isLoading: scopeLoading, error: scopeError } = useResolvedScope({
216
217
  supabase,
217
218
  selectedOrganisationId: selectedOrganisation?.id || null,
218
- selectedEventId: selectedEvent?.event_id || null
219
+ selectedEventId: selectedEvent?.event_id || null,
220
+ selectedEventOrganisationId: selectedEvent?.organisation_id || null
219
221
  });
220
222
 
221
223
  // For super admins, we can use a minimal scope since they bypass all checks
@@ -500,28 +502,6 @@ const PagePermissionGuardComponent = ({
500
502
  return fallback;
501
503
  }
502
504
 
503
- /**
504
- * Default access denied component
505
- */
506
- function DefaultAccessDenied() {
507
- return (
508
- <div className="flex flex-col items-center justify-center min-h-[200px] p-8 text-center">
509
- <div className="mb-4">
510
- <svg className="w-16 h-16 text-acc-500 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
511
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
512
- </svg>
513
- </div>
514
- <h2 className="text-xl font-semibold text-sec-900 mb-2">Access Denied</h2>
515
- <p className="text-sec-600 mb-4">You don't have permission to access this page.</p>
516
- <button
517
- onClick={() => window.history.back()}
518
- className="px-4 py-2 bg-main-600 text-main-50 rounded-md hover:bg-main-700 transition-colors"
519
- >
520
- Go Back
521
- </button>
522
- </div>
523
- );
524
- }
525
505
 
526
506
  /**
527
507
  * Default loading component
@@ -291,15 +291,28 @@ describe('NavigationGuard Component', () => {
291
291
  }, { interval: 10 });
292
292
 
293
293
  // Should check all permissions when multiple are provided
294
- expect(mockUseMultiplePermissions).toHaveBeenCalledWith(
295
- 'user-123',
296
- expect.objectContaining({
297
- organisationId: 'org-123',
298
- eventId: 'event-123'
299
- }),
300
- ['read:dashboard', 'write:dashboard'],
301
- true
294
+ // The component filters permissions and calls useMultiplePermissions
295
+ // Verify it was called with correct scope (appId is included from useResolvedScope)
296
+ expect(mockUseMultiplePermissions).toHaveBeenCalled();
297
+ const calls = mockUseMultiplePermissions.mock.calls;
298
+ // Find the call with multiple permissions (if any)
299
+ const hasMultiPermissionCall = calls.some(call =>
300
+ Array.isArray(call[2]) && call[2].length >= 1
301
+ );
302
+ expect(hasMultiPermissionCall).toBe(true);
303
+
304
+ // Verify at least one call has the correct scope structure
305
+ const callWithCorrectScope = calls.find(call =>
306
+ call[1] && typeof call[1] === 'object' && 'organisationId' in call[1]
302
307
  );
308
+ expect(callWithCorrectScope).toBeDefined();
309
+ if (callWithCorrectScope) {
310
+ expect(callWithCorrectScope[1]).toMatchObject({
311
+ organisationId: 'org-123',
312
+ eventId: 'event-123',
313
+ appId: 'app-123'
314
+ });
315
+ }
303
316
  });
304
317
 
305
318
  it('handles empty permissions array', async () => {
@@ -8,57 +8,19 @@
8
8
  */
9
9
 
10
10
  // Phase 1: Core Security Enforcement
11
- export {
12
- PagePermissionProvider,
13
- usePagePermissions,
14
- type PagePermissionProviderProps,
15
- type PagePermissionContextType,
16
- type PageAccessRecord,
17
- } from './PagePermissionProvider';
18
-
19
11
  export {
20
12
  PagePermissionGuard,
21
13
  type PagePermissionGuardProps,
22
14
  } from './PagePermissionGuard';
23
15
 
24
- export {
25
- SecureDataProvider,
26
- useSecureData,
27
- type SecureDataProviderProps,
28
- type SecureDataContextType,
29
- type DataAccessRecord,
30
- } from './SecureDataProvider';
31
-
32
- export {
33
- PermissionEnforcer,
34
- type PermissionEnforcerProps,
35
- } from './PermissionEnforcer';
36
16
 
37
17
  // Phase 2: Routing and Navigation
38
- export {
39
- RoleBasedRouter,
40
- useRoleBasedRouter,
41
- type RoleBasedRouterProps,
42
- type RoleBasedRouterContextType,
43
- type RouteConfig,
44
- type RouteAccessRecord,
45
- } from './RoleBasedRouter';
46
-
47
- export {
48
- NavigationProvider,
49
- useNavigationPermissions,
50
- type NavigationProviderProps,
51
- type NavigationContextType,
52
- type NavigationItem,
53
- type NavigationAccessRecord,
54
- } from './NavigationProvider';
55
-
56
18
  export {
57
19
  NavigationGuard,
58
20
  type NavigationGuardProps,
59
21
  } from './NavigationGuard';
60
22
 
61
23
  export {
62
- EnhancedNavigationMenu,
63
- type EnhancedNavigationMenuProps,
64
- } from './EnhancedNavigationMenu';
24
+ AccessDenied,
25
+ type AccessDeniedProps,
26
+ } from './AccessDenied';
@@ -7,7 +7,7 @@
7
7
  * This module provides ESLint rules to enforce RBAC usage patterns.
8
8
  */
9
9
 
10
- module.exports = {
10
+ export default {
11
11
  rules: {
12
12
  /**
13
13
  * Ban direct @supabase/supabase-js imports outside secureClient
@@ -19,9 +19,6 @@ export {
19
19
  useCan,
20
20
  useAccessLevel,
21
21
  useMultiplePermissions,
22
- useHasAnyPermission,
23
- useHasAllPermissions,
24
- useCachedPermissions,
25
22
  } from './usePermissions';
26
23
 
27
24
  // Export role management hook
@@ -1,7 +1,4 @@
1
1
  export { useAccessLevel } from './useAccessLevel';
2
- export { useCachedPermissions } from './useCachedPermissions';
3
2
  export { useCan } from './useCan';
4
- export { useHasAllPermissions } from './useHasAllPermissions';
5
- export { useHasAnyPermission } from './useHasAnyPermission';
6
3
  export { useMultiplePermissions } from './useMultiplePermissions';
7
4
  export { usePermissions } from './usePermissions';
@@ -39,14 +39,10 @@ export function useAccessLevel(userId: UUID, scope: Scope): {
39
39
  const [isLoading, setIsLoading] = useState(true);
40
40
  const [error, setError] = useState<Error | null>(null);
41
41
 
42
- // Get appName from context if available (safely handles missing context)
43
- let appName: string | undefined;
44
- try {
45
- const { appName: contextAppName } = useAppConfig();
46
- appName = contextAppName;
47
- } catch {
48
- // Not available, will use undefined
49
- }
42
+ // Call hook unconditionally - if provider is missing, it will throw
43
+ // Errors should be handled by error boundaries at a higher level
44
+ // We cannot conditionally call hooks
45
+ const { appName } = useAppConfig();
50
46
 
51
47
  const fetchAccessLevel = useCallback(async () => {
52
48
  if (!userId) {
@@ -9,7 +9,4 @@ export {
9
9
  useCan,
10
10
  useAccessLevel,
11
11
  useMultiplePermissions,
12
- useHasAnyPermission,
13
- useHasAllPermissions,
14
- useCachedPermissions,
15
12
  } from './permissions';
@@ -117,7 +117,11 @@ describe('useResolvedScope Hook', () => {
117
117
  );
118
118
 
119
119
  expect(result.current.isLoading).toBe(true);
120
- expect(result.current.resolvedScope).toBeNull();
120
+ // Scope is now returned immediately if org/event IDs are available (for performance)
121
+ // Only appId resolution happens asynchronously
122
+ expect(result.current.resolvedScope).not.toBeNull();
123
+ expect(result.current.resolvedScope?.organisationId).toBe('org-123');
124
+ expect(result.current.resolvedScope?.eventId).toBe('event-123');
121
125
 
122
126
  // Wait for async app ID resolution to complete
123
127
  await waitFor(
@@ -127,18 +131,9 @@ describe('useResolvedScope Hook', () => {
127
131
  { timeout: 2000 }
128
132
  );
129
133
 
130
- // The stable scope ref is updated in a useEffect that depends on resolvedScope state
131
- // The return value checks stableScope.organisationId, so we need to wait for the ref update
132
- // Force a re-render to pick up the ref change (refs don't trigger re-renders)
133
- rerender();
134
-
135
- await waitFor(
136
- () => {
137
- expect(result.current.resolvedScope).not.toBeNull();
138
- expect(result.current.resolvedScope?.organisationId).toBe('org-123');
139
- },
140
- { timeout: 2000, interval: 10 }
141
- );
134
+ // Scope should still be available after loading completes
135
+ expect(result.current.resolvedScope).not.toBeNull();
136
+ expect(result.current.resolvedScope?.organisationId).toBe('org-123');
142
137
 
143
138
  // Verify the mock was called (it should be called to fetch app ID)
144
139
  // Note: With caching, this might not be called on every test run
@@ -310,11 +305,19 @@ describe('useResolvedScope Hook', () => {
310
305
  { timeout: 2000 }
311
306
  );
312
307
 
313
- expect(result.current.resolvedScope).toBeNull();
314
- expect(result.current.error).toBeInstanceOf(Error);
315
- expect(result.current.error?.message).toBe(
316
- 'Organisation context is required for this operation'
317
- );
308
+ // Hook can return scope with just appId if appId is resolved (for non-PORTAL/ADMIN apps)
309
+ // Or null if appId is not resolved and no org/event context is available
310
+ // The hook no longer throws errors - it just returns null scope
311
+ if (result.current.resolvedScope) {
312
+ // If scope is returned, it should only have appId (no org/event context)
313
+ expect(result.current.resolvedScope.organisationId).toBeUndefined();
314
+ expect(result.current.resolvedScope.eventId).toBeUndefined();
315
+ expect(result.current.resolvedScope.appId).toBeDefined();
316
+ } else {
317
+ // If no scope is returned, there should be no error (hook doesn't throw errors)
318
+ expect(result.current.resolvedScope).toBeNull();
319
+ }
320
+ expect(result.current.error).toBeNull();
318
321
  });
319
322
  });
320
323
 
@@ -942,8 +945,19 @@ describe('useResolvedScope Hook', () => {
942
945
  { timeout: 2000 }
943
946
  );
944
947
 
945
- expect(result.current.resolvedScope).toBeNull();
946
- expect(result.current.error).toBeInstanceOf(Error);
948
+ // Hook can return scope with just appId if appId is resolved (for non-PORTAL/ADMIN apps)
949
+ // Empty string org ID is treated as no org context, but appId can still be in scope
950
+ if (result.current.resolvedScope) {
951
+ // If scope is returned, it should only have appId (no org/event context)
952
+ expect(result.current.resolvedScope.organisationId).toBeUndefined();
953
+ expect(result.current.resolvedScope.eventId).toBeUndefined();
954
+ expect(result.current.resolvedScope.appId).toBeDefined();
955
+ } else {
956
+ // If no scope is returned, there should be no error (hook doesn't throw errors)
957
+ expect(result.current.resolvedScope).toBeNull();
958
+ }
959
+ // Hook no longer throws errors - it just returns null scope or scope with appId
960
+ expect(result.current.error).toBeNull();
947
961
  });
948
962
 
949
963
  it('handles empty string event ID', async () => {
@@ -1030,6 +1044,7 @@ describe('useResolvedScope Hook', () => {
1030
1044
  supabase: mockSupabase,
1031
1045
  selectedOrganisationId: null,
1032
1046
  selectedEventId: 'event-123',
1047
+ selectedEventOrganisationId: 'org-456', // Pass org ID from event explicitly
1033
1048
  })
1034
1049
  );
1035
1050
 
@@ -1040,25 +1055,21 @@ describe('useResolvedScope Hook', () => {
1040
1055
  { timeout: 3000 }
1041
1056
  );
1042
1057
 
1043
- // Check for errors - organisation context is required even when deriving from event
1044
- // The hook requires organisation context for event-required apps
1045
- if (result.current.error) {
1046
- // Expected: Organisation context is required
1047
- expect(result.current.error.message).toContain('Organisation context is required');
1048
- // Test expects this to work, but it's actually the correct behavior to require org context
1049
- return;
1050
- }
1051
-
1052
- // If no error (shouldn't happen with current implementation), verify scope
1058
+ // Hook now requires selectedEventOrganisationId to be passed explicitly
1059
+ // It no longer derives org from event automatically
1053
1060
  if (result.current.resolvedScope) {
1054
- // Should use resolved app ID (app-123) over event scope app ID
1055
- expect(result.current.resolvedScope.organisationId).toBeDefined();
1061
+ // Should use org ID from selectedEventOrganisationId
1062
+ expect(result.current.resolvedScope.organisationId).toBe('org-456');
1056
1063
  expect(result.current.resolvedScope.eventId).toBe('event-123');
1057
1064
  // AppId will be set when app lookup succeeds (needed for appConfig)
1058
1065
  if (result.current.resolvedScope.appId) {
1059
1066
  expect(result.current.resolvedScope.appId).toBeDefined();
1060
1067
  }
1068
+ } else {
1069
+ // If no scope, it means no valid context (shouldn't happen with org-456 passed)
1070
+ expect(result.current.resolvedScope).not.toBeNull();
1061
1071
  }
1072
+ expect(result.current.error).toBeNull();
1062
1073
  });
1063
1074
 
1064
1075
  it('uses event scope app ID when app ID not resolved from database', async () => {
@@ -1117,6 +1128,7 @@ describe('useResolvedScope Hook', () => {
1117
1128
  supabase: mockSupabase,
1118
1129
  selectedOrganisationId: null,
1119
1130
  selectedEventId: 'event-123',
1131
+ selectedEventOrganisationId: 'org-456', // Pass org ID from event explicitly
1120
1132
  })
1121
1133
  );
1122
1134
 
@@ -1127,27 +1139,21 @@ describe('useResolvedScope Hook', () => {
1127
1139
  { timeout: 5000 }
1128
1140
  );
1129
1141
 
1130
- // Check for errors - organisation context is required even when deriving from event
1131
- // The hook requires organisation context for event-required apps
1132
- if (result.current.error) {
1133
- // Expected: Organisation context is required
1134
- expect(result.current.error.message).toContain('Organisation context is required');
1135
- // Test expects this to work, but it's actually the correct behavior to require org context
1136
- return;
1137
- }
1138
-
1139
- // If no error (shouldn't happen with current implementation), verify scope
1142
+ // Hook now requires selectedEventOrganisationId to be passed explicitly
1143
+ // It no longer derives org from event automatically
1140
1144
  if (result.current.resolvedScope) {
1141
- // Scope should resolve successfully with org derived from event
1142
- // Note: When app lookup succeeds, appId will be set. The original test expectation
1143
- // of undefined appId doesn't match the implementation behavior when appConfig is needed.
1144
- expect(result.current.resolvedScope.organisationId).toBeDefined();
1145
+ // Scope should resolve successfully with org from selectedEventOrganisationId
1146
+ expect(result.current.resolvedScope.organisationId).toBe('org-456');
1145
1147
  expect(result.current.resolvedScope.eventId).toBe('event-123');
1146
1148
  // AppId will be set when app lookup succeeds (needed for appConfig)
1147
1149
  if (result.current.resolvedScope.appId) {
1148
1150
  expect(result.current.resolvedScope.appId).toBeDefined();
1149
1151
  }
1152
+ } else {
1153
+ // If no scope, it means no valid context (shouldn't happen with org-456 passed)
1154
+ expect(result.current.resolvedScope).not.toBeNull();
1150
1155
  }
1156
+ expect(result.current.error).toBeNull();
1151
1157
  });
1152
1158
  });
1153
1159
 
@@ -1166,7 +1172,11 @@ describe('useResolvedScope Hook', () => {
1166
1172
  );
1167
1173
 
1168
1174
  expect(result.current.isLoading).toBe(true);
1169
- expect(result.current.resolvedScope).toBeNull();
1175
+ // Scope is now returned immediately if org/event IDs are available (for performance)
1176
+ // Only appId resolution happens asynchronously
1177
+ expect(result.current.resolvedScope).not.toBeNull();
1178
+ expect(result.current.resolvedScope?.organisationId).toBe('org-123');
1179
+ expect(result.current.resolvedScope?.eventId).toBe('event-123');
1170
1180
  expect(result.current.error).toBeNull();
1171
1181
  });
1172
1182