@jmruthers/pace-core 0.5.191 → 0.6.1

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 (380) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +7 -1
  3. package/cursor-rules/00-pace-core-compliance.mdc +372 -0
  4. package/cursor-rules/01-standards-compliance.mdc +275 -0
  5. package/cursor-rules/02-project-structure.mdc +200 -0
  6. package/cursor-rules/03-solid-principles.mdc +341 -0
  7. package/cursor-rules/04-testing-standards.mdc +315 -0
  8. package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
  9. package/cursor-rules/06-code-quality.mdc +392 -0
  10. package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
  11. package/cursor-rules/CHANGELOG.md +101 -0
  12. package/cursor-rules/README.md +191 -0
  13. package/dist/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
  14. package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
  15. package/dist/{DataTable-WKRZD47S.js → DataTable-DQ7RSOHE.js} +8 -7
  16. package/dist/{PublicPageProvider-ULXC_u6U.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +37 -156
  17. package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
  18. package/dist/{UnifiedAuthProvider-FTSG5XH7.js → UnifiedAuthProvider-ATAP5UTR.js} +3 -3
  19. package/dist/{api-IHKALJZD.js → api-N774RPUA.js} +2 -2
  20. package/dist/{chunk-6C4YBBJM.js → chunk-3QRJFVBR.js} +1 -1
  21. package/dist/chunk-3QRJFVBR.js.map +1 -0
  22. package/dist/{chunk-OETXORNB.js → chunk-3XTALGJF.js} +211 -136
  23. package/dist/chunk-3XTALGJF.js.map +1 -0
  24. package/dist/{chunk-6TQDD426.js → chunk-4N5C5XZU.js} +47 -228
  25. package/dist/chunk-4N5C5XZU.js.map +1 -0
  26. package/dist/{chunk-LOMZXPSN.js → chunk-4ZC4GX36.js} +47 -74
  27. package/dist/chunk-4ZC4GX36.js.map +1 -0
  28. package/dist/{chunk-6LTQQAT6.js → chunk-BYFSK72L.js} +357 -158
  29. package/dist/chunk-BYFSK72L.js.map +1 -0
  30. package/dist/{chunk-XYXSXPUK.js → chunk-EXUD6RNJ.js} +50 -10
  31. package/dist/chunk-EXUD6RNJ.js.map +1 -0
  32. package/dist/{chunk-VKB2CO4Z.js → chunk-GLK6VM3F.js} +244 -249
  33. package/dist/chunk-GLK6VM3F.js.map +1 -0
  34. package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
  35. package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
  36. package/dist/{chunk-XNYQOL3Z.js → chunk-JBKQ3SAO.js} +9 -18
  37. package/dist/chunk-JBKQ3SAO.js.map +1 -0
  38. package/dist/{chunk-ROXMHMY2.js → chunk-KNC55RTG.js} +13 -3
  39. package/dist/{chunk-ROXMHMY2.js.map → chunk-KNC55RTG.js.map} +1 -1
  40. package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
  41. package/dist/{chunk-ULHIJK66.js → chunk-T33XF5ZC.js} +255 -140
  42. package/dist/chunk-T33XF5ZC.js.map +1 -0
  43. package/dist/{chunk-VRGWKHDB.js → chunk-XM25TVIE.js} +100 -33
  44. package/dist/chunk-XM25TVIE.js.map +1 -0
  45. package/dist/components.d.ts +4 -4
  46. package/dist/components.js +9 -9
  47. package/dist/hooks.d.ts +6 -6
  48. package/dist/hooks.js +20 -25
  49. package/dist/hooks.js.map +1 -1
  50. package/dist/index.d.ts +11 -11
  51. package/dist/index.js +18 -21
  52. package/dist/index.js.map +1 -1
  53. package/dist/providers.d.ts +3 -3
  54. package/dist/providers.js +2 -2
  55. package/dist/rbac/index.d.ts +2 -20
  56. package/dist/rbac/index.js +7 -9
  57. package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
  58. package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
  59. package/dist/utils.d.ts +1 -1
  60. package/dist/utils.js +3 -3
  61. package/docs/api/classes/ColumnFactory.md +1 -1
  62. package/docs/api/classes/ErrorBoundary.md +1 -1
  63. package/docs/api/classes/InvalidScopeError.md +1 -1
  64. package/docs/api/classes/Logger.md +1 -1
  65. package/docs/api/classes/MissingUserContextError.md +1 -1
  66. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  67. package/docs/api/classes/PermissionDeniedError.md +2 -2
  68. package/docs/api/classes/RBACAuditManager.md +2 -2
  69. package/docs/api/classes/RBACCache.md +1 -1
  70. package/docs/api/classes/RBACEngine.md +2 -2
  71. package/docs/api/classes/RBACError.md +1 -1
  72. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  73. package/docs/api/classes/SecureSupabaseClient.md +10 -10
  74. package/docs/api/classes/StorageUtils.md +1 -1
  75. package/docs/api/enums/FileCategory.md +1 -1
  76. package/docs/api/enums/LogLevel.md +1 -1
  77. package/docs/api/enums/RBACErrorCode.md +1 -1
  78. package/docs/api/enums/RPCFunction.md +1 -1
  79. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  80. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  81. package/docs/api/interfaces/AggregateConfig.md +1 -1
  82. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  83. package/docs/api/interfaces/AvatarProps.md +1 -1
  84. package/docs/api/interfaces/BadgeProps.md +1 -1
  85. package/docs/api/interfaces/ButtonProps.md +1 -1
  86. package/docs/api/interfaces/CalendarProps.md +1 -1
  87. package/docs/api/interfaces/CardProps.md +1 -1
  88. package/docs/api/interfaces/ColorPalette.md +1 -1
  89. package/docs/api/interfaces/ColorShade.md +1 -1
  90. package/docs/api/interfaces/ComplianceResult.md +1 -1
  91. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  92. package/docs/api/interfaces/DataRecord.md +1 -1
  93. package/docs/api/interfaces/DataTableAction.md +1 -1
  94. package/docs/api/interfaces/DataTableColumn.md +1 -1
  95. package/docs/api/interfaces/DataTableProps.md +1 -1
  96. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  97. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  98. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  99. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  100. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  101. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  102. package/docs/api/interfaces/ExportColumn.md +1 -1
  103. package/docs/api/interfaces/ExportOptions.md +1 -1
  104. package/docs/api/interfaces/FileDisplayProps.md +24 -11
  105. package/docs/api/interfaces/FileMetadata.md +1 -1
  106. package/docs/api/interfaces/FileReference.md +1 -1
  107. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  108. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  109. package/docs/api/interfaces/FileUploadProps.md +1 -1
  110. package/docs/api/interfaces/FooterProps.md +1 -1
  111. package/docs/api/interfaces/FormFieldProps.md +1 -1
  112. package/docs/api/interfaces/FormProps.md +1 -1
  113. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  114. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  115. package/docs/api/interfaces/InputProps.md +1 -1
  116. package/docs/api/interfaces/LabelProps.md +1 -1
  117. package/docs/api/interfaces/LoggerConfig.md +1 -1
  118. package/docs/api/interfaces/LoginFormProps.md +1 -1
  119. package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
  120. package/docs/api/interfaces/NavigationContextType.md +1 -1
  121. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  122. package/docs/api/interfaces/NavigationItem.md +1 -1
  123. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  124. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  125. package/docs/api/interfaces/Organisation.md +1 -1
  126. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  127. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  128. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  129. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  130. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  131. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  132. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  133. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  134. package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
  135. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  136. package/docs/api/interfaces/PaletteData.md +1 -1
  137. package/docs/api/interfaces/ParsedAddress.md +1 -1
  138. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  139. package/docs/api/interfaces/ProgressProps.md +1 -1
  140. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  141. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  142. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  143. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  144. package/docs/api/interfaces/QuickFix.md +1 -1
  145. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  146. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  147. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  148. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  149. package/docs/api/interfaces/RBACConfig.md +2 -2
  150. package/docs/api/interfaces/RBACContext.md +1 -1
  151. package/docs/api/interfaces/RBACLogger.md +1 -1
  152. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  153. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  154. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  155. package/docs/api/interfaces/RBACPermissionCheckResult.md +2 -2
  156. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  157. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  158. package/docs/api/interfaces/RBACResult.md +1 -1
  159. package/docs/api/interfaces/RBACRoleGrantParams.md +2 -2
  160. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  161. package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
  162. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  163. package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
  164. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  165. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  166. package/docs/api/interfaces/RBACRolesListResult.md +2 -2
  167. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  168. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  169. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  170. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  171. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  172. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  173. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  174. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  175. package/docs/api/interfaces/RouteConfig.md +2 -2
  176. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  177. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  178. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  179. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  180. package/docs/api/interfaces/SetupIssue.md +1 -1
  181. package/docs/api/interfaces/StorageConfig.md +1 -1
  182. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  183. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  184. package/docs/api/interfaces/StorageListOptions.md +1 -1
  185. package/docs/api/interfaces/StorageListResult.md +1 -1
  186. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  187. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  188. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  189. package/docs/api/interfaces/StyleImport.md +1 -1
  190. package/docs/api/interfaces/SwitchProps.md +1 -1
  191. package/docs/api/interfaces/TabsContentProps.md +1 -1
  192. package/docs/api/interfaces/TabsListProps.md +1 -1
  193. package/docs/api/interfaces/TabsProps.md +1 -1
  194. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  195. package/docs/api/interfaces/TextareaProps.md +1 -1
  196. package/docs/api/interfaces/ToastActionElement.md +1 -1
  197. package/docs/api/interfaces/ToastProps.md +1 -1
  198. package/docs/api/interfaces/UnifiedAuthContextType.md +60 -38
  199. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  200. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  201. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  202. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  203. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  204. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  205. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  206. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  207. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  208. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  209. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  210. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  211. package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
  212. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  213. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  214. package/docs/api/interfaces/UserEventAccess.md +1 -1
  215. package/docs/api/interfaces/UserMenuProps.md +1 -1
  216. package/docs/api/interfaces/UserProfile.md +1 -1
  217. package/docs/api/modules.md +194 -209
  218. package/docs/getting-started/cursor-rules.md +262 -0
  219. package/docs/getting-started/installation-guide.md +6 -1
  220. package/docs/getting-started/quick-start.md +6 -1
  221. package/docs/migration/MIGRATION_GUIDE.md +4 -4
  222. package/docs/migration/REACT_19_MIGRATION.md +227 -0
  223. package/docs/migration/database-changes-december-2025.md +2 -1
  224. package/docs/rbac/event-based-apps.md +124 -6
  225. package/docs/standards/README.md +39 -0
  226. package/docs/troubleshooting/migration.md +4 -4
  227. package/examples/PublicPages/PublicEventPage.tsx +1 -1
  228. package/package.json +11 -6
  229. package/scripts/audit-consuming-app.cjs +961 -0
  230. package/scripts/check-pace-core-compliance.cjs +315 -61
  231. package/scripts/install-cursor-rules.cjs +236 -0
  232. package/src/__tests__/helpers/test-providers.tsx +1 -1
  233. package/src/__tests__/helpers/test-utils.tsx +1 -1
  234. package/src/__tests__/rls-policies.test.ts +3 -1
  235. package/src/components/Badge/Badge.tsx +2 -4
  236. package/src/components/Button/Button.tsx +5 -4
  237. package/src/components/Calendar/Calendar.tsx +1 -1
  238. package/src/components/DataTable/DataTable.test.tsx +57 -93
  239. package/src/components/DataTable/DataTable.tsx +2 -2
  240. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
  241. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
  242. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
  243. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
  244. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
  245. package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
  246. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +88 -16
  247. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
  248. package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
  249. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  250. package/src/components/DataTable/components/DataTableCore.tsx +4 -7
  251. package/src/components/DataTable/components/DataTableModals.tsx +1 -1
  252. package/src/components/DataTable/components/EditableRow.tsx +1 -1
  253. package/src/components/DataTable/components/UnifiedTableBody.tsx +86 -17
  254. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
  255. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
  256. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
  257. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
  258. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
  259. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
  260. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
  261. package/src/components/DataTable/hooks/useColumnReordering.ts +2 -2
  262. package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
  263. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
  264. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
  265. package/src/components/Dialog/Dialog.tsx +6 -5
  266. package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
  267. package/src/components/EventSelector/EventSelector.tsx +1 -1
  268. package/src/components/FileDisplay/FileDisplay.test.tsx +4 -3
  269. package/src/components/FileDisplay/FileDisplay.tsx +16 -4
  270. package/src/components/Footer/Footer.tsx +1 -1
  271. package/src/components/Form/Form.test.tsx +36 -15
  272. package/src/components/Form/Form.tsx +30 -26
  273. package/src/components/Header/Header.tsx +1 -1
  274. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
  275. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
  276. package/src/components/Input/Input.tsx +28 -30
  277. package/src/components/Label/Label.tsx +1 -1
  278. package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
  279. package/src/components/LoginForm/LoginForm.test.tsx +42 -42
  280. package/src/components/LoginForm/LoginForm.tsx +8 -8
  281. package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
  282. package/src/components/NavigationMenu/NavigationMenu.tsx +2 -11
  283. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -1
  284. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
  285. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +75 -52
  286. package/src/components/PaceAppLayout/PaceAppLayout.tsx +98 -69
  287. package/src/components/PaceAppLayout/README.md +1 -1
  288. package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -8
  289. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
  290. package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
  291. package/src/components/Progress/Progress.tsx +1 -1
  292. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
  293. package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
  294. package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
  295. package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
  296. package/src/components/Select/Select.tsx +33 -22
  297. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
  298. package/src/components/Table/Table.tsx +1 -1
  299. package/src/components/Textarea/Textarea.tsx +27 -29
  300. package/src/components/Toast/Toast.tsx +1 -1
  301. package/src/components/Tooltip/Tooltip.tsx +1 -1
  302. package/src/components/UserMenu/UserMenu.tsx +1 -1
  303. package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
  304. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +14 -7
  305. package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
  306. package/src/hooks/public/usePublicEvent.ts +1 -1
  307. package/src/hooks/public/usePublicEventLogo.ts +1 -1
  308. package/src/hooks/public/usePublicRouteParams.ts +1 -1
  309. package/src/hooks/services/useAuthService.ts +21 -3
  310. package/src/hooks/services/useEventService.ts +21 -3
  311. package/src/hooks/services/useInactivityService.ts +21 -3
  312. package/src/hooks/services/useOrganisationService.ts +21 -3
  313. package/src/hooks/useDataTableState.ts +8 -18
  314. package/src/hooks/useFileDisplay.ts +10 -17
  315. package/src/hooks/useFocusManagement.ts +2 -2
  316. package/src/hooks/useFocusTrap.ts +4 -4
  317. package/src/hooks/useFormDialog.ts +8 -7
  318. package/src/hooks/useInactivityTracker.ts +1 -1
  319. package/src/hooks/usePermissionCache.ts +1 -1
  320. package/src/hooks/useSecureDataAccess.test.ts +16 -9
  321. package/src/hooks/useSecureDataAccess.ts +22 -6
  322. package/src/hooks/useToast.ts +2 -2
  323. package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
  324. package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
  325. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
  326. package/src/providers/services/EventServiceProvider.tsx +0 -8
  327. package/src/providers/services/UnifiedAuthProvider.tsx +196 -46
  328. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
  329. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +34 -40
  330. package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
  331. package/src/rbac/adapters.tsx +3 -22
  332. package/src/rbac/api.test.ts +2 -2
  333. package/src/rbac/api.ts +7 -1
  334. package/src/rbac/components/EnhancedNavigationMenu.tsx +3 -16
  335. package/src/rbac/components/NavigationGuard.tsx +2 -11
  336. package/src/rbac/components/NavigationProvider.tsx +1 -2
  337. package/src/rbac/components/PagePermissionGuard.tsx +1 -1
  338. package/src/rbac/components/PagePermissionProvider.tsx +1 -1
  339. package/src/rbac/components/PermissionEnforcer.tsx +46 -13
  340. package/src/rbac/components/RoleBasedRouter.tsx +1 -1
  341. package/src/rbac/components/SecureDataProvider.tsx +1 -2
  342. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
  343. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
  344. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
  345. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
  346. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
  347. package/src/rbac/engine.ts +14 -2
  348. package/src/rbac/hooks/index.ts +0 -3
  349. package/src/rbac/hooks/usePermissions.ts +51 -11
  350. package/src/rbac/hooks/useRBAC.ts +3 -13
  351. package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
  352. package/src/rbac/hooks/useResolvedScope.ts +58 -33
  353. package/src/rbac/hooks/useSecureSupabase.ts +4 -9
  354. package/src/rbac/secureClient.ts +43 -0
  355. package/src/services/EventService.ts +4 -57
  356. package/src/services/InactivityService.ts +127 -34
  357. package/src/services/OrganisationService.ts +68 -10
  358. package/src/utils/security/secureDataAccess.test.ts +31 -20
  359. package/src/utils/security/secureDataAccess.ts +4 -3
  360. package/dist/chunk-6C4YBBJM.js.map +0 -1
  361. package/dist/chunk-6LTQQAT6.js.map +0 -1
  362. package/dist/chunk-6TQDD426.js.map +0 -1
  363. package/dist/chunk-LOMZXPSN.js.map +0 -1
  364. package/dist/chunk-OETXORNB.js.map +0 -1
  365. package/dist/chunk-ULHIJK66.js.map +0 -1
  366. package/dist/chunk-VKB2CO4Z.js.map +0 -1
  367. package/dist/chunk-VRGWKHDB.js.map +0 -1
  368. package/dist/chunk-XNYQOL3Z.js.map +0 -1
  369. package/dist/chunk-XYXSXPUK.js.map +0 -1
  370. package/scripts/check-pace-core-compliance.js +0 -512
  371. package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
  372. package/src/utils/context/superAdminOverride.ts +0 -58
  373. /package/dist/{DataTable-WKRZD47S.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
  374. /package/dist/{UnifiedAuthProvider-FTSG5XH7.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
  375. /package/dist/{api-IHKALJZD.js.map → api-N774RPUA.js.map} +0 -0
  376. /package/dist/{chunk-QWWZ5CAQ.js.map → chunk-LXQLPRQ2.js.map} +0 -0
  377. /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
  378. /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
  379. /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
  380. /package/examples/{rbac → RBAC}/index.ts +0 -0
@@ -57,7 +57,7 @@
57
57
  * - Minimal DOM structure (native HTML element)
58
58
  *
59
59
  * @dependencies
60
- * - React 18+ - Hooks and refs
60
+ * - React 19+ - Hooks and refs
61
61
  * - Tailwind CSS - Styling
62
62
  */
63
63
 
@@ -165,12 +165,8 @@ describe('ProtectedRoute Component', () => {
165
165
 
166
166
  expect(screen.getByTestId('outlet')).toBeInTheDocument();
167
167
 
168
- // Wait a bit for the logger to be called (it's called during render)
169
- await waitFor(() => {
170
- expect(consoleDebugSpy).toHaveBeenCalledWith(
171
- expect.stringContaining('[DEBUG] [ProtectedRoute] Events available but none selected - allowing render so selector is visible')
172
- );
173
- }, { timeout: 1000 });
168
+ // Note: ProtectedRoute no longer logs this debug message - it just allows rendering
169
+ // The component allows rendering when events exist but none selected to make the selector visible
174
170
  });
175
171
 
176
172
  it('renders session restoration loader when session is restoring', () => {
@@ -461,9 +457,9 @@ describe('ProtectedRoute Component', () => {
461
457
  renderWithProviders(<ProtectedRoute requireEvent={true} />);
462
458
 
463
459
  expect(screen.getByTestId('outlet')).toBeInTheDocument();
464
- expect(consoleDebugSpy).toHaveBeenCalledWith(
465
- expect.stringContaining('[DEBUG] [ProtectedRoute] Events available but none selected - allowing render so selector is visible')
466
- );
460
+
461
+ // Note: ProtectedRoute no longer logs this debug message - it just allows rendering
462
+ // The component allows rendering when events exist but none selected to make the selector visible
467
463
  });
468
464
 
469
465
  it('renders outlet when event is selected', () => {
@@ -359,7 +359,6 @@ export function ProtectedRoute({
359
359
  // The event selector will be visible and user can select, or auto-selection will kick in
360
360
  if (!selectedEvent) {
361
361
  // Log for debugging - this is expected behavior, not an error
362
- logger.debug('ProtectedRoute', 'Events available but none selected - allowing render so selector is visible');
363
362
  return <Outlet />;
364
363
  }
365
364
 
@@ -58,7 +58,7 @@
58
58
  * - Error handling for invalid routes
59
59
  *
60
60
  * @dependencies
61
- * - React 18+ - Component framework
61
+ * - React 19+ - Component framework
62
62
  * - React Router - Routing integration
63
63
  * - Public hooks - Data access
64
64
  * - Tailwind CSS - Styling
@@ -102,7 +102,6 @@ export function PublicPageProvider({ children, appName }: PublicPageProviderProp
102
102
  return null;
103
103
  }
104
104
  const client = createClient<Database>(supabaseUrl, supabaseKey);
105
- logger.info('PublicPageProvider', 'Supabase client created successfully for public pages');
106
105
  return client;
107
106
  }, [supabaseUrl, supabaseKey]);
108
107
 
@@ -53,7 +53,7 @@ export interface UseSelectStateProps {
53
53
  export interface UseSelectEventsProps {
54
54
  state: SelectState;
55
55
  actions: SelectActions;
56
- selectRef: React.RefObject<HTMLElement>;
56
+ selectRef: React.RefObject<HTMLElement | null>;
57
57
  }
58
58
 
59
59
  export interface UseSelectSearchProps {
@@ -299,12 +299,13 @@ export const useSelectSearch = ({
299
299
  if (!React.isValidElement(child)) return child;
300
300
 
301
301
  // Check if this is a SelectItem by looking for the data-testid or value prop
302
- const isSelectItem = child.props &&
303
- (child.props['data-testid'] === 'select-item' ||
304
- child.props.value !== undefined);
302
+ const childProps = child.props as { 'data-testid'?: string; value?: unknown; children?: React.ReactNode; [key: string]: unknown };
303
+ const isSelectItem = childProps &&
304
+ (childProps['data-testid'] === 'select-item' ||
305
+ childProps.value !== undefined);
305
306
 
306
307
  if (isSelectItem) {
307
- const childText = React.Children.toArray(child.props.children).join(' ').toLowerCase();
308
+ const childText = React.Children.toArray(childProps.children).join(' ').toLowerCase();
308
309
  const searchLower = searchTerm.toLowerCase();
309
310
 
310
311
  if (childText.includes(searchLower)) {
@@ -313,8 +314,8 @@ export const useSelectSearch = ({
313
314
  return null;
314
315
  }
315
316
 
316
- if (child.props.children) {
317
- const filteredChildChildren = filterChildren(child.props.children);
317
+ if (childProps.children) {
318
+ const filteredChildChildren = filterChildren(childProps.children);
318
319
  if (React.Children.count(filteredChildChildren) > 0) {
319
320
  return React.cloneElement(child, {}, filteredChildChildren);
320
321
  }
@@ -359,8 +360,11 @@ const useSelectContext = () => {
359
360
  const getTextContent = (children: React.ReactNode): string => {
360
361
  if (typeof children === 'string') return children;
361
362
  if (typeof children === 'number') return children.toString();
362
- if (React.isValidElement(children) && children.props.children) {
363
- return getTextContent(children.props.children);
363
+ if (React.isValidElement(children)) {
364
+ const props = children.props as { children?: React.ReactNode; [key: string]: unknown };
365
+ if (props.children) {
366
+ return getTextContent(props.children);
367
+ }
364
368
  }
365
369
  if (Array.isArray(children)) {
366
370
  return children.map(getTextContent).join('');
@@ -443,7 +447,7 @@ export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectS
443
447
  const internalRef = React.useRef<HTMLFormElement>(null);
444
448
  const selectRef = React.useMemo(() => {
445
449
  if (ref && typeof ref === 'object' && 'current' in ref) {
446
- return ref as React.RefObject<HTMLFormElement>;
450
+ return ref as React.RefObject<HTMLFormElement | null>;
447
451
  }
448
452
  return internalRef;
449
453
  }, [ref]);
@@ -620,7 +624,7 @@ export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerPr
620
624
  const opensUpward = direction === 'up';
621
625
 
622
626
  // Use ref to store the latest handleClick to avoid re-creating the effect
623
- const handleClickRef = React.useRef<(e: React.MouseEvent) => void>();
627
+ const handleClickRef = React.useRef<(e: React.MouseEvent) => void>(undefined);
624
628
 
625
629
  const handleClick = React.useCallback((e: React.MouseEvent) => {
626
630
  if (disabled) {
@@ -687,22 +691,26 @@ export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerPr
687
691
  };
688
692
 
689
693
  if (asChild) {
690
- const childChildren = React.Children.toArray((children as React.ReactElement).props.children);
691
- const hasChevron = childChildren.some(child =>
692
- React.isValidElement(child) &&
693
- (child.type === ChevronDown ||
694
- (child.type === 'svg' && child.props['data-testid'] === 'chevron-down') ||
695
- (typeof child === 'object' && 'type' in child && typeof child.type === 'function' && child.type.name === 'ChevronDown'))
696
- );
694
+ const childElement = children as React.ReactElement<{ children?: React.ReactNode; [key: string]: unknown }>;
695
+ const childChildren = React.Children.toArray(childElement.props.children);
696
+ const hasChevron = childChildren.some(child => {
697
+ if (React.isValidElement(child)) {
698
+ const childProps = child.props as { 'data-testid'?: string; [key: string]: unknown };
699
+ return child.type === ChevronDown ||
700
+ (child.type === 'svg' && childProps['data-testid'] === 'chevron-down') ||
701
+ (typeof child === 'object' && 'type' in child && typeof child.type === 'function' && child.type.name === 'ChevronDown');
702
+ }
703
+ return false;
704
+ });
697
705
 
698
706
  // Merge child's className with triggerProps className
699
- const childClassName = (children as React.ReactElement).props.className;
707
+ const childClassName = (children as React.ReactElement<any>).props.className;
700
708
  const mergedClassName = cn(
701
709
  triggerProps.className,
702
710
  childClassName
703
711
  );
704
712
 
705
- return React.cloneElement(children as React.ReactElement, {
713
+ return React.cloneElement(children as React.ReactElement<any>, {
706
714
  ...triggerProps,
707
715
  className: mergedClassName,
708
716
  children: hasChevron ? childChildren : [
@@ -915,8 +923,11 @@ export const SelectItem = React.forwardRef<HTMLLIElement, SelectItemProps>(
915
923
  const getTextContent = (children: React.ReactNode): string => {
916
924
  if (typeof children === 'string') return children;
917
925
  if (typeof children === 'number') return children.toString();
918
- if (React.isValidElement(children) && children.props.children) {
919
- return getTextContent(children.props.children);
926
+ if (React.isValidElement(children)) {
927
+ const props = children.props as { children?: React.ReactNode; [key: string]: unknown };
928
+ if (props.children) {
929
+ return getTextContent(props.children);
930
+ }
920
931
  }
921
932
  if (Array.isArray(children)) {
922
933
  return children.map(getTextContent).join('');
@@ -30,7 +30,7 @@
30
30
  * - High contrast support
31
31
  *
32
32
  * @dependencies
33
- * - React 18+ - Component framework
33
+ * - React 19+ - Component framework
34
34
  * - LoadingSpinner - Spinner component
35
35
  * - Tailwind CSS - Styling
36
36
  */
@@ -85,7 +85,7 @@
85
85
  * - Caption for table description
86
86
  *
87
87
  * @dependencies
88
- * - React 18+ - Hooks and refs
88
+ * - React 19+ - Hooks and refs
89
89
  * - Tailwind CSS - Styling
90
90
  */
91
91
 
@@ -93,35 +93,33 @@ export interface TextareaProps
93
93
  * />
94
94
  * ```
95
95
  */
96
- const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
97
- ({ className, variant = 'default', size = 'md', error, ...props }, ref) => {
98
- return (
99
- <textarea
100
- className={cn(
101
- // Base styles (matching Input component)
102
- 'flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
103
-
104
- // Variant styles
105
- {
106
- 'border-input': variant === 'default' && !error,
107
- 'border-destructive focus-visible:ring-destructive': variant === 'destructive' || error,
108
- },
109
-
110
- // Size styles
111
- {
112
- 'min-h-[60px] px-2 py-1 text-xs': size === 'sm',
113
- 'min-h-[80px] px-3 py-2 text-sm': size === 'md',
114
- 'min-h-[100px] px-4 py-3 text-base': size === 'lg',
115
- },
116
-
117
- className
118
- )}
119
- ref={ref}
120
- {...props}
121
- />
122
- );
123
- }
124
- );
96
+ function Textarea({ className, variant = 'default', size = 'md', error, ref, ...props }: TextareaProps & { ref?: React.Ref<HTMLTextAreaElement> }) {
97
+ return (
98
+ <textarea
99
+ className={cn(
100
+ // Base styles (matching Input component)
101
+ 'flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
102
+
103
+ // Variant styles
104
+ {
105
+ 'border-input': variant === 'default' && !error,
106
+ 'border-destructive focus-visible:ring-destructive': variant === 'destructive' || error,
107
+ },
108
+
109
+ // Size styles
110
+ {
111
+ 'min-h-[60px] px-2 py-1 text-xs': size === 'sm',
112
+ 'min-h-[80px] px-3 py-2 text-sm': size === 'md',
113
+ 'min-h-[100px] px-4 py-3 text-base': size === 'lg',
114
+ },
115
+
116
+ className
117
+ )}
118
+ ref={ref}
119
+ {...props}
120
+ />
121
+ );
122
+ }
125
123
 
126
124
  Textarea.displayName = 'Textarea';
127
125
 
@@ -81,7 +81,7 @@
81
81
  * @dependencies
82
82
  * - @radix-ui/react-toast - Core toast functionality
83
83
  * - lucide-react - Icons
84
- * - React 18+ - Hooks and refs
84
+ * - React 19+ - Hooks and refs
85
85
  * - Tailwind CSS - Styling
86
86
  */
87
87
 
@@ -67,7 +67,7 @@
67
67
  *
68
68
  * @dependencies
69
69
  * - @radix-ui/react-tooltip - Core tooltip functionality
70
- * - React 18+ - Hooks and refs
70
+ * - React 19+ - Hooks and refs
71
71
  * - Tailwind CSS - Styling and animations
72
72
  */
73
73
 
@@ -90,7 +90,7 @@
90
90
  * - Optimized avatar rendering
91
91
  *
92
92
  * @dependencies
93
- * - React 18+ - Hooks and memo
93
+ * - React 19+ - Hooks and memo
94
94
  * - @supabase/supabase-js - User type
95
95
  * - lucide-react - Icons
96
96
  * - DropdownMenu components
@@ -1,6 +1,7 @@
1
1
  import React, { useState, useEffect } from 'react';
2
2
  import { screen, fireEvent, waitFor, act } from '@testing-library/react';
3
3
  import { renderHook } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
4
5
  import '@testing-library/jest-dom';
5
6
  import { vi, describe, it, beforeEach, afterEach } from 'vitest';
6
7
  import { renderWithProviders } from '../../__tests__/helpers/test-utils';
@@ -16,7 +17,7 @@ import { Button } from '../../components/Button/Button';
16
17
  import { Input } from '../../components/Input/Input';
17
18
  import { Form } from '../../components/Form/Form';
18
19
  import { z } from 'zod';
19
- import { useForm } from 'react-hook-form';
20
+ import { useForm, useFormState } from 'react-hook-form';
20
21
 
21
22
  // Mock the Logger module
22
23
  vi.mock('../../utils/core/logger', () => {
@@ -137,47 +138,57 @@ const ValidatedForm = ({ onSubmit }: ValidatedFormProps = {}) => {
137
138
  onSubmit={handleSubmit}
138
139
  >
139
140
  {(methods) => (
140
- <>
141
- <div>
142
- <Input
143
- {...methods.register('name')}
144
- data-testid="name-input"
145
- placeholder="Name"
146
- />
147
- {methods.formState.errors.name && (
148
- <div data-testid="name-error">{methods.formState.errors.name.message}</div>
149
- )}
150
- </div>
151
- <div>
152
- <Input
153
- {...methods.register('email')}
154
- data-testid="email-input"
155
- placeholder="Email"
156
- />
157
- {methods.formState.errors.email && (
158
- <div data-testid="email-error">{methods.formState.errors.email.message}</div>
159
- )}
160
- </div>
161
- <div>
162
- <Input
163
- {...methods.register('age', { valueAsNumber: true })}
164
- data-testid="age-input"
165
- type="number"
166
- placeholder="Age"
167
- />
168
- {methods.formState.errors.age && (
169
- <div data-testid="age-error">{methods.formState.errors.age.message}</div>
170
- )}
171
- </div>
172
- <Button type="submit" data-testid="submit-button">
173
- Submit
174
- </Button>
175
- </>
141
+ <ValidatedFormFields methods={methods} />
176
142
  )}
177
143
  </Form>
178
144
  );
179
145
  };
180
146
 
147
+ // Separate component to use useFormState hook for reactive error updates
148
+ const ValidatedFormFields = ({ methods }: { methods: ReturnType<typeof useForm> }) => {
149
+ // Use useFormState to subscribe to form state changes, including errors
150
+ const { errors } = useFormState({ control: methods.control });
151
+
152
+ return (
153
+ <>
154
+ <div>
155
+ <Input
156
+ {...methods.register('name')}
157
+ data-testid="name-input"
158
+ placeholder="Name"
159
+ />
160
+ {errors.name && (
161
+ <div data-testid="name-error">{errors.name.message}</div>
162
+ )}
163
+ </div>
164
+ <div>
165
+ <Input
166
+ {...methods.register('email')}
167
+ data-testid="email-input"
168
+ placeholder="Email"
169
+ />
170
+ {errors.email && (
171
+ <div data-testid="email-error">{errors.email.message}</div>
172
+ )}
173
+ </div>
174
+ <div>
175
+ <Input
176
+ {...methods.register('age', { valueAsNumber: true })}
177
+ data-testid="age-input"
178
+ type="number"
179
+ placeholder="Age"
180
+ />
181
+ {errors.age && (
182
+ <div data-testid="age-error">{errors.age.message}</div>
183
+ )}
184
+ </div>
185
+ <Button type="submit" data-testid="submit-button">
186
+ Submit
187
+ </Button>
188
+ </>
189
+ );
190
+ };
191
+
181
192
  // Test component for useKeyboardShortcuts integration
182
193
  const KeyboardShortcutsComponent = () => {
183
194
  const [isModalOpen, setIsModalOpen] = useState(false);
@@ -312,14 +323,13 @@ describe('Hooks Integration', () => {
312
323
  });
313
324
 
314
325
  it('handles rapid input changes correctly', async () => {
326
+ const user = userEvent.setup();
315
327
  renderWithProviders(<DebouncedSearch />);
316
328
 
317
329
  const searchInput = screen.getByTestId('search-input');
318
330
 
319
- // Type rapidly
320
- fireEvent.change(searchInput, { target: { value: 'a' } });
321
- fireEvent.change(searchInput, { target: { value: 'ab' } });
322
- fireEvent.change(searchInput, { target: { value: 'abc' } });
331
+ // Type rapidly using userEvent
332
+ await user.type(searchInput, 'abc');
323
333
 
324
334
  // Should not show results immediately
325
335
  expect(screen.queryByTestId('result-0')).not.toBeInTheDocument();
@@ -329,7 +339,10 @@ describe('Hooks Integration', () => {
329
339
  expect(screen.getByTestId('debounced-term')).toHaveTextContent('Debounced: abc');
330
340
  }, { timeout: 400 });
331
341
 
332
- expect(screen.getByTestId('result-0')).toHaveTextContent('Result for: abc');
342
+ // Wait for results to appear after debounced term updates
343
+ await waitFor(() => {
344
+ expect(screen.getByTestId('result-0')).toHaveTextContent('Result for: abc');
345
+ }, { timeout: 500 });
333
346
  });
334
347
  });
335
348
 
@@ -362,6 +375,7 @@ describe('Hooks Integration', () => {
362
375
 
363
376
  describe('useZodForm Integration', () => {
364
377
  it('validates form with real-time feedback', async () => {
378
+ const user = userEvent.setup();
365
379
  renderWithProviders(<ValidatedForm />);
366
380
 
367
381
  const nameInput = screen.getByTestId('name-input');
@@ -369,21 +383,32 @@ describe('Hooks Integration', () => {
369
383
  const ageInput = screen.getByTestId('age-input');
370
384
  const submitButton = screen.getByTestId('submit-button');
371
385
 
372
- // Test validation errors
373
- await act(async () => {
374
- fireEvent.change(nameInput, { target: { value: 'a' } });
375
- fireEvent.change(emailInput, { target: { value: 'invalid-email' } });
376
- fireEvent.change(ageInput, { target: { value: '15' } });
377
- });
378
- await act(async () => {
379
- fireEvent.click(submitButton);
380
- });
386
+ // Test validation errors - use userEvent for proper async handling
387
+ await user.clear(nameInput);
388
+ await user.type(nameInput, 'a');
389
+ await user.clear(emailInput);
390
+ await user.type(emailInput, 'invalid-email');
391
+ await user.clear(ageInput);
392
+ await user.type(ageInput, '15');
393
+
394
+ // Submit the form to trigger validation
395
+ await user.click(submitButton);
381
396
 
397
+ // Wait for validation errors to appear after form submission
398
+ // The render prop function needs to re-render when formState.errors changes
399
+ // Wait for all errors to appear together
382
400
  await waitFor(() => {
383
- expect(screen.getByTestId('name-error')).toHaveTextContent('Name must be at least 2 characters');
384
- expect(screen.getByTestId('email-error')).toHaveTextContent('Invalid email address');
385
- expect(screen.getByTestId('age-error')).toHaveTextContent('Must be at least 18 years old');
386
- });
401
+ const nameError = screen.queryByTestId('name-error');
402
+ const emailError = screen.queryByTestId('email-error');
403
+ const ageError = screen.queryByTestId('age-error');
404
+
405
+ expect(nameError).toBeInTheDocument();
406
+ expect(nameError).toHaveTextContent('Name must be at least 2 characters');
407
+ expect(emailError).toBeInTheDocument();
408
+ expect(emailError).toHaveTextContent('Invalid email address');
409
+ expect(ageError).toBeInTheDocument();
410
+ expect(ageError).toHaveTextContent('Must be at least 18 years old');
411
+ }, { timeout: 5000 });
387
412
  });
388
413
 
389
414
  it('submits form with valid data', async () => {
@@ -5,7 +5,7 @@ import { useUnifiedAuth } from '../../providers';
5
5
  import { useOrganisations } from '../../hooks/useOrganisations';
6
6
  import { testDataGenerators } from '../../__tests__/helpers/test-utils';
7
7
  import { useResolvedScope } from '../../rbac/hooks/useResolvedScope';
8
- import { useSuperAdminBypass } from '../../rbac/hooks/useSuperAdminBypass';
8
+ import { useOrganisationSecurity } from '../useOrganisationSecurity';
9
9
 
10
10
  // Mock dependencies
11
11
  vi.mock('../../providers', () => ({
@@ -28,8 +28,8 @@ vi.mock('../../rbac/hooks/useResolvedScope', () => ({
28
28
  useResolvedScope: vi.fn(),
29
29
  }));
30
30
 
31
- vi.mock('../../rbac/hooks/useSuperAdminBypass', () => ({
32
- useSuperAdminBypass: vi.fn(),
31
+ vi.mock('../useOrganisationSecurity', () => ({
32
+ useOrganisationSecurity: vi.fn(),
33
33
  }));
34
34
 
35
35
  const mockUseUnifiedAuth = {
@@ -114,10 +114,17 @@ describe('useSecureDataAccess', () => {
114
114
  isLoading: false,
115
115
  error: null,
116
116
  });
117
- // Default mock for useSuperAdminBypass - not super admin
118
- vi.mocked(useSuperAdminBypass).mockReturnValue({
119
- isSuperAdmin: false,
120
- });
117
+ // Default mock for useOrganisationSecurity - not super admin
118
+ vi.mocked(useOrganisationSecurity).mockReturnValue({
119
+ superAdminContext: { isSuperAdmin: false, hasGlobalAccess: false, canManageAllOrganisations: false },
120
+ validateOrganisationAccess: vi.fn(),
121
+ hasMinimumRole: vi.fn(),
122
+ canAccessChildOrganisations: vi.fn(),
123
+ checkPermission: vi.fn(),
124
+ getPermissions: vi.fn(),
125
+ logOrganisationAccess: vi.fn(),
126
+ canManageOrganisation: vi.fn(),
127
+ } as any);
121
128
  });
122
129
 
123
130
  describe('validateContext', () => {