@jmruthers/pace-core 0.6.1 → 0.6.3

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 (549) hide show
  1. package/CHANGELOG.md +88 -10
  2. package/cursor-rules/00-pace-core-compliance.mdc +46 -87
  3. package/cursor-rules/01-standards-compliance.mdc +16 -47
  4. package/cursor-rules/02-project-structure.mdc +4 -4
  5. package/cursor-rules/03-solid-principles.mdc +45 -164
  6. package/cursor-rules/04-testing-standards.mdc +22 -69
  7. package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
  8. package/cursor-rules/06-code-quality.mdc +42 -125
  9. package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
  10. package/cursor-rules/08-markup-quality.mdc +452 -0
  11. package/cursor-rules/CHANGELOG.md +18 -0
  12. package/cursor-rules/README.md +2 -1
  13. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-Cb34EQs3.d.ts} +63 -1
  14. package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
  15. package/dist/{DataTable-DQ7RSOHE.js → DataTable-THFPBKTP.js} +12 -10
  16. package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DEMpysFR.d.ts} +394 -171
  17. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +30 -8
  18. package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
  19. package/dist/{api-N774RPUA.js → api-IAGWF3ZG.js} +10 -10
  20. package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
  21. package/dist/{chunk-JBKQ3SAO.js → chunk-2T2IG7T7.js} +107 -57
  22. package/dist/chunk-2T2IG7T7.js.map +1 -0
  23. package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
  24. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  25. package/dist/{chunk-3XTALGJF.js → chunk-6Z7LTB3D.js} +69 -240
  26. package/dist/chunk-6Z7LTB3D.js.map +1 -0
  27. package/dist/{chunk-4ZC4GX36.js → chunk-CNCQDFLN.js} +199 -46
  28. package/dist/chunk-CNCQDFLN.js.map +1 -0
  29. package/dist/chunk-DGUM43GV.js +11 -0
  30. package/dist/{chunk-BYFSK72L.js → chunk-DWUBLJJM.js} +361 -187
  31. package/dist/chunk-DWUBLJJM.js.map +1 -0
  32. package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
  33. package/dist/chunk-FFQEQTNW.js.map +1 -0
  34. package/dist/chunk-FMUCXFII.js +76 -0
  35. package/dist/chunk-FMUCXFII.js.map +1 -0
  36. package/dist/{chunk-4N5C5XZU.js → chunk-HFZBI76P.js} +4 -4
  37. package/dist/chunk-HFZBI76P.js.map +1 -0
  38. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  39. package/dist/chunk-L4OXEN46.js.map +1 -0
  40. package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
  41. package/dist/chunk-M43Y4SSO.js.map +1 -0
  42. package/dist/{chunk-I7PSE6JW.js → chunk-M7MPQISP.js} +3 -76
  43. package/dist/chunk-M7MPQISP.js.map +1 -0
  44. package/dist/chunk-PQBSKX33.js +7793 -0
  45. package/dist/chunk-PQBSKX33.js.map +1 -0
  46. package/dist/chunk-QRPVRXYT.js +226 -0
  47. package/dist/chunk-QRPVRXYT.js.map +1 -0
  48. package/dist/{chunk-KNC55RTG.js → chunk-RWEBCB47.js} +194 -416
  49. package/dist/chunk-RWEBCB47.js.map +1 -0
  50. package/dist/{chunk-XM25TVIE.js → chunk-YDQHOZNA.js} +843 -388
  51. package/dist/chunk-YDQHOZNA.js.map +1 -0
  52. package/dist/{chunk-GLK6VM3F.js → chunk-ZNIWI3UC.js} +739 -737
  53. package/dist/chunk-ZNIWI3UC.js.map +1 -0
  54. package/dist/components.d.ts +5 -5
  55. package/dist/components.js +18 -16
  56. package/dist/components.js.map +1 -1
  57. package/dist/contextValidator-3JNZKUTX.js +9 -0
  58. package/dist/contextValidator-3JNZKUTX.js.map +1 -0
  59. package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
  60. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  61. package/dist/hooks.d.ts +55 -122
  62. package/dist/hooks.js +10 -13
  63. package/dist/hooks.js.map +1 -1
  64. package/dist/index.d.ts +60 -13
  65. package/dist/index.js +30 -25
  66. package/dist/index.js.map +1 -1
  67. package/dist/providers.d.ts +21 -3
  68. package/dist/providers.js +4 -3
  69. package/dist/rbac/index.d.ts +210 -139
  70. package/dist/rbac/index.js +17 -13
  71. package/dist/styles/index.js +1 -1
  72. package/dist/theming/runtime.d.ts +1 -13
  73. package/dist/theming/runtime.js +2 -2
  74. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  75. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  76. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  77. package/dist/types.d.ts +2 -2
  78. package/dist/types.js +1 -1
  79. package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +38 -17
  80. package/dist/utils.d.ts +4 -5
  81. package/dist/utils.js +17 -19
  82. package/dist/utils.js.map +1 -1
  83. package/docs/api/README.md +21 -17
  84. package/docs/api/modules.md +4191 -2967
  85. package/docs/architecture/database-schema-requirements.md +161 -0
  86. package/docs/components/context-selector.md +126 -0
  87. package/docs/core-concepts/rbac-system.md +3 -3
  88. package/docs/documentation-index.md +2 -4
  89. package/docs/getting-started/cursor-rules.md +2 -1
  90. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  91. package/docs/migration/MIGRATION_GUIDE.md +2 -24
  92. package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
  93. package/docs/migration/README.md +52 -6
  94. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  95. package/docs/migration/database-changes-december-2025.md +3 -3
  96. package/docs/pace-mint-fix-auto-selection.md +218 -0
  97. package/docs/pace-mint-rbac-setup.md +391 -0
  98. package/docs/rbac/event-based-apps.md +1 -1
  99. package/docs/rbac/getting-started.md +1 -1
  100. package/docs/rbac/quick-start.md +1 -1
  101. package/docs/rbac/secure-client-protection.md +330 -0
  102. package/docs/standards/README.md +1 -0
  103. package/package.json +4 -3
  104. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  105. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  106. package/scripts/audit/core/checks/bundle.cjs +142 -0
  107. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +784 -685
  108. package/scripts/audit/core/checks/config.cjs +54 -0
  109. package/scripts/audit/core/checks/coverage.cjs +84 -0
  110. package/scripts/audit/core/checks/dependencies.cjs +985 -0
  111. package/scripts/audit/core/checks/documentation.cjs +268 -0
  112. package/scripts/audit/core/checks/environment.cjs +116 -0
  113. package/scripts/audit/core/checks/error-handling.cjs +340 -0
  114. package/scripts/audit/core/checks/forms.cjs +172 -0
  115. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  116. package/scripts/audit/core/checks/hooks.cjs +334 -0
  117. package/scripts/audit/core/checks/imports.cjs +244 -0
  118. package/scripts/audit/core/checks/performance.cjs +325 -0
  119. package/scripts/audit/core/checks/routes.cjs +117 -0
  120. package/scripts/audit/core/checks/state.cjs +130 -0
  121. package/scripts/audit/core/checks/structure.cjs +65 -0
  122. package/scripts/audit/core/checks/style.cjs +584 -0
  123. package/scripts/audit/core/checks/testing.cjs +122 -0
  124. package/scripts/audit/core/checks/typescript.cjs +61 -0
  125. package/scripts/audit/core/scanner.cjs +199 -0
  126. package/scripts/audit/core/utils.cjs +137 -0
  127. package/scripts/audit/index.cjs +223 -0
  128. package/scripts/audit/reporters/console.cjs +151 -0
  129. package/scripts/audit/reporters/json.cjs +54 -0
  130. package/scripts/audit/reporters/markdown.cjs +124 -0
  131. package/scripts/audit-consuming-app.cjs +61 -936
  132. package/scripts/build-docs/build-decision.js +240 -0
  133. package/scripts/build-docs/cache-utils.js +105 -0
  134. package/scripts/build-docs/content-normalization.js +150 -0
  135. package/scripts/build-docs/file-utils.js +105 -0
  136. package/scripts/build-docs/git-utils.js +86 -0
  137. package/scripts/build-docs/hash-utils.js +116 -0
  138. package/scripts/build-docs/typedoc-runner.js +220 -0
  139. package/scripts/build-docs-incremental.js +77 -913
  140. package/scripts/utils/command-runner.js +16 -11
  141. package/scripts/validate-formats.js +61 -56
  142. package/scripts/validate-master.js +74 -69
  143. package/scripts/validate-pre-publish.js +70 -65
  144. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  145. package/src/components/Alert/Alert.test.tsx +12 -18
  146. package/src/components/Alert/Alert.tsx +5 -7
  147. package/src/components/Avatar/Avatar.test.tsx +4 -4
  148. package/src/components/Badge/Badge.tsx +14 -0
  149. package/src/components/Button/Button.tsx +22 -0
  150. package/src/components/Calendar/Calendar.tsx +8 -2
  151. package/src/components/Card/Card.tsx +4 -0
  152. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  153. package/src/components/Checkbox/Checkbox.tsx +2 -2
  154. package/src/components/ContextSelector/ContextSelector.tsx +384 -0
  155. package/src/components/ContextSelector/index.ts +3 -0
  156. package/src/components/DataTable/DataTable.tsx +38 -4
  157. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  158. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
  159. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  160. package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
  161. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  162. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  163. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  164. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  165. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  166. package/src/components/DataTable/components/DataTableCore.tsx +196 -554
  167. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  168. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  169. package/src/components/DataTable/components/DataTableModals.tsx +8 -0
  170. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  171. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  172. package/src/components/DataTable/components/EditFields.tsx +307 -0
  173. package/src/components/DataTable/components/EditableRow.tsx +8 -0
  174. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  175. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  176. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  177. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  178. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  179. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  180. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  181. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  182. package/src/components/DataTable/components/UnifiedTableBody.tsx +63 -851
  183. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  184. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  185. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  186. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  187. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  188. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  189. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  190. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  191. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  192. package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
  193. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  194. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  195. package/src/components/DataTable/hooks/useDataTablePermissions.ts +127 -33
  196. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  197. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  198. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  199. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  200. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  201. package/src/components/DataTable/styles.ts +6 -6
  202. package/src/components/DataTable/types.ts +6 -10
  203. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  204. package/src/components/DataTable/utils/debugTools.ts +18 -113
  205. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  206. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  207. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  208. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  209. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  210. package/src/components/Dialog/Dialog.tsx +31 -3
  211. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  212. package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
  213. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  214. package/src/components/ErrorBoundary/index.ts +27 -2
  215. package/src/components/FileDisplay/FileDisplay.tsx +74 -28
  216. package/src/components/FileUpload/FileUpload.tsx +22 -2
  217. package/src/components/Footer/Footer.test.tsx +16 -16
  218. package/src/components/Footer/Footer.tsx +14 -11
  219. package/src/components/Form/Form.tsx +1 -0
  220. package/src/components/Header/Header.test.tsx +43 -73
  221. package/src/components/Header/Header.tsx +59 -49
  222. package/src/components/Input/Input.test.tsx +2 -2
  223. package/src/components/Input/Input.tsx +8 -4
  224. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  225. package/src/components/LoginForm/LoginForm.tsx +4 -0
  226. package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
  227. package/src/components/NavigationMenu/types.ts +56 -0
  228. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  229. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
  230. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
  231. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
  232. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +13 -11
  233. package/src/components/PaceAppLayout/PaceAppLayout.tsx +167 -44
  234. package/src/components/PaceAppLayout/README.md +14 -17
  235. package/src/components/PaceAppLayout/test-setup.tsx +3 -4
  236. package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
  237. package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
  238. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  239. package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
  240. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  241. package/src/components/Select/Select.tsx +80 -434
  242. package/src/components/Select/context.ts +23 -0
  243. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  244. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  245. package/src/components/Select/hooks/useSelectState.ts +104 -0
  246. package/src/components/Select/index.ts +9 -1
  247. package/src/components/Select/types.ts +123 -0
  248. package/src/components/Select/utils/text.ts +26 -0
  249. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
  250. package/src/components/Switch/Switch.tsx +4 -4
  251. package/src/components/Tabs/Tabs.tsx +1 -1
  252. package/src/components/Toast/Toast.tsx +4 -0
  253. package/src/components/Tooltip/Tooltip.tsx +2 -2
  254. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  255. package/src/components/UserMenu/UserMenu.tsx +21 -18
  256. package/src/components/index.ts +7 -7
  257. package/src/eslint-rules/pace-core-compliance.cjs +106 -0
  258. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  259. package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
  260. package/src/hooks/index.ts +1 -2
  261. package/src/hooks/public/usePublicEvent.ts +4 -0
  262. package/src/hooks/public/usePublicEventLogo.ts +4 -0
  263. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  264. package/src/hooks/public/usePublicRouteParams.ts +4 -0
  265. package/src/hooks/services/useAuth.ts +32 -0
  266. package/src/hooks/services/useCurrentEvent.ts +6 -0
  267. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  268. package/src/hooks/useAppConfig.ts +15 -30
  269. package/src/hooks/useDebounce.ts +9 -0
  270. package/src/hooks/useEventTheme.ts +6 -0
  271. package/src/hooks/useFileDisplay.ts +81 -50
  272. package/src/hooks/useFileReference.ts +25 -7
  273. package/src/hooks/useFileUrl.ts +11 -1
  274. package/src/hooks/useFocusManagement.ts +14 -0
  275. package/src/hooks/useFocusTrap.ts +3 -0
  276. package/src/hooks/useInactivityTracker.ts +3 -0
  277. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  278. package/src/hooks/useOrganisationPermissions.ts +4 -0
  279. package/src/hooks/useOrganisationSecurity.ts +4 -0
  280. package/src/hooks/usePerformanceMonitor.ts +4 -0
  281. package/src/hooks/usePermissionCache.ts +7 -0
  282. package/src/hooks/useQueryCache.ts +12 -1
  283. package/src/hooks/useSessionRestoration.ts +4 -0
  284. package/src/hooks/useStorage.ts +4 -0
  285. package/src/hooks/useToast.ts +1 -1
  286. package/src/index.ts +6 -6
  287. package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
  288. package/src/providers/services/AuthServiceProvider.tsx +35 -7
  289. package/src/providers/services/EventServiceProvider.tsx +51 -5
  290. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  291. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  292. package/src/providers/services/UnifiedAuthProvider.tsx +126 -134
  293. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
  294. package/src/rbac/README.md +1 -1
  295. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
  296. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  297. package/src/rbac/adapters.tsx +12 -3
  298. package/src/rbac/api.test.ts +59 -51
  299. package/src/rbac/api.ts +246 -167
  300. package/src/rbac/components/NavigationProvider.tsx +4 -1
  301. package/src/rbac/components/PagePermissionGuard.tsx +185 -17
  302. package/src/rbac/components/RoleBasedRouter.tsx +5 -1
  303. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  304. package/src/rbac/components/SecureDataProvider.tsx +20 -5
  305. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  306. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  307. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  308. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  309. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  310. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  311. package/src/rbac/engine.ts +38 -14
  312. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
  313. package/src/rbac/hooks/permissions/index.ts +7 -0
  314. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  315. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  316. package/src/rbac/hooks/permissions/useCan.ts +377 -0
  317. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  318. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  319. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  320. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  321. package/src/rbac/hooks/useCan.test.ts +64 -66
  322. package/src/rbac/hooks/usePermissions.ts +14 -995
  323. package/src/rbac/hooks/useRBAC.test.ts +1 -5
  324. package/src/rbac/hooks/useRBAC.ts +36 -37
  325. package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
  326. package/src/rbac/hooks/useResolvedScope.ts +35 -40
  327. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  328. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  329. package/src/rbac/hooks/useSecureSupabase.ts +27 -7
  330. package/src/rbac/index.ts +7 -0
  331. package/src/rbac/permissions.ts +0 -30
  332. package/src/rbac/secureClient.test.ts +22 -18
  333. package/src/rbac/secureClient.ts +294 -68
  334. package/src/rbac/security.ts +0 -17
  335. package/src/rbac/types.ts +9 -0
  336. package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
  337. package/src/rbac/utils/clientSecurity.ts +93 -0
  338. package/src/rbac/utils/contextValidator.ts +77 -168
  339. package/src/services/AuthService.ts +39 -7
  340. package/src/services/EventService.ts +186 -54
  341. package/src/services/OrganisationService.ts +81 -14
  342. package/src/services/__tests__/EventService.test.ts +1 -2
  343. package/src/services/base/BaseService.ts +3 -0
  344. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  345. package/src/theming/parseEventColours.ts +5 -19
  346. package/src/types/vitest-globals.d.ts +51 -26
  347. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  348. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  349. package/src/utils/__tests__/index.unit.test.ts +2 -2
  350. package/src/utils/audit/audit.ts +0 -3
  351. package/src/utils/core/cn.ts +1 -1
  352. package/src/utils/dynamic/dynamicUtils.ts +7 -4
  353. package/src/utils/file-reference/index.ts +53 -1
  354. package/src/utils/formatting/formatting.ts +8 -18
  355. package/src/utils/index.ts +0 -1
  356. package/dist/chunk-3QRJFVBR.js.map +0 -1
  357. package/dist/chunk-3XTALGJF.js.map +0 -1
  358. package/dist/chunk-4N5C5XZU.js.map +0 -1
  359. package/dist/chunk-4ZC4GX36.js.map +0 -1
  360. package/dist/chunk-7D4SUZUM.js +0 -38
  361. package/dist/chunk-BYFSK72L.js.map +0 -1
  362. package/dist/chunk-EXUD6RNJ.js +0 -451
  363. package/dist/chunk-EXUD6RNJ.js.map +0 -1
  364. package/dist/chunk-GLK6VM3F.js.map +0 -1
  365. package/dist/chunk-I7PSE6JW.js.map +0 -1
  366. package/dist/chunk-JBKQ3SAO.js.map +0 -1
  367. package/dist/chunk-KNC55RTG.js.map +0 -1
  368. package/dist/chunk-LXQLPRQ2.js.map +0 -1
  369. package/dist/chunk-R77UEZ4E.js.map +0 -1
  370. package/dist/chunk-SQGMNID3.js.map +0 -1
  371. package/dist/chunk-T33XF5ZC.js +0 -12922
  372. package/dist/chunk-T33XF5ZC.js.map +0 -1
  373. package/dist/chunk-XM25TVIE.js.map +0 -1
  374. package/docs/api/classes/ColumnFactory.md +0 -243
  375. package/docs/api/classes/ErrorBoundary.md +0 -144
  376. package/docs/api/classes/InvalidScopeError.md +0 -73
  377. package/docs/api/classes/Logger.md +0 -178
  378. package/docs/api/classes/MissingUserContextError.md +0 -66
  379. package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
  380. package/docs/api/classes/PermissionDeniedError.md +0 -73
  381. package/docs/api/classes/RBACAuditManager.md +0 -297
  382. package/docs/api/classes/RBACCache.md +0 -322
  383. package/docs/api/classes/RBACEngine.md +0 -171
  384. package/docs/api/classes/RBACError.md +0 -76
  385. package/docs/api/classes/RBACNotInitializedError.md +0 -66
  386. package/docs/api/classes/SecureSupabaseClient.md +0 -160
  387. package/docs/api/classes/StorageUtils.md +0 -328
  388. package/docs/api/enums/FileCategory.md +0 -184
  389. package/docs/api/enums/LogLevel.md +0 -54
  390. package/docs/api/enums/RBACErrorCode.md +0 -228
  391. package/docs/api/enums/RPCFunction.md +0 -118
  392. package/docs/api/interfaces/AddressFieldProps.md +0 -241
  393. package/docs/api/interfaces/AddressFieldRef.md +0 -94
  394. package/docs/api/interfaces/AggregateConfig.md +0 -43
  395. package/docs/api/interfaces/AutocompleteOptions.md +0 -75
  396. package/docs/api/interfaces/AvatarProps.md +0 -128
  397. package/docs/api/interfaces/BadgeProps.md +0 -27
  398. package/docs/api/interfaces/ButtonProps.md +0 -53
  399. package/docs/api/interfaces/CalendarProps.md +0 -70
  400. package/docs/api/interfaces/CardProps.md +0 -66
  401. package/docs/api/interfaces/ColorPalette.md +0 -7
  402. package/docs/api/interfaces/ColorShade.md +0 -66
  403. package/docs/api/interfaces/ComplianceResult.md +0 -30
  404. package/docs/api/interfaces/DataAccessRecord.md +0 -96
  405. package/docs/api/interfaces/DataRecord.md +0 -11
  406. package/docs/api/interfaces/DataTableAction.md +0 -249
  407. package/docs/api/interfaces/DataTableColumn.md +0 -504
  408. package/docs/api/interfaces/DataTableProps.md +0 -625
  409. package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
  410. package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
  411. package/docs/api/interfaces/DatabaseIssue.md +0 -41
  412. package/docs/api/interfaces/EmptyStateConfig.md +0 -61
  413. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
  414. package/docs/api/interfaces/EventAppRoleData.md +0 -71
  415. package/docs/api/interfaces/ExportColumn.md +0 -90
  416. package/docs/api/interfaces/ExportOptions.md +0 -126
  417. package/docs/api/interfaces/FileDisplayProps.md +0 -249
  418. package/docs/api/interfaces/FileMetadata.md +0 -129
  419. package/docs/api/interfaces/FileReference.md +0 -118
  420. package/docs/api/interfaces/FileSizeLimits.md +0 -7
  421. package/docs/api/interfaces/FileUploadOptions.md +0 -139
  422. package/docs/api/interfaces/FileUploadProps.md +0 -293
  423. package/docs/api/interfaces/FooterProps.md +0 -105
  424. package/docs/api/interfaces/FormFieldProps.md +0 -166
  425. package/docs/api/interfaces/FormProps.md +0 -113
  426. package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
  427. package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
  428. package/docs/api/interfaces/InputProps.md +0 -53
  429. package/docs/api/interfaces/LabelProps.md +0 -107
  430. package/docs/api/interfaces/LoggerConfig.md +0 -62
  431. package/docs/api/interfaces/LoginFormProps.md +0 -184
  432. package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
  433. package/docs/api/interfaces/NavigationContextType.md +0 -164
  434. package/docs/api/interfaces/NavigationGuardProps.md +0 -139
  435. package/docs/api/interfaces/NavigationItem.md +0 -120
  436. package/docs/api/interfaces/NavigationMenuProps.md +0 -221
  437. package/docs/api/interfaces/NavigationProviderProps.md +0 -117
  438. package/docs/api/interfaces/Organisation.md +0 -140
  439. package/docs/api/interfaces/OrganisationContextType.md +0 -388
  440. package/docs/api/interfaces/OrganisationMembership.md +0 -140
  441. package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
  442. package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
  443. package/docs/api/interfaces/PaceAppLayoutProps.md +0 -406
  444. package/docs/api/interfaces/PaceLoginPageProps.md +0 -47
  445. package/docs/api/interfaces/PageAccessRecord.md +0 -85
  446. package/docs/api/interfaces/PagePermissionContextType.md +0 -140
  447. package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
  448. package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
  449. package/docs/api/interfaces/PaletteData.md +0 -41
  450. package/docs/api/interfaces/ParsedAddress.md +0 -120
  451. package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
  452. package/docs/api/interfaces/ProgressProps.md +0 -42
  453. package/docs/api/interfaces/ProtectedRouteProps.md +0 -97
  454. package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
  455. package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
  456. package/docs/api/interfaces/PublicPageLayoutProps.md +0 -198
  457. package/docs/api/interfaces/QuickFix.md +0 -52
  458. package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
  459. package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
  460. package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
  461. package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
  462. package/docs/api/interfaces/RBACConfig.md +0 -133
  463. package/docs/api/interfaces/RBACContext.md +0 -52
  464. package/docs/api/interfaces/RBACLogger.md +0 -112
  465. package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
  466. package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
  467. package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
  468. package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
  469. package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
  470. package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
  471. package/docs/api/interfaces/RBACResult.md +0 -58
  472. package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
  473. package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
  474. package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
  475. package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
  476. package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
  477. package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
  478. package/docs/api/interfaces/RBACRolesListParams.md +0 -52
  479. package/docs/api/interfaces/RBACRolesListResult.md +0 -74
  480. package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
  481. package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
  482. package/docs/api/interfaces/ResourcePermissions.md +0 -155
  483. package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
  484. package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
  485. package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
  486. package/docs/api/interfaces/RoleManagementResult.md +0 -52
  487. package/docs/api/interfaces/RouteAccessRecord.md +0 -107
  488. package/docs/api/interfaces/RouteConfig.md +0 -134
  489. package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
  490. package/docs/api/interfaces/SecureDataContextType.md +0 -168
  491. package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
  492. package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
  493. package/docs/api/interfaces/SetupIssue.md +0 -41
  494. package/docs/api/interfaces/StorageConfig.md +0 -41
  495. package/docs/api/interfaces/StorageFileInfo.md +0 -74
  496. package/docs/api/interfaces/StorageFileMetadata.md +0 -151
  497. package/docs/api/interfaces/StorageListOptions.md +0 -99
  498. package/docs/api/interfaces/StorageListResult.md +0 -41
  499. package/docs/api/interfaces/StorageUploadOptions.md +0 -101
  500. package/docs/api/interfaces/StorageUploadResult.md +0 -63
  501. package/docs/api/interfaces/StorageUrlOptions.md +0 -60
  502. package/docs/api/interfaces/StyleImport.md +0 -19
  503. package/docs/api/interfaces/SwitchProps.md +0 -34
  504. package/docs/api/interfaces/TabsContentProps.md +0 -9
  505. package/docs/api/interfaces/TabsListProps.md +0 -9
  506. package/docs/api/interfaces/TabsProps.md +0 -9
  507. package/docs/api/interfaces/TabsTriggerProps.md +0 -50
  508. package/docs/api/interfaces/TextareaProps.md +0 -53
  509. package/docs/api/interfaces/ToastActionElement.md +0 -9
  510. package/docs/api/interfaces/ToastProps.md +0 -9
  511. package/docs/api/interfaces/UnifiedAuthContextType.md +0 -820
  512. package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -171
  513. package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
  514. package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
  515. package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -136
  516. package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
  517. package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
  518. package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -81
  519. package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
  520. package/docs/api/interfaces/UsePublicEventReturn.md +0 -68
  521. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
  522. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -120
  523. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -94
  524. package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
  525. package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
  526. package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
  527. package/docs/api/interfaces/UserEventAccess.md +0 -118
  528. package/docs/api/interfaces/UserMenuProps.md +0 -86
  529. package/docs/api/interfaces/UserProfile.md +0 -63
  530. package/docs/migration/quick-migration-guide.md +0 -356
  531. package/docs/migration/service-architecture.md +0 -281
  532. package/src/components/EventSelector/EventSelector.test.tsx +0 -720
  533. package/src/components/EventSelector/EventSelector.tsx +0 -420
  534. package/src/components/EventSelector/index.ts +0 -3
  535. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
  536. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -324
  537. package/src/components/OrganisationSelector/index.ts +0 -9
  538. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  539. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  540. package/src/hooks/useSecureDataAccess.ts +0 -681
  541. /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-THFPBKTP.js.map} +0 -0
  542. /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
  543. /package/dist/{api-N774RPUA.js.map → api-IAGWF3ZG.js.map} +0 -0
  544. /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
  545. /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
  546. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  547. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  548. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  549. /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
