@jmruthers/pace-core 0.6.4 → 0.6.6

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 (387) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +5 -403
  3. package/core-usage-manifest.json +93 -0
  4. package/cursor-rules/00-pace-core-compliance.mdc +128 -26
  5. package/cursor-rules/01-standards-compliance.mdc +49 -8
  6. package/cursor-rules/02-project-structure.mdc +6 -0
  7. package/cursor-rules/03-solid-principles.mdc +2 -0
  8. package/cursor-rules/04-testing-standards.mdc +2 -0
  9. package/cursor-rules/05-bug-reports-and-features.mdc +2 -0
  10. package/cursor-rules/06-code-quality.mdc +2 -0
  11. package/cursor-rules/07-tech-stack-compliance.mdc +2 -0
  12. package/cursor-rules/08-markup-quality.mdc +52 -27
  13. package/cursor-rules/09-rbac-compliance.mdc +462 -0
  14. package/cursor-rules/10-error-handling-patterns.mdc +179 -0
  15. package/cursor-rules/11-performance-optimization.mdc +169 -0
  16. package/cursor-rules/12-ci-cd-integration.mdc +150 -0
  17. package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
  18. package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-2N_tqbfq.d.ts} +1 -1
  19. package/dist/DataTable-LRJL4IRV.js +15 -0
  20. package/dist/{PublicPageProvider-DEMpysFR.d.ts → PublicPageProvider-BBH6Vqg7.d.ts} +72 -139
  21. package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
  22. package/dist/api-Y4MQWOFW.js +4 -0
  23. package/dist/audit-MYQXYZFU.js +3 -0
  24. package/dist/{chunk-J36DSWQK.js → chunk-2HGJFNAH.js} +8 -28
  25. package/dist/{chunk-OEWDTMG7.js → chunk-3O3WHILE.js} +38 -121
  26. package/dist/{chunk-M43Y4SSO.js → chunk-3QC3KRHK.js} +1 -14
  27. package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
  28. package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
  29. package/dist/chunk-4T7OBVTU.js +62 -0
  30. package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
  31. package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
  32. package/dist/{chunk-NN6WWZ5U.js → chunk-7TYHROIV.js} +579 -563
  33. package/dist/{chunk-M7MPQISP.js → chunk-A55DK444.js} +9 -16
  34. package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
  35. package/dist/{chunk-L4OXEN46.js → chunk-BVP2BCJF.js} +2 -16
  36. package/dist/chunk-C7NSAPTL.js +1 -0
  37. package/dist/{chunk-YKRAFF5K.js → chunk-FENMYN2U.js} +73 -149
  38. package/dist/{chunk-AVMLPIM7.js → chunk-FTCRZOG2.js} +284 -432
  39. package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
  40. package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
  41. package/dist/{chunk-I6DAQMWX.js → chunk-LAZMKTTF.js} +930 -891
  42. package/dist/{chunk-5EC5MEWX.js → chunk-MAGBIDNS.js} +77 -222
  43. package/dist/chunk-MBADTM7L.js +64 -0
  44. package/dist/chunk-OHIK3MIO.js +994 -0
  45. package/dist/{chunk-6SOIHG6Z.js → chunk-S7DKJPLT.js} +115 -44
  46. package/dist/{chunk-FMUCXFII.js → chunk-SD6WQY43.js} +1 -5
  47. package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
  48. package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
  49. package/dist/{chunk-FFQEQTNW.js → chunk-UIYSCEV7.js} +134 -45
  50. package/dist/{chunk-3LPHPB62.js → chunk-ZFYPMX46.js} +271 -87
  51. package/dist/{chunk-7JPAB3T5.js → chunk-ZS5VO5JB.js} +1989 -1283
  52. package/dist/components.d.ts +6 -6
  53. package/dist/components.js +57 -267
  54. package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
  55. package/dist/eslint-rules/index.cjs +22 -0
  56. package/dist/eslint-rules/rules/compliance.cjs +348 -0
  57. package/dist/eslint-rules/rules/components.cjs +113 -0
  58. package/dist/eslint-rules/rules/imports.cjs +102 -0
  59. package/dist/eslint-rules/rules/rbac.cjs +790 -0
  60. package/dist/eslint-rules/utils/helpers.cjs +42 -0
  61. package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
  62. package/dist/hooks.d.ts +5 -5
  63. package/dist/hooks.js +62 -270
  64. package/dist/icons/index.d.ts +1 -0
  65. package/dist/icons/index.js +1 -0
  66. package/dist/index.d.ts +36 -26
  67. package/dist/index.js +87 -690
  68. package/dist/providers.d.ts +2 -2
  69. package/dist/providers.js +8 -35
  70. package/dist/rbac/eslint-rules.d.ts +46 -44
  71. package/dist/rbac/eslint-rules.js +7 -4
  72. package/dist/rbac/index.d.ts +124 -594
  73. package/dist/rbac/index.js +14 -207
  74. package/dist/styles/index.js +2 -12
  75. package/dist/theming/runtime.js +3 -19
  76. package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
  77. package/dist/{types-CkbwOr4Y.d.ts → types-B-K_5VnO.d.ts} +4 -0
  78. package/dist/types-t9H8qKRw.d.ts +55 -0
  79. package/dist/types.d.ts +1 -1
  80. package/dist/types.js +7 -94
  81. package/dist/{usePublicRouteParams-i3qtoBgg.d.ts → usePublicRouteParams-COZ28Mvq.d.ts} +9 -9
  82. package/dist/utils.d.ts +24 -117
  83. package/dist/utils.js +54 -392
  84. package/docs/README.md +16 -6
  85. package/docs/api/README.md +4 -402
  86. package/docs/api/modules.md +454 -930
  87. package/docs/api-reference/components.md +3 -1
  88. package/docs/api-reference/deprecated.md +31 -6
  89. package/docs/api-reference/rpc-functions.md +78 -3
  90. package/docs/best-practices/accessibility.md +6 -3
  91. package/docs/getting-started/cursor-rules.md +3 -23
  92. package/docs/getting-started/dependencies.md +650 -0
  93. package/docs/getting-started/installation-guide.md +20 -7
  94. package/docs/getting-started/quick-start.md +23 -12
  95. package/docs/implementation-guides/permission-enforcement.md +4 -0
  96. package/docs/rbac/MIGRATION_GUIDE.md +819 -0
  97. package/docs/rbac/RBAC_CONTRACT.md +724 -0
  98. package/docs/rbac/README.md +12 -3
  99. package/docs/rbac/edge-functions-guide.md +376 -0
  100. package/docs/rbac/secure-client-protection.md +0 -34
  101. package/docs/standards/00-pace-core-compliance.md +967 -0
  102. package/docs/standards/01-standards-compliance.md +188 -0
  103. package/docs/standards/02-project-structure.md +985 -0
  104. package/docs/standards/03-solid-principles.md +39 -0
  105. package/docs/standards/04-testing-standards.md +36 -0
  106. package/docs/standards/05-bug-reports-and-features.md +27 -0
  107. package/docs/standards/{04-code-style-standard.md → 06-code-quality.md} +2 -0
  108. package/docs/standards/07-tech-stack-compliance.md +30 -0
  109. package/docs/standards/08-markup-quality.md +345 -0
  110. package/docs/standards/{07-rbac-and-rls-standard.md → 09-rbac-compliance.md} +149 -54
  111. package/docs/standards/10-error-handling-patterns.md +401 -0
  112. package/docs/standards/11-performance-optimization.md +348 -0
  113. package/docs/standards/12-ci-cd-integration.md +370 -0
  114. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +192 -0
  115. package/docs/standards/README.md +62 -33
  116. package/docs/troubleshooting/organisation-context-setup.md +42 -19
  117. package/eslint-config-pace-core.cjs +20 -4
  118. package/package.json +31 -21
  119. package/scripts/audit/audit-compliance.cjs +1295 -0
  120. package/scripts/audit/audit-components.cjs +260 -0
  121. package/scripts/audit/audit-dependencies.cjs +395 -0
  122. package/scripts/audit/audit-rbac.cjs +954 -0
  123. package/scripts/audit/audit-standards.cjs +1268 -0
  124. package/scripts/audit/index.cjs +1898 -194
  125. package/scripts/install-cursor-rules.cjs +259 -8
  126. package/scripts/validate-master.js +1 -1
  127. package/src/__tests__/fixtures/supabase.ts +1 -1
  128. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +1 -1
  129. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
  130. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
  131. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +3 -3
  132. package/src/__tests__/helpers/component-test-utils.tsx +1 -1
  133. package/src/__tests__/helpers/supabaseMock.ts +2 -2
  134. package/src/__tests__/public-recipe-view.test.ts +38 -9
  135. package/src/components/Button/Button.tsx +5 -1
  136. package/src/components/ContextSelector/ContextSelector.tsx +42 -39
  137. package/src/components/DataTable/__tests__/keyboard.test.tsx +15 -2
  138. package/src/components/DataTable/components/DataTableBody.tsx +55 -31
  139. package/src/components/DataTable/components/DataTableCore.tsx +186 -13
  140. package/src/components/DataTable/components/DataTableLayout.tsx +30 -5
  141. package/src/components/DataTable/components/EditFields.tsx +23 -3
  142. package/src/components/DataTable/components/EditableRow.tsx +7 -2
  143. package/src/components/DataTable/components/ImportModal.tsx +4 -6
  144. package/src/components/DataTable/components/RowComponent.tsx +12 -0
  145. package/src/components/DataTable/components/ViewRowModal.tsx +4 -4
  146. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +455 -96
  147. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +122 -58
  148. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
  149. package/src/components/DataTable/core/DataTableContext.tsx +1 -1
  150. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
  151. package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
  152. package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
  153. package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
  154. package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
  155. package/src/components/DataTable/types.ts +5 -0
  156. package/src/components/DateTimeField/DateTimeField.tsx +20 -20
  157. package/src/components/DateTimeField/README.md +5 -2
  158. package/src/components/Dialog/Dialog.test.tsx +361 -318
  159. package/src/components/Dialog/Dialog.tsx +1154 -323
  160. package/src/components/Dialog/index.ts +3 -3
  161. package/src/components/FileDisplay/FileDisplay.test.tsx +45 -2
  162. package/src/components/FileDisplay/FileDisplay.tsx +28 -22
  163. package/src/components/Form/Form.test.tsx +9 -10
  164. package/src/components/Form/Form.tsx +369 -9
  165. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +28 -28
  166. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +40 -54
  167. package/src/components/LoginForm/LoginForm.tsx +2 -2
  168. package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
  169. package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
  170. package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
  171. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
  172. package/src/components/PaceAppLayout/PaceAppLayout.tsx +30 -41
  173. package/src/components/PaceAppLayout/README.md +10 -9
  174. package/src/components/PaceAppLayout/test-setup.tsx +40 -31
  175. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
  176. package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
  177. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +61 -0
  178. package/src/components/PasswordChange/PasswordChangeForm.tsx +20 -13
  179. package/src/components/PublicLayout/PublicLayout.test.tsx +7 -3
  180. package/src/components/PublicLayout/PublicPageLayout.tsx +5 -8
  181. package/src/components/Select/Select.tsx +23 -21
  182. package/src/components/Select/types.ts +1 -1
  183. package/src/components/UserMenu/UserMenu.test.tsx +38 -6
  184. package/src/components/UserMenu/UserMenu.tsx +39 -34
  185. package/src/components/index.ts +3 -4
  186. package/src/eslint-rules/index.cjs +22 -0
  187. package/src/eslint-rules/rules/compliance.cjs +348 -0
  188. package/src/eslint-rules/rules/components.cjs +113 -0
  189. package/src/eslint-rules/rules/imports.cjs +102 -0
  190. package/src/eslint-rules/rules/rbac.cjs +790 -0
  191. package/src/eslint-rules/utils/helpers.cjs +42 -0
  192. package/src/eslint-rules/utils/manifest-loader.cjs +75 -0
  193. package/src/hooks/__tests__/hooks.integration.test.tsx +6 -8
  194. package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
  195. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
  196. package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
  197. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
  198. package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
  199. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
  200. package/src/hooks/public/usePublicEvent.ts +62 -190
  201. package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
  202. package/src/hooks/public/usePublicEventLogo.ts +19 -9
  203. package/src/hooks/useAppConfig.ts +26 -24
  204. package/src/hooks/useEventTheme.test.ts +211 -233
  205. package/src/hooks/useEventTheme.ts +19 -28
  206. package/src/hooks/useEvents.ts +11 -7
  207. package/src/hooks/useKeyboardShortcuts.ts +1 -1
  208. package/src/hooks/useOrganisationPermissions.ts +9 -11
  209. package/src/hooks/useOrganisations.ts +13 -7
  210. package/src/hooks/useQueryCache.ts +0 -1
  211. package/src/hooks/useSessionDraft.ts +380 -0
  212. package/src/hooks/useSessionRestoration.ts +3 -1
  213. package/src/icons/index.ts +27 -0
  214. package/src/index.ts +16 -1
  215. package/src/providers/OrganisationProvider.tsx +23 -14
  216. package/src/providers/services/EventServiceProvider.tsx +1 -24
  217. package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
  218. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -0
  219. package/src/rbac/README.md +20 -20
  220. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
  221. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
  222. package/src/rbac/adapters.tsx +7 -295
  223. package/src/rbac/api.test.ts +44 -56
  224. package/src/rbac/api.ts +10 -17
  225. package/src/rbac/cache-invalidation.ts +0 -1
  226. package/src/rbac/compliance/index.ts +10 -0
  227. package/src/rbac/compliance/pattern-detector.ts +553 -0
  228. package/src/rbac/compliance/runtime-compliance.ts +22 -0
  229. package/src/rbac/components/AccessDenied.tsx +150 -0
  230. package/src/rbac/components/NavigationGuard.tsx +12 -20
  231. package/src/rbac/components/PagePermissionGuard.tsx +4 -24
  232. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
  233. package/src/rbac/components/index.ts +3 -41
  234. package/src/rbac/eslint-rules.js +1 -1
  235. package/src/rbac/hooks/index.ts +0 -3
  236. package/src/rbac/hooks/permissions/index.ts +0 -3
  237. package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
  238. package/src/rbac/hooks/usePermissions.ts +0 -3
  239. package/src/rbac/hooks/useRBAC.test.ts +21 -3
  240. package/src/rbac/hooks/useRBAC.ts +4 -3
  241. package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
  242. package/src/rbac/hooks/useResolvedScope.ts +58 -140
  243. package/src/rbac/hooks/useResourcePermissions.test.ts +241 -60
  244. package/src/rbac/hooks/useResourcePermissions.ts +182 -63
  245. package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
  246. package/src/rbac/hooks/useRoleManagement.ts +147 -19
  247. package/src/rbac/hooks/useSecureSupabase.ts +4 -8
  248. package/src/rbac/index.ts +7 -9
  249. package/src/rbac/permissions.ts +17 -17
  250. package/src/rbac/utils/contextValidator.ts +45 -7
  251. package/src/services/AuthService.ts +132 -23
  252. package/src/services/EventService.ts +4 -97
  253. package/src/services/InactivityService.ts +155 -58
  254. package/src/services/OrganisationService.ts +7 -44
  255. package/src/services/__tests__/OrganisationService.test.ts +26 -8
  256. package/src/services/base/BaseService.ts +0 -3
  257. package/src/styles/core.css +4 -0
  258. package/src/types/database.generated.ts +4733 -3809
  259. package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
  260. package/src/utils/context/organisationContext.test.ts +13 -28
  261. package/src/utils/context/organisationContext.ts +21 -52
  262. package/src/utils/dynamic/dynamicUtils.ts +1 -1
  263. package/src/utils/file-reference/index.ts +39 -15
  264. package/src/utils/formatting/formatDateTime.test.ts +3 -2
  265. package/src/utils/formatting/formatTime.test.ts +3 -2
  266. package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
  267. package/src/utils/index.ts +4 -1
  268. package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
  269. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
  270. package/src/utils/persistence/keyDerivation.ts +304 -0
  271. package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
  272. package/src/utils/security/secureStorage.ts +5 -5
  273. package/src/utils/storage/helpers.ts +3 -3
  274. package/src/utils/supabase/createBaseClient.ts +147 -0
  275. package/src/utils/timezone/timezone.test.ts +1 -2
  276. package/src/utils/timezone/timezone.ts +1 -1
  277. package/src/utils/validation/csrf.ts +4 -4
  278. package/cursor-rules/CHANGELOG.md +0 -119
  279. package/cursor-rules/README.md +0 -192
  280. package/dist/DataTable-E7YQZD7D.js +0 -175
  281. package/dist/DataTable-E7YQZD7D.js.map +0 -1
  282. package/dist/UnifiedAuthProvider-QPXO24B4.js +0 -18
  283. package/dist/UnifiedAuthProvider-QPXO24B4.js.map +0 -1
  284. package/dist/api-6LVZTHDS.js +0 -52
  285. package/dist/api-6LVZTHDS.js.map +0 -1
  286. package/dist/audit-V53FV5AG.js +0 -17
  287. package/dist/audit-V53FV5AG.js.map +0 -1
  288. package/dist/chunk-36LVWXB2.js +0 -227
  289. package/dist/chunk-36LVWXB2.js.map +0 -1
  290. package/dist/chunk-3LPHPB62.js.map +0 -1
  291. package/dist/chunk-5DRSZLL2.js.map +0 -1
  292. package/dist/chunk-5EC5MEWX.js.map +0 -1
  293. package/dist/chunk-63FOKYGO.js.map +0 -1
  294. package/dist/chunk-6SOIHG6Z.js.map +0 -1
  295. package/dist/chunk-7JPAB3T5.js.map +0 -1
  296. package/dist/chunk-ATKZM7RX.js +0 -2053
  297. package/dist/chunk-ATKZM7RX.js.map +0 -1
  298. package/dist/chunk-AVMLPIM7.js.map +0 -1
  299. package/dist/chunk-DGUM43GV.js.map +0 -1
  300. package/dist/chunk-E66EQZE6.js.map +0 -1
  301. package/dist/chunk-FFQEQTNW.js.map +0 -1
  302. package/dist/chunk-FMUCXFII.js.map +0 -1
  303. package/dist/chunk-G37KK66H.js.map +0 -1
  304. package/dist/chunk-I6DAQMWX.js.map +0 -1
  305. package/dist/chunk-J36DSWQK.js.map +0 -1
  306. package/dist/chunk-KQCRWDSA.js +0 -1
  307. package/dist/chunk-KQCRWDSA.js.map +0 -1
  308. package/dist/chunk-L4OXEN46.js.map +0 -1
  309. package/dist/chunk-LMC26NLJ.js +0 -84
  310. package/dist/chunk-LMC26NLJ.js.map +0 -1
  311. package/dist/chunk-M43Y4SSO.js.map +0 -1
  312. package/dist/chunk-M7MPQISP.js.map +0 -1
  313. package/dist/chunk-NN6WWZ5U.js.map +0 -1
  314. package/dist/chunk-OEWDTMG7.js.map +0 -1
  315. package/dist/chunk-PWLANIRT.js.map +0 -1
  316. package/dist/chunk-QXHPKYJV.js.map +0 -1
  317. package/dist/chunk-VBXEHIUJ.js.map +0 -1
  318. package/dist/chunk-YKRAFF5K.js.map +0 -1
  319. package/dist/chunk-ZSAAAMVR.js.map +0 -1
  320. package/dist/components.js.map +0 -1
  321. package/dist/contextValidator-OOPCLPZW.js +0 -9
  322. package/dist/contextValidator-OOPCLPZW.js.map +0 -1
  323. package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
  324. package/dist/hooks.js.map +0 -1
  325. package/dist/index.js.map +0 -1
  326. package/dist/providers.js.map +0 -1
  327. package/dist/rbac/eslint-rules.js.map +0 -1
  328. package/dist/rbac/index.js.map +0 -1
  329. package/dist/styles/index.js.map +0 -1
  330. package/dist/theming/runtime.js.map +0 -1
  331. package/dist/types.js.map +0 -1
  332. package/dist/utils.js.map +0 -1
  333. package/docs/standards/01-architecture-standard.md +0 -44
  334. package/docs/standards/02-api-and-rpc-standard.md +0 -39
  335. package/docs/standards/03-component-standard.md +0 -32
  336. package/docs/standards/05-security-standard.md +0 -44
  337. package/docs/standards/06-testing-and-docs-standard.md +0 -29
  338. package/docs/standards/pace-core-compliance.md +0 -432
  339. package/scripts/audit/core/checks/accessibility.cjs +0 -197
  340. package/scripts/audit/core/checks/api-usage.cjs +0 -191
  341. package/scripts/audit/core/checks/bundle.cjs +0 -142
  342. package/scripts/audit/core/checks/compliance.cjs +0 -2706
  343. package/scripts/audit/core/checks/config.cjs +0 -54
  344. package/scripts/audit/core/checks/coverage.cjs +0 -84
  345. package/scripts/audit/core/checks/dependencies.cjs +0 -994
  346. package/scripts/audit/core/checks/documentation.cjs +0 -268
  347. package/scripts/audit/core/checks/environment.cjs +0 -116
  348. package/scripts/audit/core/checks/error-handling.cjs +0 -340
  349. package/scripts/audit/core/checks/forms.cjs +0 -172
  350. package/scripts/audit/core/checks/heuristics.cjs +0 -68
  351. package/scripts/audit/core/checks/hooks.cjs +0 -334
  352. package/scripts/audit/core/checks/imports.cjs +0 -244
  353. package/scripts/audit/core/checks/performance.cjs +0 -325
  354. package/scripts/audit/core/checks/routes.cjs +0 -117
  355. package/scripts/audit/core/checks/state.cjs +0 -130
  356. package/scripts/audit/core/checks/structure.cjs +0 -65
  357. package/scripts/audit/core/checks/style.cjs +0 -584
  358. package/scripts/audit/core/checks/testing.cjs +0 -122
  359. package/scripts/audit/core/checks/typescript.cjs +0 -61
  360. package/scripts/audit/core/scanner.cjs +0 -199
  361. package/scripts/audit/core/utils.cjs +0 -137
  362. package/scripts/audit/reporters/console.cjs +0 -151
  363. package/scripts/audit/reporters/json.cjs +0 -54
  364. package/scripts/audit/reporters/markdown.cjs +0 -124
  365. package/scripts/audit-consuming-app.cjs +0 -86
  366. package/src/eslint-rules/pace-core-compliance.cjs +0 -510
  367. package/src/eslint-rules/pace-core-compliance.js +0 -638
  368. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
  369. package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
  370. package/src/rbac/components/NavigationProvider.test.tsx +0 -481
  371. package/src/rbac/components/NavigationProvider.tsx +0 -345
  372. package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
  373. package/src/rbac/components/PagePermissionProvider.tsx +0 -279
  374. package/src/rbac/components/PermissionEnforcer.tsx +0 -312
  375. package/src/rbac/components/RoleBasedRouter.tsx +0 -440
  376. package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
  377. package/src/rbac/components/SecureDataProvider.tsx +0 -339
  378. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
  379. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
  380. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
  381. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
  382. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
  383. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
  384. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
  385. package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
  386. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
  387. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
