@jmruthers/pace-core 0.6.9 → 0.6.10

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 (665) hide show
  1. package/audit-tool/audits/02-project-structure.cjs +62 -0
  2. package/audit-tool/audits/03-architecture.cjs +145 -19
  3. package/audit-tool/audits/04-code-quality.cjs +86 -1
  4. package/audit-tool/audits/06-security-rbac.cjs +109 -11
  5. package/cursor-rules/02-project-structure.mdc +2 -26
  6. package/cursor-rules/05-styling.mdc +84 -6
  7. package/cursor-rules/06-security-rbac.mdc +124 -1
  8. package/dist/{DataTable-SOAFXIWY.js → DataTable-SAXFG4XI.js} +11 -13
  9. package/dist/{AuthService-DmfO5rGS.d.ts → InactivityServiceProvider-DHryoh6K.d.ts} +24 -249
  10. package/dist/UnifiedAuthProvider-BBD2PS3Q.js +7 -0
  11. package/dist/{UnifiedAuthProvider-CKvHP1MK.d.ts → UnifiedAuthProvider-CiBAl9-s.d.ts} +34 -22
  12. package/dist/{api-7P7DI652.js → api-F47QJ7FX.js} +3 -3
  13. package/dist/assets/app-icons/admin_favicon.svg +462 -0
  14. package/dist/assets/app-icons/base_favicon.svg +85 -0
  15. package/dist/assets/app-icons/cake_favicon.svg +68 -0
  16. package/dist/assets/app-icons/core_favicon.svg +256 -0
  17. package/dist/assets/app-icons/gear_favicon.svg +91 -0
  18. package/dist/assets/app-icons/medi_favicon.svg +92 -0
  19. package/dist/assets/app-icons/mint_favicon.svg +83 -0
  20. package/dist/assets/app-icons/pace_favicon.svg +49 -0
  21. package/dist/assets/app-icons/pump_favicon.svg +68 -0
  22. package/dist/assets/app-icons/seed_favicon.svg +91 -0
  23. package/dist/assets/app-icons/team_favicon.svg +67 -0
  24. package/dist/assets/app-icons/trac_favicon.svg +112 -0
  25. package/dist/assets/app-icons/trip_favicon.svg +102 -0
  26. package/dist/audit-Z6ZZBWLU.js +3 -0
  27. package/dist/chunk-3GWSPISD.js +61 -0
  28. package/dist/{chunk-4DDCYDQ3.js → chunk-66R6RLUZ.js} +12 -27
  29. package/dist/{chunk-FYHN4DD5.js → chunk-7YDC7LMU.js} +80 -8
  30. package/dist/{chunk-S7DKJPLT.js → chunk-BCTXBU6U.js} +22 -17
  31. package/dist/{chunk-TTRFSOKR.js → chunk-BTHN5MKC.js} +4 -4
  32. package/dist/{chunk-A3W6LW53.js → chunk-DDMPHZ3D.js} +6 -18
  33. package/dist/{chunk-MPBLMWVR.js → chunk-FBZ7U3ID.js} +140 -92
  34. package/dist/chunk-FN52B75D.js +246 -0
  35. package/dist/{chunk-5W2A3DRC.js → chunk-JJEYZ3DX.js} +5 -4
  36. package/dist/chunk-KPYQWGFQ.js +183 -0
  37. package/dist/{chunk-C7ZQ5O4C.js → chunk-KSNLMI7N.js} +3 -3
  38. package/dist/chunk-KYURMOQM.js +977 -0
  39. package/dist/{chunk-LX6U42O3.js → chunk-LNHFAF4X.js} +160 -58
  40. package/dist/{chunk-J2U36LHD.js → chunk-MPY44PWB.js} +620 -627
  41. package/dist/{chunk-AHU7G2R5.js → chunk-NIU6DPQV.js} +10 -6
  42. package/dist/{chunk-HF6O3O37.js → chunk-RMLY6KB5.js} +1 -1
  43. package/dist/{chunk-6GLLNA6U.js → chunk-SACF5YSM.js} +1 -1
  44. package/dist/{chunk-5HNSDQWH.js → chunk-TFIPNIPE.js} +865 -532
  45. package/dist/{chunk-OJ4SKRSV.js → chunk-UZNAFKGW.js} +25 -5
  46. package/dist/chunk-W46INAVW.js +1216 -0
  47. package/dist/chunk-X5EAU5G7.js +793 -0
  48. package/dist/{chunk-T5CVK4R3.js → chunk-Y4PF6HIM.js} +110 -64
  49. package/dist/components.d.ts +8 -86
  50. package/dist/components.js +21 -55
  51. package/dist/eslint-rules/rules/05-styling.cjs +507 -0
  52. package/dist/eslint-rules/rules/06-security-rbac.cjs +10 -0
  53. package/dist/{event-CW5YB_2p.d.ts → event-WTAQuGcq.d.ts} +1 -1
  54. package/dist/{functions-lBy5L2ry.d.ts → functions-DH45k8ec.d.ts} +1 -1
  55. package/dist/hooks.d.ts +11 -10
  56. package/dist/hooks.js +69 -44
  57. package/dist/index.d.ts +379 -31
  58. package/dist/index.js +46 -32
  59. package/dist/papaparseLoader-WG2UXQ22.js +7 -0
  60. package/dist/providers.d.ts +28 -14
  61. package/dist/providers.js +5 -5
  62. package/dist/rbac/eslint-rules.js +2 -2
  63. package/dist/rbac/index.d.ts +57 -213
  64. package/dist/rbac/index.js +11 -11
  65. package/dist/theming/runtime.d.ts +9 -3
  66. package/dist/theming/runtime.js +2 -2
  67. package/dist/{timezone-0AyangqX.d.ts → timezone-K-ptz3HO.d.ts} +21 -22
  68. package/dist/{types-t9H8qKRw.d.ts → types-BE2sEHKd.d.ts} +1 -1
  69. package/dist/{types-BeoeWV5I.d.ts → types-CvOPXWWZ.d.ts} +6 -5
  70. package/dist/{types-DXstZpNI.d.ts → types-D05dCGma.d.ts} +56 -149
  71. package/dist/types-Dr8sNhER.d.ts +50 -0
  72. package/dist/types.d.ts +4 -4
  73. package/dist/{PublicPageProvider-CIGSujI2.d.ts → usePublicPageContext-vxBlEHO9.d.ts} +294 -151
  74. package/dist/{usePublicRouteParams-DQLrDqDb.d.ts → usePublicRouteParams-G3Ks53mk.d.ts} +7 -6
  75. package/dist/utils.d.ts +300 -136
  76. package/dist/utils.js +42 -41
  77. package/dist/{validation-643vUDZW.d.ts → validation-g5n0hDkh.d.ts} +2 -2
  78. package/docs/api/modules.md +542 -549
  79. package/docs/api-reference/components.md +5 -5
  80. package/docs/implementation-guides/data-tables.md +190 -8
  81. package/docs/rbac/RBAC_CONTRACT.md +0 -12
  82. package/docs/standards/2-project-structure-standards.md +12 -74
  83. package/docs/standards/6-security-rbac-standards.md +222 -7
  84. package/docs/standards/7-api-tech-stack-standards.md +91 -3
  85. package/docs/testing/README.md +10 -0
  86. package/docs/testing/test-setup-for-consumers.md +914 -0
  87. package/eslint-config-pace-core.cjs +4 -0
  88. package/package.json +1 -1
  89. package/scripts/eslint-audit.cjs +110 -11
  90. package/src/__mocks__/lucide-react.ts +0 -2
  91. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -2
  92. package/src/__tests__/index.test.ts +532 -0
  93. package/src/__tests__/integration/UserProfile.test.tsx +1 -1
  94. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -8
  95. package/src/__tests__/rls-policies.test.ts +3 -2
  96. package/src/assets/app-icons/admin_favicon.svg +462 -0
  97. package/src/assets/app-icons/base_favicon.svg +85 -0
  98. package/src/assets/app-icons/cake_favicon.svg +68 -0
  99. package/src/assets/app-icons/core_favicon.svg +256 -0
  100. package/src/assets/app-icons/gear_favicon.svg +91 -0
  101. package/src/assets/app-icons/index.ts +83 -0
  102. package/src/assets/app-icons/medi_favicon.svg +92 -0
  103. package/src/assets/app-icons/mint_favicon.svg +83 -0
  104. package/src/assets/app-icons/pace_favicon.svg +49 -0
  105. package/src/assets/app-icons/pump_favicon.svg +68 -0
  106. package/src/assets/app-icons/seed_favicon.svg +91 -0
  107. package/src/assets/app-icons/team_favicon.svg +67 -0
  108. package/src/assets/app-icons/trac_favicon.svg +112 -0
  109. package/src/assets/app-icons/trip_favicon.svg +102 -0
  110. package/src/components/AddressField/AddressField.test.tsx +378 -3
  111. package/src/components/AddressField/AddressField.tsx +2 -2
  112. package/src/components/AddressField/types.ts +2 -2
  113. package/src/components/Alert/Alert.test.tsx +35 -25
  114. package/src/components/Alert/Alert.tsx +8 -8
  115. package/src/components/AppSwitcher/AppSwitcher.test.tsx +1250 -0
  116. package/src/components/AppSwitcher/AppSwitcher.tsx +315 -0
  117. package/src/components/Avatar/Avatar.test.tsx +11 -1
  118. package/src/components/Avatar/Avatar.tsx +3 -2
  119. package/src/components/Badge/Badge.test.tsx +11 -1
  120. package/src/components/Button/Button.test.tsx +13 -3
  121. package/src/components/Calendar/Calendar.test.tsx +523 -131
  122. package/src/components/Calendar/Calendar.tsx +107 -488
  123. package/src/components/Card/Card.test.tsx +220 -249
  124. package/src/components/Checkbox/Checkbox.test.tsx +58 -174
  125. package/src/components/ContextSelector/ContextSelector.tsx +3 -3
  126. package/src/components/ContextSelector/__tests__/ContextSelector.test.tsx +360 -0
  127. package/src/components/DataTable/DataTable.tsx +2 -2
  128. package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +1 -1
  129. package/src/components/DataTable/__tests__/DataTable.export.test.tsx +2 -2
  130. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +1 -1
  131. package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +16 -14
  132. package/src/components/DataTable/__tests__/DataTable.test.tsx +2 -2
  133. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +1 -1
  134. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +76 -580
  135. package/src/components/DataTable/__tests__/README.md +1 -1
  136. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +1 -1
  137. package/src/components/DataTable/__tests__/keyboard.test.tsx +1 -1
  138. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +1 -3
  139. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -6
  140. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +14 -6
  141. package/src/components/DataTable/components/ActionButtons.tsx +9 -4
  142. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +3 -3
  143. package/src/components/DataTable/components/ColumnFilter.tsx +2 -7
  144. package/src/components/DataTable/components/DataTableCore.tsx +44 -52
  145. package/src/components/DataTable/components/DataTableLayout.tsx +37 -26
  146. package/src/components/DataTable/components/DataTableModals.tsx +118 -30
  147. package/src/components/DataTable/components/DataTableToolbar.tsx +2 -2
  148. package/src/components/DataTable/components/EditFields.tsx +6 -47
  149. package/src/components/DataTable/components/EditableRow.tsx +8 -8
  150. package/src/components/DataTable/components/EmptyState.tsx +6 -3
  151. package/src/components/DataTable/components/FilterRow.tsx +18 -11
  152. package/src/components/DataTable/components/GroupingDropdown.tsx +0 -1
  153. package/src/components/DataTable/components/ImportModal.tsx +305 -133
  154. package/src/components/DataTable/components/LoadingState.tsx +2 -2
  155. package/src/components/DataTable/components/PaginationControls.tsx +0 -4
  156. package/src/components/DataTable/components/RowComponent.tsx +42 -22
  157. package/src/components/DataTable/components/UnifiedTableBody.tsx +52 -12
  158. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +51 -463
  159. package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +122 -116
  160. package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +40 -68
  161. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +9 -137
  162. package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +57 -17
  163. package/src/components/DataTable/components/__tests__/DataTableCore.test.tsx +792 -0
  164. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +24 -65
  165. package/src/components/DataTable/components/__tests__/DataTableLayout.test.tsx +467 -0
  166. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +8 -125
  167. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +528 -56
  168. package/src/components/DataTable/components/__tests__/EditFields.test.tsx +526 -0
  169. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +1 -68
  170. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +8 -25
  171. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +3 -62
  172. package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +9 -14
  173. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +50 -186
  174. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +39 -97
  175. package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +13 -103
  176. package/src/components/DataTable/components/__tests__/RowComponent.test.tsx +629 -0
  177. package/src/components/DataTable/components/__tests__/SortIndicator.test.tsx +135 -0
  178. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +31 -171
  179. package/src/components/DataTable/components/__tests__/cellValueUtils.test.ts +453 -0
  180. package/src/components/DataTable/components/hooks/useImportModalFocus.test.ts +184 -0
  181. package/src/components/DataTable/components/hooks/usePermissionTracking.test.ts +381 -0
  182. package/src/components/DataTable/context/DataTableContext.tsx +9 -10
  183. package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +12 -26
  184. package/src/components/DataTable/core/ColumnFactory.ts +3 -3
  185. package/src/components/DataTable/core/ColumnManager.ts +0 -1
  186. package/src/components/DataTable/core/DataManager.ts +4 -2
  187. package/src/components/DataTable/core/LocalDataAdapter.ts +1 -1
  188. package/src/components/DataTable/core/PluginRegistry.ts +2 -2
  189. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +114 -2
  190. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +103 -5
  191. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +57 -0
  192. package/src/components/DataTable/core/__tests__/DataManager.test.ts +63 -0
  193. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +42 -9
  194. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +29 -7
  195. package/src/components/DataTable/core/__tests__/StateManager.test.ts +58 -4
  196. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +16 -21
  197. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +93 -4
  198. package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +227 -54
  199. package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +215 -62
  200. package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +217 -39
  201. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +101 -6
  202. package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +157 -27
  203. package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +80 -0
  204. package/src/components/DataTable/hooks/__tests__/useKeyboardNavigation.test.ts +787 -0
  205. package/src/components/DataTable/hooks/__tests__/useServerSideDataEffect.test.ts +258 -0
  206. package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +128 -77
  207. package/src/components/DataTable/hooks/__tests__/useTableHandlers.test.ts +440 -0
  208. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
  209. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
  210. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +1 -1
  211. package/src/components/DataTable/hooks/useDataTablePermissions.ts +11 -22
  212. package/src/components/DataTable/hooks/useDataTableState.ts +20 -24
  213. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +5 -5
  214. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +13 -1
  215. package/src/components/DataTable/hooks/useTableColumns.ts +15 -39
  216. package/src/components/DataTable/hooks/useTableHandlers.ts +8 -20
  217. package/src/components/DataTable/index.ts +24 -2
  218. package/src/components/DataTable/types.ts +6 -3
  219. package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +3 -67
  220. package/src/components/DataTable/utils/__tests__/aggregationUtils.test.ts +288 -0
  221. package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +3 -60
  222. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +1 -1
  223. package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +9 -21
  224. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +102 -86
  225. package/src/components/DataTable/utils/__tests__/paginationUtils.test.ts +593 -0
  226. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +33 -49
  227. package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +1 -0
  228. package/src/components/DataTable/utils/a11yUtils.ts +1 -1
  229. package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
  230. package/src/components/DataTable/utils/errorHandling.ts +3 -1
  231. package/src/components/DataTable/utils/exportUtils.ts +1 -1
  232. package/src/components/DataTable/utils/flexibleImport.ts +2 -2
  233. package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
  234. package/src/components/DataTable/utils/paginationUtils.ts +1 -1
  235. package/src/components/DataTable/utils/performanceUtils.ts +1 -1
  236. package/src/components/DataTable/utils/selectFieldUtils.ts +0 -7
  237. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +17 -24
  238. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
  239. package/src/components/DateTimeField/DateTimeField.test.tsx +2 -15
  240. package/src/components/DateTimeField/DateTimeField.tsx +1 -1
  241. package/src/components/Dialog/Dialog.test.tsx +2007 -407
  242. package/src/components/Dialog/Dialog.tsx +97 -192
  243. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +2 -62
  244. package/src/components/ErrorBoundary/ErrorBoundaryContext.context.ts +17 -0
  245. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +2 -45
  246. package/src/components/ErrorBoundary/ErrorBoundaryContext.types.ts +41 -0
  247. package/src/components/ErrorBoundary/index.ts +3 -4
  248. package/src/components/ErrorBoundary/useErrorBoundaryContext.ts +20 -0
  249. package/src/components/FileDisplay/FileDisplay.test.tsx +454 -222
  250. package/src/components/FileDisplay/FileDisplay.tsx +14 -12
  251. package/src/components/FileDisplay/index.tsx +1 -1
  252. package/src/components/FileUpload/FileUpload.test.tsx +54 -18
  253. package/src/components/FileUpload/FileUpload.tsx +10 -7
  254. package/src/components/FileUpload/index.tsx +1 -1
  255. package/src/components/Footer/Footer.test.tsx +33 -114
  256. package/src/components/Form/Form.test.tsx +388 -68
  257. package/src/components/Form/Form.tsx +57 -42
  258. package/src/components/Header/Header.test.tsx +645 -154
  259. package/src/components/Header/Header.tsx +52 -43
  260. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
  261. package/src/components/Input/Input.test.tsx +34 -120
  262. package/src/components/Label/Label.test.tsx +47 -46
  263. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +9 -12
  264. package/src/components/LoginForm/LoginForm.test.tsx +0 -1
  265. package/src/components/NavigationMenu/NavigationMenu.test.tsx +1399 -82
  266. package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
  267. package/src/components/NavigationMenu/__tests__/useNavigationFiltering.test.ts +1934 -0
  268. package/src/components/NavigationMenu/useNavigationFiltering.ts +5 -15
  269. package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1307 -0
  270. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +47 -46
  271. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
  272. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +87 -66
  273. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +245 -39
  274. package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -20
  275. package/src/components/PaceAppLayout/README.md +0 -9
  276. package/src/components/PaceAppLayout/test-setup.tsx +15 -9
  277. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +759 -3
  278. package/src/components/PaceLoginPage/PaceLoginPage.tsx +2 -3
  279. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
  280. package/src/components/Progress/Progress.test.tsx +127 -1
  281. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
  282. package/src/components/ProtectedRoute/ProtectedRoute.tsx +24 -6
  283. package/src/components/PublicLayout/PublicLayout.test.tsx +1435 -14
  284. package/src/components/PublicLayout/PublicPageContext.ts +28 -0
  285. package/src/components/PublicLayout/PublicPageLayout.tsx +6 -6
  286. package/src/components/PublicLayout/PublicPageProvider.tsx +2 -41
  287. package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
  288. package/src/components/Select/Select.test.tsx +46 -9
  289. package/src/components/Select/Select.tsx +31 -24
  290. package/src/components/Select/__tests__/context.test.tsx +56 -0
  291. package/src/components/Select/hooks/__tests__/useSelectEvents.test.ts +279 -0
  292. package/src/components/Select/hooks/__tests__/useSelectSearch.test.tsx +295 -0
  293. package/src/components/Select/hooks/__tests__/useSelectState.test.ts +254 -0
  294. package/src/components/Select/hooks/useSelectState.ts +16 -16
  295. package/src/components/Select/types.ts +3 -0
  296. package/src/components/Select/utils/__tests__/text.test.tsx +104 -0
  297. package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +28 -112
  298. package/src/components/Switch/Switch.test.tsx +57 -153
  299. package/src/components/Table/Table.test.tsx +47 -317
  300. package/src/components/Tabs/Tabs.tsx +3 -3
  301. package/src/components/Textarea/Textarea.test.tsx +11 -38
  302. package/src/components/Toast/Toast.test.tsx +78 -569
  303. package/src/components/Tooltip/Tooltip.test.tsx +4 -21
  304. package/src/components/UserMenu/UserMenu.test.tsx +1 -21
  305. package/src/components/UserMenu/UserMenu.tsx +0 -1
  306. package/src/components/__tests__/index.test.ts +346 -0
  307. package/src/components/index.ts +12 -1
  308. package/src/constants/__tests__/performance.test.ts +91 -0
  309. package/src/hooks/__tests__/ServiceHooks.test.tsx +239 -129
  310. package/src/hooks/__tests__/hooks.integration.test.tsx +4 -3
  311. package/src/hooks/__tests__/useApiFetch.unit.test.ts +1 -1
  312. package/src/hooks/__tests__/useAppConfig.unit.test.ts +88 -29
  313. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +282 -98
  314. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +53 -109
  315. package/src/hooks/__tests__/useDataTableState.test.ts +143 -49
  316. package/src/hooks/__tests__/useDebounce.unit.test.ts +94 -19
  317. package/src/hooks/__tests__/useEvents.unit.test.ts +100 -125
  318. package/src/hooks/__tests__/useFileDisplay.test.ts +540 -0
  319. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +1 -4
  320. package/src/hooks/__tests__/useFileUrl.unit.test.ts +27 -247
  321. package/src/hooks/__tests__/useFileUrlCache.test.ts +246 -56
  322. package/src/hooks/__tests__/useFocusManagement.unit.test.ts +442 -68
  323. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +345 -560
  324. package/src/hooks/__tests__/useFormDialog.test.ts +51 -222
  325. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +1 -1
  326. package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +1 -4
  327. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -1
  328. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +1 -1
  329. package/src/hooks/__tests__/usePermissionCache.test.ts +506 -0
  330. package/src/hooks/__tests__/usePreventTabReload.test.ts +255 -36
  331. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +17 -8
  332. package/src/hooks/__tests__/usePublicEvent.test.ts +16 -24
  333. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +12 -4
  334. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +3 -6
  335. package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +1 -2
  336. package/src/hooks/__tests__/useQueryCache.test.ts +313 -66
  337. package/src/hooks/__tests__/useSessionDraft.test.ts +496 -103
  338. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +2 -2
  339. package/src/hooks/__tests__/useStorage.unit.test.ts +72 -102
  340. package/src/hooks/__tests__/useToast.test.ts +413 -0
  341. package/src/hooks/__tests__/useToast.unit.test.tsx +1 -1
  342. package/src/hooks/__tests__/useZodForm.unit.test.tsx +175 -21
  343. package/src/hooks/index.ts +13 -1
  344. package/src/hooks/public/usePublicEvent.test.ts +304 -0
  345. package/src/hooks/public/usePublicEvent.ts +11 -11
  346. package/src/hooks/public/usePublicEventLogo.test.ts +655 -120
  347. package/src/hooks/public/usePublicEventLogo.ts +2 -2
  348. package/src/hooks/public/usePublicFileDisplay.test.ts +723 -0
  349. package/src/hooks/public/usePublicFileDisplay.ts +79 -49
  350. package/src/hooks/public/usePublicRouteParams.test.ts +595 -0
  351. package/src/hooks/public/usePublicRouteParams.ts +2 -2
  352. package/src/hooks/services/useAuthService.ts +1 -1
  353. package/src/hooks/services/useEventService.ts +1 -1
  354. package/src/hooks/useAccessibleApps.test.ts +400 -0
  355. package/src/hooks/useAccessibleApps.ts +264 -0
  356. package/src/hooks/useAddressAutocomplete.test.ts +165 -42
  357. package/src/hooks/useAddressAutocomplete.ts +41 -28
  358. package/src/hooks/useAppConfig.ts +13 -3
  359. package/src/hooks/useDataTablePerformance.ts +13 -12
  360. package/src/hooks/useDataTableState.ts +5 -5
  361. package/src/hooks/useEventTheme.test.ts +66 -17
  362. package/src/hooks/useEventTheme.ts +1 -1
  363. package/src/hooks/useEvents.ts +8 -1
  364. package/src/hooks/useFileDisplay.ts +66 -33
  365. package/src/hooks/useFileReference.test.ts +365 -87
  366. package/src/hooks/useFileReference.ts +2 -6
  367. package/src/hooks/useFileUrlCache.ts +4 -1
  368. package/src/hooks/useFormDialog.ts +2 -2
  369. package/src/hooks/useInactivityTracker.ts +3 -3
  370. package/src/hooks/useOrganisationPermissions.test.ts +1 -2
  371. package/src/hooks/useOrganisationPermissions.ts +1 -4
  372. package/src/hooks/useOrganisationSecurity.test.ts +1 -30
  373. package/src/hooks/useOrganisationSecurity.ts +3 -3
  374. package/src/hooks/useOrganisations.ts +1 -1
  375. package/src/hooks/usePerformanceMonitor.ts +1 -1
  376. package/src/hooks/usePermissionCache.ts +2 -6
  377. package/src/hooks/useQueryCache.ts +7 -7
  378. package/src/hooks/useSessionDraft.ts +14 -11
  379. package/src/hooks/useSessionRestoration.ts +1 -1
  380. package/src/hooks/useStorage.ts +75 -40
  381. package/src/hooks/useToast.ts +2 -2
  382. package/src/hooks/useZodForm.ts +3 -3
  383. package/src/icons/__tests__/index.test.ts +133 -0
  384. package/src/icons/index.ts +1 -1
  385. package/src/index.ts +43 -4
  386. package/src/providers/OrganisationProvider.test.tsx +40 -0
  387. package/src/providers/OrganisationProvider.tsx +5 -5
  388. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
  389. package/src/providers/__tests__/AuthProvider.test.tsx +22 -91
  390. package/src/providers/__tests__/EventProvider.test.tsx +16 -80
  391. package/src/providers/__tests__/InactivityProvider.test.tsx +29 -173
  392. package/src/providers/__tests__/OrganisationProvider.test.tsx +4 -5
  393. package/src/providers/__tests__/OrganisationProvider.wrapper.test.tsx +591 -0
  394. package/src/providers/__tests__/ProviderLifecycle.test.tsx +80 -196
  395. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +40 -133
  396. package/src/providers/__tests__/index.test.ts +138 -0
  397. package/src/providers/services/AuthServiceContext.ts +27 -0
  398. package/src/providers/services/AuthServiceProvider.tsx +81 -20
  399. package/src/providers/services/EventServiceContext.ts +25 -0
  400. package/src/providers/services/EventServiceProvider.tsx +11 -20
  401. package/src/providers/services/InactivityServiceContext.ts +25 -0
  402. package/src/providers/services/InactivityServiceProvider.tsx +7 -17
  403. package/src/providers/services/OrganisationServiceContext.ts +25 -0
  404. package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
  405. package/src/providers/services/UnifiedAuthContext.ts +99 -0
  406. package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
  407. package/src/providers/services/UnifiedAuthProvider.tsx +38 -143
  408. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +61 -95
  409. package/src/providers/services/__tests__/AuthServiceProvider.test.tsx +638 -0
  410. package/src/providers/services/__tests__/EventServiceProvider.test.tsx +839 -0
  411. package/src/providers/services/__tests__/InactivityServiceProvider.test.tsx +662 -0
  412. package/src/providers/services/__tests__/OrganisationServiceProvider.test.tsx +440 -0
  413. package/src/providers/services/__tests__/UnifiedAuthProvider.advanced.test.tsx +435 -0
  414. package/src/providers/services/__tests__/UnifiedAuthProvider.appId.test.tsx +408 -0
  415. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +55 -48
  416. package/src/providers/services/__tests__/contexts.test.tsx +281 -0
  417. package/src/providers/services/__tests__/useUnifiedAuth.test.tsx +251 -0
  418. package/src/providers/services/useUnifiedAuth.ts +29 -0
  419. package/src/rbac/README.md +5 -5
  420. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +9 -14
  421. package/src/rbac/__tests__/audit-batched.test.ts +550 -0
  422. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +1 -14
  423. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +43 -12
  424. package/src/rbac/__tests__/cache-invalidation.test.ts +8 -14
  425. package/src/rbac/__tests__/engine.comprehensive.test.ts +2 -7
  426. package/src/rbac/__tests__/index.test.ts +107 -0
  427. package/src/rbac/__tests__/performance.test.ts +451 -0
  428. package/src/rbac/__tests__/rbac-core.test.tsx +2 -2
  429. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -5
  430. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +1 -7
  431. package/src/rbac/__tests__/rbac-functions.test.ts +0 -1
  432. package/src/rbac/__tests__/rbac-integration.test.ts +0 -1
  433. package/src/rbac/__tests__/scenarios.user-role.test.tsx +21 -32
  434. package/src/rbac/adapters.test.tsx +654 -0
  435. package/src/rbac/adapters.tsx +24 -9
  436. package/src/rbac/api.test.ts +13 -217
  437. package/src/rbac/api.ts +85 -16
  438. package/src/rbac/audit-batched.ts +5 -4
  439. package/src/rbac/audit.test.ts +225 -28
  440. package/src/rbac/audit.ts +22 -17
  441. package/src/rbac/cache-invalidation.ts +18 -15
  442. package/src/rbac/cache.test.ts +123 -63
  443. package/src/rbac/cache.ts +3 -4
  444. package/src/rbac/components/AccessDenied.tsx +20 -18
  445. package/src/rbac/components/NavigationGuard.tsx +10 -8
  446. package/src/rbac/components/PagePermissionGuard.tsx +27 -25
  447. package/src/rbac/components/__tests__/AccessDenied.test.tsx +324 -0
  448. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +242 -71
  449. package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +20 -37
  450. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +18 -17
  451. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +452 -129
  452. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -13
  453. package/src/rbac/config.test.ts +131 -48
  454. package/src/rbac/config.ts +11 -8
  455. package/src/rbac/docs/event-based-apps.md +26 -13
  456. package/src/rbac/engine.test.ts +496 -146
  457. package/src/rbac/engine.ts +53 -13
  458. package/src/rbac/errors.test.ts +99 -87
  459. package/src/rbac/eslint-rules.js +2 -2
  460. package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -5
  461. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +601 -1
  462. package/src/rbac/hooks/permissions/__tests__/useAccessLevel.test.ts +622 -0
  463. package/src/rbac/hooks/permissions/__tests__/useCan.test.ts +798 -0
  464. package/src/rbac/hooks/permissions/__tests__/useMultiplePermissions.test.ts +843 -0
  465. package/src/rbac/hooks/permissions/__tests__/usePermissions.test.ts +545 -0
  466. package/src/rbac/hooks/permissions/useAccessLevel.ts +7 -8
  467. package/src/rbac/hooks/permissions/useCan.ts +12 -10
  468. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +57 -8
  469. package/src/rbac/hooks/permissions/usePermissions.ts +15 -14
  470. package/src/rbac/hooks/useCan.test.ts +319 -3
  471. package/src/rbac/hooks/usePermissions.test.ts +426 -0
  472. package/src/rbac/hooks/usePermissions.ts +5 -7
  473. package/src/rbac/hooks/useRBAC.test.ts +1669 -2
  474. package/src/rbac/hooks/useRBAC.ts +7 -11
  475. package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
  476. package/src/rbac/hooks/useResolvedScope.ts +4 -1
  477. package/src/rbac/hooks/useResourcePermissions.test.ts +538 -1
  478. package/src/rbac/hooks/useResourcePermissions.ts +9 -7
  479. package/src/rbac/hooks/useRoleManagement.test.ts +659 -1
  480. package/src/rbac/hooks/useRoleManagement.ts +16 -12
  481. package/src/rbac/hooks/useSecureSupabase.ts +11 -12
  482. package/src/rbac/index.ts +32 -32
  483. package/src/rbac/permissions.test.ts +149 -68
  484. package/src/rbac/permissions.ts +0 -3
  485. package/src/rbac/request-deduplication.test.ts +347 -0
  486. package/src/rbac/secureClient.test.ts +112 -159
  487. package/src/rbac/secureClient.ts +46 -26
  488. package/src/rbac/security.test.ts +125 -44
  489. package/src/rbac/security.ts +7 -6
  490. package/src/rbac/types.test.ts +236 -0
  491. package/src/rbac/types.ts +7 -5
  492. package/src/rbac/utils/__tests__/clientSecurity.test.ts +192 -0
  493. package/src/rbac/utils/__tests__/contextValidator.test.ts +1 -3
  494. package/src/rbac/utils/__tests__/deep-equal.test.ts +23 -0
  495. package/src/rbac/utils/__tests__/eventContext.test.ts +10 -57
  496. package/src/rbac/utils/clientSecurity.ts +6 -4
  497. package/src/rbac/utils/contextValidator.ts +1 -2
  498. package/src/rbac/utils/eventContext.ts +2 -2
  499. package/src/services/AuthService.ts +13 -11
  500. package/src/services/EventService.ts +4 -5
  501. package/src/services/OrganisationService.ts +13 -30
  502. package/src/services/__tests__/AuthService.edge-cases.test.ts +746 -0
  503. package/src/services/__tests__/AuthService.restoreSession.test.ts +23 -3
  504. package/src/services/__tests__/AuthService.test.ts +4 -8
  505. package/src/services/__tests__/BaseService.edge-cases.test.ts +506 -0
  506. package/src/services/__tests__/BaseService.test.ts +49 -0
  507. package/src/services/__tests__/EventService.edge-cases.test.ts +633 -0
  508. package/src/services/__tests__/EventService.eventColours.test.ts +0 -12
  509. package/src/services/__tests__/EventService.test.ts +0 -7
  510. package/src/services/__tests__/InactivityService.edge-cases.test.ts +492 -0
  511. package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -5
  512. package/src/services/__tests__/OrganisationService.edge-cases.test.ts +633 -0
  513. package/src/services/base/BaseService.test.ts +214 -0
  514. package/src/services/interfaces/IOrganisationService.ts +0 -1
  515. package/src/services/interfaces/__tests__/IAuthService.test.ts +190 -0
  516. package/src/services/interfaces/__tests__/IEventService.test.ts +176 -0
  517. package/src/services/interfaces/__tests__/IInactivityService.test.ts +183 -0
  518. package/src/services/interfaces/__tests__/IOrganisationService.test.ts +207 -0
  519. package/src/styles/core.css +1 -0
  520. package/src/theming/__tests__/runtime.test.ts +29 -94
  521. package/src/theming/parseEventColours.ts +18 -9
  522. package/src/theming/runtime.ts +1 -5
  523. package/src/types/__tests__/core.test.ts +397 -0
  524. package/src/types/__tests__/database-generated.test.ts +78 -0
  525. package/src/types/__tests__/file-reference.test.ts +270 -366
  526. package/src/types/__tests__/guards.test.ts +26 -26
  527. package/src/types/__tests__/index.test.ts +265 -0
  528. package/src/types/__tests__/type-validation.test.ts +3 -3
  529. package/src/types/__tests__/validation.test.ts +0 -2
  530. package/src/types/auth.ts +0 -1
  531. package/src/types/event.ts +1 -1
  532. package/src/types/rpc-responses.ts +33 -0
  533. package/src/types/supabase.ts +1 -2
  534. package/src/types/vitest-globals.d.ts +1 -1
  535. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +64 -77
  536. package/src/utils/__tests__/dynamicUtils.unit.test.ts +13 -0
  537. package/src/utils/__tests__/formatDate.unit.test.ts +1 -1
  538. package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -1
  539. package/src/utils/__tests__/logger.unit.test.ts +1 -1
  540. package/src/utils/__tests__/performanceBenchmark.test.ts +1 -2
  541. package/src/utils/__tests__/performanceBudgets.unit.test.ts +48 -13
  542. package/src/utils/__tests__/request-deduplication.test.ts +349 -0
  543. package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -1
  544. package/src/utils/__tests__/timezone.test.ts +1 -1
  545. package/src/utils/__tests__/validation.unit.test.ts +1 -2
  546. package/src/utils/__tests__/validationUtils.unit.test.ts +1 -1
  547. package/src/utils/app/appConfig.test.ts +235 -0
  548. package/src/utils/app/appIdResolver.test.ts +188 -20
  549. package/src/utils/app/appNameResolver.test.ts +18 -10
  550. package/src/utils/app/appNameResolver.ts +11 -9
  551. package/src/utils/app/appPortMap.test.ts +125 -0
  552. package/src/utils/app/appPortMap.ts +51 -0
  553. package/src/utils/app/buildAppUrl.test.ts +273 -0
  554. package/src/utils/app/buildAppUrl.ts +114 -0
  555. package/src/utils/audit/audit.test.ts +354 -39
  556. package/src/utils/context/organisationContext.test.ts +10 -4
  557. package/src/utils/context/organisationContext.ts +5 -5
  558. package/src/utils/context/sessionTracking.test.ts +354 -0
  559. package/src/utils/core/__tests__/cn.test.ts +66 -0
  560. package/src/utils/core/__tests__/debugLogger.test.ts +113 -0
  561. package/src/utils/core/__tests__/logger.test.ts +217 -0
  562. package/src/utils/core/debugLogger.ts +15 -8
  563. package/src/utils/core/logger.ts +20 -16
  564. package/src/utils/device/deviceFingerprint.test.ts +8 -5
  565. package/src/utils/device/deviceFingerprint.ts +3 -3
  566. package/src/utils/dynamic/__tests__/dynamicUtils.test.ts +185 -0
  567. package/src/utils/dynamic/__tests__/lazyLoad.test.tsx +156 -0
  568. package/src/utils/dynamic/createLazyComponent.tsx +38 -0
  569. package/src/utils/dynamic/dynamicUtils.ts +6 -6
  570. package/src/utils/dynamic/lazyLoad.tsx +8 -36
  571. package/src/utils/dynamic/papaparseLoader.ts +7 -0
  572. package/src/utils/file-reference/__tests__/file-reference.test.ts +583 -145
  573. package/src/utils/file-reference/index.ts +0 -1
  574. package/src/utils/formatting/formatDate.test.ts +22 -148
  575. package/src/utils/formatting/formatDateTime.test.ts +41 -119
  576. package/src/utils/formatting/formatDateTimeTimezone.test.ts +40 -84
  577. package/src/utils/formatting/formatNumber.test.ts +259 -0
  578. package/src/utils/formatting/formatTime.test.ts +36 -128
  579. package/src/utils/formatting/formatting.ts +1 -1
  580. package/src/utils/google-places/googlePlacesUtils.test.ts +72 -3
  581. package/src/utils/google-places/googlePlacesUtils.ts +15 -2
  582. package/src/utils/google-places/loadGoogleMapsScript.test.ts +58 -1
  583. package/src/utils/google-places/loadGoogleMapsScript.ts +2 -1
  584. package/src/utils/index.ts +52 -11
  585. package/src/utils/location/location.test.ts +18 -115
  586. package/src/utils/performance/__tests__/bundleAnalysis.test.ts +148 -0
  587. package/src/utils/performance/__tests__/performanceBenchmark.test.ts +251 -0
  588. package/src/utils/performance/__tests__/performanceBudgets.test.ts +241 -0
  589. package/src/utils/performance/bundleAnalysis.ts +16 -22
  590. package/src/utils/performance/performanceBenchmark.ts +12 -4
  591. package/src/utils/performance/performanceBudgets.ts +9 -6
  592. package/src/utils/permissions/__tests__/permissionTypes.test.ts +149 -0
  593. package/src/utils/permissions/permissionUtils.test.ts +20 -42
  594. package/src/utils/persistence/__tests__/keyDerivation.test.ts +180 -9
  595. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +164 -16
  596. package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
  597. package/src/utils/request-deduplication.ts +6 -4
  598. package/src/utils/security/auth-utils.ts +7 -7
  599. package/src/utils/security/secureDataAccess.test.ts +22 -191
  600. package/src/utils/security/secureErrors.test.ts +163 -0
  601. package/src/utils/security/secureStorage.test.ts +156 -0
  602. package/src/utils/security/secureStorage.ts +1 -1
  603. package/src/utils/security/security.test.ts +204 -0
  604. package/src/utils/security/securityMonitor.test.ts +90 -0
  605. package/src/utils/security/securityMonitor.ts +1 -1
  606. package/src/utils/storage/__tests__/config.unit.test.ts +239 -0
  607. package/src/utils/storage/__tests__/index.unit.test.ts +64 -12
  608. package/src/utils/storage/helpers.test.ts +757 -430
  609. package/src/utils/storage/helpers.ts +1 -2
  610. package/src/utils/storage/{index.ts → storageUtils.ts} +1 -36
  611. package/src/utils/storage/types.ts +2 -2
  612. package/src/utils/supabase/createBaseClient.test.ts +201 -0
  613. package/src/utils/supabase/createBaseClient.ts +2 -1
  614. package/src/utils/timezone/timezone.test.ts +25 -43
  615. package/src/utils/validation/__tests__/common.test.ts +115 -0
  616. package/src/utils/validation/__tests__/csrf.test.ts +65 -0
  617. package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +27 -7
  618. package/src/utils/validation/__tests__/passwordSchema.test.ts +164 -0
  619. package/src/utils/validation/__tests__/schema.test.ts +127 -0
  620. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +76 -3
  621. package/src/utils/validation/__tests__/user.test.ts +173 -0
  622. package/src/utils/validation/__tests__/validation.test.ts +197 -0
  623. package/src/utils/validation/__tests__/validationUtils.test.ts +265 -43
  624. package/src/utils/validation/htmlSanitization.ts +27 -31
  625. package/src/utils/validation/schema.ts +6 -3
  626. package/src/utils/validation/sqlInjectionProtection.ts +2 -2
  627. package/src/vite-env.d.ts +6 -0
  628. package/dist/DataTable-DRUIgtUH.d.ts +0 -166
  629. package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
  630. package/dist/audit-MYQXYZFU.js +0 -3
  631. package/dist/chunk-7ILTDCL2.js +0 -80
  632. package/dist/chunk-EF2UGZWY.js +0 -611
  633. package/dist/chunk-FEJLJNWA.js +0 -181
  634. package/dist/chunk-GS5672WG.js +0 -2003
  635. package/dist/chunk-S6ZQKDY6.js +0 -62
  636. package/dist/chunk-Z2FNRKF3.js +0 -994
  637. package/dist/useToast-AyaT-x7p.d.ts +0 -68
  638. package/src/components/DataTable/components/index.ts +0 -16
  639. package/src/components/DataTable/core/index.ts +0 -1
  640. package/src/components/DataTable/hooks/index.ts +0 -13
  641. package/src/components/DataTable/utils/index.ts +0 -10
  642. package/src/components/PublicLayout/index.ts +0 -32
  643. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
  644. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
  645. package/src/hooks/public/index.ts +0 -36
  646. package/src/hooks/usePermissionCache.test.ts +0 -536
  647. package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
  648. package/src/rbac/audit-enhanced.ts +0 -384
  649. package/src/rbac/compliance/database-validator.ts +0 -165
  650. package/src/rbac/compliance/index.ts +0 -48
  651. package/src/rbac/compliance/pattern-detector.ts +0 -553
  652. package/src/rbac/compliance/quick-fix-suggestions.ts +0 -209
  653. package/src/rbac/compliance/runtime-compliance.ts +0 -99
  654. package/src/rbac/compliance/setup-validator.ts +0 -131
  655. package/src/rbac/components/index.ts +0 -26
  656. package/src/rbac/hooks/index.ts +0 -34
  657. package/src/rbac/hooks/permissions/index.ts +0 -4
  658. package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
  659. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
  660. package/src/utils/app/appNameResolver.simple.test.ts +0 -212
  661. package/src/utils/google-places/index.ts +0 -26
  662. package/src/utils/location/index.ts +0 -16
  663. package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
  664. package/src/utils/timezone/index.ts +0 -17
  665. package/src/utils/validation/index.ts +0 -73
