@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.
- package/CHANGELOG.md +104 -0
- package/README.md +5 -403
- package/audit-tool/00-dependencies.cjs +394 -0
- package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
- package/audit-tool/audits/02-project-structure.cjs +255 -0
- package/audit-tool/audits/03-architecture.cjs +196 -0
- package/audit-tool/audits/04-code-quality.cjs +149 -0
- package/audit-tool/audits/05-styling.cjs +224 -0
- package/audit-tool/audits/06-security-rbac.cjs +544 -0
- package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
- package/audit-tool/audits/08-testing-documentation.cjs +202 -0
- package/audit-tool/audits/09-operations.cjs +208 -0
- package/audit-tool/index.cjs +291 -0
- package/audit-tool/utils/code-utils.cjs +218 -0
- package/audit-tool/utils/file-utils.cjs +230 -0
- package/audit-tool/utils/report-utils.cjs +241 -0
- package/core-usage-manifest.json +93 -0
- package/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/01-pace-core-compliance.mdc +586 -0
- package/cursor-rules/02-project-structure.mdc +42 -4
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
- package/cursor-rules/06-security-rbac.mdc +518 -0
- package/cursor-rules/07-api-tech-stack.mdc +377 -0
- package/cursor-rules/08-testing-documentation.mdc +324 -0
- package/cursor-rules/09-operations.mdc +365 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/DataTable-7PMH7XN7.js +15 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
- package/dist/chunk-6F3IILHI.js +62 -0
- package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
- package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
- package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
- package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
- package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/chunk-GHYHJTYV.js +994 -0
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
- package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
- package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
- package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
- package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
- package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
- package/dist/components.d.ts +7 -5
- package/dist/components.js +46 -257
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +35 -0
- package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
- package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
- package/dist/eslint-rules/rules/05-styling.cjs +61 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/eslint-rules/utils/helpers.cjs +42 -0
- package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +62 -172
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +12 -11
- package/dist/index.js +67 -660
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +8 -35
- package/dist/rbac/eslint-rules.d.ts +46 -44
- package/dist/rbac/eslint-rules.js +7 -4
- package/dist/rbac/index.d.ts +109 -586
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- package/dist/theming/runtime.d.ts +14 -1
- package/dist/theming/runtime.js +3 -19
- package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
- package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +17 -7
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +301 -871
- package/docs/api-reference/components.md +21 -21
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/hooks.md +80 -80
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/api-reference/types.md +1 -1
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -1
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +6 -6
- package/docs/core-concepts/permissions.md +6 -6
- package/docs/documentation-index.md +12 -18
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +4 -4
- package/docs/getting-started/examples/full-featured-app.md +1 -1
- package/docs/getting-started/faq.md +2 -2
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-reference.md +4 -4
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/authentication.md +15 -15
- package/docs/implementation-guides/component-styling.md +1 -1
- package/docs/implementation-guides/data-tables.md +126 -33
- package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
- package/docs/implementation-guides/dynamic-colors.md +3 -3
- package/docs/implementation-guides/file-upload-storage.md +2 -2
- package/docs/implementation-guides/hierarchical-datatable.md +40 -60
- package/docs/implementation-guides/inactivity-tracking.md +3 -3
- package/docs/implementation-guides/large-datasets.md +3 -2
- package/docs/implementation-guides/organisation-security.md +2 -2
- package/docs/implementation-guides/performance.md +2 -2
- package/docs/implementation-guides/permission-enforcement.md +5 -1
- package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
- package/docs/migration/V0.4.0_rbac-migration.md +6 -6
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +17 -8
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/event-based-apps.md +3 -3
- package/docs/rbac/examples.md +41 -41
- package/docs/rbac/getting-started.md +37 -37
- package/docs/rbac/performance.md +1 -1
- package/docs/rbac/quick-start.md +52 -52
- package/docs/rbac/secure-client-protection.md +1 -35
- package/docs/rbac/troubleshooting.md +1 -1
- package/docs/security/README.md +5 -5
- package/docs/standards/0-standards-overview.md +220 -0
- package/docs/standards/1-pace-core-compliance-standards.md +986 -0
- package/docs/standards/2-project-structure-standards.md +949 -0
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/5-styling-standards.md +348 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
- package/docs/standards/7-api-tech-stack-standards.md +662 -0
- package/docs/standards/8-testing-documentation-standards.md +401 -0
- package/docs/standards/9-operations-standards.md +1102 -0
- package/docs/standards/README.md +185 -57
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/common-issues.md +2 -2
- package/docs/troubleshooting/debugging.md +9 -9
- package/docs/troubleshooting/migration.md +4 -4
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +33 -6
- package/package.json +35 -23
- package/scripts/install-cursor-rules.cjs +25 -6
- package/scripts/install-eslint-config.cjs +284 -0
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
- package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
- package/src/__tests__/templates/component.test.template.tsx +18 -15
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/Calendar/Calendar.tsx +201 -47
- package/src/components/ContextSelector/ContextSelector.tsx +106 -119
- package/src/components/DataTable/AUDIT_REPORT.md +293 -0
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
- package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +12 -9
- package/src/components/DataTable/components/EmptyState.tsx +10 -9
- package/src/components/DataTable/components/FilterRow.tsx +2 -4
- package/src/components/DataTable/components/ImportModal.tsx +124 -126
- package/src/components/DataTable/components/LoadingState.tsx +5 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/SortIndicator.tsx +50 -0
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/components/index.ts +2 -1
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
- package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
- package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
- package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
- package/src/components/DataTable/types.ts +5 -18
- package/src/components/DataTable/utils/a11yUtils.ts +17 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
- package/src/components/DateTimeField/DateTimeField.tsx +10 -9
- package/src/components/Dialog/Dialog.test.tsx +128 -104
- package/src/components/Dialog/Dialog.tsx +742 -24
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
- package/src/components/FileDisplay/FileDisplay.tsx +23 -17
- package/src/components/FileUpload/FileUpload.test.tsx +52 -14
- package/src/components/FileUpload/FileUpload.tsx +112 -130
- package/src/components/Form/Form.test.tsx +6 -8
- package/src/components/Form/Form.tsx +365 -4
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +109 -98
- package/src/components/Select/types.ts +4 -1
- package/src/components/UserMenu/UserMenu.tsx +9 -6
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
- package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
- package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
- package/src/hooks/public/usePublicEvent.ts +67 -195
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +24 -14
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +5 -5
- package/src/hooks/useAppConfig.ts +28 -26
- package/src/hooks/useEventTheme.test.ts +217 -239
- package/src/hooks/useEventTheme.ts +16 -28
- package/src/hooks/useFileDisplay.ts +2 -2
- package/src/hooks/useOrganisationPermissions.ts +5 -7
- package/src/hooks/useQueryCache.ts +0 -1
- package/src/hooks/useSessionDraft.ts +380 -0
- package/src/hooks/useSessionRestoration.ts +3 -1
- package/src/icons/index.ts +27 -0
- package/src/index.ts +5 -0
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
- package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
- package/src/providers/__tests__/EventProvider.test.tsx +61 -61
- package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
- package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
- package/src/rbac/adapters.tsx +7 -295
- package/src/rbac/api.test.ts +44 -56
- package/src/rbac/api.ts +10 -17
- package/src/rbac/cache-invalidation.ts +0 -1
- package/src/rbac/compliance/index.ts +10 -0
- package/src/rbac/compliance/pattern-detector.ts +553 -0
- package/src/rbac/compliance/runtime-compliance.ts +22 -0
- package/src/rbac/components/AccessDenied.tsx +150 -0
- package/src/rbac/components/NavigationGuard.tsx +12 -20
- package/src/rbac/components/PagePermissionGuard.tsx +4 -24
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
- package/src/rbac/components/index.ts +3 -41
- package/src/rbac/eslint-rules.js +1 -1
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/permissions/index.ts +0 -3
- package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
- package/src/rbac/hooks/usePermissions.ts +0 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
- package/src/rbac/hooks/useResolvedScope.ts +58 -140
- package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
- package/src/rbac/hooks/useResourcePermissions.ts +139 -48
- package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
- package/src/rbac/hooks/useRoleManagement.ts +147 -19
- package/src/rbac/hooks/useSecureSupabase.ts +4 -8
- package/src/rbac/index.ts +7 -9
- package/src/rbac/utils/contextValidator.ts +9 -7
- package/src/services/AuthService.ts +130 -18
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +16 -0
- package/src/services/OrganisationService.ts +7 -44
- package/src/services/__tests__/OrganisationService.test.ts +26 -8
- package/src/services/base/BaseService.ts +0 -3
- package/src/styles/core.css +7 -0
- package/src/theming/__tests__/parseEventColours.test.ts +9 -3
- package/src/theming/parseEventColours.ts +22 -10
- package/src/types/database.generated.ts +4733 -3809
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
- package/src/utils/context/organisationContext.test.ts +13 -28
- package/src/utils/context/organisationContext.ts +21 -52
- package/src/utils/dynamic/dynamicUtils.ts +1 -1
- package/src/utils/file-reference/index.ts +39 -15
- package/src/utils/formatting/formatDateTime.test.ts +3 -2
- package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
- package/src/utils/index.ts +4 -1
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
- package/src/utils/persistence/keyDerivation.ts +304 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
- package/src/utils/security/secureStorage.ts +5 -5
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/helpers.ts +3 -3
- package/src/utils/supabase/createBaseClient.ts +147 -0
- package/src/utils/timezone/timezone.test.ts +1 -2
- package/src/utils/timezone/timezone.ts +1 -1
- package/src/utils/validation/csrf.ts +4 -4
- package/cursor-rules/00-pace-core-compliance.mdc +0 -331
- package/cursor-rules/01-standards-compliance.mdc +0 -244
- package/cursor-rules/04-testing-standards.mdc +0 -268
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
- package/cursor-rules/06-code-quality.mdc +0 -309
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
- package/cursor-rules/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-AOVNCPTX.js +0 -175
- package/dist/DataTable-AOVNCPTX.js.map +0 -1
- package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
- package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
- package/dist/api-O6HTBX5Y.js +0 -52
- package/dist/api-O6HTBX5Y.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6COVEUS7.js.map +0 -1
- package/dist/chunk-AFVQODI2.js +0 -263
- package/dist/chunk-AFVQODI2.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.js.map +0 -1
- package/dist/chunk-EFN2EIMK.js.map +0 -1
- package/dist/chunk-FFQEQTNW.js.map +0 -1
- package/dist/chunk-FMUCXFII.js.map +0 -1
- package/dist/chunk-G37KK66H.js.map +0 -1
- package/dist/chunk-G7QEZTYQ.js +0 -2053
- package/dist/chunk-G7QEZTYQ.js.map +0 -1
- package/dist/chunk-HU2C6SSC.js.map +0 -1
- package/dist/chunk-IHB5DR3H.js.map +0 -1
- package/dist/chunk-IVOFDYWT.js.map +0 -1
- package/dist/chunk-J36DSWQK.js.map +0 -1
- package/dist/chunk-JGRYX5UX.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js +0 -1
- package/dist/chunk-KQCRWDSA.js.map +0 -1
- package/dist/chunk-L4OXEN46.js.map +0 -1
- package/dist/chunk-LMC26NLJ.js +0 -84
- package/dist/chunk-LMC26NLJ.js.map +0 -1
- package/dist/chunk-M43Y4SSO.js.map +0 -1
- package/dist/chunk-M7MPQISP.js.map +0 -1
- package/dist/chunk-NTM7ZSB6.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-RGAWHO7N.js.map +0 -1
- package/dist/chunk-UPPMRMYG.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-5OGXSPKS.js +0 -9
- package/dist/contextValidator-5OGXSPKS.js.map +0 -1
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
- package/dist/hooks.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/providers.js.map +0 -1
- package/dist/rbac/eslint-rules.js.map +0 -1
- package/dist/rbac/index.js.map +0 -1
- package/dist/styles/index.js.map +0 -1
- package/dist/theming/runtime.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -601
- package/docs/best-practices/common-patterns.md +0 -516
- package/docs/best-practices/deployment.md +0 -1103
- package/docs/best-practices/performance.md +0 -1328
- package/docs/best-practices/security.md +0 -940
- package/docs/best-practices/testing.md +0 -1034
- package/docs/rbac/compliance/compliance-guide.md +0 -544
- package/docs/standards/01-architecture-standard.md +0 -44
- package/docs/standards/02-api-and-rpc-standard.md +0 -39
- package/docs/standards/03-component-standard.md +0 -32
- package/docs/standards/04-code-style-standard.md +0 -32
- package/docs/standards/05-security-standard.md +0 -44
- package/docs/standards/06-testing-and-docs-standard.md +0 -29
- package/docs/standards/pace-core-compliance.md +0 -432
- package/scripts/audit/core/checks/accessibility.cjs +0 -197
- package/scripts/audit/core/checks/api-usage.cjs +0 -191
- package/scripts/audit/core/checks/bundle.cjs +0 -142
- package/scripts/audit/core/checks/compliance.cjs +0 -2706
- package/scripts/audit/core/checks/config.cjs +0 -54
- package/scripts/audit/core/checks/coverage.cjs +0 -84
- package/scripts/audit/core/checks/dependencies.cjs +0 -994
- package/scripts/audit/core/checks/documentation.cjs +0 -268
- package/scripts/audit/core/checks/environment.cjs +0 -116
- package/scripts/audit/core/checks/error-handling.cjs +0 -340
- package/scripts/audit/core/checks/forms.cjs +0 -172
- package/scripts/audit/core/checks/heuristics.cjs +0 -68
- package/scripts/audit/core/checks/hooks.cjs +0 -334
- package/scripts/audit/core/checks/imports.cjs +0 -244
- package/scripts/audit/core/checks/performance.cjs +0 -325
- package/scripts/audit/core/checks/routes.cjs +0 -117
- package/scripts/audit/core/checks/state.cjs +0 -130
- package/scripts/audit/core/checks/structure.cjs +0 -65
- package/scripts/audit/core/checks/style.cjs +0 -584
- package/scripts/audit/core/checks/testing.cjs +0 -122
- package/scripts/audit/core/checks/typescript.cjs +0 -61
- package/scripts/audit/core/scanner.cjs +0 -199
- package/scripts/audit/core/utils.cjs +0 -137
- package/scripts/audit/index.cjs +0 -223
- package/scripts/audit/reporters/console.cjs +0 -151
- package/scripts/audit/reporters/json.cjs +0 -54
- package/scripts/audit/reporters/markdown.cjs +0 -124
- package/scripts/audit-consuming-app.cjs +0 -86
- package/src/components/DataTable/components/DataTableBody.tsx +0 -454
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
- package/src/components/DataTable/components/ExpandButton.tsx +0 -113
- package/src/components/DataTable/components/GroupHeader.tsx +0 -54
- package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
- package/src/components/DataTable/core/DataTableContext.tsx +0 -216
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
- package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
- package/src/components/DataTable/utils/debugTools.ts +0 -514
- package/src/eslint-rules/pace-core-compliance.js +0 -638
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
- package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
- package/src/rbac/components/NavigationProvider.test.tsx +0 -481
- package/src/rbac/components/NavigationProvider.tsx +0 -345
- package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
- package/src/rbac/components/PagePermissionProvider.tsx +0 -279
- package/src/rbac/components/PermissionEnforcer.tsx +0 -312
- package/src/rbac/components/RoleBasedRouter.tsx +0 -440
- package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
- package/src/rbac/components/SecureDataProvider.tsx +0 -339
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
- 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
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
155
|
-
<
|
|
156
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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-
|
|
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
|
-
<
|
|
263
|
+
<main className="grid place-items-center size-full">
|
|
264
264
|
<LoadingSpinner />
|
|
265
|
-
</
|
|
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
|
-
<
|
|
297
|
+
<main className="grid place-items-center size-full">
|
|
298
298
|
<LoadingSpinner />
|
|
299
|
-
</
|
|
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
|
-
<
|
|
312
|
+
<main className="grid place-items-center size-full">
|
|
313
313
|
<LoadingSpinner />
|
|
314
|
-
</
|
|
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
|
-
<
|
|
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
|
-
</
|
|
343
|
+
</main>
|
|
344
344
|
);
|
|
345
345
|
}
|
|
346
346
|
|