@jmruthers/pace-core 0.5.193 → 0.6.2

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 (577) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +7 -1
  3. package/cursor-rules/00-pace-core-compliance.mdc +299 -0
  4. package/cursor-rules/01-standards-compliance.mdc +244 -0
  5. package/cursor-rules/02-project-structure.mdc +200 -0
  6. package/cursor-rules/03-solid-principles.mdc +222 -0
  7. package/cursor-rules/04-testing-standards.mdc +268 -0
  8. package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
  9. package/cursor-rules/06-code-quality.mdc +309 -0
  10. package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
  11. package/cursor-rules/08-markup-quality.mdc +452 -0
  12. package/cursor-rules/CHANGELOG.md +119 -0
  13. package/cursor-rules/README.md +192 -0
  14. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
  15. package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
  16. package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
  17. package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
  18. package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  19. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  20. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  21. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  22. package/dist/chunk-24UVZUZG.js.map +1 -0
  23. package/dist/{chunk-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
  24. package/dist/chunk-2UOI2FG5.js.map +1 -0
  25. package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
  26. package/dist/chunk-3XC4CPTD.js.map +1 -0
  27. package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
  28. package/dist/chunk-6J4GEEJR.js.map +1 -0
  29. package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
  30. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  31. package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
  32. package/dist/chunk-EHMR7VYL.js.map +1 -0
  33. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  34. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  35. package/dist/{chunk-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
  36. package/dist/chunk-FFQEQTNW.js.map +1 -0
  37. package/dist/chunk-FMUCXFII.js +76 -0
  38. package/dist/chunk-FMUCXFII.js.map +1 -0
  39. package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
  40. package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
  41. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  42. package/dist/chunk-L4OXEN46.js.map +1 -0
  43. package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
  44. package/dist/chunk-M43Y4SSO.js.map +1 -0
  45. package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
  46. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  47. package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
  48. package/dist/chunk-NECFR5MM.js.map +1 -0
  49. package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
  50. package/dist/chunk-SFZUDBL5.js.map +1 -0
  51. package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
  52. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  53. package/dist/components.d.ts +6 -6
  54. package/dist/components.js +15 -12
  55. package/dist/components.js.map +1 -1
  56. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  57. package/dist/hooks.d.ts +59 -126
  58. package/dist/hooks.js +19 -28
  59. package/dist/hooks.js.map +1 -1
  60. package/dist/index.d.ts +63 -16
  61. package/dist/index.js +23 -24
  62. package/dist/index.js.map +1 -1
  63. package/dist/providers.d.ts +21 -3
  64. package/dist/providers.js +2 -2
  65. package/dist/rbac/index.d.ts +146 -115
  66. package/dist/rbac/index.js +8 -11
  67. package/dist/theming/runtime.d.ts +1 -13
  68. package/dist/theming/runtime.js +1 -1
  69. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  70. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  71. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  72. package/dist/types.d.ts +2 -2
  73. package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
  74. package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
  75. package/dist/utils.d.ts +4 -5
  76. package/dist/utils.js +15 -15
  77. package/dist/utils.js.map +1 -1
  78. package/docs/api/README.md +7 -1
  79. package/docs/api/classes/ColumnFactory.md +8 -8
  80. package/docs/api/classes/InvalidScopeError.md +4 -4
  81. package/docs/api/classes/Logger.md +1 -1
  82. package/docs/api/classes/MissingUserContextError.md +4 -4
  83. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  84. package/docs/api/classes/PermissionDeniedError.md +4 -4
  85. package/docs/api/classes/RBACAuditManager.md +1 -1
  86. package/docs/api/classes/RBACCache.md +1 -1
  87. package/docs/api/classes/RBACEngine.md +1 -1
  88. package/docs/api/classes/RBACError.md +4 -4
  89. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  90. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  91. package/docs/api/classes/StorageUtils.md +1 -1
  92. package/docs/api/enums/FileCategory.md +1 -1
  93. package/docs/api/enums/LogLevel.md +1 -1
  94. package/docs/api/enums/RBACErrorCode.md +1 -1
  95. package/docs/api/enums/RPCFunction.md +1 -1
  96. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  97. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  98. package/docs/api/interfaces/AggregateConfig.md +4 -4
  99. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  100. package/docs/api/interfaces/AvatarProps.md +1 -1
  101. package/docs/api/interfaces/BadgeProps.md +9 -2
  102. package/docs/api/interfaces/ButtonProps.md +7 -4
  103. package/docs/api/interfaces/CalendarProps.md +8 -5
  104. package/docs/api/interfaces/CardProps.md +8 -5
  105. package/docs/api/interfaces/ColorPalette.md +1 -1
  106. package/docs/api/interfaces/ColorShade.md +1 -1
  107. package/docs/api/interfaces/ComplianceResult.md +1 -1
  108. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  109. package/docs/api/interfaces/DataRecord.md +1 -1
  110. package/docs/api/interfaces/DataTableAction.md +24 -21
  111. package/docs/api/interfaces/DataTableColumn.md +31 -31
  112. package/docs/api/interfaces/DataTableProps.md +1 -1
  113. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  114. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  115. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  116. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  117. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  118. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  119. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  120. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  121. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  122. package/docs/api/interfaces/ExportColumn.md +1 -1
  123. package/docs/api/interfaces/ExportOptions.md +8 -8
  124. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  125. package/docs/api/interfaces/FileMetadata.md +1 -1
  126. package/docs/api/interfaces/FileReference.md +1 -1
  127. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  128. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  129. package/docs/api/interfaces/FileUploadProps.md +26 -23
  130. package/docs/api/interfaces/FooterProps.md +10 -8
  131. package/docs/api/interfaces/FormFieldProps.md +10 -10
  132. package/docs/api/interfaces/FormProps.md +1 -1
  133. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  134. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  135. package/docs/api/interfaces/InputProps.md +7 -4
  136. package/docs/api/interfaces/LabelProps.md +1 -1
  137. package/docs/api/interfaces/LoggerConfig.md +1 -1
  138. package/docs/api/interfaces/LoginFormProps.md +14 -11
  139. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  140. package/docs/api/interfaces/NavigationContextType.md +1 -1
  141. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  142. package/docs/api/interfaces/NavigationItem.md +11 -11
  143. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  144. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  145. package/docs/api/interfaces/Organisation.md +1 -1
  146. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  147. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  148. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  149. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  150. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  151. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  152. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  153. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  154. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  155. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  156. package/docs/api/interfaces/PaletteData.md +1 -1
  157. package/docs/api/interfaces/ParsedAddress.md +1 -1
  158. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  159. package/docs/api/interfaces/ProgressProps.md +1 -1
  160. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  161. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  162. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  163. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  164. package/docs/api/interfaces/QuickFix.md +1 -1
  165. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  166. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  167. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  168. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  169. package/docs/api/interfaces/RBACConfig.md +1 -1
  170. package/docs/api/interfaces/RBACContext.md +1 -1
  171. package/docs/api/interfaces/RBACLogger.md +1 -1
  172. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  173. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  174. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  175. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  176. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  177. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  178. package/docs/api/interfaces/RBACResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  181. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  182. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  183. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  184. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  185. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  186. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  187. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  188. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  189. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  190. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  191. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  192. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  193. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  194. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  195. package/docs/api/interfaces/RouteConfig.md +1 -1
  196. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  197. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  198. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  199. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  200. package/docs/api/interfaces/SetupIssue.md +1 -1
  201. package/docs/api/interfaces/StorageConfig.md +1 -1
  202. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  203. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  204. package/docs/api/interfaces/StorageListOptions.md +1 -1
  205. package/docs/api/interfaces/StorageListResult.md +1 -1
  206. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  207. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  208. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  209. package/docs/api/interfaces/StyleImport.md +1 -1
  210. package/docs/api/interfaces/SwitchProps.md +1 -1
  211. package/docs/api/interfaces/TabsContentProps.md +1 -1
  212. package/docs/api/interfaces/TabsListProps.md +1 -1
  213. package/docs/api/interfaces/TabsProps.md +1 -1
  214. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  215. package/docs/api/interfaces/TextareaProps.md +1 -1
  216. package/docs/api/interfaces/ToastActionElement.md +4 -1
  217. package/docs/api/interfaces/ToastProps.md +1 -1
  218. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  219. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  220. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  221. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  222. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  223. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  224. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  225. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  226. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  227. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  228. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  229. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  230. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  231. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  232. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  233. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  234. package/docs/api/interfaces/UserEventAccess.md +14 -11
  235. package/docs/api/interfaces/UserMenuProps.md +8 -6
  236. package/docs/api/interfaces/UserProfile.md +1 -1
  237. package/docs/api/modules.md +575 -634
  238. package/docs/architecture/database-schema-requirements.md +161 -0
  239. package/docs/core-concepts/rbac-system.md +3 -3
  240. package/docs/documentation-index.md +2 -4
  241. package/docs/getting-started/cursor-rules.md +263 -0
  242. package/docs/getting-started/installation-guide.md +6 -1
  243. package/docs/getting-started/quick-start.md +6 -1
  244. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  245. package/docs/migration/MIGRATION_GUIDE.md +6 -28
  246. package/docs/migration/README.md +52 -6
  247. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  248. package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -0
  249. package/docs/migration/database-changes-december-2025.md +3 -3
  250. package/docs/rbac/event-based-apps.md +1 -1
  251. package/docs/rbac/getting-started.md +1 -1
  252. package/docs/rbac/quick-start.md +1 -1
  253. package/docs/standards/README.md +40 -0
  254. package/docs/troubleshooting/migration.md +4 -4
  255. package/examples/PublicPages/PublicEventPage.tsx +1 -1
  256. package/package.json +12 -6
  257. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  258. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  259. package/scripts/audit/core/checks/bundle.cjs +142 -0
  260. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +737 -691
  261. package/scripts/audit/core/checks/config.cjs +54 -0
  262. package/scripts/audit/core/checks/coverage.cjs +84 -0
  263. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  264. package/scripts/audit/core/checks/documentation.cjs +203 -0
  265. package/scripts/audit/core/checks/environment.cjs +128 -0
  266. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  267. package/scripts/audit/core/checks/forms.cjs +172 -0
  268. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  269. package/scripts/audit/core/checks/hooks.cjs +334 -0
  270. package/scripts/audit/core/checks/imports.cjs +244 -0
  271. package/scripts/audit/core/checks/performance.cjs +325 -0
  272. package/scripts/audit/core/checks/routes.cjs +117 -0
  273. package/scripts/audit/core/checks/state.cjs +130 -0
  274. package/scripts/audit/core/checks/structure.cjs +65 -0
  275. package/scripts/audit/core/checks/style.cjs +584 -0
  276. package/scripts/audit/core/checks/testing.cjs +122 -0
  277. package/scripts/audit/core/checks/typescript.cjs +61 -0
  278. package/scripts/audit/core/scanner.cjs +199 -0
  279. package/scripts/audit/core/utils.cjs +137 -0
  280. package/scripts/audit/index.cjs +223 -0
  281. package/scripts/audit/reporters/console.cjs +151 -0
  282. package/scripts/audit/reporters/json.cjs +54 -0
  283. package/scripts/audit/reporters/markdown.cjs +124 -0
  284. package/scripts/audit-consuming-app.cjs +86 -0
  285. package/scripts/build-docs/build-decision.js +240 -0
  286. package/scripts/build-docs/cache-utils.js +105 -0
  287. package/scripts/build-docs/content-normalization.js +150 -0
  288. package/scripts/build-docs/file-utils.js +105 -0
  289. package/scripts/build-docs/git-utils.js +86 -0
  290. package/scripts/build-docs/hash-utils.js +116 -0
  291. package/scripts/build-docs/typedoc-runner.js +220 -0
  292. package/scripts/build-docs-incremental.js +77 -913
  293. package/scripts/install-cursor-rules.cjs +236 -0
  294. package/scripts/utils/command-runner.js +16 -11
  295. package/scripts/validate-formats.js +61 -56
  296. package/scripts/validate-master.js +74 -69
  297. package/scripts/validate-pre-publish.js +70 -65
  298. package/src/__tests__/helpers/test-providers.tsx +1 -1
  299. package/src/__tests__/helpers/test-utils.tsx +1 -1
  300. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  301. package/src/components/Alert/Alert.test.tsx +12 -18
  302. package/src/components/Alert/Alert.tsx +5 -7
  303. package/src/components/Avatar/Avatar.test.tsx +4 -4
  304. package/src/components/Badge/Badge.tsx +16 -4
  305. package/src/components/Button/Button.tsx +27 -4
  306. package/src/components/Calendar/Calendar.tsx +9 -3
  307. package/src/components/Card/Card.tsx +4 -0
  308. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  309. package/src/components/Checkbox/Checkbox.tsx +2 -2
  310. package/src/components/DataTable/DataTable.test.tsx +57 -93
  311. package/src/components/DataTable/DataTable.tsx +40 -6
  312. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  313. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
  314. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
  315. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  316. package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
  317. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  318. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  319. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  320. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  321. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  322. package/src/components/DataTable/components/DataTableCore.tsx +200 -561
  323. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  324. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  325. package/src/components/DataTable/components/DataTableModals.tsx +9 -1
  326. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  327. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  328. package/src/components/DataTable/components/EditFields.tsx +307 -0
  329. package/src/components/DataTable/components/EditableRow.tsx +9 -1
  330. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  331. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  332. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  333. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  334. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  335. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  336. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  337. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  338. package/src/components/DataTable/components/UnifiedTableBody.tsx +62 -852
  339. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  340. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  341. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
  342. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
  343. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
  344. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
  345. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
  346. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
  347. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
  348. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  349. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  350. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  351. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  352. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  353. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  354. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  355. package/src/components/DataTable/hooks/useColumnReordering.ts +14 -2
  356. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  357. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  358. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  359. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  360. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  361. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
  362. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  363. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  364. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  365. package/src/components/DataTable/styles.ts +6 -6
  366. package/src/components/DataTable/types.ts +6 -10
  367. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  368. package/src/components/DataTable/utils/debugTools.ts +18 -113
  369. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  370. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  371. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  372. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  373. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  374. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
  375. package/src/components/Dialog/Dialog.tsx +8 -7
  376. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  377. package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
  378. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  379. package/src/components/ErrorBoundary/index.ts +27 -2
  380. package/src/components/EventSelector/EventSelector.tsx +4 -1
  381. package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
  382. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  383. package/src/components/FileUpload/FileUpload.tsx +22 -2
  384. package/src/components/Footer/Footer.test.tsx +16 -16
  385. package/src/components/Footer/Footer.tsx +15 -12
  386. package/src/components/Form/Form.test.tsx +36 -15
  387. package/src/components/Form/Form.tsx +31 -26
  388. package/src/components/Header/Header.tsx +22 -11
  389. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
  390. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
  391. package/src/components/Input/Input.test.tsx +2 -2
  392. package/src/components/Input/Input.tsx +36 -34
  393. package/src/components/Label/Label.tsx +1 -1
  394. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  395. package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
  396. package/src/components/LoginForm/LoginForm.test.tsx +42 -42
  397. package/src/components/LoginForm/LoginForm.tsx +12 -8
  398. package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
  399. package/src/components/NavigationMenu/types.ts +56 -0
  400. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  401. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  402. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
  403. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
  404. package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
  405. package/src/components/PaceAppLayout/README.md +1 -1
  406. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  407. package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
  408. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
  409. package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
  410. package/src/components/Progress/Progress.tsx +1 -1
  411. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  412. package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
  413. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  414. package/src/components/Select/Select.tsx +95 -438
  415. package/src/components/Select/context.ts +23 -0
  416. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  417. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  418. package/src/components/Select/hooks/useSelectState.ts +104 -0
  419. package/src/components/Select/index.ts +9 -1
  420. package/src/components/Select/types.ts +123 -0
  421. package/src/components/Select/utils/text.ts +26 -0
  422. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +5 -6
  423. package/src/components/Switch/Switch.tsx +4 -4
  424. package/src/components/Table/Table.tsx +1 -1
  425. package/src/components/Tabs/Tabs.tsx +1 -1
  426. package/src/components/Textarea/Textarea.tsx +27 -29
  427. package/src/components/Toast/Toast.tsx +5 -1
  428. package/src/components/Tooltip/Tooltip.tsx +3 -3
  429. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  430. package/src/components/UserMenu/UserMenu.tsx +22 -19
  431. package/src/components/index.ts +2 -2
  432. package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
  433. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  434. package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
  435. package/src/hooks/index.ts +1 -2
  436. package/src/hooks/public/usePublicEvent.ts +5 -1
  437. package/src/hooks/public/usePublicEventLogo.ts +5 -1
  438. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  439. package/src/hooks/public/usePublicRouteParams.ts +5 -1
  440. package/src/hooks/services/useAuth.ts +32 -0
  441. package/src/hooks/services/useCurrentEvent.ts +6 -0
  442. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  443. package/src/hooks/useDataTableState.ts +8 -18
  444. package/src/hooks/useDebounce.ts +9 -0
  445. package/src/hooks/useEventTheme.ts +6 -0
  446. package/src/hooks/useFileDisplay.ts +4 -0
  447. package/src/hooks/useFileReference.ts +25 -7
  448. package/src/hooks/useFileUrl.ts +11 -1
  449. package/src/hooks/useFocusManagement.ts +16 -2
  450. package/src/hooks/useFocusTrap.ts +7 -4
  451. package/src/hooks/useFormDialog.ts +8 -7
  452. package/src/hooks/useInactivityTracker.ts +4 -1
  453. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  454. package/src/hooks/useOrganisationPermissions.ts +4 -0
  455. package/src/hooks/useOrganisationSecurity.ts +4 -0
  456. package/src/hooks/usePerformanceMonitor.ts +4 -0
  457. package/src/hooks/usePermissionCache.ts +8 -1
  458. package/src/hooks/useQueryCache.ts +12 -1
  459. package/src/hooks/useSessionRestoration.ts +4 -0
  460. package/src/hooks/useStorage.ts +4 -0
  461. package/src/hooks/useToast.ts +3 -3
  462. package/src/index.ts +2 -1
  463. package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
  464. package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
  465. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
  466. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  467. package/src/providers/services/EventServiceProvider.tsx +18 -0
  468. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  469. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  470. package/src/providers/services/UnifiedAuthProvider.tsx +58 -22
  471. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
  472. package/src/rbac/README.md +1 -1
  473. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
  474. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  475. package/src/rbac/adapters.tsx +14 -5
  476. package/src/rbac/api.ts +100 -67
  477. package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
  478. package/src/rbac/components/NavigationGuard.tsx +1 -1
  479. package/src/rbac/components/NavigationProvider.tsx +5 -2
  480. package/src/rbac/components/PagePermissionGuard.tsx +158 -18
  481. package/src/rbac/components/PagePermissionProvider.tsx +1 -1
  482. package/src/rbac/components/PermissionEnforcer.tsx +1 -1
  483. package/src/rbac/components/RoleBasedRouter.tsx +6 -2
  484. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  485. package/src/rbac/components/SecureDataProvider.tsx +21 -6
  486. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  487. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  488. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  489. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  490. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  491. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  492. package/src/rbac/engine.ts +38 -14
  493. package/src/rbac/hooks/permissions/index.ts +7 -0
  494. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  495. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  496. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  497. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  498. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  499. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  500. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  501. package/src/rbac/hooks/useCan.test.ts +71 -64
  502. package/src/rbac/hooks/usePermissions.ts +14 -995
  503. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  504. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  505. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  506. package/src/rbac/permissions.ts +0 -30
  507. package/src/rbac/secureClient.ts +212 -61
  508. package/src/rbac/types.ts +8 -0
  509. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  510. package/src/theming/parseEventColours.ts +5 -19
  511. package/src/types/vitest-globals.d.ts +51 -26
  512. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  513. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  514. package/src/utils/__tests__/index.unit.test.ts +2 -2
  515. package/src/utils/audit/audit.ts +0 -3
  516. package/src/utils/core/cn.ts +1 -1
  517. package/src/utils/file-reference/index.ts +53 -1
  518. package/src/utils/formatting/formatting.ts +8 -18
  519. package/src/utils/index.ts +0 -1
  520. package/src/utils/security/secureDataAccess.test.ts +31 -20
  521. package/src/utils/security/secureDataAccess.ts +4 -3
  522. package/dist/chunk-6C4YBBJM.js +0 -628
  523. package/dist/chunk-6C4YBBJM.js.map +0 -1
  524. package/dist/chunk-7D4SUZUM.js 2.map +0 -1
  525. package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
  526. package/dist/chunk-7EQTDTTJ.js.map +0 -1
  527. package/dist/chunk-7FLMSG37.js 2.map +0 -1
  528. package/dist/chunk-7FLMSG37.js.map +0 -1
  529. package/dist/chunk-BC4IJKSL.js.map +0 -1
  530. package/dist/chunk-E3SPN4VZ.js +0 -12917
  531. package/dist/chunk-E3SPN4VZ.js.map +0 -1
  532. package/dist/chunk-E66EQZE6 5.js +0 -37
  533. package/dist/chunk-E66EQZE6.js 2.map +0 -1
  534. package/dist/chunk-HWIIPPNI.js.map +0 -1
  535. package/dist/chunk-I7PSE6JW 5.js +0 -191
  536. package/dist/chunk-I7PSE6JW.js 2.map +0 -1
  537. package/dist/chunk-I7PSE6JW.js.map +0 -1
  538. package/dist/chunk-IIELH4DL.js.map +0 -1
  539. package/dist/chunk-KNC55RTG.js 5.map +0 -1
  540. package/dist/chunk-KNC55RTG.js.map +0 -1
  541. package/dist/chunk-KQCRWDSA.js 5.map +0 -1
  542. package/dist/chunk-LFNCN2SP.js +0 -412
  543. package/dist/chunk-LFNCN2SP.js 2.map +0 -1
  544. package/dist/chunk-LFNCN2SP.js.map +0 -1
  545. package/dist/chunk-LMC26NLJ 2.js +0 -84
  546. package/dist/chunk-NOAYCWCX.js +0 -4993
  547. package/dist/chunk-NOAYCWCX.js.map +0 -1
  548. package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
  549. package/dist/chunk-QWWZ5CAQ.js.map +0 -1
  550. package/dist/chunk-QXHPKYJV 3.js +0 -113
  551. package/dist/chunk-R77UEZ4E.js +0 -68
  552. package/dist/chunk-R77UEZ4E.js.map +0 -1
  553. package/dist/chunk-SQGMNID3.js.map +0 -1
  554. package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
  555. package/dist/chunk-XNXXZ43G.js.map +0 -1
  556. package/dist/chunk-ZSAAAMVR 6.js +0 -25
  557. package/dist/components.js 5.map +0 -1
  558. package/dist/styles/index 2.js +0 -12
  559. package/dist/styles/index.js 5.map +0 -1
  560. package/dist/theming/runtime 5.js +0 -19
  561. package/dist/theming/runtime.js 5.map +0 -1
  562. package/docs/api/classes/ErrorBoundary.md +0 -144
  563. package/docs/migration/quick-migration-guide.md +0 -356
  564. package/docs/migration/service-architecture.md +0 -281
  565. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  566. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  567. package/src/hooks/useSecureDataAccess.ts +0 -666
  568. /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  569. /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  570. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  571. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  572. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  573. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  574. /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
  575. /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
  576. /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
  577. /package/examples/{rbac → RBAC}/index.ts +0 -0
@@ -8,10 +8,12 @@
8
8
  * Tests form rendering, validation, submission, and accessibility.
9
9
  */
10
10
 
11
+ import React from 'react';
11
12
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
12
13
  import { render, screen, waitFor, act } from '@testing-library/react';
13
14
  import userEvent from '@testing-library/user-event';
14
15
  import { z } from 'zod';
16
+ import { useWatch, useFormState, useFormContext } from 'react-hook-form';
15
17
  import { Form, FormField } from './index';
16
18
  import { renderWithProviders } from '../../__tests__/helpers/test-utils';
17
19
 
@@ -108,27 +110,46 @@ describe('Form Component', () => {
108
110
 
109
111
  it('shares live form state with render prop children', async () => {
110
112
  const onSubmit = vi.fn();
111
-
112
- renderWithProviders(
113
- <Form onSubmit={onSubmit} defaultValues={{ name: '' }}>
114
- {({ formState, watch }) => (
115
- <>
116
- <span data-testid="dirty-flag">{formState.isDirty ? 'dirty' : 'pristine'}</span>
117
- <span data-testid="watched-value">{watch('name')}</span>
118
- <FormField name="name" label="Name" />
119
- </>
120
- )}
121
- </Form>
122
- );
113
+ const InnerComponent = () => {
114
+ // useWatch and useFormState must be called inside FormProvider context
115
+ // useFormState subscribes to form state changes and triggers re-renders
116
+ const { control } = useFormContext();
117
+ const nameValue = useWatch({ name: 'name', control });
118
+ const { isDirty } = useFormState({ control });
119
+
120
+ return (
121
+ <>
122
+ <span data-testid="dirty-flag">{isDirty ? 'dirty' : 'pristine'}</span>
123
+ <span data-testid="watched-value">{nameValue || ''}</span>
124
+ <FormField name="name" label="Name" />
125
+ </>
126
+ );
127
+ };
128
+
129
+ const TestComponent = () => {
130
+ return (
131
+ <Form onSubmit={onSubmit} defaultValues={{ name: '' }}>
132
+ <InnerComponent />
133
+ </Form>
134
+ );
135
+ };
136
+
137
+ renderWithProviders(<TestComponent />);
123
138
 
124
139
  const input = screen.getByLabelText('Name');
125
140
  expect(screen.getByTestId('dirty-flag')).toHaveTextContent('pristine');
141
+
142
+ // Type into the input field
143
+ await user.clear(input);
126
144
  await user.type(input, 'Jane');
127
145
 
146
+ // Wait for both the watched value and dirty state to update
128
147
  await waitFor(() => {
129
- expect(screen.getByTestId('dirty-flag')).toHaveTextContent('dirty');
130
- expect(screen.getByTestId('watched-value')).toHaveTextContent('Jane');
131
- });
148
+ const watchedValue = screen.getByTestId('watched-value');
149
+ const dirtyFlag = screen.getByTestId('dirty-flag');
150
+ expect(watchedValue).toHaveTextContent('Jane');
151
+ expect(dirtyFlag).toHaveTextContent('dirty');
152
+ }, { timeout: 5000 });
132
153
  });
133
154
  });
