@jmruthers/pace-core 0.6.5 → 0.6.7

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 (473) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +5 -403
  3. package/audit-tool/00-dependencies.cjs +394 -0
  4. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  5. package/audit-tool/audits/02-project-structure.cjs +255 -0
  6. package/audit-tool/audits/03-architecture.cjs +196 -0
  7. package/audit-tool/audits/04-code-quality.cjs +149 -0
  8. package/audit-tool/audits/05-styling.cjs +224 -0
  9. package/audit-tool/audits/06-security-rbac.cjs +544 -0
  10. package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
  11. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  12. package/audit-tool/audits/09-operations.cjs +208 -0
  13. package/audit-tool/index.cjs +291 -0
  14. package/audit-tool/utils/code-utils.cjs +218 -0
  15. package/audit-tool/utils/file-utils.cjs +230 -0
  16. package/audit-tool/utils/report-utils.cjs +241 -0
  17. package/core-usage-manifest.json +93 -0
  18. package/cursor-rules/00-standards-overview.mdc +156 -0
  19. package/cursor-rules/01-pace-core-compliance.mdc +586 -0
  20. package/cursor-rules/02-project-structure.mdc +42 -4
  21. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
  22. package/cursor-rules/04-code-quality.mdc +419 -0
  23. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
  24. package/cursor-rules/06-security-rbac.mdc +518 -0
  25. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  26. package/cursor-rules/08-testing-documentation.mdc +324 -0
  27. package/cursor-rules/09-operations.mdc +365 -0
  28. package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
  29. package/dist/DataTable-7PMH7XN7.js +15 -0
  30. package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  31. package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
  32. package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
  33. package/dist/api-Y4MQWOFW.js +4 -0
  34. package/dist/audit-MYQXYZFU.js +3 -0
  35. package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
  36. package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
  37. package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
  38. package/dist/chunk-6F3IILHI.js +62 -0
  39. package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
  40. package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
  41. package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
  42. package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
  43. package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
  44. package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
  45. package/dist/chunk-C7NSAPTL.js +1 -0
  46. package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
  47. package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
  48. package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
  49. package/dist/chunk-GHYHJTYV.js +994 -0
  50. package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
  51. package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
  52. package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
  53. package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
  54. package/dist/chunk-MBADTM7L.js +64 -0
  55. package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
  56. package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
  57. package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
  58. package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
  59. package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
  60. package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
  61. package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
  62. package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
  63. package/dist/components.d.ts +7 -5
  64. package/dist/components.js +46 -257
  65. package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
  66. package/dist/eslint-rules/index.cjs +35 -0
  67. package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
  68. package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
  69. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  70. package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
  71. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
  72. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  73. package/dist/eslint-rules/utils/helpers.cjs +42 -0
  74. package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
  75. package/dist/hooks.d.ts +6 -6
  76. package/dist/hooks.js +62 -172
  77. package/dist/icons/index.d.ts +1 -0
  78. package/dist/icons/index.js +1 -0
  79. package/dist/index.d.ts +12 -11
  80. package/dist/index.js +67 -660
  81. package/dist/providers.d.ts +2 -2
  82. package/dist/providers.js +8 -35
  83. package/dist/rbac/eslint-rules.d.ts +46 -44
  84. package/dist/rbac/eslint-rules.js +7 -4
  85. package/dist/rbac/index.d.ts +109 -586
  86. package/dist/rbac/index.js +14 -207
  87. package/dist/styles/index.js +2 -12
  88. package/dist/theming/runtime.d.ts +14 -1
  89. package/dist/theming/runtime.js +3 -19
  90. package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
  91. package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
  92. package/dist/types-t9H8qKRw.d.ts +55 -0
  93. package/dist/types.d.ts +1 -1
  94. package/dist/types.js +7 -94
  95. package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
  96. package/dist/utils.d.ts +24 -117
  97. package/dist/utils.js +54 -392
  98. package/docs/README.md +17 -7
  99. package/docs/api/README.md +4 -402
  100. package/docs/api/modules.md +301 -871
  101. package/docs/api-reference/components.md +21 -21
  102. package/docs/api-reference/deprecated.md +31 -6
  103. package/docs/api-reference/hooks.md +80 -80
  104. package/docs/api-reference/rpc-functions.md +78 -3
  105. package/docs/api-reference/types.md +1 -1
  106. package/docs/api-reference/utilities.md +1 -1
  107. package/docs/architecture/README.md +1 -1
  108. package/docs/core-concepts/events.md +3 -3
  109. package/docs/core-concepts/organisations.md +6 -6
  110. package/docs/core-concepts/permissions.md +6 -6
  111. package/docs/documentation-index.md +12 -18
  112. package/docs/getting-started/cursor-rules.md +3 -23
  113. package/docs/getting-started/dependencies.md +650 -0
  114. package/docs/getting-started/documentation-index.md +1 -1
  115. package/docs/getting-started/examples/README.md +4 -4
  116. package/docs/getting-started/examples/full-featured-app.md +1 -1
  117. package/docs/getting-started/faq.md +2 -2
  118. package/docs/getting-started/installation-guide.md +20 -7
  119. package/docs/getting-started/quick-reference.md +4 -4
  120. package/docs/getting-started/quick-start.md +23 -12
  121. package/docs/implementation-guides/authentication.md +15 -15
  122. package/docs/implementation-guides/component-styling.md +1 -1
  123. package/docs/implementation-guides/data-tables.md +126 -33
  124. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  125. package/docs/implementation-guides/dynamic-colors.md +3 -3
  126. package/docs/implementation-guides/file-upload-storage.md +2 -2
  127. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  128. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  129. package/docs/implementation-guides/large-datasets.md +3 -2
  130. package/docs/implementation-guides/organisation-security.md +2 -2
  131. package/docs/implementation-guides/performance.md +2 -2
  132. package/docs/implementation-guides/permission-enforcement.md +5 -1
  133. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  134. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  135. package/docs/rbac/MIGRATION_GUIDE.md +819 -0
  136. package/docs/rbac/RBAC_CONTRACT.md +724 -0
  137. package/docs/rbac/README.md +17 -8
  138. package/docs/rbac/advanced-patterns.md +6 -6
  139. package/docs/rbac/api-reference.md +20 -20
  140. package/docs/rbac/edge-functions-guide.md +376 -0
  141. package/docs/rbac/event-based-apps.md +3 -3
  142. package/docs/rbac/examples.md +41 -41
  143. package/docs/rbac/getting-started.md +37 -37
  144. package/docs/rbac/performance.md +1 -1
  145. package/docs/rbac/quick-start.md +52 -52
  146. package/docs/rbac/secure-client-protection.md +1 -35
  147. package/docs/rbac/troubleshooting.md +1 -1
  148. package/docs/security/README.md +5 -5
  149. package/docs/standards/0-standards-overview.md +220 -0
  150. package/docs/standards/1-pace-core-compliance-standards.md +986 -0
  151. package/docs/standards/2-project-structure-standards.md +949 -0
  152. package/docs/standards/3-architecture-standards.md +606 -0
  153. package/docs/standards/4-code-quality-standards.md +728 -0
  154. package/docs/standards/5-styling-standards.md +348 -0
  155. package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
  156. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  157. package/docs/standards/8-testing-documentation-standards.md +401 -0
  158. package/docs/standards/9-operations-standards.md +1102 -0
  159. package/docs/standards/README.md +185 -57
  160. package/docs/troubleshooting/README.md +4 -4
  161. package/docs/troubleshooting/common-issues.md +2 -2
  162. package/docs/troubleshooting/debugging.md +9 -9
  163. package/docs/troubleshooting/migration.md +4 -4
  164. package/docs/troubleshooting/organisation-context-setup.md +42 -19
  165. package/eslint-config-pace-core.cjs +33 -6
  166. package/package.json +35 -23
  167. package/scripts/install-cursor-rules.cjs +25 -6
  168. package/scripts/install-eslint-config.cjs +284 -0
  169. package/src/__tests__/fixtures/supabase.ts +1 -1
  170. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
  171. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
  172. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
  173. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  174. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
  175. package/src/__tests__/helpers/component-test-utils.tsx +1 -1
  176. package/src/__tests__/helpers/supabaseMock.ts +2 -2
  177. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  178. package/src/__tests__/public-recipe-view.test.ts +38 -9
  179. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  180. package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
  181. package/src/__tests__/templates/component.test.template.tsx +18 -15
  182. package/src/components/Button/Button.tsx +5 -1
  183. package/src/components/Calendar/Calendar.tsx +201 -47
  184. package/src/components/ContextSelector/ContextSelector.tsx +106 -119
  185. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  186. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
  187. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
  188. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  189. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  190. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  191. package/src/components/DataTable/components/DataTableCore.tsx +186 -13
  192. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  193. package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
  194. package/src/components/DataTable/components/EditFields.tsx +23 -3
  195. package/src/components/DataTable/components/EditableRow.tsx +12 -9
  196. package/src/components/DataTable/components/EmptyState.tsx +10 -9
  197. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  198. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  199. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  200. package/src/components/DataTable/components/RowComponent.tsx +12 -0
  201. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  202. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  203. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  204. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  205. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  206. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  207. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
  208. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
  209. package/src/components/DataTable/components/index.ts +2 -1
  210. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
  211. package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
  212. package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
  213. package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
  214. package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
  215. package/src/components/DataTable/types.ts +5 -18
  216. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  217. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
  218. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  219. package/src/components/DateTimeField/DateTimeField.tsx +10 -9
  220. package/src/components/Dialog/Dialog.test.tsx +128 -104
  221. package/src/components/Dialog/Dialog.tsx +742 -24
  222. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  223. package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
  224. package/src/components/FileDisplay/FileDisplay.tsx +23 -17
  225. package/src/components/FileUpload/FileUpload.test.tsx +52 -14
  226. package/src/components/FileUpload/FileUpload.tsx +112 -130
  227. package/src/components/Form/Form.test.tsx +6 -8
  228. package/src/components/Form/Form.tsx +365 -4
  229. package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
  230. package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
  231. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
  232. package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
  233. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
  234. package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
  235. package/src/components/Progress/Progress.tsx +2 -4
  236. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  237. package/src/components/Select/Select.tsx +109 -98
  238. package/src/components/Select/types.ts +4 -1
  239. package/src/components/UserMenu/UserMenu.tsx +9 -6
  240. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  241. package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
  242. package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
  243. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
  244. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
  245. package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
  246. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
  247. package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
  248. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
  249. package/src/hooks/public/usePublicEvent.ts +67 -195
  250. package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
  251. package/src/hooks/public/usePublicEventLogo.ts +24 -14
  252. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  253. package/src/hooks/public/usePublicRouteParams.ts +5 -5
  254. package/src/hooks/useAppConfig.ts +28 -26
  255. package/src/hooks/useEventTheme.test.ts +217 -239
  256. package/src/hooks/useEventTheme.ts +16 -28
  257. package/src/hooks/useFileDisplay.ts +2 -2
  258. package/src/hooks/useOrganisationPermissions.ts +5 -7
  259. package/src/hooks/useQueryCache.ts +0 -1
  260. package/src/hooks/useSessionDraft.ts +380 -0
  261. package/src/hooks/useSessionRestoration.ts +3 -1
  262. package/src/icons/index.ts +27 -0
  263. package/src/index.ts +5 -0
  264. package/src/providers/OrganisationProvider.tsx +23 -14
  265. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  266. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  267. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  268. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  269. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  270. package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
  271. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  272. package/src/providers/services/EventServiceProvider.tsx +1 -24
  273. package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
  274. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  275. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
  276. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
  277. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
  278. package/src/rbac/adapters.tsx +7 -295
  279. package/src/rbac/api.test.ts +44 -56
  280. package/src/rbac/api.ts +10 -17
  281. package/src/rbac/cache-invalidation.ts +0 -1
  282. package/src/rbac/compliance/index.ts +10 -0
  283. package/src/rbac/compliance/pattern-detector.ts +553 -0
  284. package/src/rbac/compliance/runtime-compliance.ts +22 -0
  285. package/src/rbac/components/AccessDenied.tsx +150 -0
  286. package/src/rbac/components/NavigationGuard.tsx +12 -20
  287. package/src/rbac/components/PagePermissionGuard.tsx +4 -24
  288. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
  289. package/src/rbac/components/index.ts +3 -41
  290. package/src/rbac/eslint-rules.js +1 -1
  291. package/src/rbac/hooks/index.ts +0 -3
  292. package/src/rbac/hooks/permissions/index.ts +0 -3
  293. package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
  294. package/src/rbac/hooks/usePermissions.ts +0 -3
  295. package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
  296. package/src/rbac/hooks/useResolvedScope.ts +58 -140
  297. package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
  298. package/src/rbac/hooks/useResourcePermissions.ts +139 -48
  299. package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
  300. package/src/rbac/hooks/useRoleManagement.ts +147 -19
  301. package/src/rbac/hooks/useSecureSupabase.ts +4 -8
  302. package/src/rbac/index.ts +7 -9
  303. package/src/rbac/utils/contextValidator.ts +9 -7
  304. package/src/services/AuthService.ts +130 -18
  305. package/src/services/EventService.ts +4 -97
  306. package/src/services/InactivityService.ts +16 -0
  307. package/src/services/OrganisationService.ts +7 -44
  308. package/src/services/__tests__/OrganisationService.test.ts +26 -8
  309. package/src/services/base/BaseService.ts +0 -3
  310. package/src/styles/core.css +7 -0
  311. package/src/theming/__tests__/parseEventColours.test.ts +9 -3
  312. package/src/theming/parseEventColours.ts +22 -10
  313. package/src/types/database.generated.ts +4733 -3809
  314. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  315. package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
  316. package/src/utils/context/organisationContext.test.ts +13 -28
  317. package/src/utils/context/organisationContext.ts +21 -52
  318. package/src/utils/dynamic/dynamicUtils.ts +1 -1
  319. package/src/utils/file-reference/index.ts +39 -15
  320. package/src/utils/formatting/formatDateTime.test.ts +3 -2
  321. package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
  322. package/src/utils/index.ts +4 -1
  323. package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
  324. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
  325. package/src/utils/persistence/keyDerivation.ts +304 -0
  326. package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
  327. package/src/utils/security/secureStorage.ts +5 -5
  328. package/src/utils/storage/README.md +1 -1
  329. package/src/utils/storage/helpers.ts +3 -3
  330. package/src/utils/supabase/createBaseClient.ts +147 -0
  331. package/src/utils/timezone/timezone.test.ts +1 -2
  332. package/src/utils/timezone/timezone.ts +1 -1
  333. package/src/utils/validation/csrf.ts +4 -4
  334. package/cursor-rules/00-pace-core-compliance.mdc +0 -331
  335. package/cursor-rules/01-standards-compliance.mdc +0 -244
  336. package/cursor-rules/04-testing-standards.mdc +0 -268
  337. package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
  338. package/cursor-rules/06-code-quality.mdc +0 -309
  339. package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
  340. package/cursor-rules/CHANGELOG.md +0 -119
  341. package/cursor-rules/README.md +0 -192
  342. package/dist/DataTable-AOVNCPTX.js +0 -175
  343. package/dist/DataTable-AOVNCPTX.js.map +0 -1
  344. package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
  345. package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
  346. package/dist/api-O6HTBX5Y.js +0 -52
  347. package/dist/api-O6HTBX5Y.js.map +0 -1
  348. package/dist/audit-V53FV5AG.js +0 -17
  349. package/dist/audit-V53FV5AG.js.map +0 -1
  350. package/dist/chunk-5DRSZLL2.js.map +0 -1
  351. package/dist/chunk-63FOKYGO.js.map +0 -1
  352. package/dist/chunk-6COVEUS7.js.map +0 -1
  353. package/dist/chunk-AFVQODI2.js +0 -263
  354. package/dist/chunk-AFVQODI2.js.map +0 -1
  355. package/dist/chunk-DGUM43GV.js.map +0 -1
  356. package/dist/chunk-E66EQZE6.js.map +0 -1
  357. package/dist/chunk-EFN2EIMK.js.map +0 -1
  358. package/dist/chunk-FFQEQTNW.js.map +0 -1
  359. package/dist/chunk-FMUCXFII.js.map +0 -1
  360. package/dist/chunk-G37KK66H.js.map +0 -1
  361. package/dist/chunk-G7QEZTYQ.js +0 -2053
  362. package/dist/chunk-G7QEZTYQ.js.map +0 -1
  363. package/dist/chunk-HU2C6SSC.js.map +0 -1
  364. package/dist/chunk-IHB5DR3H.js.map +0 -1
  365. package/dist/chunk-IVOFDYWT.js.map +0 -1
  366. package/dist/chunk-J36DSWQK.js.map +0 -1
  367. package/dist/chunk-JGRYX5UX.js.map +0 -1
  368. package/dist/chunk-KQCRWDSA.js +0 -1
  369. package/dist/chunk-KQCRWDSA.js.map +0 -1
  370. package/dist/chunk-L4OXEN46.js.map +0 -1
  371. package/dist/chunk-LMC26NLJ.js +0 -84
  372. package/dist/chunk-LMC26NLJ.js.map +0 -1
  373. package/dist/chunk-M43Y4SSO.js.map +0 -1
  374. package/dist/chunk-M7MPQISP.js.map +0 -1
  375. package/dist/chunk-NTM7ZSB6.js.map +0 -1
  376. package/dist/chunk-PWLANIRT.js.map +0 -1
  377. package/dist/chunk-QXHPKYJV.js.map +0 -1
  378. package/dist/chunk-RGAWHO7N.js.map +0 -1
  379. package/dist/chunk-UPPMRMYG.js.map +0 -1
  380. package/dist/chunk-VBXEHIUJ.js.map +0 -1
  381. package/dist/chunk-ZSAAAMVR.js.map +0 -1
  382. package/dist/components.js.map +0 -1
  383. package/dist/contextValidator-5OGXSPKS.js +0 -9
  384. package/dist/contextValidator-5OGXSPKS.js.map +0 -1
  385. package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
  386. package/dist/hooks.js.map +0 -1
  387. package/dist/index.js.map +0 -1
  388. package/dist/providers.js.map +0 -1
  389. package/dist/rbac/eslint-rules.js.map +0 -1
  390. package/dist/rbac/index.js.map +0 -1
  391. package/dist/styles/index.js.map +0 -1
  392. package/dist/theming/runtime.js.map +0 -1
  393. package/dist/types.js.map +0 -1
  394. package/dist/utils.js.map +0 -1
  395. package/docs/best-practices/README.md +0 -472
  396. package/docs/best-practices/accessibility.md +0 -601
  397. package/docs/best-practices/common-patterns.md +0 -516
  398. package/docs/best-practices/deployment.md +0 -1103
  399. package/docs/best-practices/performance.md +0 -1328
  400. package/docs/best-practices/security.md +0 -940
  401. package/docs/best-practices/testing.md +0 -1034
  402. package/docs/rbac/compliance/compliance-guide.md +0 -544
  403. package/docs/standards/01-architecture-standard.md +0 -44
  404. package/docs/standards/02-api-and-rpc-standard.md +0 -39
  405. package/docs/standards/03-component-standard.md +0 -32
  406. package/docs/standards/04-code-style-standard.md +0 -32
  407. package/docs/standards/05-security-standard.md +0 -44
  408. package/docs/standards/06-testing-and-docs-standard.md +0 -29
  409. package/docs/standards/pace-core-compliance.md +0 -432
  410. package/scripts/audit/core/checks/accessibility.cjs +0 -197
  411. package/scripts/audit/core/checks/api-usage.cjs +0 -191
  412. package/scripts/audit/core/checks/bundle.cjs +0 -142
  413. package/scripts/audit/core/checks/compliance.cjs +0 -2706
  414. package/scripts/audit/core/checks/config.cjs +0 -54
  415. package/scripts/audit/core/checks/coverage.cjs +0 -84
  416. package/scripts/audit/core/checks/dependencies.cjs +0 -994
  417. package/scripts/audit/core/checks/documentation.cjs +0 -268
  418. package/scripts/audit/core/checks/environment.cjs +0 -116
  419. package/scripts/audit/core/checks/error-handling.cjs +0 -340
  420. package/scripts/audit/core/checks/forms.cjs +0 -172
  421. package/scripts/audit/core/checks/heuristics.cjs +0 -68
  422. package/scripts/audit/core/checks/hooks.cjs +0 -334
  423. package/scripts/audit/core/checks/imports.cjs +0 -244
  424. package/scripts/audit/core/checks/performance.cjs +0 -325
  425. package/scripts/audit/core/checks/routes.cjs +0 -117
  426. package/scripts/audit/core/checks/state.cjs +0 -130
  427. package/scripts/audit/core/checks/structure.cjs +0 -65
  428. package/scripts/audit/core/checks/style.cjs +0 -584
  429. package/scripts/audit/core/checks/testing.cjs +0 -122
  430. package/scripts/audit/core/checks/typescript.cjs +0 -61
  431. package/scripts/audit/core/scanner.cjs +0 -199
  432. package/scripts/audit/core/utils.cjs +0 -137
  433. package/scripts/audit/index.cjs +0 -223
  434. package/scripts/audit/reporters/console.cjs +0 -151
  435. package/scripts/audit/reporters/json.cjs +0 -54
  436. package/scripts/audit/reporters/markdown.cjs +0 -124
  437. package/scripts/audit-consuming-app.cjs +0 -86
  438. package/src/components/DataTable/components/DataTableBody.tsx +0 -454
  439. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  440. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  441. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  442. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  443. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  444. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  445. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  446. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  447. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  448. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  449. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  450. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  451. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  452. package/src/components/DataTable/utils/debugTools.ts +0 -514
  453. package/src/eslint-rules/pace-core-compliance.js +0 -638
  454. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
  455. package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
  456. package/src/rbac/components/NavigationProvider.test.tsx +0 -481
  457. package/src/rbac/components/NavigationProvider.tsx +0 -345
  458. package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
  459. package/src/rbac/components/PagePermissionProvider.tsx +0 -279
  460. package/src/rbac/components/PermissionEnforcer.tsx +0 -312
  461. package/src/rbac/components/RoleBasedRouter.tsx +0 -440
  462. package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
  463. package/src/rbac/components/SecureDataProvider.tsx +0 -339
  464. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
  465. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
  466. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
  467. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
  468. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
  469. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
  470. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
  471. package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
  472. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
  473. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
