@jmruthers/pace-core 0.6.4 → 0.6.6
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/core-usage-manifest.json +93 -0
- package/cursor-rules/00-pace-core-compliance.mdc +128 -26
- package/cursor-rules/01-standards-compliance.mdc +49 -8
- package/cursor-rules/02-project-structure.mdc +6 -0
- package/cursor-rules/03-solid-principles.mdc +2 -0
- package/cursor-rules/04-testing-standards.mdc +2 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +2 -0
- package/cursor-rules/06-code-quality.mdc +2 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +2 -0
- package/cursor-rules/08-markup-quality.mdc +52 -27
- package/cursor-rules/09-rbac-compliance.mdc +462 -0
- package/cursor-rules/10-error-handling-patterns.mdc +179 -0
- package/cursor-rules/11-performance-optimization.mdc +169 -0
- package/cursor-rules/12-ci-cd-integration.mdc +150 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-2N_tqbfq.d.ts} +1 -1
- package/dist/DataTable-LRJL4IRV.js +15 -0
- package/dist/{PublicPageProvider-DEMpysFR.d.ts → PublicPageProvider-BBH6Vqg7.d.ts} +72 -139
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-J36DSWQK.js → chunk-2HGJFNAH.js} +8 -28
- package/dist/{chunk-OEWDTMG7.js → chunk-3O3WHILE.js} +38 -121
- package/dist/{chunk-M43Y4SSO.js → chunk-3QC3KRHK.js} +1 -14
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/chunk-4T7OBVTU.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-NN6WWZ5U.js → chunk-7TYHROIV.js} +579 -563
- package/dist/{chunk-M7MPQISP.js → chunk-A55DK444.js} +9 -16
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-L4OXEN46.js → chunk-BVP2BCJF.js} +2 -16
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-YKRAFF5K.js → chunk-FENMYN2U.js} +73 -149
- package/dist/{chunk-AVMLPIM7.js → chunk-FTCRZOG2.js} +284 -432
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-I6DAQMWX.js → chunk-LAZMKTTF.js} +930 -891
- package/dist/{chunk-5EC5MEWX.js → chunk-MAGBIDNS.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/chunk-OHIK3MIO.js +994 -0
- package/dist/{chunk-6SOIHG6Z.js → chunk-S7DKJPLT.js} +115 -44
- package/dist/{chunk-FMUCXFII.js → chunk-SD6WQY43.js} +1 -5
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-FFQEQTNW.js → chunk-UIYSCEV7.js} +134 -45
- package/dist/{chunk-3LPHPB62.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-7JPAB3T5.js → chunk-ZS5VO5JB.js} +1989 -1283
- package/dist/components.d.ts +6 -6
- package/dist/components.js +57 -267
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +22 -0
- package/dist/eslint-rules/rules/compliance.cjs +348 -0
- package/dist/eslint-rules/rules/components.cjs +113 -0
- package/dist/eslint-rules/rules/imports.cjs +102 -0
- package/dist/eslint-rules/rules/rbac.cjs +790 -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 +5 -5
- package/dist/hooks.js +62 -270
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +36 -26
- package/dist/index.js +87 -690
- 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 +124 -594
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- 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-B-K_5VnO.d.ts} +4 -0
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-i3qtoBgg.d.ts → usePublicRouteParams-COZ28Mvq.d.ts} +9 -9
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +16 -6
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +454 -930
- package/docs/api-reference/components.md +3 -1
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/best-practices/accessibility.md +6 -3
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/permission-enforcement.md +4 -0
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +12 -3
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/secure-client-protection.md +0 -34
- package/docs/standards/00-pace-core-compliance.md +967 -0
- package/docs/standards/01-standards-compliance.md +188 -0
- package/docs/standards/02-project-structure.md +985 -0
- package/docs/standards/03-solid-principles.md +39 -0
- package/docs/standards/04-testing-standards.md +36 -0
- package/docs/standards/05-bug-reports-and-features.md +27 -0
- package/docs/standards/{04-code-style-standard.md → 06-code-quality.md} +2 -0
- package/docs/standards/07-tech-stack-compliance.md +30 -0
- package/docs/standards/08-markup-quality.md +345 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 09-rbac-compliance.md} +149 -54
- package/docs/standards/10-error-handling-patterns.md +401 -0
- package/docs/standards/11-performance-optimization.md +348 -0
- package/docs/standards/12-ci-cd-integration.md +370 -0
- package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +192 -0
- package/docs/standards/README.md +62 -33
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +20 -4
- package/package.json +31 -21
- package/scripts/audit/audit-compliance.cjs +1295 -0
- package/scripts/audit/audit-components.cjs +260 -0
- package/scripts/audit/audit-dependencies.cjs +395 -0
- package/scripts/audit/audit-rbac.cjs +954 -0
- package/scripts/audit/audit-standards.cjs +1268 -0
- package/scripts/audit/index.cjs +1898 -194
- package/scripts/install-cursor-rules.cjs +259 -8
- package/scripts/validate-master.js +1 -1
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +1 -1
- 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-utils.test.tsx +3 -3
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/ContextSelector/ContextSelector.tsx +42 -39
- package/src/components/DataTable/__tests__/keyboard.test.tsx +15 -2
- package/src/components/DataTable/components/DataTableBody.tsx +55 -31
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableLayout.tsx +30 -5
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +7 -2
- package/src/components/DataTable/components/ImportModal.tsx +4 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/ViewRowModal.tsx +4 -4
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +455 -96
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +122 -58
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/core/DataTableContext.tsx +1 -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 -0
- package/src/components/DateTimeField/DateTimeField.tsx +20 -20
- package/src/components/DateTimeField/README.md +5 -2
- package/src/components/Dialog/Dialog.test.tsx +361 -318
- package/src/components/Dialog/Dialog.tsx +1154 -323
- package/src/components/Dialog/index.ts +3 -3
- package/src/components/FileDisplay/FileDisplay.test.tsx +45 -2
- package/src/components/FileDisplay/FileDisplay.tsx +28 -22
- package/src/components/Form/Form.test.tsx +9 -10
- package/src/components/Form/Form.tsx +369 -9
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +28 -28
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +40 -54
- package/src/components/LoginForm/LoginForm.tsx +2 -2
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +30 -41
- package/src/components/PaceAppLayout/README.md +10 -9
- package/src/components/PaceAppLayout/test-setup.tsx +40 -31
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +61 -0
- package/src/components/PasswordChange/PasswordChangeForm.tsx +20 -13
- package/src/components/PublicLayout/PublicLayout.test.tsx +7 -3
- package/src/components/PublicLayout/PublicPageLayout.tsx +5 -8
- package/src/components/Select/Select.tsx +23 -21
- package/src/components/Select/types.ts +1 -1
- package/src/components/UserMenu/UserMenu.test.tsx +38 -6
- package/src/components/UserMenu/UserMenu.tsx +39 -34
- package/src/components/index.ts +3 -4
- package/src/eslint-rules/index.cjs +22 -0
- package/src/eslint-rules/rules/compliance.cjs +348 -0
- package/src/eslint-rules/rules/components.cjs +113 -0
- package/src/eslint-rules/rules/imports.cjs +102 -0
- package/src/eslint-rules/rules/rbac.cjs +790 -0
- package/src/eslint-rules/utils/helpers.cjs +42 -0
- package/src/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/src/hooks/__tests__/hooks.integration.test.tsx +6 -8
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- 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 +62 -190
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +19 -9
- package/src/hooks/useAppConfig.ts +26 -24
- package/src/hooks/useEventTheme.test.ts +211 -233
- package/src/hooks/useEventTheme.ts +19 -28
- package/src/hooks/useEvents.ts +11 -7
- package/src/hooks/useKeyboardShortcuts.ts +1 -1
- package/src/hooks/useOrganisationPermissions.ts +9 -11
- package/src/hooks/useOrganisations.ts +13 -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 +16 -1
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -0
- package/src/rbac/README.md +20 -20
- 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/useRBAC.test.ts +21 -3
- package/src/rbac/hooks/useRBAC.ts +4 -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 +241 -60
- package/src/rbac/hooks/useResourcePermissions.ts +182 -63
- 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/permissions.ts +17 -17
- package/src/rbac/utils/contextValidator.ts +45 -7
- package/src/services/AuthService.ts +132 -23
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +155 -58
- 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 +4 -0
- package/src/types/database.generated.ts +4733 -3809
- 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/formatting/formatTime.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/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/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-E7YQZD7D.js +0 -175
- package/dist/DataTable-E7YQZD7D.js.map +0 -1
- package/dist/UnifiedAuthProvider-QPXO24B4.js +0 -18
- package/dist/UnifiedAuthProvider-QPXO24B4.js.map +0 -1
- package/dist/api-6LVZTHDS.js +0 -52
- package/dist/api-6LVZTHDS.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-36LVWXB2.js +0 -227
- package/dist/chunk-36LVWXB2.js.map +0 -1
- package/dist/chunk-3LPHPB62.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-5EC5MEWX.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6SOIHG6Z.js.map +0 -1
- package/dist/chunk-7JPAB3T5.js.map +0 -1
- package/dist/chunk-ATKZM7RX.js +0 -2053
- package/dist/chunk-ATKZM7RX.js.map +0 -1
- package/dist/chunk-AVMLPIM7.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.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-I6DAQMWX.js.map +0 -1
- package/dist/chunk-J36DSWQK.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-NN6WWZ5U.js.map +0 -1
- package/dist/chunk-OEWDTMG7.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-YKRAFF5K.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-OOPCLPZW.js +0 -9
- package/dist/contextValidator-OOPCLPZW.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/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/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/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/eslint-rules/pace-core-compliance.cjs +0 -510
- 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
|
@@ -115,18 +115,12 @@ export function usePublicEvent(
|
|
|
115
115
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
116
116
|
const [error, setError] = useState<Error | null>(null);
|
|
117
117
|
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// Fallback to direct environment variable access if not in PublicPageProvider
|
|
125
|
-
environment = {
|
|
126
|
-
supabaseUrl: (import.meta as any).env?.VITE_SUPABASE_URL || (import.meta as any).env?.NEXT_PUBLIC_SUPABASE_URL || null,
|
|
127
|
-
supabaseKey: (import.meta as any).env?.VITE_SUPABASE_PUBLISHABLE_KEY || (import.meta as any).env?.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || null
|
|
128
|
-
};
|
|
129
|
-
}
|
|
118
|
+
// Call hook unconditionally - if provider is missing, it will throw
|
|
119
|
+
// Errors should be handled by error boundaries at a higher level
|
|
120
|
+
// We cannot conditionally call hooks
|
|
121
|
+
// For public pages, PublicPageProvider should always be present
|
|
122
|
+
const publicPageContext = usePublicPageContext();
|
|
123
|
+
const environment = publicPageContext.environment;
|
|
130
124
|
|
|
131
125
|
// Create a simple Supabase client for public access
|
|
132
126
|
const supabase = useMemo(() => {
|
|
@@ -140,17 +134,6 @@ export function usePublicEvent(
|
|
|
140
134
|
return createClient<Database>(environment.supabaseUrl, environment.supabaseKey);
|
|
141
135
|
}, [environment.supabaseUrl, environment.supabaseKey]);
|
|
142
136
|
|
|
143
|
-
// Helper function to try refreshing schema cache
|
|
144
|
-
const refreshSchemaCache = useCallback(async () => {
|
|
145
|
-
try {
|
|
146
|
-
// Try to trigger a schema refresh by querying a system table
|
|
147
|
-
await (supabase as any).from('information_schema.routines').select('routine_name').limit(1);
|
|
148
|
-
} catch (error) {
|
|
149
|
-
// Ignore errors, this is just an attempt to refresh cache
|
|
150
|
-
logger.debug('usePublicEvent', 'Schema cache refresh attempt failed:', error);
|
|
151
|
-
}
|
|
152
|
-
}, [supabase]);
|
|
153
|
-
|
|
154
137
|
const fetchEvent = useCallback(async (): Promise<void> => {
|
|
155
138
|
if (!eventCode || !supabase) {
|
|
156
139
|
setError(new Error('Invalid event code or Supabase client not available'));
|
|
@@ -174,177 +157,66 @@ export function usePublicEvent(
|
|
|
174
157
|
setIsLoading(true);
|
|
175
158
|
setError(null);
|
|
176
159
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
logger.warn('usePublicEvent', 'RPC still failing after cache refresh, falling back to direct table access');
|
|
214
|
-
|
|
215
|
-
// Fallback: Direct table access with public RLS policy
|
|
216
|
-
const tableResponse2 = await (supabase as any)
|
|
217
|
-
.from('core_events')
|
|
218
|
-
.select(`
|
|
219
|
-
event_id,
|
|
220
|
-
event_name,
|
|
221
|
-
event_date,
|
|
222
|
-
event_venue,
|
|
223
|
-
event_participants,
|
|
224
|
-
event_colours,
|
|
225
|
-
organisation_id,
|
|
226
|
-
event_days,
|
|
227
|
-
event_typicalunit,
|
|
228
|
-
event_rounddown,
|
|
229
|
-
event_youthmultiplier,
|
|
230
|
-
event_catering_email,
|
|
231
|
-
event_news,
|
|
232
|
-
event_billing,
|
|
233
|
-
event_email
|
|
234
|
-
`)
|
|
235
|
-
.eq('event_code', eventCode)
|
|
236
|
-
.eq('is_visible', true)
|
|
237
|
-
.not('organisation_id', 'is', null)
|
|
238
|
-
.limit(1)
|
|
239
|
-
.single();
|
|
240
|
-
|
|
241
|
-
const tableData = tableResponse2?.data;
|
|
242
|
-
const tableError = tableResponse2?.error;
|
|
243
|
-
|
|
244
|
-
if (tableError) {
|
|
245
|
-
throw new Error(tableError?.message || 'Failed to fetch event from table');
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (!tableData) {
|
|
249
|
-
setEvent(null);
|
|
250
|
-
setError(new Error('Event not found'));
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
160
|
+
// Use direct table query (removed legacy RPC get_public_event_by_code)
|
|
161
|
+
const tableResponse = await (supabase as any)
|
|
162
|
+
.from('core_events')
|
|
163
|
+
.select(`
|
|
164
|
+
event_id,
|
|
165
|
+
event_name,
|
|
166
|
+
event_date,
|
|
167
|
+
event_venue,
|
|
168
|
+
event_participants,
|
|
169
|
+
event_colours,
|
|
170
|
+
organisation_id,
|
|
171
|
+
event_days,
|
|
172
|
+
event_typicalunit,
|
|
173
|
+
event_rounddown,
|
|
174
|
+
event_youthmultiplier,
|
|
175
|
+
event_catering_email,
|
|
176
|
+
event_news,
|
|
177
|
+
event_billing,
|
|
178
|
+
event_email
|
|
179
|
+
`)
|
|
180
|
+
.eq('event_code', eventCode)
|
|
181
|
+
.eq('is_visible', true)
|
|
182
|
+
.not('organisation_id', 'is', null)
|
|
183
|
+
.limit(1)
|
|
184
|
+
.single();
|
|
185
|
+
|
|
186
|
+
const tableData = tableResponse?.data;
|
|
187
|
+
const tableError = tableResponse?.error;
|
|
188
|
+
|
|
189
|
+
if (tableError) {
|
|
190
|
+
// Transform "No rows found" to user-friendly "Event not found"
|
|
191
|
+
const errorMessage = tableError.message === 'No rows found' || tableError.code === 'PGRST116'
|
|
192
|
+
? 'Event not found'
|
|
193
|
+
: (tableError?.message || 'Failed to fetch event from table');
|
|
194
|
+
throw new Error(errorMessage);
|
|
195
|
+
}
|
|
253
196
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
.eq('record_id', tableData.event_id)
|
|
260
|
-
.eq('is_public', true)
|
|
261
|
-
.eq('file_metadata->>category', 'event_logos')
|
|
262
|
-
.limit(1)
|
|
263
|
-
.single();
|
|
264
|
-
|
|
265
|
-
const logoData = logoResponse?.data;
|
|
197
|
+
if (!tableData) {
|
|
198
|
+
setEvent(null);
|
|
199
|
+
setError(new Error('Event not found'));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
266
202
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
setIsLoading(false);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
} else {
|
|
281
|
-
if (!data || data.length === 0 || !data[0]) {
|
|
282
|
-
setEvent(null);
|
|
283
|
-
setError(new Error('Event not found'));
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
eventData = data[0];
|
|
287
|
-
}
|
|
288
|
-
} catch (rpcError) {
|
|
289
|
-
// If RPC call fails for any reason (including schema cache issues), try direct table access
|
|
290
|
-
logger.warn('usePublicEvent', 'RPC call failed, falling back to direct table access:', rpcError);
|
|
203
|
+
// Get event logo from core_file_references
|
|
204
|
+
const logoResponse = await (supabase as any)
|
|
205
|
+
.from('core_file_references')
|
|
206
|
+
.select('file_path')
|
|
207
|
+
.eq('table_name', 'core_events')
|
|
208
|
+
.eq('record_id', tableData.event_id)
|
|
209
|
+
.eq('is_public', true)
|
|
210
|
+
.eq('file_metadata->>category', 'event_logos')
|
|
211
|
+
.limit(1)
|
|
212
|
+
.single();
|
|
291
213
|
|
|
292
|
-
|
|
293
|
-
.from('core_events')
|
|
294
|
-
.select(`
|
|
295
|
-
event_id,
|
|
296
|
-
event_name,
|
|
297
|
-
event_date,
|
|
298
|
-
event_venue,
|
|
299
|
-
event_participants,
|
|
300
|
-
event_colours,
|
|
301
|
-
organisation_id,
|
|
302
|
-
event_days,
|
|
303
|
-
event_typicalunit,
|
|
304
|
-
event_rounddown,
|
|
305
|
-
event_youthmultiplier,
|
|
306
|
-
event_catering_email,
|
|
307
|
-
event_news,
|
|
308
|
-
event_billing,
|
|
309
|
-
event_email
|
|
310
|
-
`)
|
|
311
|
-
.eq('event_code', eventCode)
|
|
312
|
-
.eq('is_visible', true)
|
|
313
|
-
.not('organisation_id', 'is', null)
|
|
314
|
-
.limit(1)
|
|
315
|
-
.single();
|
|
316
|
-
|
|
317
|
-
const tableData = tableResponse?.data;
|
|
318
|
-
const tableError = tableResponse?.error;
|
|
214
|
+
const logoData = logoResponse?.data;
|
|
319
215
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (!tableData) {
|
|
325
|
-
setEvent(null);
|
|
326
|
-
setError(new Error('Event not found'));
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Get event logo from core_file_references
|
|
331
|
-
const logoResponse = await (supabase as any)
|
|
332
|
-
.from('core_file_references')
|
|
333
|
-
.select('file_path')
|
|
334
|
-
.eq('table_name', 'core_events')
|
|
335
|
-
.eq('record_id', tableData.event_id)
|
|
336
|
-
.eq('is_public', true)
|
|
337
|
-
.eq('file_metadata->>category', 'event_logos')
|
|
338
|
-
.limit(1)
|
|
339
|
-
.single();
|
|
340
|
-
|
|
341
|
-
const logoData = logoResponse?.data;
|
|
342
|
-
|
|
343
|
-
eventData = {
|
|
344
|
-
...tableData,
|
|
345
|
-
event_logo: logoData?.file_path || null
|
|
346
|
-
};
|
|
347
|
-
}
|
|
216
|
+
const eventData = {
|
|
217
|
+
...tableData,
|
|
218
|
+
event_logo: logoData?.file_path || null
|
|
219
|
+
};
|
|
348
220
|
|
|
349
221
|
// Transform to Event type
|
|
350
222
|
const transformedEvent: Event = {
|
|
@@ -33,6 +33,15 @@ describe('usePublicEventLogo', () => {
|
|
|
33
33
|
fetchMock = vi.fn().mockResolvedValue({ ok: true });
|
|
34
34
|
(globalThis as any).fetch = fetchMock;
|
|
35
35
|
clearPublicLogoCache();
|
|
36
|
+
|
|
37
|
+
// Setup default mock for the query chain: .from('core_file_references').select(...).eq(...).eq(...).eq(...).eq(...).limit(1).single()
|
|
38
|
+
const mockQueryBuilder = {
|
|
39
|
+
select: vi.fn().mockReturnThis(),
|
|
40
|
+
eq: vi.fn().mockReturnThis(),
|
|
41
|
+
limit: vi.fn().mockReturnThis(),
|
|
42
|
+
single: vi.fn().mockResolvedValue({ data: null, error: null })
|
|
43
|
+
};
|
|
44
|
+
(mockSupabase.from as any).mockReturnValue(mockQueryBuilder);
|
|
36
45
|
});
|
|
37
46
|
|
|
38
47
|
afterEach(() => {
|
|
@@ -44,9 +53,19 @@ describe('usePublicEventLogo', () => {
|
|
|
44
53
|
(globalThis as any).fetch = originalFetch;
|
|
45
54
|
});
|
|
46
55
|
|
|
47
|
-
it('fetches and caches the logo when
|
|
56
|
+
it('fetches and caches the logo when query succeeds', async () => {
|
|
48
57
|
const logoUrl = 'https://cdn.example.com/logo.png';
|
|
49
|
-
|
|
58
|
+
// usePublicEventLogo uses: .from('core_file_references').select('file_path').eq(...).eq(...).eq(...).eq(...).limit(1).single()
|
|
59
|
+
const mockQueryBuilder = {
|
|
60
|
+
select: vi.fn().mockReturnThis(),
|
|
61
|
+
eq: vi.fn().mockReturnThis(),
|
|
62
|
+
limit: vi.fn().mockReturnThis(),
|
|
63
|
+
single: vi.fn().mockResolvedValue({
|
|
64
|
+
data: { file_path: logoUrl },
|
|
65
|
+
error: null
|
|
66
|
+
})
|
|
67
|
+
};
|
|
68
|
+
(mockSupabase.from as any).mockReturnValue(mockQueryBuilder);
|
|
50
69
|
|
|
51
70
|
const { result } = renderHook(() =>
|
|
52
71
|
usePublicEventLogo('event-123', 'Big Event', VALID_ORG_ID, {
|
|
@@ -54,7 +73,7 @@ describe('usePublicEventLogo', () => {
|
|
|
54
73
|
})
|
|
55
74
|
);
|
|
56
75
|
|
|
57
|
-
await waitFor(() => expect(result.current.logoUrl).toBe(logoUrl));
|
|
76
|
+
await waitFor(() => expect(result.current.logoUrl).toBe(logoUrl), { timeout: 2000 });
|
|
58
77
|
expect(result.current.fallbackText).toBe('BE');
|
|
59
78
|
expect(fetchMock).toHaveBeenCalledWith(logoUrl, { method: 'HEAD' });
|
|
60
79
|
|
|
@@ -66,9 +85,16 @@ describe('usePublicEventLogo', () => {
|
|
|
66
85
|
it('refetch clears cache and requests fresh data', async () => {
|
|
67
86
|
const firstUrl = 'https://cdn.example.com/logo.png';
|
|
68
87
|
const secondUrl = 'https://cdn.example.com/logo-2.png';
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.
|
|
88
|
+
|
|
89
|
+
const mockQueryBuilder = {
|
|
90
|
+
select: vi.fn().mockReturnThis(),
|
|
91
|
+
eq: vi.fn().mockReturnThis(),
|
|
92
|
+
limit: vi.fn().mockReturnThis(),
|
|
93
|
+
single: vi.fn()
|
|
94
|
+
.mockResolvedValueOnce({ data: { file_path: firstUrl }, error: null })
|
|
95
|
+
.mockResolvedValueOnce({ data: { file_path: secondUrl }, error: null })
|
|
96
|
+
};
|
|
97
|
+
(mockSupabase.from as any).mockReturnValue(mockQueryBuilder);
|
|
72
98
|
|
|
73
99
|
const { result } = renderHook(() =>
|
|
74
100
|
usePublicEventLogo('event-123', 'Big Event', VALID_ORG_ID, {
|
|
@@ -76,18 +102,27 @@ describe('usePublicEventLogo', () => {
|
|
|
76
102
|
})
|
|
77
103
|
);
|
|
78
104
|
|
|
79
|
-
await waitFor(() => expect(result.current.logoUrl).toBe(firstUrl));
|
|
105
|
+
await waitFor(() => expect(result.current.logoUrl).toBe(firstUrl), { timeout: 2000 });
|
|
80
106
|
|
|
81
107
|
await act(async () => {
|
|
82
108
|
await result.current.refetch();
|
|
83
109
|
});
|
|
84
110
|
|
|
85
|
-
await waitFor(() => expect(result.current.logoUrl).toBe(secondUrl));
|
|
86
|
-
expect(
|
|
111
|
+
await waitFor(() => expect(result.current.logoUrl).toBe(secondUrl), { timeout: 2000 });
|
|
112
|
+
expect(mockQueryBuilder.single).toHaveBeenCalledTimes(2);
|
|
87
113
|
});
|
|
88
114
|
|
|
89
|
-
it('exposes error state when
|
|
90
|
-
|
|
115
|
+
it('exposes error state when query fails', async () => {
|
|
116
|
+
const mockQueryBuilder = {
|
|
117
|
+
select: vi.fn().mockReturnThis(),
|
|
118
|
+
eq: vi.fn().mockReturnThis(),
|
|
119
|
+
limit: vi.fn().mockReturnThis(),
|
|
120
|
+
single: vi.fn().mockResolvedValue({
|
|
121
|
+
data: null,
|
|
122
|
+
error: { message: 'Query failed', code: 'PGRST116' }
|
|
123
|
+
})
|
|
124
|
+
};
|
|
125
|
+
(mockSupabase.from as any).mockReturnValue(mockQueryBuilder);
|
|
91
126
|
|
|
92
127
|
const { result } = renderHook(() =>
|
|
93
128
|
usePublicEventLogo('event-123', 'Big Event', VALID_ORG_ID, {
|
|
@@ -95,15 +130,24 @@ describe('usePublicEventLogo', () => {
|
|
|
95
130
|
})
|
|
96
131
|
);
|
|
97
132
|
|
|
98
|
-
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
133
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false), { timeout: 2000 });
|
|
99
134
|
expect(result.current.logoUrl).toBeNull();
|
|
100
|
-
expect(result.current.error).
|
|
135
|
+
expect(result.current.error).toBeNull(); // No error for PGRST116 (not found)
|
|
101
136
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
102
137
|
});
|
|
103
138
|
|
|
104
139
|
it('skips image validation when disabled', async () => {
|
|
105
140
|
const logoUrl = 'https://cdn.example.com/logo.png';
|
|
106
|
-
|
|
141
|
+
const mockQueryBuilder = {
|
|
142
|
+
select: vi.fn().mockReturnThis(),
|
|
143
|
+
eq: vi.fn().mockReturnThis(),
|
|
144
|
+
limit: vi.fn().mockReturnThis(),
|
|
145
|
+
single: vi.fn().mockResolvedValue({
|
|
146
|
+
data: { file_path: logoUrl },
|
|
147
|
+
error: null
|
|
148
|
+
})
|
|
149
|
+
};
|
|
150
|
+
(mockSupabase.from as any).mockReturnValue(mockQueryBuilder);
|
|
107
151
|
|
|
108
152
|
const { result } = renderHook(() =>
|
|
109
153
|
usePublicEventLogo('event-123', 'Big Event', VALID_ORG_ID, {
|
|
@@ -112,7 +156,7 @@ describe('usePublicEventLogo', () => {
|
|
|
112
156
|
})
|
|
113
157
|
);
|
|
114
158
|
|
|
115
|
-
await waitFor(() => expect(result.current.logoUrl).toBe(logoUrl));
|
|
159
|
+
await waitFor(() => expect(result.current.logoUrl).toBe(logoUrl), { timeout: 2000 });
|
|
116
160
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
117
161
|
});
|
|
118
162
|
|
|
@@ -130,7 +174,16 @@ describe('usePublicEventLogo', () => {
|
|
|
130
174
|
|
|
131
175
|
it('clears cached logo entries when clearPublicLogoCache is called', async () => {
|
|
132
176
|
const logoUrl = 'https://cdn.example.com/logo.png';
|
|
133
|
-
|
|
177
|
+
const mockQueryBuilder = {
|
|
178
|
+
select: vi.fn().mockReturnThis(),
|
|
179
|
+
eq: vi.fn().mockReturnThis(),
|
|
180
|
+
limit: vi.fn().mockReturnThis(),
|
|
181
|
+
single: vi.fn().mockResolvedValue({
|
|
182
|
+
data: { file_path: logoUrl },
|
|
183
|
+
error: null
|
|
184
|
+
})
|
|
185
|
+
};
|
|
186
|
+
(mockSupabase.from as any).mockReturnValue(mockQueryBuilder);
|
|
134
187
|
|
|
135
188
|
const { result } = renderHook(() =>
|
|
136
189
|
usePublicEventLogo('event-123', 'Big Event', VALID_ORG_ID, {
|
|
@@ -138,7 +191,7 @@ describe('usePublicEventLogo', () => {
|
|
|
138
191
|
})
|
|
139
192
|
);
|
|
140
193
|
|
|
141
|
-
await waitFor(() => expect(result.current.logoUrl).toBe(logoUrl));
|
|
194
|
+
await waitFor(() => expect(result.current.logoUrl).toBe(logoUrl), { timeout: 2000 });
|
|
142
195
|
expect(getPublicLogoCacheStats().size).toBe(1);
|
|
143
196
|
|
|
144
197
|
clearPublicLogoCache();
|
|
@@ -183,22 +183,32 @@ export function usePublicEventLogo(
|
|
|
183
183
|
setIsLoading(true);
|
|
184
184
|
setError(null);
|
|
185
185
|
|
|
186
|
-
//
|
|
187
|
-
const { data, error:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
186
|
+
// Query logo directly from core_file_references (removed legacy RPC get_public_event_logo)
|
|
187
|
+
const { data: logoData, error: queryError } = await (supabase as any)
|
|
188
|
+
.from('core_file_references')
|
|
189
|
+
.select('file_path')
|
|
190
|
+
.eq('table_name', 'core_events')
|
|
191
|
+
.eq('record_id', eventId)
|
|
192
|
+
.eq('is_public', true)
|
|
193
|
+
.eq('file_metadata->>category', 'event_logos')
|
|
194
|
+
.limit(1)
|
|
195
|
+
.single();
|
|
191
196
|
|
|
192
|
-
if (
|
|
193
|
-
|
|
197
|
+
if (queryError) {
|
|
198
|
+
// If no logo found, that's okay - return null
|
|
199
|
+
if (queryError.code === 'PGRST116') {
|
|
200
|
+
setLogoUrl(null);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
throw new Error(queryError.message || 'Failed to fetch logo');
|
|
194
204
|
}
|
|
195
205
|
|
|
196
|
-
if (!
|
|
206
|
+
if (!logoData || !logoData.file_path) {
|
|
197
207
|
setLogoUrl(null);
|
|
198
208
|
return;
|
|
199
209
|
}
|
|
200
210
|
|
|
201
|
-
const logoUrl =
|
|
211
|
+
const logoUrl = logoData.file_path;
|
|
202
212
|
|
|
203
213
|
// Validate image existence if requested
|
|
204
214
|
if (validateImage) {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { useMemo, useContext } from 'react';
|
|
25
|
-
import { useUnifiedAuth } from '../providers/services/UnifiedAuthProvider';
|
|
25
|
+
import { useUnifiedAuth, UnifiedAuthContext } from '../providers/services/UnifiedAuthProvider';
|
|
26
26
|
import { useIsPublicPage, PublicPageContext } from '../components/PublicLayout/PublicPageProvider';
|
|
27
27
|
|
|
28
28
|
export interface UseAppConfigReturn {
|
|
@@ -36,6 +36,8 @@ export interface UseAppConfigReturn {
|
|
|
36
36
|
* @returns App configuration and loading state
|
|
37
37
|
*/
|
|
38
38
|
export function useAppConfig(): UseAppConfigReturn {
|
|
39
|
+
// Call all hooks unconditionally at the top level
|
|
40
|
+
// Hooks must be called in the same order on every render
|
|
39
41
|
// Check if we're in a public page context first
|
|
40
42
|
const isPublicPage = useIsPublicPage();
|
|
41
43
|
|
|
@@ -43,10 +45,20 @@ export function useAppConfig(): UseAppConfigReturn {
|
|
|
43
45
|
const publicPageContext = useContext(PublicPageContext);
|
|
44
46
|
const hasPublicContext = publicPageContext !== undefined;
|
|
45
47
|
const contextAppName = publicPageContext?.appName || null;
|
|
48
|
+
|
|
49
|
+
// For authenticated pages, use UnifiedAuthProvider
|
|
50
|
+
// Use useContext directly to handle missing provider gracefully
|
|
51
|
+
// This allows the hook to work in test environments without providers
|
|
52
|
+
const authContext = useContext(UnifiedAuthContext);
|
|
53
|
+
const authAppName = authContext?.appName;
|
|
54
|
+
|
|
46
55
|
const getNodeEnvVar = (key: string): string | undefined =>
|
|
47
56
|
typeof process !== 'undefined' && process.env ? process.env[key] : undefined;
|
|
48
57
|
|
|
49
|
-
|
|
58
|
+
// Call all useMemo hooks unconditionally - use conditional logic only for computed values
|
|
59
|
+
const publicAppName = useMemo(() => {
|
|
60
|
+
if (!isPublicPage) return null;
|
|
61
|
+
|
|
50
62
|
const getAppName = (): string => {
|
|
51
63
|
// When tests mock public mode without the provider, default to PACE
|
|
52
64
|
if (!hasPublicContext) {
|
|
@@ -82,27 +94,17 @@ export function useAppConfig(): UseAppConfigReturn {
|
|
|
82
94
|
return 'PACE';
|
|
83
95
|
};
|
|
84
96
|
|
|
85
|
-
return
|
|
86
|
-
|
|
87
|
-
isLoading: false
|
|
88
|
-
}), [contextAppName, hasPublicContext]);
|
|
89
|
-
}
|
|
97
|
+
return getAppName();
|
|
98
|
+
}, [isPublicPage, contextAppName, hasPublicContext]);
|
|
90
99
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
appName: contextAppName ||
|
|
102
|
-
getNodeEnvVar('VITE_APP_NAME') ||
|
|
103
|
-
getNodeEnvVar('NEXT_PUBLIC_APP_NAME') ||
|
|
104
|
-
'PACE',
|
|
105
|
-
isLoading: false
|
|
106
|
-
}), [contextAppName]);
|
|
107
|
-
}
|
|
100
|
+
const authenticatedAppName = useMemo(() => {
|
|
101
|
+
if (isPublicPage) return null;
|
|
102
|
+
return authAppName || 'PACE';
|
|
103
|
+
}, [isPublicPage, authAppName]);
|
|
104
|
+
|
|
105
|
+
// Return the appropriate value based on context
|
|
106
|
+
return useMemo(() => ({
|
|
107
|
+
appName: publicAppName || authenticatedAppName || 'PACE',
|
|
108
|
+
isLoading: false
|
|
109
|
+
}), [publicAppName, authenticatedAppName]);
|
|
108
110
|
}
|