134
155
 
@@ -68,7 +68,7 @@
68
68
  * - react-hook-form - Form state management
69
69
  * - @hookform/resolvers/zod - Zod validation integration
70
70
  * - zod - Schema validation
71
- * - React 18+ - Hooks and context
71
+ * - React 19+ - Hooks and context
72
72
  */
73
73
 
74
74
  import React from 'react';
@@ -153,6 +153,7 @@ export function Form<TFieldValues extends FieldValues = FieldValues>({
153
153
  resolver: schema ? zodResolver(schema) : undefined,
154
154
  defaultValues,
155
155
  mode,
156
+ shouldUnregister: false,
156
157
  });
157
158
 
158
159
  const handleSubmit = methods.handleSubmit(onSubmit, onError);
@@ -224,7 +225,7 @@ export interface FormFieldProps<
224
225
  field: ControllerRenderProps<TFieldValues, TName>;
225
226
  fieldState: ControllerFieldState;
226
227
  formState: UseFormStateReturn<TFieldValues>;
227
- }) => React.ReactElement;
228
+ }) => React.ReactElement<any>;
228
229
 
229
230
  /**
230
231
  * Test ID
@@ -318,13 +319,7 @@ export function FormField<
318
319
  'data-testid': testId,
319
320
  className,
320
321
  }: FormFieldProps<TFieldValues, TName>) {
321
- const { control, formState: { errors } } = useFormContext<TFieldValues>();
322
- const fieldError = errors[name];
323
-
324
- // Safely extract error message
325
- const errorMessage = fieldError && typeof fieldError === 'object' && 'message' in fieldError
326
- ? String(fieldError.message)
327
- : undefined;
322
+ const { control } = useFormContext<TFieldValues>();
328
323
 
329
324
  return (
330
325
  <div className={cn("space-y-2", className)}>
@@ -344,32 +339,42 @@ export function FormField<
344
339
  control={control}
345
340
  rules={validation}
346
341
  render={(props) => {
342
+ const { field, fieldState } = props;
343
+ const fieldError = fieldState.error;
344
+
345
+ // Safely extract error message
346
+ const errorMessage = fieldError && typeof fieldError === 'object' && 'message' in fieldError
347
+ ? String(fieldError.message)
348
+ : undefined;
349
+
347
350
  if (render) {
348
351
  return render(props);
349
352
  }
350
353
 
351
354
  return (
352
- <input
353
- {...props.field}
354
- id={name}
355
- type={type}
356
- placeholder={placeholder}
357
- data-testid={testId}
358
- className={cn(
359
- "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium 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",
360
- fieldError && "border-destructive focus-visible:ring-destructive"
355
+ <>
356
+ <input
357
+ {...field}
358
+ id={name}
359
+ type={type}
360
+ placeholder={placeholder}
361
+ data-testid={testId}
362
+ aria-label={label || placeholder || name}
363
+ className={cn(
364
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium 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",
365
+ fieldError && "border-destructive focus-visible:ring-destructive"
366
+ )}
367
+ {...inputProps}
368
+ />
369
+ {errorMessage && (
370
+ <p className="text-destructive" role="alert">
371
+ {errorMessage}
372
+ </p>
361
373
  )}
362
- {...inputProps}
363
- />
374
+ </>
364
375
  );
365
376
  }}
366
377
  />
367
-
368
- {errorMessage && (
369
- <p className="text-destructive" role="alert">
370
- {errorMessage}
371
- </p>
372
- )}
373
378
  </div>
374
379
  );
375
380
  }
@@ -42,10 +42,10 @@
42
42
  * ]}
43
43
  * currentPath="/dashboard"
44
44
  * actions={
45
- * <div className="flex gap-2">
45
+ * <>
46
46
  * <Button variant="outline">Export</Button>
47
47
  * <Button>New Item</Button>
48
- * </div>
48
+ * </>
49
49
  * }
50
50
  * user={currentUser}
51
51
  * onSignOut={handleSignOut}
@@ -79,7 +79,7 @@
79
79
  *
80
80
  * @dependencies
81
81
  * - @supabase/supabase-js - User authentication
82
- * - React 18+ - Component framework
82
+ * - React 19+ - Component framework
83
83
  * - Tailwind CSS - Styling
84
84
  * - NavigationMenu component
85
85
  * - UserMenu component
@@ -199,10 +199,10 @@ export interface HeaderProps {
199
199
  *
200
200
  * function HeaderWithActions() {
201
201
  * const customActions = (
202
- * <div className="flex items-center gap-2">
202
+ * <>
203
203
  * <Button variant="outline" size="sm">Export</Button>
204
204
  * <Button size="sm">New Item</Button>
205
- * </div>
205
+ * </>
206
206
  * );
207
207
  *
208
208
  * return (
@@ -238,6 +238,13 @@ export interface HeaderProps {
238
238
  *
239
239
  * @since 0.1.0
240
240
  */