@@ -15,6 +15,7 @@ import { renderWithProviders } from '../../__tests__/helpers/test-utils';
15
15
  import { Logger, LogLevel } from '../../utils/core/logger';
16
16
  import { clearPalette } from '../../theming/runtime';
17
17
  import { EventServiceContext } from '../../providers/services/EventServiceProvider';
18
+ import { UnifiedAuthContext } from '../../providers/services/UnifiedAuthProvider';
18
19
 
19
20
  // Mock React Router
20
21
  const mockNavigate = vi.fn();
@@ -47,36 +48,66 @@ const mockSupabase = {
47
48
  // Mock the UnifiedAuthProvider
48
49
  const mockAuthContext = {
49
50
  user: null as User | null,
51
+ session: null,
50
52
  isAuthenticated: false,
51
53
  isLoading: false,
54
+ authLoading: false,
52
55
  authError: null as Error | null,
56
+ error: null,
53
57
  hasRole: vi.fn(),
54
58
  getUserRole: vi.fn(),
55
59
  signIn: vi.fn(),
56
60
  signOut: vi.fn(),
61
+ signUp: vi.fn(),
62
+ resetPassword: vi.fn(),
63
+ updatePassword: vi.fn(),
57
64
  refreshSession: vi.fn(),
58
65
  supabase: mockSupabase,
59
66
  appName: 'Test App',
67
+ appId: undefined,
60
68
  hasErrors: false,
61
- // RBAC context
62
- globalRole: null,
63
- organisationRole: null,
64
- eventAppRole: null,
65
- rbacLoading: false,
66
- rbacError: null,
69
+ // Organisation context
70
+ selectedOrganisation: null,
71
+ selectedOrganisationId: null,
72
+ organisations: [],
73
+ userMemberships: [],
74
+ organisationLoading: false,
75
+ organisationError: null,
76
+ hasValidOrganisationContext: false,
77
+ isContextReady: false,
78
+ switchOrganisation: vi.fn(),
79
+ validateOrganisationAccess: vi.fn(),
80
+ refreshOrganisations: vi.fn(),
81
+ ensureOrganisationContext: vi.fn(),
82
+ isOrganisationSecure: vi.fn(),
83
+ getPrimaryOrganisation: vi.fn(),
84
+ // Event context
85
+ events: [],
86
+ selectedEvent: null,
87
+ selectedEventId: null,
88
+ eventLoading: false,
89
+ eventError: null,
90
+ setSelectedEvent: vi.fn(),
91
+ refreshEvents: vi.fn(),
67
92
  // Inactivity context
68
93
  isIdle: false,
69
- timeUntilIdle: 0,
70
- resetInactivityTimer: vi.fn(),
71
- inactivityLoading: false,
72
- inactivityError: null,
94
+ timeRemaining: 0,
95
+ showWarning: false,
96
+ showInactivityWarning: false,
97
+ inactivityTimeRemaining: 0,
98
+ isTracking: false,
99
+ resetActivity: vi.fn(),
100
+ startTracking: vi.fn(),
101
+ stopTracking: vi.fn(),
102
+ handleIdleLogout: vi.fn(),
103
+ handleStaySignedIn: vi.fn(),
104
+ handleSignOutNow: vi.fn(),
105
+ // Session restoration
106
+ sessionRestoration: { state: 'idle', error: null },
107
+ sessionRestorationTimedOut: false,
108
+ sessionRestorationTimeoutMs: 5000,
73
109
  };
74
110
 
75
- // Mock the useUnifiedAuth hook - needs to match the actual import path
76
- vi.mock('../../providers', () => ({
77
- useUnifiedAuth: () => mockAuthContext,
78
- UnifiedAuthProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
79
- }));
80
111
 
81
112
  vi.mock('../../theming/runtime', () => ({
82
113
  clearPalette: vi.fn(),
@@ -105,6 +136,16 @@ const resetAuthContext = () => {
105
136
  mockAuthContext.supabase = mockSupabase;
106
137
  };
107
138
 
139
+ // Helper to render with UnifiedAuthContext
140
+ const renderWithAuthContext = (ui: React.ReactElement, options: { withRouter?: boolean } = {}) => {
141
+ return renderWithProviders(
142
+ <UnifiedAuthContext.Provider value={mockAuthContext as any}>
143
+ {ui}
144
+ </UnifiedAuthContext.Provider>,
145
+ options
146
+ );
147
+ };
148
+
108
149
  describe('PaceLoginPage Component', () => {
109
150
  let originalMode: string | undefined;
110
151
 
@@ -141,7 +182,7 @@ describe('PaceLoginPage Component', () => {
141
182
 
142
183
  describe('Side Effects', () => {
143
184
  it('clears theme palette on mount and when login route is active', () => {
144
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
185
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
145
186
 
146
187
  expect(clearPalette).toHaveBeenCalled();
147
188
  });
@@ -151,9 +192,11 @@ describe('PaceLoginPage Component', () => {
151
192
  window.history.pushState({}, '', '/login');
152
193
 
153
194
  renderWithProviders(
154
- <EventServiceContext.Provider value={{ eventService: { restorePersistedEvent } as any }}>
155
- <PaceLoginPage appName="Test App" />
156
- </EventServiceContext.Provider>
195
+ <UnifiedAuthContext.Provider value={mockAuthContext as any}>
196
+ <EventServiceContext.Provider value={{ eventService: { restorePersistedEvent } as any }}>
197
+ <PaceLoginPage appName="Test App" />
198
+ </EventServiceContext.Provider>
199
+ </UnifiedAuthContext.Provider>
157
200
  );
158
201
 
159
202
  await waitFor(() => {
@@ -165,19 +208,19 @@ describe('PaceLoginPage Component', () => {
165
208
  // Basic rendering tests
166
209
  describe('Rendering', () => {
167
210
  it('renders with default props', () => {
168
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
211
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
169
212
 
170
213
  expect(screen.getByLabelText('Test App Login Page')).toBeInTheDocument();
171
214
  });
172
215
 
173
216
  it('renders with custom app name', () => {
174
- renderWithProviders(<PaceLoginPage appName="My Application" />, { withRouter: false });
217
+ renderWithAuthContext(<PaceLoginPage appName="My Application" />, { withRouter: false });
175
218
 
176
219
  expect(screen.getByLabelText('My Application Login Page')).toBeInTheDocument();
177
220
  });
178
221
 
179
222
  it('renders with custom redirect path', () => {
180
- renderWithProviders(
223
+ renderWithAuthContext(
181
224
  <PaceLoginPage
182
225
  appName="Test App"
183
226
  onSuccessRedirectPath="/dashboard"
@@ -188,7 +231,7 @@ describe('PaceLoginPage Component', () => {
188
231
  });
189
232
 
190
233
  it('renders app logo with correct attributes', () => {
191
- renderWithProviders(<PaceLoginPage appName="TestApp" />, { withRouter: false });
234
+ renderWithAuthContext(<PaceLoginPage appName="TestApp" />, { withRouter: false });
192
235
 
193
236
  const logo = screen.getByAltText('TestApp logo');
194
237
  expect(logo).toBeInTheDocument();
@@ -197,13 +240,13 @@ describe('PaceLoginPage Component', () => {
197
240
  });
198
241
 
199
242
  it('renders LoginForm component', () => {
200
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
243
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
201
244
 
202
245
  expect(screen.getByTestId('login-form')).toBeInTheDocument();
203
246
  });
204
247
 
205
248
  it('passes correct props to LoginForm', () => {
206
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
249
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
207
250
 
208
251
  // Check that LoginForm receives the app name
209
252
  expect(screen.getByText('Sign in to Test App')).toBeInTheDocument();
@@ -215,7 +258,7 @@ describe('PaceLoginPage Component', () => {
215
258
  it('handles loading state', () => {
216
259
  mockAuthContext.isLoading = true;
217
260
 
218
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
261
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
219
262
 
220
263
  expect(screen.getByLabelText('Test App Login Page')).toBeInTheDocument();
221
264
  });
@@ -225,7 +268,7 @@ describe('PaceLoginPage Component', () => {
225
268
  mockAuthContext.isLoading = false;
226
269
  mockAuthContext.hasRole.mockReturnValue(false);
227
270
 
228
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
271
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
229
272
 
230
273
  expect(screen.getByLabelText('Test App Login Page')).toBeInTheDocument();
231
274
  });
@@ -233,7 +276,7 @@ describe('PaceLoginPage Component', () => {
233
276
  it('handles authentication error', () => {
234
277
  mockAuthContext.authError = new Error('Authentication failed');
235
278
 
236
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
279
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
237
280
 
238
281
  expect(screen.getByText('Authentication failed')).toBeInTheDocument();
239
282
  expect(screen.getByText('Authentication failed')).toHaveClass('text-destructive');
@@ -243,7 +286,7 @@ describe('PaceLoginPage Component', () => {
243
286
  const errorMessage = 'Invalid credentials';
244
287
  mockAuthContext.authError = new Error(errorMessage);
245
288
 
246
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
289
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
247
290
 
248
291
  const errorElement = screen.getByText(errorMessage);
249
292
  expect(errorElement).toBeInTheDocument();
@@ -260,7 +303,7 @@ describe('PaceLoginPage Component', () => {
260
303
  (authErr as any).code = 'session_missing';
261
304
  mockAuthContext.authError = authErr;
262
305
 
263
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
306
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
264
307
 
265
308
  expect(screen.queryByText(/Auth session missing/i)).toBeNull();
266
309
  });
@@ -278,11 +321,13 @@ describe('PaceLoginPage Component', () => {
278
321
  vi.mocked(isSuperAdmin).mockResolvedValue(true);
279
322
 
280
323
  renderWithProviders(
281
- <PaceLoginPage
282
- appName="Test App"
283
- onSuccessRedirectPath="/admin"
284
- requireAppAccess={true}
285
- />
324
+ <UnifiedAuthContext.Provider value={mockAuthContext as any}>
325
+ <PaceLoginPage
326
+ appName="Test App"
327
+ onSuccessRedirectPath="/admin"
328
+ requireAppAccess={true}
329
+ />
330
+ </UnifiedAuthContext.Provider>
286
331
  );
287
332
 
288
333
  await waitFor(() => {
@@ -295,7 +340,7 @@ describe('PaceLoginPage Component', () => {
295
340
  mockAuthContext.isLoading = false;
296
341
  mockAuthContext.hasRole.mockReturnValue(false);
297
342
 
298
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
343
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
299
344
 
300
345
  await waitFor(() => {
301
346
  expect(mockNavigate).not.toHaveBeenCalled();
@@ -319,10 +364,12 @@ describe('PaceLoginPage Component', () => {
319
364
  console.error = vi.fn();
320
365
 
321
366
  renderWithProviders(
322
- <PaceLoginPage
323
- appName="Test App"
324
- requireAppAccess={true}
325
- />
367
+ <UnifiedAuthContext.Provider value={mockAuthContext as any}>
368
+ <PaceLoginPage
369
+ appName="Test App"
370
+ requireAppAccess={true}
371
+ />
372
+ </UnifiedAuthContext.Provider>
326
373
  );
327
374
 
328
375
  await waitFor(() => {
@@ -340,7 +387,7 @@ describe('PaceLoginPage Component', () => {
340
387
  const user = userEvent.setup();
341
388
  mockAuthContext.signIn.mockResolvedValue({ error: null });
342
389
 
343
- renderWithProviders(
390
+ renderWithAuthContext(
344
391
  <PaceLoginPage
345
392
  appName="Test App"
346
393
  onSuccessRedirectPath="/dashboard"
@@ -369,7 +416,7 @@ describe('PaceLoginPage Component', () => {
369
416
  const signInError = new Error('Invalid credentials');
370
417
  mockAuthContext.signIn.mockResolvedValue({ error: signInError });
371
418
 
372
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
419
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
373
420
 
374
421
  const emailInput = screen.getByLabelText('Email');
375
422
  const passwordInput = screen.getByLabelText('Password');
@@ -399,7 +446,7 @@ describe('PaceLoginPage Component', () => {
399
446
 
400
447
  const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
401
448
 
402
- renderWithProviders(
449
+ renderWithAuthContext(
403
450
  <PaceLoginPage appName="Test App" requireAppAccess={false} />,
404
451
  { withRouter: false }
405
452
  );
@@ -430,7 +477,7 @@ describe('PaceLoginPage Component', () => {
430
477
  });
431
478
  mockAuthContext.signIn.mockReturnValue(signInPromise);
432
479
 
433
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
480
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
434
481
 
435
482
  const emailInput = screen.getByLabelText('Email');
436
483
  const passwordInput = screen.getByLabelText('Password');
@@ -459,14 +506,14 @@ describe('PaceLoginPage Component', () => {
459
506
  // Error handling tests
460
507
  describe('Error Handling', () => {
461
508
  it('handles missing app name gracefully', () => {
462
- renderWithProviders(<PaceLoginPage appName="" />, { withRouter: false });
509
+ renderWithAuthContext(<PaceLoginPage appName="" />, { withRouter: false });
463
510
 
464
511
  // Check that the component renders even with empty app name
465
512
  expect(screen.getByTestId('login-form')).toBeInTheDocument();
466
513
  });
467
514
 
468
515
  it('handles undefined redirect path', () => {
469
- renderWithProviders(<PaceLoginPage appName="Test App" onSuccessRedirectPath={undefined} />, { withRouter: false });
516
+ renderWithAuthContext(<PaceLoginPage appName="Test App" onSuccessRedirectPath={undefined} />, { withRouter: false });
470
517
 
471
518
  expect(screen.getByLabelText('Test App Login Page')).toBeInTheDocument();
472
519
  });
@@ -478,7 +525,7 @@ describe('PaceLoginPage Component', () => {
478
525
 
479
526
  const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
480
527
 
481
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
528
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
482
529
 
483
530
  const emailInput = screen.getByLabelText('Email');
484
531
  const passwordInput = screen.getByLabelText('Password');
@@ -502,14 +549,14 @@ describe('PaceLoginPage Component', () => {
502
549
  // Accessibility tests
503
550
  describe('Accessibility', () => {
504
551
  it('has proper ARIA attributes', () => {
505
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
552
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
506
553
 
507
554
  const main = screen.getByLabelText('Test App Login Page');
508
555
  expect(main).toHaveAttribute('aria-label', 'Test App Login Page');
509
556
  });
510
557
 
511
558
  it('has proper semantic structure', () => {
512
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
559
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
513
560
 
514
561
  expect(screen.getByLabelText('Test App Login Page')).toBeInTheDocument();
515
562
  expect(screen.getByRole('img')).toBeInTheDocument();
@@ -517,7 +564,7 @@ describe('PaceLoginPage Component', () => {
517
564
  });
518
565
 
519
566
  it('has accessible form elements', () => {
520
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
567
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
521
568
 
522
569
  expect(screen.getByLabelText('Email')).toBeInTheDocument();
523
570
  expect(screen.getByLabelText('Password')).toBeInTheDocument();
@@ -527,7 +574,7 @@ describe('PaceLoginPage Component', () => {
527
574
  it('announces errors to screen readers', () => {
528
575
  mockAuthContext.authError = new Error('Authentication failed');
529
576
 
530
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
577
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
531
578
 
532
579
  const errorElement = screen.getByText('Authentication failed');
533
580
  expect(errorElement).toBeInTheDocument();
@@ -537,7 +584,7 @@ describe('PaceLoginPage Component', () => {
537
584
  // Integration tests
538
585
  describe('Integration', () => {
539
586
  it('integrates with LoginForm component', () => {
540
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
587
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
541
588
 
542
589
  // Check that LoginForm is rendered with correct props
543
590
  expect(screen.getByTestId('login-form')).toBeInTheDocument();
@@ -552,7 +599,7 @@ describe('PaceLoginPage Component', () => {
552
599
  });
553
600
  mockAuthContext.signIn.mockReturnValue(signInPromise);
554
601
 
555
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
602
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
556
603
 
557
604
  const emailInput = screen.getByLabelText('Email');
558
605
  const passwordInput = screen.getByLabelText('Password');
@@ -574,21 +621,21 @@ describe('PaceLoginPage Component', () => {
574
621
  // Layout and styling tests
575
622
  describe('Layout and Styling', () => {
576
623
  it('has correct main container classes', () => {
577
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
624
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
578
625
 
579
626
  const main = screen.getByLabelText('Test App Login Page');
580
627
  expect(main).toHaveClass('min-h-screen', 'grid', 'mx-auto', 'w-fit', 'content-center', 'justify-items-center', 'gap-y-8');
581
628
  });
582
629
 
583
630
  it('renders logo with correct styling', () => {
584
- renderWithProviders(<PaceLoginPage appName="TestApp" />, { withRouter: false });
631
+ renderWithAuthContext(<PaceLoginPage appName="TestApp" />, { withRouter: false });
585
632
 
586
633
  const logo = screen.getByAltText('TestApp logo');
587
634
  expect(logo).toHaveClass('h-48');
588
635
  });
589
636
 
590
637
  it('renders LoginForm with correct width class', () => {
591
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
638
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
592
639
 
593
640
  const form = screen.getByTestId('login-form');
594
641
  expect(form.closest('.w-md')).toBeInTheDocument();
@@ -600,7 +647,7 @@ describe('PaceLoginPage Component', () => {
600
647
  it('handles empty form submission', async () => {
601
648
  const user = userEvent.setup();
602
649
 
603
- renderWithProviders(<PaceLoginPage appName="Test App" />, { withRouter: false });
650
+ renderWithAuthContext(<PaceLoginPage appName="Test App" />, { withRouter: false });
604
651
 
605
652
  const submitButton = screen.getByRole('button', { name: /sign in/i });
606
653
  await user.click(submitButton);
@@ -612,7 +659,7 @@ describe('PaceLoginPage Component', () => {
612
659
  it('handles very long app names', () => {
613
660
  const longAppName = 'A'.repeat(100);
614
661
 
615
- renderWithProviders(<PaceLoginPage appName={longAppName} />, { withRouter: false });
662
+ renderWithAuthContext(<PaceLoginPage appName={longAppName} />, { withRouter: false });
616
663
 
617
664
  expect(screen.getByLabelText(`${longAppName} Login Page`)).toBeInTheDocument();
618
665
  });
@@ -620,7 +667,7 @@ describe('PaceLoginPage Component', () => {
620
667
  it('handles special characters in app name', () => {
621
668
  const specialAppName = 'Test & Co. (Ltd.)';
622
669
 
623
- renderWithProviders(<PaceLoginPage appName={specialAppName} />, { withRouter: false });
670
+ renderWithAuthContext(<PaceLoginPage appName={specialAppName} />, { withRouter: false });
624
671
 
625
672
  expect(screen.getByLabelText(`${specialAppName} Login Page`)).toBeInTheDocument();
626
673
  });
@@ -655,7 +702,7 @@ describe('PaceLoginPage Component', () => {
655
702
  };
656
703
  mockAuthContext.supabase = missingAppSupabase as any;
657
704
 
658
- renderWithProviders(
705
+ renderWithAuthContext(
659
706
  <PaceLoginPage appName="Test App" requireAppAccess />
660
707
  );
661
708
 
@@ -727,7 +774,7 @@ describe('PaceLoginPage Component', () => {
727
774
 
728
775
  mockAuthContext.supabase = supabaseWithNoOrg as any;
729
776
 
730
- renderWithProviders(
777
+ renderWithAuthContext(
731
778
  <PaceLoginPage appName="Test App" requireAppAccess />
732
779
  );
733
780
 
@@ -123,7 +123,7 @@
123
123
 
124
124
  import React, { useEffect, useState, useContext } from 'react';
125
125
  import { useNavigate, useLocation } from 'react-router-dom';
126
- import { useUnifiedAuth } from '../../providers';
126
+ import { UnifiedAuthContext } from '../../providers/services/UnifiedAuthProvider';
127
127
  import { isSuperAdmin } from '../../rbac/api';
128
128
  import { LoginForm } from '../LoginForm';
129
129
  import { Button, Input, Label } from '..';
@@ -172,8 +172,11 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
172
172
  onSuccessRedirectPath = '/',
173
173
  requireAppAccess = false
174
174
  }) => {
175
- const { signIn, isAuthenticated, isLoading, authError, user, supabase } = useUnifiedAuth();
176
-
175
+ // Call all hooks unconditionally at the top level
176
+ // Hooks must be called in the same order on every render
177
+ // Use useContext directly instead of useUnifiedAuth() to avoid throwing
178
+ // if context isn't available yet (e.g., during initial render)
179
+ const authContext = useContext(UnifiedAuthContext);
177
180
  const navigate = useNavigate();
178
181
  const location = useLocation();
179
182
  const [isSigningIn, setIsSigningIn] = useState(false);
@@ -185,6 +188,15 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
185
188
  const eventServiceContext = useContext(EventServiceContext);
186
189
  const eventService = eventServiceContext?.eventService || null;
187
190
 
191
+ // Destructure auth context values with defaults to handle missing context
192
+ // These are used in useEffect dependency arrays, so they must always be defined
193
+ const signIn = authContext?.signIn || (async () => ({ error: null }));
194
+ const isAuthenticated = authContext?.isAuthenticated ?? false;
195
+ const isLoading = authContext?.isLoading ?? false;
196
+ const authError = authContext?.authError ?? null;
197
+ const user = authContext?.user ?? null;
198
+ const supabase = authContext?.supabase ?? null;
199
+
188
200
  // Clear any active event theme when login page mounts
189
201
  // This ensures the login screen always uses default colors
190
202
  useEffect(() => {
@@ -356,6 +368,18 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
356
368
  }
357
369
  };
358
370
 
371
+ // Handle missing auth context - show loading state
372
+ // This check happens after all hooks are called
373
+ if (!authContext) {
374
+ return (
375
+ <main className="min-h-screen flex items-center justify-center">
376
+ <section className="text-center">
377
+ <p>Loading...</p>
378
+ </section>
379
+ </main>
380
+ );
381
+ }
382
+
359
383
  return (
360
384
  <main className="min-h-screen grid mx-auto w-fit content-center justify-items-center gap-y-8" aria-label={`${appName} Login Page`}>
361
385
  <img
@@ -97,10 +97,8 @@ const Progress = React.forwardRef<
97
97
  <progress
98
98
  ref={ref}
99
99
  className={cn(
100
- 'appearance-none border-0 h-2 w-full rounded-full overflow-hidden transition-all accent-primary',
101
- isIndeterminate
102
- ? 'bg-gradient-to-r from-primary/10 via-primary/90 to-primary/10'
103
- : 'bg-primary/20',
100
+ 'appearance-none border-0 h-2 w-full rounded-full overflow-hidden transition-all accent-main-800',
101
+ !isIndeterminate && 'bg-sec-600/50',
104
102
  className
105
103
  )}
106
104
  {...(isIndeterminate ? {} : { value })}
@@ -260,9 +260,9 @@ export function ProtectedRoute({
260
260
  // Use isLoading (combined loading state) for consistency with simpler implementations
261
261
  if (isLoading && !sessionRestoration.hasTimedOut) {
262
262
  return loadingFallback || (
263
- <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
263
+ <main className="grid place-items-center size-full">
264
264
  <LoadingSpinner />
265
- </div>
265
+ </main>
266
266
  );
267
267
  }
268
268
 
@@ -294,9 +294,9 @@ export function ProtectedRoute({
294
294
  const isTabVisible = typeof document !== 'undefined' && !document.hidden;
295
295
  if (tabJustBecameVisibleRef.current || (isTabVisible && wasAuthenticatedRef.current && isLoading)) {
296
296
  return loadingFallback || (
297
- <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
297
+ <main className="grid place-items-center size-full">
298
298
  <LoadingSpinner />
299
- </div>
299
+ </main>
300
300
  );
301
301
  }
302
302
 
@@ -309,9 +309,9 @@ export function ProtectedRoute({
309
309
  // Show loading state while we wait for session refresh (unless we're not loading)
310
310
  if (isLoading) {
311
311
  return loadingFallback || (
312
- <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
312
+ <main className="grid place-items-center size-full">
313
313
  <LoadingSpinner />
314
- </div>
314
+ </main>
315
315
  );
316
316
  }
317
317
 
@@ -333,14 +333,14 @@ export function ProtectedRoute({
333
333
  // If no events are available, show error message
334
334
  if (!events || events.length === 0) {
335
335
  return noEventsFallback || (
336
- <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh', padding: '2rem' }}>
336
+ <main className="grid place-items-center text-center min-h-screen p-8">
337
337
  <Alert variant="destructive" className="max-w-md">
338
338
  <AlertTitle>No Events Available</AlertTitle>
339
339
  <AlertDescription>
340
340
  You don't have access to any events. Please contact your administrator if you believe this is an error.
341
341
  </AlertDescription>
342
342
  </Alert>
343
- </div>
343
+ </main>
344
344
  );
345
345
  }
346
346