@@ -40,13 +40,16 @@
40
40
  * - Missing user context results in all permissions being denied
41
41
  */
42
42
 
43
- import { useMemo } from 'react';
43
+ import { useMemo, useState, useEffect, useRef } from 'react';
44
44
  import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
45
45
  import { useOrganisations } from '../../hooks/useOrganisations';
46
46
  import { useEvents } from '../../hooks/useEvents';
47
+ import type { Event } from '../../types/event';
47
48
  import { useResolvedScope } from './useResolvedScope';
48
49
  import { useCan } from './usePermissions';
49
- import type { Scope } from '../types';
50
+ import { isSuperAdmin } from '../api';
51
+ import { createLogger } from '../../utils/core/logger';
52
+ import type { Scope, Permission } from '../types';
50
53
 
51
54
  export interface UseResourcePermissionsOptions {
52
55
  /** Whether to check read permissions (default: false) */
@@ -80,15 +83,22 @@ export interface ResourcePermissions {
80
83
  * and provides a simple API for permission checking.
81
84
  *
82
85
  * **Page Permission Support:**
83
- * When an `appId` is available in the resolved scope, the resource name is passed
84
- * as `pageId` to enable page-based permission checks. This allows the hook to work
85
- * with both resource-based permissions (when appId is not available) and page-based
86
- * permissions (when appId is available and the resource is a registered page).
86
+ * When an `appId` is available in the resolved scope, the hook automatically:
87
+ * 1. Waits for scope resolution to complete (including `appId` being set)
88
+ * 2. Constructs permission strings with the `page.` prefix (e.g., `create:page.planning`)
89
+ * 3. Passes the resource name as `pageId` to enable page-based permission checks
87
90
  *
88
- * The RPC function `rbac_check_permission_simplified` will automatically resolve
89
- * the page name to a page ID and check page permissions if the resource matches
90
- * a registered page in `rbac_app_pages`. If the resource is not a registered page,
91
- * it will fall back to resource-based permission checking.
91
+ * This ensures permission strings match the format returned by `rbac_permissions_get`
92
+ * (e.g., `create:page.planning`) rather than resource-based format (e.g., `create:planning`).
93
+ *
94
+ * **Scope Resolution Timing:**
95
+ * The hook waits for scope resolution to complete before constructing permission strings.
96
+ * This prevents timing issues where permission checks use the wrong format (e.g., `delete:planning`
97
+ * instead of `delete:page.planning`) when `appId` is not yet available in the scope.
98
+ *
99
+ * The RPC function `rbac_check_permission_simplified` will resolve the page name to a page ID
100
+ * and check page permissions if the resource matches a registered page in `rbac_app_pages`.
101
+ * If the resource is not a registered page, it will fall back to resource-based permission checking.
92
102
  *
93
103
  * @param resource - The resource name (e.g., 'contacts', 'risks', 'planning')
94
104
  * Can be a resource name or a page name registered in rbac_app_pages
@@ -133,15 +143,80 @@ export function useResourcePermissions(
133
143
  options: UseResourcePermissionsOptions = {}
134
144
  ): ResourcePermissions {
135
145
  const { enableRead = false, requireScope = true } = options;
146
+ const logger = createLogger('ResourcePermissions');
136
147
 
137
148
  // Get user and supabase client from UnifiedAuth
138
149
  const { user, supabase } = useUnifiedAuth();
150
+
151
+ // Super admin status - check if user has super admin privileges
152
+ // Super admins bypass all permission checks (similar to useDataTablePermissions)
153
+ // PERFORMANCE OPTIMIZATION: Check super admin once and share with all useCan hooks to avoid duplicate queries
154
+ // Use null to indicate "not checked yet" vs false which means "checked and not super admin"
155
+ const [isSuperAdminUser, setIsSuperAdminUser] = useState<boolean | null>(null);
156
+ const [isCheckingSuperAdmin, setIsCheckingSuperAdmin] = useState<boolean>(() => !!user?.id);
157
+ const lastCheckedUserIdRef = useRef<string | null>(null);
158
+ const isCheckingRef = useRef<boolean>(false);
159
+
160
+ useEffect(() => {
161
+ // Skip if already checked for this user ID
162
+ if (lastCheckedUserIdRef.current === user?.id && isSuperAdminUser !== null) {
163
+ return;
164
+ }
165
+
166
+ // Skip if already checking
167
+ if (isCheckingRef.current) {
168
+ return;
169
+ }
170
+
171
+ const checkSuperAdminStatus = async () => {
172
+ if (!user?.id) {
173
+ setIsSuperAdminUser(false); // No user = not super admin
174
+ setIsCheckingSuperAdmin(false);
175
+ lastCheckedUserIdRef.current = null;
176
+ return;
177
+ }
178
+
179
+ // Mark as checking and track user ID
180
+ isCheckingRef.current = true;
181
+ lastCheckedUserIdRef.current = user.id;
182
+
183
+ const startTime = Date.now();
184
+ setIsCheckingSuperAdmin(true);
185
+
186
+ // Add timeout to prevent infinite hanging
187
+ const timeoutId = setTimeout(() => {
188
+ logger.warn('useResourcePermissions', 'Super admin check taking longer than 5 seconds', {
189
+ userId: user?.id,
190
+ elapsedMs: Date.now() - startTime,
191
+ });
192
+ }, 5000);
193
+
194
+ try {
195
+ const superAdminStatus = await isSuperAdmin(user.id);
196
+ setIsSuperAdminUser(superAdminStatus);
197
+ } catch (error) {
198
+ const elapsed = Date.now() - startTime;
199
+ logger.error('useResourcePermissions', 'Error checking super admin status', {
200
+ userId: user?.id,
201
+ error,
202
+ elapsedMs: elapsed,
203
+ });
204
+ setIsSuperAdminUser(false); // Error = assume not super admin for security
205
+ } finally {
206
+ clearTimeout(timeoutId);
207
+ setIsCheckingSuperAdmin(false);
208
+ isCheckingRef.current = false;
209
+ }
210
+ };
211
+
212
+ checkSuperAdminStatus();
213
+ }, [user?.id, logger]);
139
214
 
140
215
  // Get selected organisation
141
216
  const { selectedOrganisation } = useOrganisations();
142
217
 
143
218
  // Get selected event (optional - wrap in try/catch)
144
- let selectedEvent: { event_id: string } | null = null;
219
+ let selectedEvent: Event | null = null;
145
220
  try {
146
221
  const eventsContext = useEvents();
147
222
  selectedEvent = eventsContext.selectedEvent;
@@ -154,52 +229,70 @@ export function useResourcePermissions(
154
229
  const { resolvedScope, isLoading: scopeLoading, error: scopeError } = useResolvedScope({
155
230
  supabase,
156
231
  selectedOrganisationId: selectedOrganisation?.id || null,
157
- selectedEventId: selectedEvent?.event_id || null
232
+ selectedEventId: selectedEvent?.event_id || null,
233
+ selectedEventOrganisationId: selectedEvent?.organisation_id || null
158
234
  });
159
235
 
160
- // Create fallback scope if resolvedScope is not available
236
+ // CRITICAL FIX: Only use resolvedScope when it's available (not during loading)
237
+ // This ensures we wait for appId to be resolved before constructing permission strings
238
+ // If resolvedScope is null (still loading), we can't determine if we should use page permissions
239
+ // so we must wait for scope resolution to complete
161
240
  const scope: Scope = resolvedScope || {
162
241
  organisationId: selectedOrganisation?.id || '',
163
242
  eventId: selectedEvent?.event_id || undefined,
164
243
  appId: undefined
165
244
  };
166
245
 
167
- // If we have an appId in scope, pass the resource name as pageId to enable page permission checks
168
- // The RPC function rbac_check_permission_simplified will resolve the page name to a page ID
169
- // and check page permissions if the resource is a registered page
170
- // This allows useResourcePermissions to work with both resource-based and page-based permissions
171
- const pageId = scope.appId ? resource : undefined;
246
+ // CRITICAL FIX: Only use page permissions when appId is actually available in resolvedScope
247
+ // If scope is still loading (resolvedScope is null), we can't know if appId will be available
248
+ // so we must wait for scope resolution before constructing page permission strings
249
+ // This prevents using wrong permission format (delete:planning instead of delete:page.planning)
250
+ const hasAppId = !!resolvedScope?.appId;
251
+ const pageId = hasAppId ? resource : undefined;
252
+
253
+ // When appId is available in resolved scope, construct permission strings with page. prefix
254
+ // This matches the format that rbac_permissions_get returns (e.g., 'create:page.planning')
255
+ // and ensures consistent permission checking for page-based resources
256
+ // IMPORTANT: Only use page format when appId is actually resolved, not during loading
257
+ const isPagePermission = hasAppId && !!pageId;
258
+ const createPermission = isPagePermission ? `create:page.${resource}` : `create:${resource}`;
259
+ const updatePermission = isPagePermission ? `update:page.${resource}` : `update:${resource}`;
260
+ const deletePermission = isPagePermission ? `delete:page.${resource}` : `delete:${resource}`;
261
+ const readPermission = isPagePermission ? `read:page.${resource}` : `read:${resource}`;
172
262
 
173
263
  // Permission checks for create, update, delete
174
- // Pass null for super admin status (not checked yet - hook will check if needed)
175
- // PERFORMANCE: These hooks will each check super admin separately - could be optimized in future
264
+ // PERFORMANCE OPTIMIZATION: Pass precomputed super admin status to avoid duplicate checks
265
+ // Each useCan hook would normally check super admin separately (4+ queries), but we check once here
266
+ // and share the result. Pass null if not checked yet (hooks will check), false/true if checked.
267
+ // CRITICAL: useCan will wait for appId when pageId is provided (it checks needsAppIdForPageName)
268
+ // But we must ensure permission strings are correct before calling useCan
176
269
  const { can: canCreateResult, isLoading: createLoading, error: createError } = useCan(
177
270
  user?.id || '',
178
271
  scope,
179
- `create:${resource}` as const,
272
+ createPermission as Permission,
180
273
  pageId, // Pass resource name as pageId when appId is available to enable page permission checks
181
274
  true, // useCache
182
- null, // precomputedSuperAdmin - not checked yet
275
+ isSuperAdminUser, // precomputedSuperAdmin - null if checking, false/true if checked
183
276
  undefined // appName
184
277
  );
185
278
 
186
279
  const { can: canUpdateResult, isLoading: updateLoading, error: updateError } = useCan(
187
280
  user?.id || '',
188
281
  scope,
189
- `update:${resource}` as const,
282
+ updatePermission as Permission,
190
283
  pageId, // Pass resource name as pageId when appId is available to enable page permission checks
191
284
  true, // useCache
192
- null, // precomputedSuperAdmin - not checked yet
285
+ isSuperAdminUser, // precomputedSuperAdmin - null if checking, false/true if checked
193
286
  undefined // appName
194
287
  );
195
288
 
196
289
  const { can: canDeleteResult, isLoading: deleteLoading, error: deleteError } = useCan(
197
290
  user?.id || '',
198
291
  scope,
199
- `delete:${resource}` as const,
292
+ deletePermission as Permission,
200
293
  pageId, // Pass resource name as pageId when appId is available to enable page permission checks
201
294
  true, // useCache
202
- null, // precomputedSuperAdmin - not checked yet
295
+ isSuperAdminUser, // precomputedSuperAdmin - null if checking, false/true if checked
203
296
  undefined // appName
204
297
  );
205
298
 
@@ -207,17 +300,25 @@ export function useResourcePermissions(
207
300
  const { can: canReadResult, isLoading: readLoading, error: readError } = useCan(
208
301
  user?.id || '',
209
302
  scope,
210
- `read:${resource}` as const,
303
+ readPermission as Permission,
211
304
  pageId, // Pass resource name as pageId when appId is available to enable page permission checks
212
305
  true, // useCache
213
- null, // precomputedSuperAdmin - not checked yet
306
+ isSuperAdminUser, // precomputedSuperAdmin - null if checking, false/true if checked
214
307
  undefined // appName
215
308
  );
216
309
 
217
310
  // Aggregate loading states - any permission check or scope resolution loading
311
+ // CRITICAL: When requireScope is true, we must wait for scope resolution to complete
312
+ // so we can determine the correct permission format (page vs resource permissions)
313
+ // This prevents using wrong permission format (delete:planning instead of delete:page.planning)
314
+ // Also wait for super admin check to complete to ensure accurate permission results
218
315
  const isLoading = useMemo(() => {
219
- return scopeLoading || createLoading || updateLoading || deleteLoading || (enableRead && readLoading);
220
- }, [scopeLoading, createLoading, updateLoading, deleteLoading, readLoading, enableRead]);
316
+ // If scope resolution is required, wait for it to complete
317
+ const waitingForScope = requireScope && scopeLoading;
318
+ // Wait for super admin check to complete (null means still checking)
319
+ const waitingForSuperAdmin = isSuperAdminUser === null && isCheckingSuperAdmin;
320
+ return waitingForScope || waitingForSuperAdmin || createLoading || updateLoading || deleteLoading || (enableRead && readLoading);
321
+ }, [scopeLoading, requireScope, isSuperAdminUser, isCheckingSuperAdmin, createLoading, updateLoading, deleteLoading, readLoading, enableRead]);
221
322
 
222
323
  // Aggregate errors - prefer scope error, then any permission error
223
324
  const error = useMemo(() => {
@@ -232,41 +333,59 @@ export function useResourcePermissions(
232
333
  // Return wrapper functions that take resource name and return permission result
233
334
  // Note: The resource parameter in the function is for consistency with the API,
234
335
  // but we're checking permissions for the resource passed to the hook
235
- return useMemo(() => ({
236
- canCreate: (res: string) => {
237
- // For now, we only check the resource passed to the hook
238
- // Future enhancement could support checking different resources
239
- if (res !== resource) {
240
- return false;
241
- }
242
- return canCreateResult;
243
- },
244
- canUpdate: (res: string) => {
245
- if (res !== resource) {
246
- return false;
247
- }
248
- return canUpdateResult;
249
- },
250
- canDelete: (res: string) => {
251
- if (res !== resource) {
252
- return false;
253
- }
254
- return canDeleteResult;
255
- },
256
- canRead: (res: string) => {
257
- if (!enableRead) {
258
- return true; // If read checking is disabled, allow read
336
+ // CRITICAL FIX: When isSuperAdminUser is true, immediately grant all permissions without waiting
337
+ // for useCan results. This ensures super admins can use resource operations immediately.
338
+ return useMemo(() => {
339
+ // Helper to create a permission result that bypasses for super admins
340
+ const createSuperAdminAwarePermission = (result: boolean) => {
341
+ // CRITICAL: If super admin is confirmed, immediately grant permission
342
+ // Don't wait for useCan results - super admins bypass all checks
343
+ if (isSuperAdminUser === true) {
344
+ return true;
259
345
  }
260
- if (res !== resource) {
261
- return false;
262
- }
263
- return canReadResult;
264
- },
265
- scope,
266
- isLoading,
267
- error
268
- }), [
346
+
347
+ // For non-super-admins or while checking, use normal permission results
348
+ return result;
349
+ };
350
+
351
+ return {
352
+ canCreate: (res: string) => {
353
+ // For now, we only check the resource passed to the hook
354
+ // Future enhancement could support checking different resources
355
+ if (res !== resource) {
356
+ return false;
357
+ }
358
+ return createSuperAdminAwarePermission(canCreateResult);
359
+ },
360
+ canUpdate: (res: string) => {
361
+ if (res !== resource) {
362
+ return false;
363
+ }
364
+ return createSuperAdminAwarePermission(canUpdateResult);
365
+ },
366
+ canDelete: (res: string) => {
367
+ if (res !== resource) {
368
+ return false;
369
+ }
370
+ return createSuperAdminAwarePermission(canDeleteResult);
371
+ },
372
+ canRead: (res: string) => {
373
+ if (!enableRead) {
374
+ return true; // If read checking is disabled, allow read
375
+ }
376
+ if (res !== resource) {
377
+ return false;
378
+ }
379
+ return createSuperAdminAwarePermission(canReadResult);
380
+ },
381
+ scope,
382
+ isLoading: isCheckingSuperAdmin || isLoading,
383
+ error
384
+ };
385
+ }, [
269
386
  resource,
387
+ isSuperAdminUser,
388
+ isCheckingSuperAdmin,
270
389
  canCreateResult,
271
390
  canUpdateResult,
272
391
  canDeleteResult,
@@ -8,7 +8,7 @@
8
8
  * Tests focus on behavior: role granting, revoking, loading states, and error handling.
9
9
  */
10
10
 
11
- import { renderHook, waitFor } from '@testing-library/react';
11
+ import { renderHook, waitFor, act } from '@testing-library/react';
12
12
  import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
13
13
  import { useRoleManagement } from './useRoleManagement';
14
14
  import type { SupabaseClient } from '@supabase/supabase-js';
@@ -105,7 +105,7 @@ describe('useRoleManagement Hook', () => {
105
105
  describe('revokeEventAppRole', () => {
106
106
  it('revokes role successfully', async () => {
107
107
  (mockSupabase.rpc as any).mockResolvedValue({
108
- data: true,
108
+ data: [{ success: true, message: 'Role revoked successfully', revoked_count: 1 }],
109
109
  error: null,
110
110
  });
111
111
 
@@ -117,19 +117,18 @@ describe('useRoleManagement Hook', () => {
117
117
  expect(revokeResult.message).toBe('Role revoked successfully');
118
118
  expect(revokeResult.error).toBeUndefined();
119
119
  expect(result.current.error).toBeNull();
120
- expect(mockSupabase.rpc).toHaveBeenCalledWith('revoke_event_app_role', {
120
+ expect(mockSupabase.rpc).toHaveBeenCalledWith('rbac_role_revoke', {
121
121
  p_user_id: 'user-456',
122
- p_organisation_id: 'org-123',
123
- p_event_id: 'event-123',
124
- p_app_id: 'app-123',
125
- p_role: 'viewer',
122
+ p_role_type: 'event_app',
123
+ p_role_name: 'viewer',
124
+ p_context_id: 'event-123:app-123',
126
125
  p_revoked_by: 'user-123',
127
126
  });
128
127
  });
129
128
 
130
129
  it('handles case when role not found', async () => {
131
130
  (mockSupabase.rpc as any).mockResolvedValue({
132
- data: false,
131
+ data: [{ success: false, message: 'No matching role found', revoked_count: 0, error_code: 'ROLE_NOT_FOUND' }],
133
132
  error: null,
134
133
  });
135
134
 
@@ -138,13 +137,55 @@ describe('useRoleManagement Hook', () => {
138
137
  const revokeResult = await result.current.revokeEventAppRole(mockRoleParams);
139
138
 
140
139
  expect(revokeResult.success).toBe(false);
141
- expect(revokeResult.message).toBe('No role found to revoke');
140
+ expect(revokeResult.message).toBe('No matching role found');
142
141
  expect(revokeResult.error).toBe('No matching role found');
143
142
  });
144
143
 
144
+ it('handles empty data response', async () => {
145
+ (mockSupabase.rpc as any).mockResolvedValue({
146
+ data: [],
147
+ error: null,
148
+ });
149
+
150
+ const { result } = renderHook(() => useRoleManagement());
151
+
152
+ const revokeResult = await result.current.revokeEventAppRole(mockRoleParams);
153
+
154
+ expect(revokeResult.success).toBe(false);
155
+ expect(revokeResult.error).toBe('No response from database - role revocation may have failed');
156
+ });
157
+
158
+ it('handles null data response', async () => {
159
+ (mockSupabase.rpc as any).mockResolvedValue({
160
+ data: null,
161
+ error: null,
162
+ });
163
+
164
+ const { result } = renderHook(() => useRoleManagement());
165
+
166
+ const revokeResult = await result.current.revokeEventAppRole(mockRoleParams);
167
+
168
+ expect(revokeResult.success).toBe(false);
169
+ expect(revokeResult.error).toBe('No response from database - role revocation may have failed');
170
+ });
171
+
172
+ it('handles result with success false but no message', async () => {
173
+ (mockSupabase.rpc as any).mockResolvedValue({
174
+ data: [{ success: false, message: null, revoked_count: 0, error_code: 'ROLE_NOT_FOUND' }],
175
+ error: null,
176
+ });
177
+
178
+ const { result } = renderHook(() => useRoleManagement());
179
+
180
+ const revokeResult = await result.current.revokeEventAppRole(mockRoleParams);
181
+
182
+ expect(revokeResult.success).toBe(false);
183
+ expect(revokeResult.error).toBe('ROLE_NOT_FOUND');
184
+ });
185
+
145
186
  it('uses provided revoked_by parameter', async () => {
146
187
  (mockSupabase.rpc as any).mockResolvedValue({
147
- data: true,
188
+ data: [{ success: true, message: 'Role revoked successfully', revoked_count: 1 }],
148
189
  error: null,
149
190
  });
150
191
 
@@ -156,7 +197,7 @@ describe('useRoleManagement Hook', () => {
156
197
  });
157
198
 
158
199
  expect(mockSupabase.rpc).toHaveBeenCalledWith(
159
- 'revoke_event_app_role',
200
+ 'rbac_role_revoke',
160
201
  expect.objectContaining({
161
202
  p_revoked_by: 'admin-789',
162
203
  })
@@ -165,7 +206,7 @@ describe('useRoleManagement Hook', () => {
165
206
 
166
207
  it('uses user ID as revoked_by when not provided', async () => {
167
208
  (mockSupabase.rpc as any).mockResolvedValue({
168
- data: true,
209
+ data: [{ success: true, message: 'Role revoked successfully', revoked_count: 1 }],
169
210
  error: null,
170
211
  });
171
212
 
@@ -174,7 +215,7 @@ describe('useRoleManagement Hook', () => {
174
215
  await result.current.revokeEventAppRole(mockRoleParams);
175
216
 
176
217
  expect(mockSupabase.rpc).toHaveBeenCalledWith(
177
- 'revoke_event_app_role',
218
+ 'rbac_role_revoke',
178
219
  expect.objectContaining({
179
220
  p_revoked_by: 'user-123',
180
221
  })
@@ -262,7 +303,7 @@ describe('useRoleManagement Hook', () => {
262
303
  );
263
304
 
264
305
  resolvePromise!({
265
- data: true,
306
+ data: [{ success: true, message: 'Role revoked successfully', revoked_count: 1 }],
266
307
  error: null,
267
308
  });
268
309
 
@@ -461,14 +502,16 @@ describe('useRoleManagement Hook', () => {
461
502
 
462
503
  const { result } = renderHook(() => useRoleManagement());
463
504
 
505
+ // Call grantEventAppRole - loading state is set synchronously, but React state updates are async
464
506
  const grantPromise = result.current.grantEventAppRole(mockRoleParams);
465
507
 
466
- // Loading state is set synchronously, but React state updates are async
508
+ // Wait for React to process the state update (setIsLoading(true))
509
+ // Increased timeout to allow React to batch and process state updates
467
510
  await waitFor(
468
511
  () => {
469
512
  expect(result.current.isLoading).toBe(true);
470
513
  },
471
- { timeout: 100 }
514
+ { timeout: 1000 }
472
515
  );
473
516
 
474
517
  resolvePromise!({
@@ -797,9 +840,9 @@ describe('useRoleManagement Hook', () => {
797
840
  });
798
841
 
799
842
  expect(mockSupabase.rpc).toHaveBeenCalledWith(
800
- 'revoke_event_app_role',
843
+ 'rbac_role_revoke',
801
844
  expect.objectContaining({
802
- p_role: role,
845
+ p_role_name: role,
803
846
  })
804
847
  );
805
848
  }
@@ -825,7 +868,7 @@ describe('useRoleManagement Hook', () => {
825
868
 
826
869
  // Second call succeeds
827
870
  (mockSupabase.rpc as any).mockResolvedValueOnce({
828
- data: true,
871
+ data: [{ success: true, message: 'Role revoked successfully', revoked_count: 1 }],
829
872
  error: null,
830
873
  });
831
874
 
@@ -850,7 +893,7 @@ describe('useRoleManagement Hook', () => {
850
893
 
851
894
  // Second call succeeds
852
895
  (mockSupabase.rpc as any).mockResolvedValueOnce({
853
- data: true,
896
+ data: [{ success: true, message: 'Role revoked successfully', revoked_count: 1 }],
854
897
  error: null,
855
898
  });
856
899
 
@@ -864,7 +907,7 @@ describe('useRoleManagement Hook', () => {
864
907
  describe('Concurrent Operations', () => {
865
908
  it('handles concurrent role operations', async () => {
866
909
  (mockSupabase.rpc as any).mockResolvedValue({
867
- data: true,
910
+ data: [{ success: true, message: 'Role revoked successfully', revoked_count: 1 }],
868
911
  error: null,
869
912
  });
870
913
 
@@ -885,7 +928,7 @@ describe('useRoleManagement Hook', () => {
885
928
 
886
929
  it('handles rapid sequential operations', async () => {
887
930
  (mockSupabase.rpc as any).mockResolvedValue({
888
- data: true,
931
+ data: [{ success: true, message: 'Role revoked successfully', revoked_count: 1 }],
889
932
  error: null,
890
933
  });
891
934