241
+ /**
242
+ * Header component for application layouts.
243
+ * Provides navigation, user menu, organisation/event selectors, and customizable branding.
244
+ *
245
+ * @param props - Header configuration
246
+ * @returns The rendered header
247
+ */
241
248
  export function Header({
242
249
  logoUrl,
243
250
  logoAlt = 'Logo',
@@ -278,7 +285,7 @@ export function Header({
278
285
  "w-full border-b border-main-200 h-16 shadow-sm bg-main-100 ",
279
286
  className
280
287
  )} role="banner">
281
- <nav className="px-4 w-[min(var(--app-width),100%)] mx-auto flex items-center gap-4 h-full">
288
+ <nav className="px-4 w-[min(var(--app-width),100%)] mx-auto grid grid-cols-[auto_1fr_auto_auto_auto_auto] items-center gap-4 h-full">
282
289
  {/* Logo */}
283
290
  {logo ? (
284
291
  logoHref ? (
@@ -333,10 +340,7 @@ export function Header({
333
340
  itemsPreFiltered={true}
334
341
  />
335
342
  )}
336
-
337
343
 
338
- {/* Right side: Organisation Selector, Event Selector, Actions, and User Menu */}
339
- <div className="flex items-center gap-4 ml-auto">
340
344
  {/* Organisation Selector - Only show if user has organisations */}
341
345
  {showOrgSelector ? (
342
346
  <OrganisationSelectorConditional />
@@ -346,7 +350,15 @@ export function Header({
346
350
  {showEventSelector ? (
347
351
  <EventSelector
348
352
  placeholder="Select event"
349
- className="w-96"
353
+ className={cn(
354
+ "w-96",
355
+ // If both org selector and actions exist, EventSelector uses 1 column
356
+ // If only one exists, EventSelector spans 2 columns
357
+ // If neither exists, EventSelector spans 3 columns
358
+ showOrgSelector && actions ? "col-span-1" :
359
+ showOrgSelector || actions ? "col-span-2" :
360
+ "col-span-3"
361
+ )}
350
362
  data-testid="event-selector"
351
363
  />
352
364
  ) : null}
@@ -367,7 +379,6 @@ export function Header({
367
379
  />
368
380
  )
369
381
  )}
370
- </div>
371
382
 
372
383
  </nav>
373
384
  </header>
@@ -65,7 +65,7 @@ vi.mock('lucide-react', () => ({
65
65
  }));
66
66
 
67
67
  describe('InactivityWarningModal Component', () => {
68
- const defaultProps = {
68
+ const baseProps = {
69
69
  isOpen: true,
70
70
  timeRemaining: 45,
71
71
  onStaySignedIn: vi.fn(),
@@ -74,20 +74,20 @@ describe('InactivityWarningModal Component', () => {
74
74
 
75
75
  describe('Rendering', () => {
76
76
  it('renders when isOpen is true', () => {
77
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
77
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
78
78
 
79
79
  expect(screen.getByTestId('dialog')).toBeInTheDocument();
80
80
  expect(screen.getByTestId('inactivity-warning-modal')).toBeInTheDocument();
81
81
  });
82
82
 
83
83
  it('does not render when isOpen is false', () => {
84
- renderWithProviders(<InactivityWarningModal {...defaultProps} isOpen={false} />);
84
+ renderWithProviders(<InactivityWarningModal {...baseProps} isOpen={false} />);
85
85
 
86
86
  expect(screen.queryByTestId('dialog')).not.toBeInTheDocument();
87
87
  });
88
88
 
89
89
  it('renders with default title and description', () => {
90
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
90
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
91
91
 
92
92
  expect(screen.getByText('Session Timeout Warning')).toBeInTheDocument();
93
93
  expect(screen.getByText("You've been inactive for a while. Your session will expire soon for security reasons.")).toBeInTheDocument();
@@ -95,7 +95,7 @@ describe('InactivityWarningModal Component', () => {
95
95
 
96
96
  it('renders with custom title and description', () => {
97
97
  const customProps = {
98
- ...defaultProps,
98
+ ...baseProps,
99
99
  title: 'Custom Warning',
100
100
  description: 'Custom description text',
101
101
  };
@@ -107,58 +107,58 @@ describe('InactivityWarningModal Component', () => {
107
107
  });
108
108
 
109
109
  it('renders with custom className', () => {
110
- renderWithProviders(<InactivityWarningModal {...defaultProps} className="custom-modal" />);
110
+ renderWithProviders(<InactivityWarningModal {...baseProps} className="custom-modal" />);
111
111
 
112
112
  const modal = screen.getByTestId('inactivity-warning-modal');
113
113
  expect(modal).toHaveClass('custom-modal');
114
114
  });
115
115
 
116
116
  it('renders countdown timer with correct time format', () => {
117
- renderWithProviders(<InactivityWarningModal {...defaultProps} timeRemaining={125} />);
117
+ renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={125} />);
118
118
 
119
119
  expect(screen.getByText('02:05')).toBeInTheDocument();
120
120
  });
121
121
 
122
122
  it('renders countdown timer with single digit minutes', () => {
123
- renderWithProviders(<InactivityWarningModal {...defaultProps} timeRemaining={65} />);
123
+ renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={65} />);
124
124
 
125
125
  expect(screen.getByText('01:05')).toBeInTheDocument();
126
126
  });
127
127
 
128
128
  it('renders countdown timer with zero seconds', () => {
129
- renderWithProviders(<InactivityWarningModal {...defaultProps} timeRemaining={60} />);
129
+ renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={60} />);
130
130
 
131
131
  expect(screen.getByText('01:00')).toBeInTheDocument();
132
132
  });
133
133
 
134
134
  it('renders countdown timer with single digit seconds', () => {
135
- renderWithProviders(<InactivityWarningModal {...defaultProps} timeRemaining={5} />);
135
+ renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={5} />);
136
136
 
137
137
  expect(screen.getByText('00:05')).toBeInTheDocument();
138
138
  });
139
139
 
140
140
  it('renders countdown timer with zero time', () => {
141
- renderWithProviders(<InactivityWarningModal {...defaultProps} timeRemaining={0} />);
141
+ renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={0} />);
142
142
 
143
143
  expect(screen.getByText('00:00')).toBeInTheDocument();
144
144
  });
145
145
 
146
146
  it('renders action buttons with correct text', () => {
147
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
147
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
148
148
 
149
149
  expect(screen.getByRole('button', { name: 'Stay Signed In' })).toBeInTheDocument();
150
150
  expect(screen.getByRole('button', { name: 'Sign Out Now' })).toBeInTheDocument();
151
151
  });
152
152
 
153
153
  it('renders icons correctly', () => {
154
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
154
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
155
155
 
156
156
  expect(screen.getByTestId('alert-triangle-icon')).toBeInTheDocument();
157
157
  expect(screen.getByTestId('clock-icon')).toBeInTheDocument();
158
158
  });
159
159
 
160
160
  it('renders additional security information', () => {
161
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
161
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
162
162
 
163
163
  expect(screen.getByText("For security reasons, you'll be automatically signed out after 30 minutes of inactivity.")).toBeInTheDocument();
164
164
  });
@@ -178,7 +178,7 @@ describe('InactivityWarningModal Component', () => {
178
178
 
179
179
  testCases.forEach(({ input, expected }) => {
180
180
  const { unmount } = renderWithProviders(
181
- <InactivityWarningModal {...defaultProps} timeRemaining={input} />
181
+ <InactivityWarningModal {...baseProps} timeRemaining={input} />
182
182
  );
183
183
 
184
184
  expect(screen.getByText(expected)).toBeInTheDocument();
@@ -188,12 +188,12 @@ describe('InactivityWarningModal Component', () => {
188
188
 
189
189
  it('updates display time when timeRemaining prop changes', () => {
190
190
  const { rerender } = renderWithProviders(
191
- <InactivityWarningModal {...defaultProps} timeRemaining={60} />
191
+ <InactivityWarningModal {...baseProps} timeRemaining={60} />
192
192
  );
193
193
 
194
194
  expect(screen.getByText('01:00')).toBeInTheDocument();
195
195
 
196
- rerender(<InactivityWarningModal {...defaultProps} timeRemaining={30} />);
196
+ rerender(<InactivityWarningModal {...baseProps} timeRemaining={30} />);
197
197
 
198
198
  expect(screen.getByText('00:30')).toBeInTheDocument();
199
199
  });
@@ -205,7 +205,7 @@ describe('InactivityWarningModal Component', () => {
205
205
  const onStaySignedIn = vi.fn();
206
206
 
207
207
  renderWithProviders(
208
- <InactivityWarningModal {...defaultProps} onStaySignedIn={onStaySignedIn} />
208
+ <InactivityWarningModal {...baseProps} onStaySignedIn={onStaySignedIn} />
209
209
  );
210
210
 
211
211
  await user.click(screen.getByRole('button', { name: 'Stay Signed In' }));
@@ -218,7 +218,7 @@ describe('InactivityWarningModal Component', () => {
218
218
  const onSignOutNow = vi.fn();
219
219
 
220
220
  renderWithProviders(
221
- <InactivityWarningModal {...defaultProps} onSignOutNow={onSignOutNow} />
221
+ <InactivityWarningModal {...baseProps} onSignOutNow={onSignOutNow} />
222
222
  );
223
223
 
224
224
  await user.click(screen.getByRole('button', { name: 'Sign Out Now' }));
@@ -231,7 +231,7 @@ describe('InactivityWarningModal Component', () => {
231
231
  const onStaySignedIn = vi.fn();
232
232
 
233
233
  renderWithProviders(
234
- <InactivityWarningModal {...defaultProps} onStaySignedIn={onStaySignedIn} />
234
+ <InactivityWarningModal {...baseProps} onStaySignedIn={onStaySignedIn} />
235
235
  );
236
236
 
237
237
  // Simulate dialog close (this would normally happen via the Dialog component)
@@ -244,7 +244,7 @@ describe('InactivityWarningModal Component', () => {
244
244
 
245
245
  describe('Button Styling and Props', () => {
246
246
  it('applies correct styling to Stay Signed In button', () => {
247
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
247
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
248
248
 
249
249
  const stayButton = screen.getByRole('button', { name: 'Stay Signed In' });
250
250
  expect(stayButton).toHaveClass('bg-main-600', 'hover:bg-main-700', 'text-main-50');
@@ -252,7 +252,7 @@ describe('InactivityWarningModal Component', () => {
252
252
  });
253
253
 
254
254
  it('applies correct styling to Sign Out Now button', () => {
255
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
255
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
256
256
 
257
257
  const signOutButton = screen.getByRole('button', { name: 'Sign Out Now' });
258
258
  expect(signOutButton).toHaveAttribute('data-variant', 'outline');
@@ -263,7 +263,7 @@ describe('InactivityWarningModal Component', () => {
263
263
 
264
264
  describe('Accessibility', () => {
265
265
  it('has proper ARIA attributes', () => {
266
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
266
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
267
267
 
268
268
  const dialog = screen.getByTestId('dialog');
269
269
  expect(dialog).toHaveAttribute('role', 'dialog');
@@ -277,14 +277,14 @@ describe('InactivityWarningModal Component', () => {
277
277
  });
278
278
 
279
279
  it('has accessible button labels', () => {
280
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
280
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
281
281
 
282
282
  expect(screen.getByRole('button', { name: 'Stay Signed In' })).toBeInTheDocument();
283
283
  expect(screen.getByRole('button', { name: 'Sign Out Now' })).toBeInTheDocument();
284
284
  });
285
285
 
286
286
  it('has proper heading structure', () => {
287
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
287
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
288
288
 
289
289
  const title = screen.getByRole('heading', { level: 2 });
290
290
  expect(title).toBeInTheDocument();
@@ -292,7 +292,7 @@ describe('InactivityWarningModal Component', () => {
292
292
  });
293
293
 
294
294
  it('provides descriptive text for screen readers', () => {
295
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
295
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
296
296
 
297
297
  expect(screen.getByText('Time remaining before automatic logout')).toBeInTheDocument();
298
298
  expect(screen.getByText("For security reasons, you'll be automatically signed out after 30 minutes of inactivity.")).toBeInTheDocument();
@@ -301,7 +301,7 @@ describe('InactivityWarningModal Component', () => {
301
301
 
302
302
  describe('Dialog Configuration', () => {
303
303
  it('configures dialog with correct props', () => {
304
- renderWithProviders(<InactivityWarningModal {...defaultProps} />);
304
+ renderWithProviders(<InactivityWarningModal {...baseProps} />);
305
305
 
306
306
  const modal = screen.getByTestId('inactivity-warning-modal');
307
307
  expect(modal).toHaveClass('sm:max-w-md');
@@ -311,7 +311,7 @@ describe('InactivityWarningModal Component', () => {
311
311
 
312
312
  it('applies custom className to dialog content', () => {
313
313
  renderWithProviders(
314
- <InactivityWarningModal {...defaultProps} className="custom-dialog-class" />
314
+ <InactivityWarningModal {...baseProps} className="custom-dialog-class" />
315
315
  );
316
316
 
317
317
  const modal = screen.getByTestId('inactivity-warning-modal');
@@ -321,14 +321,14 @@ describe('InactivityWarningModal Component', () => {
321
321
 
322
322
  describe('Edge Cases', () => {
323
323
  it('handles negative time values gracefully', () => {
324
- renderWithProviders(<InactivityWarningModal {...defaultProps} timeRemaining={-5} />);
324
+ renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={-5} />);
325
325
 
326
326
  // Should still render, but with negative time formatted as the component actually does it
327
327
  expect(screen.getByText('-1:-5')).toBeInTheDocument();
328
328
  });
329
329
 
330
330
  it('handles very large time values', () => {
331
- renderWithProviders(<InactivityWarningModal {...defaultProps} timeRemaining={999999} />);
331
+ renderWithProviders(<InactivityWarningModal {...baseProps} timeRemaining={999999} />);
332
332
 
333
333
  expect(screen.getByText('16666:39')).toBeInTheDocument();
334
334
  });
@@ -353,7 +353,7 @@ describe('InactivityWarningModal Component', () => {
353
353
  it('handles empty string title and description', () => {
354
354
  renderWithProviders(
355
355
  <InactivityWarningModal
356
- {...defaultProps}
356
+ {...baseProps}
357
357
  title=""
358
358
  description=""
359
359
  />
@@ -370,19 +370,19 @@ describe('InactivityWarningModal Component', () => {
370
370
  describe('Integration', () => {
371
371
  it('works with rapid prop changes', () => {
372
372
  const { rerender } = renderWithProviders(
373
- <InactivityWarningModal {...defaultProps} timeRemaining={60} />
373
+ <InactivityWarningModal {...baseProps} timeRemaining={60} />
374
374
  );
375
375
 
376
376
  expect(screen.getByText('01:00')).toBeInTheDocument();
377
377
 
378
378
  // Rapid changes
379
- rerender(<InactivityWarningModal {...defaultProps} timeRemaining={30} />);
379
+ rerender(<InactivityWarningModal {...baseProps} timeRemaining={30} />);
380
380
  expect(screen.getByText('00:30')).toBeInTheDocument();
381
381
 
382
- rerender(<InactivityWarningModal {...defaultProps} timeRemaining={15} />);
382
+ rerender(<InactivityWarningModal {...baseProps} timeRemaining={15} />);
383
383
  expect(screen.getByText('00:15')).toBeInTheDocument();
384
384
 
385
- rerender(<InactivityWarningModal {...defaultProps} timeRemaining={0} />);
385
+ rerender(<InactivityWarningModal {...baseProps} timeRemaining={0} />);
386
386
  expect(screen.getByText('00:00')).toBeInTheDocument();
387
387
  });
388
388
 
@@ -393,7 +393,7 @@ describe('InactivityWarningModal Component', () => {
393
393
 
394
394
  renderWithProviders(
395
395
  <InactivityWarningModal
396
- {...defaultProps}
396
+ {...baseProps}
397
397
  onStaySignedIn={onStaySignedIn}
398
398
  onSignOutNow={onSignOutNow}
399
399
  />
@@ -413,17 +413,17 @@ describe('InactivityWarningModal Component', () => {
413
413
 
414
414
  it('maintains state consistency during re-renders', () => {
415
415
  const { rerender } = renderWithProviders(
416
- <InactivityWarningModal {...defaultProps} timeRemaining={45} />
416
+ <InactivityWarningModal {...baseProps} timeRemaining={45} />
417
417
  );
418
418
 
419
419
  expect(screen.getByText('00:45')).toBeInTheDocument();
420
420
 
421
421
  // Re-render with same props
422
- rerender(<InactivityWarningModal {...defaultProps} timeRemaining={45} />);
422
+ rerender(<InactivityWarningModal {...baseProps} timeRemaining={45} />);
423
423
  expect(screen.getByText('00:45')).toBeInTheDocument();
424
424
 
425
425
  // Re-render with different props
426
- rerender(<InactivityWarningModal {...defaultProps} timeRemaining={30} />);
426
+ rerender(<InactivityWarningModal {...baseProps} timeRemaining={30} />);
427
427
  expect(screen.getByText('00:30')).toBeInTheDocument();
428
428
  });
429
429
  });
@@ -447,7 +447,7 @@ describe('InactivityWarningModal Component', () => {
447
447
 
448
448
  it('handles invalid timeRemaining values', () => {
449
449
  const invalidTimeProps = {
450
- ...defaultProps,
450
+ ...baseProps,
451
451
  timeRemaining: NaN,
452
452
  };
453
453
 
@@ -41,7 +41,7 @@
41
41
  * - Optimized timer management
42
42
  *
43
43
  * @dependencies
44
- * - React 18+ - Hooks and effects
44
+ * - React 19+ - Hooks and effects
45
45
  * - Dialog components - Modal functionality
46
46
  * - Tailwind CSS v4 - Styling
47
47
  */
@@ -420,14 +420,14 @@ describe('InputGroup Component', () => {
420
420
  // Accessibility tests
421
421
  describe('Accessibility', () => {
422
422
  it('forwards ref correctly', () => {
423
- const ref = React.createRef<HTMLDivElement>();
423
+ const ref = React.createRef<HTMLFieldSetElement>();
424
424
  renderWithProviders(
425
425
  <InputGroup ref={ref}>
426
426
  <Input placeholder="Input 1" />
427
427
  </InputGroup>
428
428
  );
429
429
 
430
- expect(ref.current).toBeInstanceOf(HTMLDivElement);
430
+ expect(ref.current).toBeInstanceOf(HTMLFieldSetElement);
431
431
  });
432
432
 
433
433
  it('maintains proper focus order', async () => {