@@ -1,720 +0,0 @@
1
- /**
2
- * @file EventSelector Component Tests
3
- * @description Comprehensive test suite for EventSelector component
4
- * @package @jmruthers/pace-core
5
- * @module Components/EventSelector
6
- * @since 0.1.0
7
- *
8
- * Comprehensive test suite for EventSelector component following testing guidelines.
9
- * Tests cover all major functionality, edge cases, and user interactions.
10
- */
11
-
12
- import React from 'react';
13
- import { screen, waitFor, act } from '@testing-library/react';
14
- import userEvent from '@testing-library/user-event';
15
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
16
- import { EventSelector, EventSelectorProps } from './EventSelector';
17
- import { renderWithProviders } from '../../__tests__/helpers/test-utils';
18
- import type { Event } from '../../types';
19
-
20
- // Mock the useEvents hook
21
- const mockUseEvents = vi.fn();
22
- vi.mock('../../hooks/useEvents', () => ({
23
- useEvents: () => mockUseEvents(),
24
- }));
25
-
26
- // Mock child components
27
- let mockOnValueChange: ((value: string) => void) | null = null;
28
-
29
- vi.mock('../Select', () => ({
30
- Select: ({ children, value, onValueChange, className }: any) => {
31
- // Store the onValueChange callback for SelectItem to use
32
- mockOnValueChange = onValueChange;
33
- return (
34
- <div data-testid="select" data-value={value} className={className}>
35
- {children}
36
- </div>
37
- );
38
- },
39
- SelectContent: ({ children }: any) => (
40
- <div data-testid="select-content">{children}</div>
41
- ),
42
- SelectItem: ({ children, value, className }: any) => (
43
- <div
44
- data-testid={`select-item-${value}`}
45
- className={className}
46
- onClick={() => mockOnValueChange?.(value)}
47
- >
48
- {children}
49
- </div>
50
- ),
51
- SelectTrigger: ({ children, className }: any) => (
52
- <button data-testid="select-trigger" className={className}>
53
- {children}
54
- </button>
55
- ),
56
- SelectValue: ({ placeholder, children }: any) => (
57
- <span data-testid="select-value">
58
- {children || placeholder}
59
- </span>
60
- ),
61
- }));
62
-
63
- vi.mock('../Alert/Alert', () => ({
64
- Alert: ({ children, variant }: any) => (
65
- <div data-testid="alert" data-variant={variant}>
66
- {children}
67
- </div>
68
- ),
69
- AlertDescription: ({ children, className }: any) => (
70
- <div data-testid="alert-description" className={className}>
71
- {children}
72
- </div>
73
- ),
74
- }));
75
-
76
- vi.mock('../Button/Button', () => ({
77
- Button: ({ children, onClick, variant, size, className }: any) => (
78
- <button
79
- data-testid="button"
80
- onClick={onClick}
81
- data-variant={variant}
82
- data-size={size}
83
- className={className}
84
- >
85
- {children}
86
- </button>
87
- ),
88
- }));
89
-
90
- vi.mock('../LoadingSpinner/LoadingSpinner', () => ({
91
- LoadingSpinner: ({ size }: any) => (
92
- <div data-testid="loading-spinner" data-size={size}>Loading...</div>
93
- ),
94
- }));
95
-
96
- vi.mock('lucide-react', () => ({
97
- RefreshCw: () => <span data-testid="icon-refresh">refresh</span>,
98
- AlertCircle: () => <span data-testid="icon-alert">alert</span>,
99
- Lock: () => <span data-testid="icon-lock">lock</span>,
100
- Calendar: () => <span data-testid="icon-calendar">calendar</span>,
101
- Star: () => <span data-testid="icon-star">star</span>,
102
- }));
103
-
104
- // Test data
105
- const mockEvents: Event[] = [
106
- {
107
- id: 'event-1',
108
- event_id: 'event-1',
109
- event_name: 'Spring Festival 2024',
110
- event_date: '2024-03-15T00:00:00Z',
111
- event_venue: 'City Park',
112
- organisation_id: 'org-1',
113
- created_at: '2023-01-01T00:00:00Z',
114
- updated_at: '2023-01-01T00:00:00Z',
115
- },
116
- {
117
- id: 'event-2',
118
- event_id: 'event-2',
119
- event_name: 'Summer Conference',
120
- event_date: '2024-06-20T00:00:00Z',
121
- event_venue: 'Convention Center',
122
- organisation_id: 'org-1',
123
- created_at: '2023-01-02T00:00:00Z',
124
- updated_at: '2023-01-02T00:00:00Z',
125
- },
126
- {
127
- id: 'event-3',
128
- event_id: 'event-3',
129
- event_name: 'Winter Gala',
130
- event_date: '2024-12-01T00:00:00Z',
131
- event_venue: 'Grand Hotel',
132
- organisation_id: 'org-1',
133
- created_at: '2023-01-03T00:00:00Z',
134
- updated_at: '2023-01-03T00:00:00Z',
135
- },
136
- ];
137
-
138
- const mockSelectedEvent = mockEvents[0];
139
-
140
- const defaultMockContext = {
141
- events: mockEvents,
142
- selectedEvent: mockSelectedEvent,
143
- isLoading: false,
144
- error: null,
145
- setSelectedEvent: vi.fn(),
146
- refreshEvents: vi.fn().mockResolvedValue(undefined),
147
- };
148
-
149
- describe('EventSelector Component', () => {
150
- beforeEach(() => {
151
- vi.clearAllMocks();
152
-
153
- // Setup the default mock context
154
- mockUseEvents.mockReturnValue(defaultMockContext);
155
- });
156
-
157
- afterEach(() => {
158
- vi.useRealTimers();
159
- mockOnValueChange = null;
160
- });
161
-
162
- describe('Rendering', () => {
163
- it('renders with default props', () => {
164
- renderWithProviders(<EventSelector />);
165
-
166
- expect(screen.getByTestId('select')).toBeInTheDocument();
167
- expect(screen.getByTestId('select-trigger')).toBeInTheDocument();
168
- });
169
-
170
- it('renders with custom placeholder', () => {
171
- mockUseEvents.mockReturnValue({
172
- ...defaultMockContext,
173
- selectedEvent: null, // No selected event should show placeholder
174
- });
175
-
176
- renderWithProviders(<EventSelector placeholder="Choose an event" />);
177
-
178
- expect(screen.getByTestId('select-value')).toBeInTheDocument();
179
- });
180
-
181
- it('renders with custom className', () => {
182
- renderWithProviders(<EventSelector className="custom-class" />);
183
-
184
- const select = screen.getByTestId('select');
185
- expect(select).toBeInTheDocument();
186
- expect(select).toBeVisible();
187
- });
188
-
189
- it('renders all events in dropdown', () => {
190
- renderWithProviders(<EventSelector />);
191
-
192
- expect(screen.getByTestId('select-content')).toBeInTheDocument();
193
- });
194
- });
195
-
196
- describe('Loading State', () => {
197
- it('shows loading spinner when isLoading is true', () => {
198
- mockUseEvents.mockReturnValue({
199
- ...defaultMockContext,
200
- isLoading: true,
201
- });
202
-
203
- renderWithProviders(<EventSelector />);
204
-
205
- expect(screen.getByTestId('loading-spinner')).toBeInTheDocument();
206
- expect(screen.getByText('Loading events...')).toBeInTheDocument();
207
- });
208
- });
209
-
210
- describe('Error State', () => {
211
- it('shows error message when error occurs', () => {
212
- const error = new Error('Failed to load events');
213
- mockUseEvents.mockReturnValue({
214
- ...defaultMockContext,
215
- error,
216
- });
217
-
218
- renderWithProviders(<EventSelector />);
219
-
220
- expect(screen.getByTestId('alert')).toBeInTheDocument();
221
- expect(screen.getByTestId('alert')).toHaveAttribute('data-variant', 'destructive');
222
- expect(screen.getByText('Failed to load events')).toBeInTheDocument();
223
- });
224
-
225
- it('shows retry button when error and showRetryButton is true', () => {
226
- const error = new Error('Failed to load events');
227
- mockUseEvents.mockReturnValue({
228
- ...defaultMockContext,
229
- error,
230
- });
231
-
232
- renderWithProviders(<EventSelector showRetryButton={true} />);
233
-
234
- expect(screen.getByTestId('button')).toBeInTheDocument();
235
- expect(screen.getByText('Retry')).toBeInTheDocument();
236
- });
237
-
238
- it('hides retry button when showRetryButton is false', () => {
239
- const error = new Error('Failed to load events');
240
- mockUseEvents.mockReturnValue({
241
- ...defaultMockContext,
242
- error,
243
- });
244
-
245
- renderWithProviders(<EventSelector showRetryButton={false} />);
246
-
247
- expect(screen.queryByTestId('button')).not.toBeInTheDocument();
248
- });
249
-
250
- it('calls refreshEvents when retry button is clicked', async () => {
251
- const refreshEventsMock = vi.fn().mockResolvedValue(undefined);
252
- const error = new Error('Failed to load events');
253
-
254
- mockUseEvents.mockReturnValue({
255
- ...defaultMockContext,
256
- error,
257
- refreshEvents: refreshEventsMock,
258
- });
259
-
260
- renderWithProviders(<EventSelector showRetryButton={true} />);
261
-
262
- const retryButton = screen.getByTestId('button');
263
- const user = userEvent.setup();
264
- await user.click(retryButton);
265
-
266
- expect(refreshEventsMock).toHaveBeenCalledTimes(1);
267
- });
268
- });
269
-
270
- describe('No Events State', () => {
271
- it('shows no events message when showNoEventsMessage is true and no events', () => {
272
- mockUseEvents.mockReturnValue({
273
- ...defaultMockContext,
274
- events: [],
275
- });
276
-
277
- renderWithProviders(<EventSelector showNoEventsMessage={true} />);
278
-
279
- expect(screen.getByTestId('alert')).toBeInTheDocument();
280
- expect(screen.getByText('No events available.')).toBeInTheDocument();
281
- });
282
-
283
- it('hides message when showNoEventsMessage is false and no events', () => {
284
- mockUseEvents.mockReturnValue({
285
- ...defaultMockContext,
286
- events: [],
287
- });
288
-
289
- const { container } = renderWithProviders(<EventSelector showNoEventsMessage={false} />);
290
-
291
- expect(container.firstChild).toBeNull();
292
- });
293
-
294
- it('shows refresh button when no events and showRetryButton is true', () => {
295
- mockUseEvents.mockReturnValue({
296
- ...defaultMockContext,
297
- events: [],
298
- });
299
-
300
- renderWithProviders(<EventSelector showRetryButton={true} />);
301
-
302
- expect(screen.getByTestId('button')).toBeInTheDocument();
303
- expect(screen.getByText('Refresh')).toBeInTheDocument();
304
- });
305
- });
306
-
307
- describe('Event Selection', () => {
308
- it('calls onEventChange when an event is selected', async () => {
309
- const onEventChangeMock = vi.fn();
310
-
311
- renderWithProviders(
312
- <EventSelector onEventChange={onEventChangeMock} />
313
- );
314
-
315
- // Find and click an event item
316
- const user = userEvent.setup();
317
- const eventItem = screen.getByTestId('select-item-event-2');
318
- await user.click(eventItem);
319
-
320
- expect(onEventChangeMock).toHaveBeenCalledTimes(1);
321
- });
322
-
323
- it('calls setSelectedEvent when an event is selected', async () => {
324
- const setSelectedEventMock = vi.fn();
325
-
326
- mockUseEvents.mockReturnValue({
327
- ...defaultMockContext,
328
- setSelectedEvent: setSelectedEventMock,
329
- });
330
-
331
- renderWithProviders(<EventSelector />);
332
-
333
- const user = userEvent.setup();
334
- const eventItem = screen.getByTestId('select-item-event-2');
335
- await user.click(eventItem);
336
-
337
- expect(setSelectedEventMock).toHaveBeenCalled();
338
- });
339
-
340
- it('displays selected event name and date', () => {
341
- renderWithProviders(<EventSelector />);
342
-
343
- expect(screen.getByTestId('select')).toHaveAttribute('data-value', 'event-1');
344
- });
345
- });
346
-
347
- describe('Event Sorting', () => {
348
- it('sorts events by date descending (newest first)', () => {
349
- const unsortedEvents: Event[] = [
350
- {
351
- id: 'event-old',
352
- event_id: 'event-old',
353
- event_name: 'Old Event',
354
- event_date: '2024-01-01T00:00:00Z',
355
- organisation_id: 'org-1',
356
- created_at: '2023-01-01T00:00:00Z',
357
- updated_at: '2023-01-01T00:00:00Z',
358
- },
359
- {
360
- id: 'event-new',
361
- event_id: 'event-new',
362
- event_name: 'New Event',
363
- event_date: '2024-12-31T00:00:00Z',
364
- organisation_id: 'org-1',
365
- created_at: '2023-01-02T00:00:00Z',
366
- updated_at: '2023-01-02T00:00:00Z',
367
- },
368
- ];
369
-
370
- mockUseEvents.mockReturnValue({
371
- ...defaultMockContext,
372
- events: unsortedEvents,
373
- });
374
-
375
- renderWithProviders(<EventSelector />);
376
-
377
- // The newest event should appear first in the DOM
378
- const items = screen.getAllByTestId(/^select-item-/);
379
- expect(items[0]).toHaveAttribute('data-testid', 'select-item-event-new');
380
- });
381
- });
382
-
383
- describe('Auto-Selection', () => {
384
- it('auto-selects next upcoming event when no event is selected', async () => {
385
- const setSelectedEventMock = vi.fn();
386
- const onEventChangeMock = vi.fn();
387
-
388
- // The component's useEffect depends on events.length, so when events.length changes,
389
- // it should trigger. However, refs are initialized with the current value, so we need
390
- // to simulate the scenario where events are loaded after the component mounts.
391
- //
392
- // Strategy: Start with a different events array length, then change it
393
- // This ensures eventsLengthChanged will be true
394
-
395
- // First render: Start with one event (different length than what we'll add)
396
- mockUseEvents.mockReturnValue({
397
- ...defaultMockContext,
398
- events: [mockEvents[0]], // Start with 1 event
399
- selectedEvent: null,
400
- isLoading: false,
401
- setSelectedEvent: setSelectedEventMock,
402
- refreshEvents: vi.fn(),
403
- clearEventSelection: vi.fn(),
404
- eventLoading: false,
405
- });
406
-
407
- const { rerender } = renderWithProviders(<EventSelector onEventChange={onEventChangeMock} />);
408
-
409
- // Wait for initial render
410
- await act(async () => {
411
- await new Promise(resolve => setTimeout(resolve, 10));
412
- });
413
-
414
- // Now change to all events (length changes from 1 to 3) - this triggers eventsLengthChanged
415
- await act(async () => {
416
- mockUseEvents.mockReturnValue({
417
- ...defaultMockContext,
418
- events: mockEvents, // Length changes from 1 to 3
419
- selectedEvent: null, // Still no event selected
420
- isLoading: false,
421
- setSelectedEvent: setSelectedEventMock,
422
- refreshEvents: vi.fn(),
423
- clearEventSelection: vi.fn(),
424
- eventLoading: false,
425
- });
426
-
427
- rerender(<EventSelector onEventChange={onEventChangeMock} />);
428
- });
429
-
430
- // Wait for the effect to run and auto-select
431
- await waitFor(() => {
432
- expect(setSelectedEventMock).toHaveBeenCalled();
433
- }, { timeout: 2000 });
434
-
435
- // Verify it selected an event
436
- // Note: The component selects the next upcoming event based on the current date
437
- // Since we're using real dates, it will select the event closest to today that's in the future
438
- // or the most recent past event if all are in the past
439
- expect(setSelectedEventMock).toHaveBeenCalled();
440
-
441
- // Get the actual event that was selected
442
- const selectedEvent = setSelectedEventMock.mock.calls[0][0];
443
- expect(selectedEvent).toBeDefined();
444
- expect(selectedEvent.event_id).toBeDefined();
445
- expect(selectedEvent.event_date).toBeDefined();
446
- });
447
- });
448
-
449
- describe('Event Details Display', () => {
450
- it('shows event details when showEventDetails is true', () => {
451
- renderWithProviders(<EventSelector showEventDetails={true} />);
452
-
453
- expect(screen.getByTestId('select-content')).toBeInTheDocument();
454
- });
455
-
456
- it('hides event details when showEventDetails is false', () => {
457
- renderWithProviders(<EventSelector showEventDetails={false} />);
458
-
459
- // Details should not be visible in the rendered content
460
- const content = screen.getByTestId('select-content');
461
- expect(content).toBeInTheDocument();
462
- });
463
- });
464
-
465
- describe('Next Event Indicator', () => {
466
- it('shows next event indicator when showNextEventIndicator is true', () => {
467
- const today = new Date('2024-03-10T12:00:00Z');
468
- vi.useFakeTimers({ now: today });
469
-
470
- renderWithProviders(<EventSelector showNextEventIndicator={true} />);
471
-
472
- // Should render the star icon for next events (events-1, 2, 3 are in the future)
473
- expect(screen.getAllByTestId('icon-star').length).toBeGreaterThan(0);
474
- });
475
-
476
- it('hides next event indicator when showNextEventIndicator is false', () => {
477
- renderWithProviders(<EventSelector showNextEventIndicator={false} />);
478
-
479
- // No star icons should be present
480
- expect(screen.queryByTestId('icon-star')).not.toBeInTheDocument();
481
- });
482
- });
483
-
484
- describe('Format Event Date', () => {
485
- it('formats today\'s date as "Today"', () => {
486
- const today = new Date('2024-03-15T12:00:00Z');
487
- vi.useFakeTimers({ now: today });
488
-
489
- const todayEvent: Event[] = [{
490
- id: 'event-today',
491
- event_id: 'event-today',
492
- event_name: 'Today Event',
493
- event_date: '2024-03-15T00:00:00Z',
494
- organisation_id: 'org-1',
495
- created_at: '2024-01-01T00:00:00Z',
496
- updated_at: '2024-01-01T00:00:00Z',
497
- }];
498
-
499
- mockUseEvents.mockReturnValue({
500
- ...defaultMockContext,
501
- events: todayEvent,
502
- selectedEvent: todayEvent[0],
503
- });
504
-
505
- renderWithProviders(<EventSelector />);
506
-
507
- // Should display "Today" for today's event
508
- expect(screen.getByText('Today')).toBeInTheDocument();
509
- });
510
-
511
- it('formats tomorrow\'s date as "Tomorrow"', () => {
512
- const today = new Date('2024-03-15T12:00:00Z');
513
- vi.useFakeTimers({ now: today });
514
-
515
- const tomorrowEvent: Event[] = [{
516
- id: 'event-tomorrow',
517
- event_id: 'event-tomorrow',
518
- event_name: 'Tomorrow Event',
519
- event_date: '2024-03-16T00:00:00Z',
520
- organisation_id: 'org-1',
521
- created_at: '2024-01-01T00:00:00Z',
522
- updated_at: '2024-01-01T00:00:00Z',
523
- }];
524
-
525
- mockUseEvents.mockReturnValue({
526
- ...defaultMockContext,
527
- events: tomorrowEvent,
528
- selectedEvent: tomorrowEvent[0],
529
- });
530
-
531
- renderWithProviders(<EventSelector />);
532
-
533
- // Should display "Tomorrow" for tomorrow's event
534
- expect(screen.getByText('Tomorrow')).toBeInTheDocument();
535
- });
536
-
537
- it('formats past/future dates as locale date string', () => {
538
- const today = new Date('2024-03-15T12:00:00Z');
539
- vi.useFakeTimers({ now: today });
540
-
541
- const futureEvent: Event[] = [{
542
- id: 'event-future',
543
- event_id: 'event-future',
544
- event_name: 'Future Event',
545
- event_date: '2024-06-20T00:00:00Z',
546
- organisation_id: 'org-1',
547
- created_at: '2024-01-01T00:00:00Z',
548
- updated_at: '2024-01-01T00:00:00Z',
549
- }];
550
-
551
- mockUseEvents.mockReturnValue({
552
- ...defaultMockContext,
553
- events: futureEvent,
554
- selectedEvent: futureEvent[0],
555
- });
556
-
557
- renderWithProviders(<EventSelector />);
558
-
559
- // Should display formatted date
560
- const dateString = new Date('2024-06-20T00:00:00Z').toLocaleDateString();
561
- expect(screen.getByText(dateString)).toBeInTheDocument();
562
- });
563
- });
564
-
565
- describe('Is Next Event', () => {
566
- it('identifies events on or after today as next events', () => {
567
- const today = new Date('2024-03-15T12:00:00Z');
568
- vi.useFakeTimers({ now: today });
569
-
570
- const futureEvent: Event[] = [{
571
- id: 'event-future',
572
- event_id: 'event-future',
573
- event_name: 'Future Event',
574
- event_date: '2024-06-20T00:00:00Z',
575
- organisation_id: 'org-1',
576
- created_at: '2024-01-01T00:00:00Z',
577
- updated_at: '2024-01-01T00:00:00Z',
578
- }];
579
-
580
- mockUseEvents.mockReturnValue({
581
- ...defaultMockContext,
582
- events: futureEvent,
583
- });
584
-
585
- renderWithProviders(<EventSelector showNextEventIndicator={true} />);
586
-
587
- // Should show star indicator for future events
588
- expect(screen.getByTestId('icon-star')).toBeInTheDocument();
589
- });
590
-
591
- it('does not identify past events as next events', () => {
592
- const today = new Date('2024-03-15T12:00:00Z');
593
- vi.useFakeTimers({ now: today });
594
-
595
- const pastEvent: Event[] = [{
596
- id: 'event-past',
597
- event_id: 'event-past',
598
- event_name: 'Past Event',
599
- event_date: '2024-01-01T00:00:00Z',
600
- organisation_id: 'org-1',
601
- created_at: '2024-01-01T00:00:00Z',
602
- updated_at: '2024-01-01T00:00:00Z',
603
- }];
604
-
605
- mockUseEvents.mockReturnValue({
606
- ...defaultMockContext,
607
- events: pastEvent,
608
- });
609
-
610
- renderWithProviders(<EventSelector showNextEventIndicator={true} />);
611
-
612
- // Should not show star indicator for past events
613
- expect(screen.queryByTestId('icon-star')).not.toBeInTheDocument();
614
- });
615
- });
616
-
617
- describe('Integration with useEvents Hook', () => {
618
- it('uses events from useEvents hook', () => {
619
- const customEvents: Event[] = [
620
- {
621
- id: 'custom-event',
622
- event_id: 'custom-event',
623
- event_name: 'Custom Event',
624
- event_date: '2024-12-01T00:00:00Z',
625
- organisation_id: 'org-1',
626
- created_at: '2024-01-01T00:00:00Z',
627
- updated_at: '2024-01-01T00:00:00Z',
628
- },
629
- ];
630
-
631
- mockUseEvents.mockReturnValue({
632
- ...defaultMockContext,
633
- events: customEvents,
634
- });
635
-
636
- renderWithProviders(<EventSelector />);
637
-
638
- expect(mockUseEvents).toHaveBeenCalled();
639
- });
640
-
641
- it('calls setSelectedEvent from useEvents hook when event changes', async () => {
642
- const setSelectedEventMock = vi.fn();
643
-
644
- mockUseEvents.mockReturnValue({
645
- ...defaultMockContext,
646
- setSelectedEvent: setSelectedEventMock,
647
- });
648
-
649
- renderWithProviders(<EventSelector />);
650
-
651
- const user = userEvent.setup();
652
- const eventItem = screen.getByTestId('select-item-event-2');
653
- await user.click(eventItem);
654
-
655
- expect(setSelectedEventMock).toHaveBeenCalled();
656
- });
657
- });
658
-
659
- describe('Edge Cases', () => {
660
- it('handles events without event_date gracefully', () => {
661
- const eventsWithoutDate: Event[] = [{
662
- id: 'no-date-event',
663
- event_id: 'no-date-event',
664
- event_name: 'Event Without Date',
665
- organisation_id: 'org-1',
666
- created_at: '2024-01-01T00:00:00Z',
667
- updated_at: '2024-01-01T00:00:00Z',
668
- }];
669
-
670
- mockUseEvents.mockReturnValue({
671
- ...defaultMockContext,
672
- events: eventsWithoutDate,
673
- selectedEvent: eventsWithoutDate[0],
674
- });
675
-
676
- renderWithProviders(<EventSelector />);
677
-
678
- expect(screen.getByTestId('select')).toBeInTheDocument();
679
- });
680
-
681
- it('handles events with missing venue gracefully', () => {
682
- const eventsWithoutVenue: Event[] = [{
683
- id: 'no-venue-event',
684
- event_id: 'no-venue-event',
685
- event_name: 'Event Without Venue',
686
- event_date: '2024-12-01T00:00:00Z',
687
- organisation_id: 'org-1',
688
- created_at: '2024-01-01T00:00:00Z',
689
- updated_at: '2024-01-01T00:00:00Z',
690
- }];
691
-
692
- mockUseEvents.mockReturnValue({
693
- ...defaultMockContext,
694
- events: eventsWithoutVenue,
695
- selectedEvent: eventsWithoutVenue[0],
696
- });
697
-
698
- renderWithProviders(<EventSelector />);
699
-
700
- expect(screen.getByTestId('select')).toBeInTheDocument();
701
- });
702
-
703
- it('handles rapid event changes without errors', async () => {
704
- const user = userEvent.setup();
705
-
706
- renderWithProviders(<EventSelector />);
707
-
708
- // Rapidly change events
709
- const event1 = screen.getByTestId('select-item-event-1');
710
- const event2 = screen.getByTestId('select-item-event-2');
711
-
712
- await user.click(event1);
713
- await user.click(event2);
714
- await user.click(event1);
715
-
716
- expect(screen.getByTestId('select')).toBeInTheDocument();
717
- });
718
- });
719
- });
720
-