@@ -10,17 +10,20 @@
10
10
  */
11
11
 
12
12
  import React from 'react';
13
- import { screen, waitFor } from '@testing-library/react';
13
+ import { screen } from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
16
16
  import { User } from '@supabase/supabase-js';
17
- import { Header, HeaderProps } from './Header';
17
+ import { Header } from './Header';
18
18
  import { renderWithProviders } from '../../__tests__/helpers/test-utils';
19
19
  import type { NavigationItem } from '../NavigationMenu';
20
+ import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
21
+ import { useOrganisations } from '../../hooks/useOrganisations';
22
+ import { useEvents } from '../../hooks/useEvents';
20
23
 
21
24
  // Mock the child components
22
25
  vi.mock('../NavigationMenu', () => ({
23
- NavigationMenu: ({ items, onNavigate, currentPath, buttonText, className }: any) => (
26
+ NavigationMenu: ({ items, _onNavigate, _currentPath, buttonText, className }: any) => (
24
27
  <div data-testid="navigation-menu" className={className}>
25
28
  <button data-testid="navigation-menu-trigger">{buttonText}</button>
26
29
  {items?.map((item: any) => (
@@ -52,12 +55,23 @@ vi.mock('../UserMenu', () => ({
52
55
  ),
53
56
  }));
54
57
 
55
- vi.mock('../ContextSelector', () => ({
56
- ContextSelector: ({ placeholder, className, 'data-testid': testId }: any) => (
57
- <div data-testid={testId || 'context-selector'} className={className}>
58
+ const mockContextSelector = vi.hoisted(() =>
59
+ vi.fn(({ placeholder, className, showOrganisations, showEvents, onOrganisationSelect, onEventSelect, 'data-testid': testId }: any) => (
60
+ <div
61
+ data-testid={testId || 'context-selector'}
62
+ className={className}
63
+ data-show-orgs={showOrganisations}
64
+ data-show-events={showEvents}
65
+ data-on-org-select={onOrganisationSelect ? 'present' : 'absent'}
66
+ data-on-event-select={onEventSelect ? 'present' : 'absent'}
67
+ >
58
68
  <button>{placeholder || 'Select organisation or event'}</button>
59
69
  </div>
60
- ),
70
+ ))
71
+ );
72
+
73
+ vi.mock('../ContextSelector', () => ({
74
+ ContextSelector: mockContextSelector,
61
75
  }));
62
76
 
63
77
  // Mock useEventService to prevent provider requirement
@@ -72,6 +86,36 @@ vi.mock('../../hooks/services/useEventService', () => ({
72
86
  }))
73
87
  }));
74
88
 
89
+ // Mock useUnifiedAuth to prevent infinite loops and async calls
90
+ vi.mock('../../providers/services/UnifiedAuthProvider', () => ({
91
+ useUnifiedAuth: vi.fn(() => ({
92
+ user: null,
93
+ isAuthenticated: false,
94
+ appName: undefined,
95
+ session: null,
96
+ authLoading: false,
97
+ authError: null,
98
+ supabase: null,
99
+ })),
100
+ }));
101
+
102
+ // Mock AppSwitcher to prevent async hook calls that could hang
103
+ // AppSwitcher uses useAccessibleApps which makes async database queries
104
+ vi.mock('../AppSwitcher/AppSwitcher', () => ({
105
+ AppSwitcher: ({ trigger, currentAppName: _currentAppName, showSwitcher, className }: any) => {
106
+ // If showSwitcher is false, return null or trigger (matches actual behavior)
107
+ if (showSwitcher === false) {
108
+ return trigger || null;
109
+ }
110
+ // If trigger is provided, return it wrapped in a div (simulating Select component)
111
+ if (trigger) {
112
+ return <div data-testid="app-switcher" className={className}>{trigger}</div>;
113
+ }
114
+ // Otherwise return a div with testid (when no trigger, AppSwitcher renders its own trigger)
115
+ return <div data-testid="app-switcher" className={className}>App Switcher</div>;
116
+ },
117
+ }));
118
+
75
119
  // Mock useOrganisations hook
76
120
  vi.mock('../../hooks/useOrganisations', () => ({
77
121
  useOrganisations: vi.fn(() => ({
@@ -100,6 +144,19 @@ vi.mock('../../hooks/useOrganisations', () => ({
100
144
  }))
101
145
  }));
102
146
 
147
+ // Mock useEvents hook
148
+ vi.mock('../../hooks/useEvents', () => ({
149
+ useEvents: vi.fn(() => ({
150
+ events: [],
151
+ selectedEvent: null,
152
+ selectedEventId: null,
153
+ eventLoading: false,
154
+ eventError: null,
155
+ setSelectedEvent: vi.fn(),
156
+ refreshEvents: vi.fn(),
157
+ })),
158
+ }));
159
+
103
160
  // Test data
104
161
  const mockUser: User = {
105
162
  id: '123',
@@ -118,16 +175,7 @@ const mockNavItems: NavigationItem[] = [
118
175
  { id: 'settings', label: 'Settings', href: '/settings' },
119
176
  ];
120
177
 
121
- const mockProps: HeaderProps = {
122
- logoUrl: '/test-logo.svg',
123
- logoAlt: 'Test Logo',
124
- navItems: mockNavItems,
125
- user: mockUser,
126
- onSignOut: vi.fn(),
127
- onChangePassword: vi.fn(),
128
- currentPath: '/dashboard',
129
- onNavigate: vi.fn(),
130
- };
178
+ // mockProps removed - not used in tests
131
179
 
132
180
  describe('Header Component', () => {
133
181
  beforeEach(() => {
@@ -175,44 +223,12 @@ describe('Header Component', () => {
175
223
  expect(logo).toHaveAttribute('alt', 'Test Logo');
176
224
  });
177
225
 
178
- it('renders custom logo component when provided', () => {
179
- const CustomLogo = () => <div data-testid="custom-logo">Custom Logo</div>;
180
-
181
- renderWithProviders(<Header logo={<CustomLogo />} />);
182
-
183
- expect(screen.getByTestId('custom-logo')).toBeInTheDocument();
184
- expect(screen.queryByRole('img')).not.toBeInTheDocument();
185
- });
186
-
187
- it('renders default logo when no logo props provided', () => {
188
- renderWithProviders(<Header />);
189
-
190
- const logo = screen.getByRole('img', { name: 'Logo' });
191
- expect(logo).toBeInTheDocument();
192
- expect(logo).toBeVisible();
193
- expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
194
- });
195
-
196
- it('uses logoAlt as fallback when logoUrl provided without logoAlt', () => {
197
- renderWithProviders(<Header logoUrl="/test-logo.svg" />);
198
-
199
- const logo = screen.getByRole('img', { name: 'Logo' });
200
- expect(logo).toHaveAttribute('alt', 'Logo');
201
- });
202
-
203
- it('prioritizes custom logo over logoUrl', () => {
204
- const CustomLogo = () => <div data-testid="custom-logo">Custom Logo</div>;
205
-
206
- renderWithProviders(
207
- <Header
208
- logo={<CustomLogo />}
209
- logoUrl="/test-logo.svg"
210
- logoAlt="Test Logo"
211
- />
212
- );
226
+ it('renders clickable logo when logoHref is provided with logoUrl', () => {
227
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoHref="/dashboard" />);
213
228
 
214
- expect(screen.getByTestId('custom-logo')).toBeInTheDocument();
215
- expect(screen.queryByRole('img')).not.toBeInTheDocument();
229
+ const logoLink = screen.getByRole('img', { name: 'Logo' }).closest('a');
230
+ expect(logoLink).toBeInTheDocument();
231
+ expect(logoLink).toHaveAttribute('href', '/dashboard');
216
232
  });
217
233
  });
218
234
 
@@ -223,16 +239,17 @@ describe('Header Component', () => {
223
239
 
224
240
  expect(screen.getByTestId('navigation-menu')).toBeInTheDocument();
225
241
  expect(screen.getByTestId('navigation-menu-trigger')).toHaveTextContent('Menu');
226
- });
227
-
228
- it('does not render navigation menu when navItems empty', () => {
229
- renderWithProviders(<Header navItems={[]} />);
230
242
 
231
- expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
243
+ mockNavItems.forEach(item => {
244
+ expect(screen.getByTestId(`nav-item-${item.id}`)).toHaveTextContent(item.label);
245
+ });
232
246
  });
233
247
 
234
- it('does not render navigation menu when navItems undefined', () => {
235
- renderWithProviders(<Header />);
248
+ it.each([
249
+ { navItems: [], description: 'empty array' },
250
+ { navItems: undefined, description: 'undefined' },
251
+ ])('does not render navigation menu when navItems is $description', ({ navItems }) => {
252
+ renderWithProviders(<Header navItems={navItems} />);
236
253
 
237
254
  expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
238
255
  });
@@ -249,39 +266,26 @@ describe('Header Component', () => {
249
266
  );
250
267
 
251
268
  expect(screen.getByTestId('navigation-menu')).toBeInTheDocument();
252
- // NavigationMenu component should receive the props
253
- });
254
-
255
- it('renders all navigation items', () => {
256
- renderWithProviders(<Header navItems={mockNavItems} />);
257
-
258
- mockNavItems.forEach(item => {
259
- expect(screen.getByTestId(`nav-item-${item.id}`)).toHaveTextContent(item.label);
260
- });
261
269
  });
262
270
  });
263
271
 
264
272
  // User menu tests
265
273
  describe('User Menu', () => {
266
- it('renders user menu when showUserMenu is true', () => {
267
- renderWithProviders(<Header user={mockUser} showUserMenu={true} />);
268
-
269
- expect(screen.getByTestId('user-menu')).toBeInTheDocument();
270
- });
271
-
272
- it('does not render user menu when showUserMenu is false', () => {
273
- renderWithProviders(<Header user={mockUser} showUserMenu={false} />);
274
-
275
- expect(screen.queryByTestId('user-menu')).not.toBeInTheDocument();
276
- });
277
-
278
- it('renders user menu by default when user provided', () => {
279
- renderWithProviders(<Header user={mockUser} />);
280
-
281
- expect(screen.getByTestId('user-menu')).toBeInTheDocument();
274
+ it.each([
275
+ { showUserMenu: true, shouldRender: true },
276
+ { showUserMenu: false, shouldRender: false },
277
+ { showUserMenu: undefined, shouldRender: true }, // default is true
278
+ ])('renders user menu based on showUserMenu prop ($showUserMenu)', ({ showUserMenu, shouldRender }) => {
279
+ renderWithProviders(<Header user={mockUser} showUserMenu={showUserMenu} />);
280
+
281
+ if (shouldRender) {
282
+ expect(screen.getByTestId('user-menu')).toBeInTheDocument();
283
+ } else {
284
+ expect(screen.queryByTestId('user-menu')).not.toBeInTheDocument();
285
+ }
282
286
  });
283
287
 
284
- it('displays user email in user menu trigger', () => {
288
+ it('displays user email when user is provided', () => {
285
289
  renderWithProviders(<Header user={mockUser} />);
286
290
 
287
291
  expect(screen.getByTestId('user-menu-trigger')).toHaveTextContent('test@example.com');
@@ -293,7 +297,7 @@ describe('Header Component', () => {
293
297
  expect(screen.getByTestId('user-menu-trigger')).toHaveTextContent('Sign In');
294
298
  });
295
299
 
296
- it('renders custom user menu when provided', () => {
300
+ it('renders custom user menu when provided (overrides default)', () => {
297
301
  const CustomUserMenu = () => <div data-testid="custom-user-menu">Custom Menu</div>;
298
302
 
299
303
  renderWithProviders(<Header user={mockUser} userMenu={<CustomUserMenu />} />);
@@ -320,36 +324,19 @@ describe('Header Component', () => {
320
324
 
321
325
  // Context selector tests
322
326
  describe('Context Selector', () => {
323
- it('renders context selector by default', () => {
324
- renderWithProviders(<Header />);
325
-
326
- expect(screen.getByTestId('context-selector')).toBeInTheDocument();
327
- });
328
-
329
- it('does not render context selector when showContextSelector is false', () => {
330
- renderWithProviders(<Header showContextSelector={false} />);
331
-
332
- expect(screen.queryByTestId('context-selector')).not.toBeInTheDocument();
333
- });
334
-
335
- it('renders context selector when showContextSelector is explicitly true', () => {
336
- renderWithProviders(<Header showContextSelector={true} />);
337
-
338
- expect(screen.getByTestId('context-selector')).toBeInTheDocument();
339
- });
340
-
341
- it('applies correct className to context selector', () => {
342
- renderWithProviders(<Header showContextSelector={true} />);
343
-
344
- const contextSelector = screen.getByTestId('context-selector');
345
- expect(contextSelector).toBeInTheDocument();
346
- expect(contextSelector).toBeVisible();
347
- });
348
-
349
- it('shows correct placeholder text', () => {
350
- renderWithProviders(<Header showContextSelector={true} />);
351
-
352
- expect(screen.getByRole('button', { name: 'Select organisation or event' })).toBeInTheDocument();
327
+ it.each([
328
+ { showContextSelector: true, shouldRender: true },
329
+ { showContextSelector: false, shouldRender: false },
330
+ { showContextSelector: undefined, shouldRender: true }, // default is true
331
+ ])('renders context selector based on showContextSelector prop ($showContextSelector)', ({ showContextSelector, shouldRender }) => {
332
+ renderWithProviders(<Header showContextSelector={showContextSelector} />);
333
+
334
+ if (shouldRender) {
335
+ expect(screen.getByTestId('context-selector')).toBeInTheDocument();
336
+ expect(screen.getByRole('button', { name: 'Select organisation or event' })).toBeInTheDocument();
337
+ } else {
338
+ expect(screen.queryByTestId('context-selector')).not.toBeInTheDocument();
339
+ }
353
340
  });
354
341
 
355
342
  it('preserves layout when context selector is hidden', () => {
@@ -365,10 +352,7 @@ describe('Header Component', () => {
365
352
  expect(nav).toBeInTheDocument();
366
353
  expect(nav).toBeVisible();
367
354
 
368
- // Context selector should not be rendered
369
355
  expect(screen.queryByTestId('context-selector')).not.toBeInTheDocument();
370
-
371
- // User menu should still be present and positioned correctly
372
356
  expect(screen.getByTestId('user-menu')).toBeInTheDocument();
373
357
  });
374
358
  });
@@ -460,28 +444,16 @@ describe('Header Component', () => {
460
444
  });
461
445
 
462
446
  describe('Layout and Responsive Design', () => {
463
- it('applies correct grid layout classes', () => {
464
- renderWithProviders(<Header />);
465
-
466
- const nav = screen.getByRole('navigation');
467
- expect(nav).toBeInTheDocument();
468
- expect(nav).toBeVisible();
469
- });
470
-
471
- it('applies correct container classes', () => {
447
+ it('applies correct layout structure', () => {
472
448
  renderWithProviders(<Header />);
473
449
 
450
+ const header = screen.getByRole('banner');
474
451
  const nav = screen.getByRole('navigation');
475
- expect(nav).toBeInTheDocument();
476
- expect(nav).toBeVisible();
477
- });
478
-
479
- it('maintains proper header height', () => {
480
- renderWithProviders(<Header />);
481
452
 
482
- const header = screen.getByRole('banner');
483
453
  expect(header).toBeInTheDocument();
484
454
  expect(header).toBeVisible();
455
+ expect(nav).toBeInTheDocument();
456
+ expect(nav).toBeVisible();
485
457
  });
486
458
  });
487
459
 
@@ -518,42 +490,51 @@ describe('Header Component', () => {
518
490
 
519
491
  // Edge cases and error handling
520
492
  describe('Edge Cases and Error Handling', () => {
521
- it('handles null user gracefully', () => {
522
- renderWithProviders(<Header user={null} />);
493
+ it.each([
494
+ { user: null, description: 'null user' },
495
+ { user: undefined, description: 'undefined user' },
496
+ ])('handles $description gracefully', ({ user }) => {
497
+ renderWithProviders(<Header user={user} />);
523
498
 
524
499
  expect(screen.getByTestId('user-menu-trigger')).toHaveTextContent('Sign In');
525
500
  });
526
501
 
527
- it('handles undefined user gracefully', () => {
528
- renderWithProviders(<Header user={undefined} />);
502
+ it.each([
503
+ { navItems: [], description: 'empty array' },
504
+ { navItems: undefined, description: 'undefined' },
505
+ ])('does not render navigation menu when navItems is $description', ({ navItems }) => {
506
+ renderWithProviders(<Header navItems={navItems} />);
529
507
 
530
- expect(screen.getByTestId('user-menu-trigger')).toHaveTextContent('Sign In');
508
+ expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
531
509
  });
532
510
 
533
- it('handles empty navItems array', () => {
534
- renderWithProviders(<Header navItems={[]} />);
511
+ it('handles missing logoUrl gracefully (uses default)', () => {
512
+ renderWithProviders(<Header logoAlt="Test Logo" />);
535
513
 
536
- expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
514
+ const logo = screen.getByRole('img', { name: 'Test Logo' });
515
+ expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
537
516
  });
538
517
 
539
- it('handles undefined navItems', () => {
540
- renderWithProviders(<Header navItems={undefined} />);
518
+ it('handles missing logoAlt gracefully (uses default)', () => {
519
+ renderWithProviders(<Header logoUrl="/test-logo.svg" />);
541
520
 
542
- expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
521
+ const logo = screen.getByRole('img', { name: 'Logo' });
522
+ expect(logo).toHaveAttribute('alt', 'Logo');
543
523
  });
544
524
 
545
- it('handles missing logoUrl gracefully', () => {
546
- renderWithProviders(<Header logoAlt="Test Logo" />);
525
+ it('handles logoHref for clickable logo', () => {
526
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoHref="/dashboard" />);
547
527
 
548
- const logo = screen.getByRole('img', { name: 'Test Logo' });
549
- expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
528
+ const logoLink = screen.getByRole('img', { name: 'Logo' }).closest('a');
529
+ expect(logoLink).toBeInTheDocument();
530
+ expect(logoLink).toHaveAttribute('href', '/dashboard');
550
531
  });
551
532
 
552
- it('handles missing logoAlt gracefully', () => {
533
+ it('renders logo without link when logoHref is not provided', () => {
553
534
  renderWithProviders(<Header logoUrl="/test-logo.svg" />);
554
535
 
555
536
  const logo = screen.getByRole('img', { name: 'Logo' });
556
- expect(logo).toHaveAttribute('alt', 'Logo');
537
+ expect(logo.closest('a')).not.toBeInTheDocument();
557
538
  });
558
539
  });
559
540
 
@@ -639,4 +620,514 @@ describe('Header Component', () => {
639
620
  expect(screen.getByTestId('user-menu')).toBeInTheDocument();
640
621
  });
641
622
  });
623
+
624
+ // AppSwitcher tests
625
+ describe('AppSwitcher Integration', () => {
626
+ beforeEach(() => {
627
+ // Reset to default mock state
628
+ vi.mocked(useUnifiedAuth).mockReturnValue({
629
+ user: null,
630
+ isAuthenticated: false,
631
+ appName: undefined,
632
+ session: null,
633
+ authLoading: false,
634
+ authError: null,
635
+ supabase: null,
636
+ });
637
+ });
638
+
639
+ it('renders AppSwitcher when authenticated and appName is provided', () => {
640
+ vi.mocked(useUnifiedAuth).mockReturnValue({
641
+ user: mockUser,
642
+ isAuthenticated: true,
643
+ appName: 'TEST',
644
+ session: null,
645
+ authLoading: false,
646
+ authError: null,
647
+ supabase: null,
648
+ });
649
+
650
+ renderWithProviders(<Header />);
651
+
652
+ expect(screen.getByTestId('app-switcher')).toBeInTheDocument();
653
+ });
654
+
655
+ it('does not render AppSwitcher when not authenticated', () => {
656
+ vi.mocked(useUnifiedAuth).mockReturnValue({
657
+ user: null,
658
+ isAuthenticated: false,
659
+ appName: undefined,
660
+ session: null,
661
+ authLoading: false,
662
+ authError: null,
663
+ supabase: null,
664
+ });
665
+
666
+ renderWithProviders(<Header />);
667
+
668
+ expect(screen.queryByTestId('app-switcher')).not.toBeInTheDocument();
669
+ });
670
+
671
+ it('does not render AppSwitcher when appName is not provided', () => {
672
+ vi.mocked(useUnifiedAuth).mockReturnValue({
673
+ user: mockUser,
674
+ isAuthenticated: true,
675
+ appName: undefined,
676
+ session: null,
677
+ authLoading: false,
678
+ authError: null,
679
+ supabase: null,
680
+ });
681
+
682
+ renderWithProviders(<Header />);
683
+
684
+ expect(screen.queryByTestId('app-switcher')).not.toBeInTheDocument();
685
+ });
686
+
687
+ it('renders AppSwitcher with correct className', () => {
688
+ vi.mocked(useUnifiedAuth).mockReturnValue({
689
+ user: mockUser,
690
+ isAuthenticated: true,
691
+ appName: 'TEST',
692
+ session: null,
693
+ authLoading: false,
694
+ authError: null,
695
+ supabase: null,
696
+ });
697
+
698
+ renderWithProviders(<Header />);
699
+
700
+ const appSwitcher = screen.getByTestId('app-switcher');
701
+ expect(appSwitcher).toBeInTheDocument();
702
+ expect(appSwitcher).toHaveClass('w-9');
703
+ });
704
+ });
705
+
706
+ // Default logo tests
707
+ describe('Default Logo Rendering', () => {
708
+ it('renders default logo when logoUrl is not provided and logoHref is provided', () => {
709
+ renderWithProviders(<Header logoHref="/dashboard" />);
710
+
711
+ const logo = screen.getByRole('img', { name: 'Logo' });
712
+ expect(logo).toBeInTheDocument();
713
+ expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
714
+
715
+ const logoLink = logo.closest('a');
716
+ expect(logoLink).toBeInTheDocument();
717
+ expect(logoLink).toHaveAttribute('href', '/dashboard');
718
+ });
719
+
720
+ it('renders default logo when logoUrl is not provided and logoHref is not provided', () => {
721
+ renderWithProviders(<Header />);
722
+
723
+ const logo = screen.getByRole('img', { name: 'Logo' });
724
+ expect(logo).toBeInTheDocument();
725
+ expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
726
+
727
+ const logoLink = logo.closest('a');
728
+ expect(logoLink).not.toBeInTheDocument();
729
+ });
730
+
731
+ it('renders default logo with custom alt text', () => {
732
+ renderWithProviders(<Header logoAlt="Custom Alt" />);
733
+
734
+ const logo = screen.getByRole('img', { name: 'Custom Alt' });
735
+ expect(logo).toHaveAttribute('alt', 'Custom Alt');
736
+ });
737
+ });
738
+
739
+ // ContextSelector integration tests
740
+ describe('ContextSelector Integration', () => {
741
+ beforeEach(() => {
742
+ mockContextSelector.mockClear();
743
+ });
744
+
745
+ it('passes showOrganisations prop to ContextSelector', () => {
746
+ renderWithProviders(<Header showContextSelector={true} showOrganisations={false} />);
747
+
748
+ const contextSelector = screen.getByTestId('context-selector');
749
+ expect(contextSelector).toHaveAttribute('data-show-orgs', 'false');
750
+
751
+ // Check that mockContextSelector was called with showOrganisations
752
+ const lastCall = mockContextSelector.mock.calls[mockContextSelector.mock.calls.length - 1];
753
+ expect(lastCall[0].showOrganisations).toBe(false);
754
+ });
755
+
756
+ it('passes showEvents prop to ContextSelector', () => {
757
+ renderWithProviders(<Header showContextSelector={true} showEvents={false} />);
758
+
759
+ const contextSelector = screen.getByTestId('context-selector');
760
+ expect(contextSelector).toHaveAttribute('data-show-events', 'false');
761
+
762
+ // Check that mockContextSelector was called with showEvents
763
+ const lastCall = mockContextSelector.mock.calls[mockContextSelector.mock.calls.length - 1];
764
+ expect(lastCall[0].showEvents).toBe(false);
765
+ });
766
+
767
+ it('uses default values for showOrganisations and showEvents', () => {
768
+ renderWithProviders(<Header showContextSelector={true} />);
769
+
770
+ const contextSelector = screen.getByTestId('context-selector');
771
+ expect(contextSelector).toHaveAttribute('data-show-orgs', 'true');
772
+ expect(contextSelector).toHaveAttribute('data-show-events', 'true');
773
+
774
+ // Check that mockContextSelector was called with default values
775
+ const lastCall = mockContextSelector.mock.calls[mockContextSelector.mock.calls.length - 1];
776
+ expect(lastCall[0].showOrganisations).toBe(true);
777
+ expect(lastCall[0].showEvents).toBe(true);
778
+ });
779
+
780
+ it('passes onOrganisationSelect callback to ContextSelector', () => {
781
+ const mockSwitchOrganisation = vi.fn().mockResolvedValue(undefined);
782
+ vi.mocked(useOrganisations).mockReturnValue({
783
+ organisations: [],
784
+ selectedOrganisation: null,
785
+ isContextReady: true,
786
+ isLoading: false,
787
+ error: null,
788
+ switchOrganisation: mockSwitchOrganisation,
789
+ refreshOrganisations: vi.fn(),
790
+ userMemberships: []
791
+ });
792
+
793
+ renderWithProviders(<Header showContextSelector={true} />);
794
+
795
+ // Check that mockContextSelector was called with onOrganisationSelect
796
+ const lastCall = mockContextSelector.mock.calls[mockContextSelector.mock.calls.length - 1];
797
+ expect(lastCall[0]).toHaveProperty('onOrganisationSelect');
798
+ expect(typeof lastCall[0].onOrganisationSelect).toBe('function');
799
+ });
800
+
801
+ it('passes onEventSelect callback to ContextSelector', () => {
802
+ vi.mocked(useEvents).mockReturnValue({
803
+ events: [],
804
+ selectedEvent: null,
805
+ selectedEventId: null,
806
+ eventLoading: false,
807
+ eventError: null,
808
+ setSelectedEvent: vi.fn(),
809
+ refreshEvents: vi.fn(),
810
+ });
811
+
812
+ renderWithProviders(<Header showContextSelector={true} />);
813
+
814
+ // Check that mockContextSelector was called with onEventSelect
815
+ const lastCall = mockContextSelector.mock.calls[mockContextSelector.mock.calls.length - 1];
816
+ expect(lastCall[0]).toHaveProperty('onEventSelect');
817
+ expect(typeof lastCall[0].onEventSelect).toBe('function');
818
+ });
819
+
820
+ it('calls setSelectedEvent(null) before switchOrganisation when organisation is selected', async () => {
821
+ const mockSwitchOrganisation = vi.fn().mockResolvedValue(undefined);
822
+ const mockSetSelectedEvent = vi.fn();
823
+
824
+ vi.mocked(useOrganisations).mockReturnValue({
825
+ organisations: [],
826
+ selectedOrganisation: null,
827
+ isContextReady: true,
828
+ isLoading: false,
829
+ error: null,
830
+ switchOrganisation: mockSwitchOrganisation,
831
+ refreshOrganisations: vi.fn(),
832
+ userMemberships: []
833
+ });
834
+
835
+ vi.mocked(useEvents).mockReturnValue({
836
+ events: [],
837
+ selectedEvent: null,
838
+ selectedEventId: null,
839
+ eventLoading: false,
840
+ eventError: null,
841
+ setSelectedEvent: mockSetSelectedEvent,
842
+ refreshEvents: vi.fn(),
843
+ });
844
+
845
+ renderWithProviders(<Header showContextSelector={true} />);
846
+
847
+ // Get the onOrganisationSelect callback from the mock call
848
+ const contextSelectorCall = mockContextSelector.mock.calls.find(
849
+ call => call[0].onOrganisationSelect
850
+ );
851
+
852
+ if (contextSelectorCall && contextSelectorCall[0].onOrganisationSelect) {
853
+ const onOrganisationSelect = contextSelectorCall[0].onOrganisationSelect;
854
+
855
+ // Call the callback
856
+ await onOrganisationSelect({ id: 'test-org-id', name: 'Test Organisation' });
857
+
858
+ // Verify setSelectedEvent is called first
859
+ expect(mockSetSelectedEvent).toHaveBeenCalledWith(null);
860
+ expect(mockSwitchOrganisation).toHaveBeenCalledWith('test-org-id');
861
+ }
862
+ });
863
+ });
864
+
865
+ // Grid layout tests
866
+ describe('Grid Layout', () => {
867
+ it('applies correct grid layout when authenticated with appName', () => {
868
+ vi.mocked(useUnifiedAuth).mockReturnValue({
869
+ user: mockUser,
870
+ isAuthenticated: true,
871
+ appName: 'TEST',
872
+ session: null,
873
+ authLoading: false,
874
+ authError: null,
875
+ supabase: null,
876
+ });
877
+
878
+ renderWithProviders(<Header />);
879
+
880
+ const nav = screen.getByRole('navigation');
881
+ // Check that the nav element has the grid class (may be in className string)
882
+ const className = nav.className;
883
+ expect(className).toContain('grid-cols-[auto_auto_1fr_auto_auto]');
884
+ });
885
+
886
+ it('applies correct grid layout when not authenticated', () => {
887
+ vi.mocked(useUnifiedAuth).mockReturnValue({
888
+ user: null,
889
+ isAuthenticated: false,
890
+ appName: undefined,
891
+ session: null,
892
+ authLoading: false,
893
+ authError: null,
894
+ supabase: null,
895
+ });
896
+
897
+ renderWithProviders(<Header />);
898
+
899
+ const nav = screen.getByRole('navigation');
900
+ const className = nav.className;
901
+ expect(className).toContain('grid-cols-[auto_1fr_auto_auto]');
902
+ });
903
+
904
+ it('applies correct grid layout when authenticated without appName', () => {
905
+ vi.mocked(useUnifiedAuth).mockReturnValue({
906
+ user: mockUser,
907
+ isAuthenticated: true,
908
+ appName: undefined,
909
+ session: null,
910
+ authLoading: false,
911
+ authError: null,
912
+ supabase: null,
913
+ });
914
+
915
+ renderWithProviders(<Header />);
916
+
917
+ const nav = screen.getByRole('navigation');
918
+ const className = nav.className;
919
+ expect(className).toContain('grid-cols-[auto_1fr_auto_auto]');
920
+ });
921
+ });
922
+
923
+ // Logo navigation tests
924
+ describe('Logo Navigation', () => {
925
+ it('renders logo link with correct href when logoHref is provided', () => {
926
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoHref="/dashboard" />);
927
+
928
+ const logoLink = screen.getByRole('img', { name: 'Logo' }).closest('a');
929
+ expect(logoLink).toBeInTheDocument();
930
+ expect(logoLink).toHaveAttribute('href', '/dashboard');
931
+ });
932
+
933
+ it('does not navigate when logoHref is not provided', () => {
934
+ renderWithProviders(<Header logoUrl="/test-logo.svg" />);
935
+
936
+ const logo = screen.getByRole('img', { name: 'Logo' });
937
+ const logoLink = logo.closest('a');
938
+ expect(logoLink).not.toBeInTheDocument();
939
+ });
940
+
941
+ it('handles logoHref with default logo', () => {
942
+ renderWithProviders(<Header logoHref="/home" />);
943
+
944
+ const logo = screen.getByRole('img', { name: 'Logo' });
945
+ const logoLink = logo.closest('a');
946
+ expect(logoLink).toBeInTheDocument();
947
+ expect(logoLink).toHaveAttribute('href', '/home');
948
+ });
949
+ });
950
+
951
+ // Additional edge cases
952
+ describe('Additional Edge Cases', () => {
953
+ it('handles className merging correctly', () => {
954
+ renderWithProviders(<Header className="custom-class" />);
955
+
956
+ const header = screen.getByRole('banner');
957
+ expect(header).toHaveClass('custom-class');
958
+ expect(header).toHaveClass('w-full'); // Default classes should still be present
959
+ });
960
+
961
+ it('handles empty navItems array', () => {
962
+ renderWithProviders(<Header navItems={[]} />);
963
+
964
+ expect(screen.queryByTestId('navigation-menu')).not.toBeInTheDocument();
965
+ });
966
+
967
+ it('handles logoUrl with empty string', () => {
968
+ renderWithProviders(<Header logoUrl="" />);
969
+
970
+ // Empty string should be treated as falsy, so default logo should render
971
+ const logo = screen.getByRole('img', { name: 'Logo' });
972
+ expect(logo).toHaveAttribute('src', expect.stringContaining('data:image/svg+xml'));
973
+ });
974
+
975
+ it('handles logoAlt with empty string', () => {
976
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoAlt="" />);
977
+
978
+ const logo = screen.getByRole('img', { name: 'Logo' }); // Falls back to default
979
+ expect(logo).toBeInTheDocument();
980
+ });
981
+
982
+ it('handles all optional props as undefined', () => {
983
+ renderWithProviders(
984
+ <Header
985
+ logoUrl={undefined}
986
+ logoAlt={undefined}
987
+ navItems={undefined}
988
+ user={undefined}
989
+ onSignOut={undefined}
990
+ onChangePassword={undefined}
991
+ actions={undefined}
992
+ userMenu={undefined}
993
+ className={undefined}
994
+ showContextSelector={undefined}
995
+ showOrganisations={undefined}
996
+ showEvents={undefined}
997
+ showUserMenu={undefined}
998
+ currentPath={undefined}
999
+ onNavigate={undefined}
1000
+ logoHref={undefined}
1001
+ />
1002
+ );
1003
+
1004
+ const header = screen.getByRole('banner');
1005
+ expect(header).toBeInTheDocument();
1006
+ });
1007
+
1008
+ it('handles logoHref with special characters', () => {
1009
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoHref="/dashboard?tab=settings" />);
1010
+
1011
+ const logoLink = screen.getByRole('img', { name: 'Logo' }).closest('a');
1012
+ expect(logoLink).toHaveAttribute('href', '/dashboard?tab=settings');
1013
+ });
1014
+
1015
+ it('handles logoHref with hash', () => {
1016
+ renderWithProviders(<Header logoUrl="/test-logo.svg" logoHref="/dashboard#section" />);
1017
+
1018
+ const logoLink = screen.getByRole('img', { name: 'Logo' }).closest('a');
1019
+ expect(logoLink).toHaveAttribute('href', '/dashboard#section');
1020
+ });
1021
+ });
1022
+
1023
+ // Comprehensive integration scenarios
1024
+ describe('Comprehensive Integration Scenarios', () => {
1025
+ it('renders full header with all features when authenticated', () => {
1026
+ vi.mocked(useUnifiedAuth).mockReturnValue({
1027
+ user: mockUser,
1028
+ isAuthenticated: true,
1029
+ appName: 'TEST',
1030
+ session: null,
1031
+ authLoading: false,
1032
+ authError: null,
1033
+ supabase: null,
1034
+ });
1035
+
1036
+ const customActions = <div data-testid="custom-actions">Actions</div>;
1037
+
1038
+ renderWithProviders(
1039
+ <Header
1040
+ logoUrl="/test-logo.svg"
1041
+ logoAlt="Test Logo"
1042
+ logoHref="/dashboard"
1043
+ navItems={mockNavItems}
1044
+ user={mockUser}
1045
+ actions={customActions}
1046
+ showContextSelector={true}
1047
+ showOrganisations={true}
1048
+ showEvents={true}
1049
+ showUserMenu={true}
1050
+ currentPath="/dashboard"
1051
+ />
1052
+ );
1053
+
1054
+ // AppSwitcher
1055
+ expect(screen.getByTestId('app-switcher')).toBeInTheDocument();
1056
+
1057
+ // Logo
1058
+ expect(screen.getByRole('img', { name: 'Test Logo' })).toBeInTheDocument();
1059
+
1060
+ // Navigation
1061
+ expect(screen.getByTestId('navigation-menu')).toBeInTheDocument();
1062
+
1063
+ // Context Selector
1064
+ expect(screen.getByTestId('context-selector')).toBeInTheDocument();
1065
+
1066
+ // Custom Actions
1067
+ expect(screen.getByTestId('custom-actions')).toBeInTheDocument();
1068
+
1069
+ // User Menu
1070
+ expect(screen.getByTestId('user-menu')).toBeInTheDocument();
1071
+ });
1072
+
1073
+ it('renders minimal header when not authenticated', () => {
1074
+ vi.mocked(useUnifiedAuth).mockReturnValue({
1075
+ user: null,
1076
+ isAuthenticated: false,
1077
+ appName: undefined,
1078
+ session: null,
1079
+ authLoading: false,
1080
+ authError: null,
1081
+ supabase: null,
1082
+ });
1083
+
1084
+ renderWithProviders(
1085
+ <Header
1086
+ logoUrl="/test-logo.svg"
1087
+ showContextSelector={false}
1088
+ showUserMenu={false}
1089
+ />
1090
+ );
1091
+
1092
+ // Should not have AppSwitcher
1093
+ expect(screen.queryByTestId('app-switcher')).not.toBeInTheDocument();
1094
+
1095
+ // Should have logo
1096
+ expect(screen.getByRole('img', { name: 'Logo' })).toBeInTheDocument();
1097
+
1098
+ // Should not have context selector
1099
+ expect(screen.queryByTestId('context-selector')).not.toBeInTheDocument();
1100
+
1101
+ // Should not have user menu
1102
+ expect(screen.queryByTestId('user-menu')).not.toBeInTheDocument();
1103
+ });
1104
+
1105
+ it('handles context selector with only organisations', () => {
1106
+ renderWithProviders(
1107
+ <Header
1108
+ showContextSelector={true}
1109
+ showOrganisations={true}
1110
+ showEvents={false}
1111
+ />
1112
+ );
1113
+
1114
+ const contextSelector = screen.getByTestId('context-selector');
1115
+ expect(contextSelector).toHaveAttribute('data-show-orgs', 'true');
1116
+ expect(contextSelector).toHaveAttribute('data-show-events', 'false');
1117
+ });
1118
+
1119
+ it('handles context selector with only events', () => {
1120
+ renderWithProviders(
1121
+ <Header
1122
+ showContextSelector={true}
1123
+ showOrganisations={false}
1124
+ showEvents={true}
1125
+ />
1126
+ );
1127
+
1128
+ const contextSelector = screen.getByTestId('context-selector');
1129
+ expect(contextSelector).toHaveAttribute('data-show-orgs', 'false');
1130
+ expect(contextSelector).toHaveAttribute('data-show-events', 'true');
1131
+ });
1132
+ });
642
1133
  });