@jmruthers/pace-core 0.5.53 → 0.5.55
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/README.md +0 -4
- package/dist/{DataTable-7FMFXA7A.js → DataTable-4T627QFJ.js} +11 -11
- package/dist/{PublicLoadingSpinner-Bq_-BeK-.d.ts → PublicLoadingSpinner-SL8WaQN7.d.ts} +2 -21
- package/dist/{api-H5A3H4IR.js → api-LUNF5O6M.js} +3 -3
- package/dist/{appConfig-BVGyuvI7.d.ts → appConfig-DjpeG6P-.d.ts} +9 -1
- package/dist/{appNameResolver-7GHF5ED2.js → appNameResolver-UURKN7NF.js} +2 -2
- package/dist/{audit-BUW3LMJB.js → audit-6TOCAMKO.js} +2 -2
- package/dist/{chunk-MZBUOP4P.js → chunk-5BSLGBYI.js} +4 -3
- package/dist/chunk-5BSLGBYI.js.map +1 -0
- package/dist/{chunk-I5Z3QH5X.js → chunk-66C4BSAY.js} +2 -2
- package/dist/{chunk-I5Z3QH5X.js.map → chunk-66C4BSAY.js.map} +1 -1
- package/dist/{chunk-MYP2EGHX.js → chunk-AJ2KMES7.js} +21 -14
- package/dist/chunk-AJ2KMES7.js.map +1 -0
- package/dist/{chunk-EL2O4IUX.js → chunk-AQFRLC7K.js} +16 -24
- package/dist/{chunk-EL2O4IUX.js.map → chunk-AQFRLC7K.js.map} +1 -1
- package/dist/{chunk-7BNPOCLL.js → chunk-B2WTCLCV.js} +6 -2
- package/dist/chunk-B2WTCLCV.js.map +1 -0
- package/dist/{chunk-WJARTBCT.js → chunk-D7ARGIA3.js} +16 -7
- package/dist/chunk-D7ARGIA3.js.map +1 -0
- package/dist/{chunk-NRK4AIHQ.js → chunk-KBRACSJI.js} +3 -3
- package/dist/{chunk-NYUJ4FJR.js → chunk-KJDPSM64.js} +7 -7
- package/dist/chunk-KJDPSM64.js.map +1 -0
- package/dist/{chunk-GWSBHC4J.js → chunk-KLPVOPRI.js} +261 -38
- package/dist/chunk-KLPVOPRI.js.map +1 -0
- package/dist/{chunk-TRIZ7IB7.js → chunk-MPQDF75X.js} +148 -288
- package/dist/chunk-MPQDF75X.js.map +1 -0
- package/dist/{chunk-MSFACPQQ.js → chunk-PAEM3OWN.js} +11 -11
- package/dist/{chunk-MSFACPQQ.js.map → chunk-PAEM3OWN.js.map} +1 -1
- package/dist/{chunk-GIO7BFE7.js → chunk-RQD3D2CO.js} +66 -169
- package/dist/{chunk-GIO7BFE7.js.map → chunk-RQD3D2CO.js.map} +1 -1
- package/dist/{chunk-YDJW5XTN.js → chunk-STT7INZR.js} +25 -1
- package/dist/chunk-STT7INZR.js.map +1 -0
- package/dist/{chunk-6MTY77WU.js → chunk-TNMXZLDR.js} +3 -3
- package/dist/{chunk-BC3S53OZ.js → chunk-UQE2Y64H.js} +30 -14
- package/dist/chunk-UQE2Y64H.js.map +1 -0
- package/dist/{chunk-22KLBHPS.js → chunk-W66AZIOH.js} +2 -2
- package/dist/chunk-W66AZIOH.js.map +1 -0
- package/dist/{chunk-SS3E6QLB.js → chunk-YNUBMSMV.js} +2 -2
- package/dist/chunk-YNUBMSMV.js.map +1 -0
- package/dist/{chunk-NZ655MWE.js → chunk-ZOD2ZY6X.js} +5 -4
- package/dist/chunk-ZOD2ZY6X.js.map +1 -0
- package/dist/{chunk-74C6SNEC.js → chunk-ZPK5656W.js} +3 -3
- package/dist/{chunk-74C6SNEC.js.map → chunk-ZPK5656W.js.map} +1 -1
- package/dist/components.d.ts +22 -899
- package/dist/components.js +436 -3118
- package/dist/components.js.map +1 -1
- package/dist/file-reference-9xUOnwyt.d.ts +70 -0
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +10 -10
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +49 -9
- package/dist/index.js +190 -25
- package/dist/index.js.map +1 -1
- package/dist/{organisation-CO3Sh3_D.d.ts → organisation-t-vvQC3g.d.ts} +1 -8
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +5 -5
- package/dist/rbac/index.d.ts +65 -46
- package/dist/rbac/index.js +10 -12
- package/dist/styles/core.css +0 -125
- package/dist/types.d.ts +2 -1
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/dist/{usePublicRouteParams-B2OcAsur.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +1 -1
- package/dist/utils.d.ts +3 -4
- package/dist/utils.js +44 -13
- package/dist/utils.js.map +1 -1
- package/docs/FILE_REFERENCE_SYSTEM.md +440 -0
- package/docs/INDEX.md +7 -5
- package/docs/README.md +0 -1
- package/docs/api/README.md +0 -4
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +2 -2
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +12 -12
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +6 -6
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +281 -0
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +204 -200
- package/docs/api-reference/components.md +141 -163
- package/docs/api-reference/hooks.md +347 -0
- package/docs/core-concepts/rbac-system.md +69 -16
- package/docs/getting-started/examples/basic-auth-app.md +0 -1
- package/docs/implementation-guides/datatable-rbac-usage.md +12 -11
- package/docs/implementation-guides/file-upload-storage.md +733 -0
- package/docs/implementation-guides/inactivity-tracking.md +779 -0
- package/docs/implementation-guides/organisation-security.md +748 -0
- package/docs/implementation-guides/public-pages-advanced.md +1022 -0
- package/docs/migration/MIGRATION_GUIDE.md +684 -0
- package/docs/migration/README.md +13 -2
- package/docs/migration/rbac-migration.md +73 -0
- package/docs/rbac/examples/rbac-rls-integration-example.md +11 -13
- package/docs/style-guide.md +269 -1
- package/package.json +1 -1
- package/src/__tests__/TESTING_GUIDELINES.md +331 -18
- package/src/__tests__/helpers/supabaseMock.ts +99 -0
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -7
- package/src/__tests__/shared.ts +6 -0
- package/src/components/DataTable/components/ActionButtons.tsx +2 -2
- package/src/components/DataTable/components/DataTableCore.tsx +2 -2
- package/src/components/DataTable/components/UnifiedTableBody.tsx +1 -1
- package/src/components/DataTable/utils/debugTools.ts +2 -2
- package/src/components/Dialog/Dialog.test.tsx +12 -2
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +6 -6
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +2 -2
- package/src/components/FileDisplay.tsx +233 -0
- package/src/components/FileUpload.tsx +176 -0
- package/src/components/Footer/Footer.test.tsx +7 -7
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +13 -6
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +30 -3
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +558 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
- package/src/components/PublicLayout/PublicPageDebugger.tsx +2 -2
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +2 -2
- package/src/components/PublicLayout/PublicPageProvider.tsx +2 -2
- package/src/components/Select/Select.test.tsx +50 -15
- package/src/components/SuperAdminGuard.tsx +2 -2
- package/src/components/__tests__/SuperAdminGuard.test.tsx +559 -0
- package/src/components/index.ts +0 -183
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +1 -1
- package/src/hooks/__tests__/useRBAC.unit.test.ts +191 -138
- package/src/hooks/public/usePublicEvent.ts +2 -2
- package/src/hooks/useAppConfig.ts +3 -3
- package/src/hooks/useComponentPerformance.ts +1 -1
- package/src/hooks/useDataTablePerformance.ts +1 -1
- package/src/hooks/useFileReference.ts +232 -0
- package/src/hooks/useOrganisationPermissions.test.ts +254 -344
- package/src/hooks/useOrganisationPermissions.ts +15 -7
- package/src/hooks/useOrganisationSecurity.test.ts +390 -402
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePermissionCache.test.ts +264 -395
- package/src/hooks/usePermissionCache.ts +34 -4
- package/src/hooks/useSecureDataAccess.test.ts +486 -0
- package/src/hooks/useSecureDataAccess.ts +4 -1
- package/src/providers/InactivityProvider.tsx +2 -2
- package/src/providers/OrganisationProvider.test.simple.tsx +168 -0
- package/src/providers/OrganisationProvider.test.tsx +168 -0
- package/src/providers/OrganisationProvider.tsx +18 -31
- package/src/providers/UnifiedAuthProvider.test.simple.tsx +205 -0
- package/src/providers/UnifiedAuthProvider.test.tsx +128 -0
- package/src/providers/__tests__/InactivityProvider.test.tsx +3 -4
- package/src/providers/__tests__/OrganisationProvider.test.tsx +19 -14
- package/src/rbac/__tests__/integration.authflow.test.tsx +123 -0
- package/src/rbac/__tests__/integration.navigation.test.tsx +72 -0
- package/src/rbac/__tests__/integration.securedata.test.tsx +92 -0
- package/src/rbac/__tests__/integration.smoke.test.tsx +73 -0
- package/src/rbac/__tests__/rbac-core.test.tsx +26 -22
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +411 -0
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +285 -0
- package/src/rbac/__tests__/rbac-functions.test.ts +655 -0
- package/src/rbac/__tests__/rbac-integration.test.ts +532 -0
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +196 -0
- package/src/rbac/api.test.ts +6 -6
- package/src/rbac/api.ts +2 -2
- package/src/rbac/audit.test.ts +485 -0
- package/src/rbac/audit.ts +7 -1
- package/src/rbac/cache-invalidation.ts +318 -0
- package/src/rbac/cache.test.ts +286 -0
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +559 -0
- package/src/rbac/components/EnhancedNavigationMenu.tsx +29 -23
- package/src/rbac/components/NavigationProvider.test.tsx +449 -0
- package/src/rbac/components/PagePermissionGuard.tsx +4 -4
- package/src/rbac/components/PagePermissionProvider.test.tsx +479 -0
- package/src/rbac/components/SecureDataProvider.test.tsx +511 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +159 -430
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +4 -5
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +112 -118
- package/src/rbac/config.test.ts +410 -0
- package/src/rbac/engine.test.simple.ts +237 -0
- package/src/rbac/engine.test.ts +233 -0
- package/src/rbac/engine.ts +37 -41
- package/src/rbac/examples/CompleteRBACExample.tsx +3 -3
- package/src/rbac/examples/EventBasedApp.tsx +4 -4
- package/src/rbac/hooks/useRBAC.simple.test.ts +16 -0
- package/src/rbac/hooks/useRBAC.test.ts +207 -455
- package/src/rbac/hooks/useRBAC.ts +30 -22
- package/src/rbac/permissions.test.ts +128 -0
- package/src/rbac/permissions.ts +56 -141
- package/src/rbac/providers/RBACProvider.tsx +1 -1
- package/src/rbac/secureClient.test.ts +444 -0
- package/src/rbac/security.test.ts +390 -0
- package/src/rbac/security.ts +1 -1
- package/src/rbac/types.test.ts +382 -0
- package/src/rbac/types.ts +2 -2
- package/src/styles/base.css +208 -0
- package/src/styles/core.css +0 -125
- package/src/styles/semantic.css +24 -0
- package/src/types/file-reference.ts +77 -0
- package/src/types/rbac-functions.ts +290 -0
- package/src/types/supabase.ts +10 -28
- package/src/types/unified.ts +4 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +81 -55
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +21 -12
- package/src/utils/__tests__/organisationContext.unit.test.ts +13 -7
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +3 -3
- package/src/utils/__tests__/sessionTracking.unit.test.ts +32 -12
- package/src/utils/appConfig.ts +1 -1
- package/src/utils/appIdResolver.test.ts +503 -0
- package/src/utils/appIdResolver.ts +1 -1
- package/src/utils/appNameResolver.test.ts +494 -0
- package/src/utils/appNameResolver.ts +3 -2
- package/src/utils/bundleAnalysis.ts +3 -3
- package/src/utils/debugLogger.ts +1 -1
- package/src/utils/file-reference.ts +263 -0
- package/src/utils/formatDate.test.ts +2 -2
- package/src/utils/organisationContext.test.ts +340 -0
- package/src/utils/organisationContext.ts +19 -6
- package/src/utils/performanceBudgets.ts +2 -2
- package/src/utils/permissionUtils.test.ts +393 -0
- package/src/utils/permissionUtils.ts +5 -2
- package/src/utils/secureDataAccess.test.ts +715 -0
- package/src/utils/secureDataAccess.ts +21 -5
- package/src/utils/sessionTracking.ts +34 -4
- package/src/utils/storage/__tests__/helpers.unit.test.ts +328 -0
- package/src/utils/storage/__tests__/index.unit.test.ts +16 -0
- package/src/utils/storage/helpers.ts +20 -25
- package/src/utils/storage/index.ts +29 -1
- package/src/vite-env.d.ts +17 -0
- package/dist/chunk-22KLBHPS.js.map +0 -1
- package/dist/chunk-7BNPOCLL.js.map +0 -1
- package/dist/chunk-BC3S53OZ.js.map +0 -1
- package/dist/chunk-GWSBHC4J.js.map +0 -1
- package/dist/chunk-MYP2EGHX.js.map +0 -1
- package/dist/chunk-MZBUOP4P.js.map +0 -1
- package/dist/chunk-NYUJ4FJR.js.map +0 -1
- package/dist/chunk-NZ655MWE.js.map +0 -1
- package/dist/chunk-SS3E6QLB.js.map +0 -1
- package/dist/chunk-TRIZ7IB7.js.map +0 -1
- package/dist/chunk-WJARTBCT.js.map +0 -1
- package/dist/chunk-YDJW5XTN.js.map +0 -1
- package/docs/print-components/README.md +0 -258
- package/docs/print-components/api-reference.md +0 -636
- package/docs/print-components/examples/README.md +0 -204
- package/docs/print-components/examples/basic-report.tsx +0 -92
- package/docs/print-components/examples/card-catalog.tsx +0 -149
- package/docs/print-components/examples/cover-page-report.tsx +0 -163
- package/docs/print-components/quick-start.md +0 -363
- package/src/components/PrintButton/PrintButton.tsx +0 -321
- package/src/components/PrintButton/PrintButtonGroup.tsx +0 -84
- package/src/components/PrintButton/PrintToolbar.tsx +0 -94
- package/src/components/PrintButton/__tests__/PrintButton.test.tsx +0 -271
- package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +0 -438
- package/src/components/PrintButton/index.ts +0 -33
- package/src/components/PrintButton/types.ts +0 -173
- package/src/components/PrintCard/PrintCard.tsx +0 -154
- package/src/components/PrintCard/PrintCardContent.tsx +0 -57
- package/src/components/PrintCard/PrintCardFooter.tsx +0 -60
- package/src/components/PrintCard/PrintCardGrid.tsx +0 -91
- package/src/components/PrintCard/PrintCardHeader.tsx +0 -78
- package/src/components/PrintCard/PrintCardImage.tsx +0 -81
- package/src/components/PrintCard/examples/PrintCardShowcase.tsx +0 -239
- package/src/components/PrintCard/index.ts +0 -34
- package/src/components/PrintCard/types.ts +0 -171
- package/src/components/PrintDataTable/PrintDataTable.tsx +0 -215
- package/src/components/PrintDataTable/PrintTableGroup.tsx +0 -90
- package/src/components/PrintDataTable/PrintTableRow.tsx +0 -76
- package/src/components/PrintDataTable/index.ts +0 -25
- package/src/components/PrintDataTable/types.ts +0 -67
- package/src/components/PrintFooter/PrintFooter.tsx +0 -183
- package/src/components/PrintFooter/PrintFooterContent.tsx +0 -71
- package/src/components/PrintFooter/PrintFooterInfo.tsx +0 -86
- package/src/components/PrintFooter/PrintPageNumber.tsx +0 -90
- package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +0 -390
- package/src/components/PrintFooter/index.ts +0 -30
- package/src/components/PrintFooter/types.ts +0 -149
- package/src/components/PrintGrid/PrintGrid.tsx +0 -180
- package/src/components/PrintGrid/PrintGridBreakpoint.tsx +0 -109
- package/src/components/PrintGrid/PrintGridContainer.tsx +0 -128
- package/src/components/PrintGrid/PrintGridItem.tsx +0 -220
- package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +0 -359
- package/src/components/PrintGrid/index.ts +0 -31
- package/src/components/PrintGrid/types.ts +0 -159
- package/src/components/PrintHeader/PrintCoverHeader.tsx +0 -230
- package/src/components/PrintHeader/PrintHeader.tsx +0 -150
- package/src/components/PrintHeader/index.ts +0 -17
- package/src/components/PrintHeader/types.ts +0 -42
- package/src/components/PrintLayout/PrintLayout.tsx +0 -122
- package/src/components/PrintLayout/PrintLayoutContext.tsx +0 -66
- package/src/components/PrintLayout/PrintPageBreak.tsx +0 -52
- package/src/components/PrintLayout/examples/PrintShowcase.tsx +0 -230
- package/src/components/PrintLayout/index.ts +0 -19
- package/src/components/PrintLayout/types.ts +0 -37
- package/src/components/PrintPageBreak/PrintPageBreak.tsx +0 -120
- package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +0 -90
- package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +0 -112
- package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +0 -279
- package/src/components/PrintPageBreak/index.ts +0 -23
- package/src/components/PrintPageBreak/types.ts +0 -94
- package/src/components/PrintSection/PrintColumn.tsx +0 -104
- package/src/components/PrintSection/PrintDivider.tsx +0 -101
- package/src/components/PrintSection/PrintSection.tsx +0 -129
- package/src/components/PrintSection/PrintSectionContent.tsx +0 -75
- package/src/components/PrintSection/PrintSectionHeader.tsx +0 -97
- package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +0 -258
- package/src/components/PrintSection/index.ts +0 -33
- package/src/components/PrintSection/types.ts +0 -155
- package/src/components/PrintText/PrintText.tsx +0 -116
- package/src/components/PrintText/index.ts +0 -16
- package/src/components/PrintText/types.ts +0 -24
- package/src/rbac/__tests__/integration.test.tsx +0 -218
- package/src/utils/print/PrintDataProcessor.ts +0 -390
- package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +0 -397
- package/src/utils/print/index.ts +0 -29
- package/src/utils/print/types.ts +0 -196
- package/src/utils/print/usePrintOptimization.ts +0 -272
- /package/dist/{DataTable-7FMFXA7A.js.map → DataTable-4T627QFJ.js.map} +0 -0
- /package/dist/{api-H5A3H4IR.js.map → api-LUNF5O6M.js.map} +0 -0
- /package/dist/{appNameResolver-7GHF5ED2.js.map → appNameResolver-UURKN7NF.js.map} +0 -0
- /package/dist/{audit-BUW3LMJB.js.map → audit-6TOCAMKO.js.map} +0 -0
- /package/dist/{chunk-NRK4AIHQ.js.map → chunk-KBRACSJI.js.map} +0 -0
- /package/dist/{chunk-6MTY77WU.js.map → chunk-TNMXZLDR.js.map} +0 -0
|
@@ -0,0 +1,1022 @@
|
|
|
1
|
+
# Public Pages System (Advanced)
|
|
2
|
+
|
|
3
|
+
> **🌐 Public Event Pages** | [← Back to Documentation](../README.md) | [↑ Table of Contents](#table-of-contents)
|
|
4
|
+
|
|
5
|
+
Complete guide to implementing public pages without authentication, including event logos, caching strategies, and SEO optimization.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
PACE Core provides a comprehensive public pages system for:
|
|
10
|
+
- **Public Event Pages** - Event information without authentication
|
|
11
|
+
- **Event Logos** - Dynamic logo loading and caching
|
|
12
|
+
- **Public Data Access** - Secure public data retrieval
|
|
13
|
+
- **SEO Optimization** - Search engine friendly pages
|
|
14
|
+
- **Performance Caching** - Optimized data loading
|
|
15
|
+
- **Error Handling** - Graceful degradation for public users
|
|
16
|
+
|
|
17
|
+
## Architecture
|
|
18
|
+
|
|
19
|
+
```mermaid
|
|
20
|
+
graph TD
|
|
21
|
+
A[Public Page Request] --> B[PublicPageLayout]
|
|
22
|
+
B --> C[PublicPageHeader]
|
|
23
|
+
C --> D[EventLogo Component]
|
|
24
|
+
D --> E[usePublicEvent Hook]
|
|
25
|
+
E --> F[Public Data Cache]
|
|
26
|
+
F --> G[Supabase Query]
|
|
27
|
+
G --> H[RLS Policies]
|
|
28
|
+
H --> I[Public Data]
|
|
29
|
+
E --> J[usePublicEventLogo Hook]
|
|
30
|
+
J --> K[Logo Cache]
|
|
31
|
+
K --> L[Storage Bucket]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### 1. Basic Public Event Page
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import {
|
|
40
|
+
PublicPageLayout,
|
|
41
|
+
PublicPageHeader,
|
|
42
|
+
EventLogo,
|
|
43
|
+
usePublicEvent
|
|
44
|
+
} from '@jmruthers/pace-core';
|
|
45
|
+
|
|
46
|
+
function PublicEventPage({ eventId }: { eventId: string }) {
|
|
47
|
+
const {
|
|
48
|
+
event,
|
|
49
|
+
loading,
|
|
50
|
+
error,
|
|
51
|
+
refetch
|
|
52
|
+
} = usePublicEvent(eventId);
|
|
53
|
+
|
|
54
|
+
if (loading) return <div>Loading event...</div>;
|
|
55
|
+
if (error) return <div>Error loading event: {error.message}</div>;
|
|
56
|
+
if (!event) return <div>Event not found</div>;
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<PublicPageLayout>
|
|
60
|
+
<PublicPageHeader
|
|
61
|
+
title={event.name}
|
|
62
|
+
description={event.description}
|
|
63
|
+
logo={<EventLogo eventId={eventId} size="large" />}
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
<main className="container mx-auto px-4 py-8">
|
|
67
|
+
<h1>{event.name}</h1>
|
|
68
|
+
<p>{event.description}</p>
|
|
69
|
+
<p>Date: {new Date(event.start_date).toLocaleDateString()}</p>
|
|
70
|
+
<p>Location: {event.location}</p>
|
|
71
|
+
</main>
|
|
72
|
+
</PublicPageLayout>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 2. Public Event with Logo
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import {
|
|
81
|
+
PublicPageLayout,
|
|
82
|
+
EventLogo,
|
|
83
|
+
usePublicEvent,
|
|
84
|
+
usePublicEventLogo
|
|
85
|
+
} from '@jmruthers/pace-core';
|
|
86
|
+
|
|
87
|
+
function EventWithLogo({ eventId }: { eventId: string }) {
|
|
88
|
+
const { event, loading: eventLoading } = usePublicEvent(eventId);
|
|
89
|
+
const {
|
|
90
|
+
logoUrl,
|
|
91
|
+
loading: logoLoading,
|
|
92
|
+
error: logoError
|
|
93
|
+
} = usePublicEventLogo(eventId, 'large');
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<PublicPageLayout>
|
|
97
|
+
<header className="bg-main-50 py-8">
|
|
98
|
+
<div className="container mx-auto px-4 text-center">
|
|
99
|
+
{logoLoading ? (
|
|
100
|
+
<div className="animate-pulse bg-sec-200 h-32 w-32 mx-auto rounded"></div>
|
|
101
|
+
) : logoError ? (
|
|
102
|
+
<div className="text-sec-500">Logo unavailable</div>
|
|
103
|
+
) : (
|
|
104
|
+
<EventLogo
|
|
105
|
+
eventId={eventId}
|
|
106
|
+
size="large"
|
|
107
|
+
className="mx-auto"
|
|
108
|
+
/>
|
|
109
|
+
)}
|
|
110
|
+
|
|
111
|
+
{event && (
|
|
112
|
+
<div className="mt-4">
|
|
113
|
+
<h1 className="text-3xl font-bold text-main-900">
|
|
114
|
+
{event.name}
|
|
115
|
+
</h1>
|
|
116
|
+
<p className="text-main-700 mt-2">
|
|
117
|
+
{event.description}
|
|
118
|
+
</p>
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
</header>
|
|
123
|
+
</PublicPageLayout>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Components
|
|
129
|
+
|
|
130
|
+
### PublicPageLayout
|
|
131
|
+
|
|
132
|
+
A layout component specifically designed for public pages without authentication.
|
|
133
|
+
|
|
134
|
+
#### Props
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
interface PublicPageLayoutProps {
|
|
138
|
+
children: React.ReactNode;
|
|
139
|
+
className?: string;
|
|
140
|
+
showHeader?: boolean;
|
|
141
|
+
showFooter?: boolean;
|
|
142
|
+
errorBoundary?: boolean;
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### Usage Examples
|
|
147
|
+
|
|
148
|
+
**Basic Layout:**
|
|
149
|
+
```tsx
|
|
150
|
+
<PublicPageLayout>
|
|
151
|
+
<h1>Public Event Page</h1>
|
|
152
|
+
<p>This page is accessible without authentication</p>
|
|
153
|
+
</PublicPageLayout>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Custom Styling:**
|
|
157
|
+
```tsx
|
|
158
|
+
<PublicPageLayout
|
|
159
|
+
className="min-h-screen bg-gradient-to-br from-main-50 to-sec-50"
|
|
160
|
+
showHeader={true}
|
|
161
|
+
showFooter={true}
|
|
162
|
+
>
|
|
163
|
+
<div className="container mx-auto px-4 py-8">
|
|
164
|
+
<h1>Custom Public Page</h1>
|
|
165
|
+
</div>
|
|
166
|
+
</PublicPageLayout>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**With Error Boundary:**
|
|
170
|
+
```tsx
|
|
171
|
+
<PublicPageLayout errorBoundary={true}>
|
|
172
|
+
<PublicEventContent eventId={eventId} />
|
|
173
|
+
</PublicPageLayout>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### PublicPageHeader
|
|
177
|
+
|
|
178
|
+
A header component for public pages with event branding and navigation.
|
|
179
|
+
|
|
180
|
+
#### Props
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
interface PublicPageHeaderProps {
|
|
184
|
+
title: string;
|
|
185
|
+
description?: string;
|
|
186
|
+
logo?: React.ReactNode;
|
|
187
|
+
navigation?: NavigationItem[];
|
|
188
|
+
className?: string;
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### Usage Examples
|
|
193
|
+
|
|
194
|
+
**Basic Header:**
|
|
195
|
+
```tsx
|
|
196
|
+
<PublicPageHeader
|
|
197
|
+
title="Event Name"
|
|
198
|
+
description="Event description"
|
|
199
|
+
logo={<EventLogo eventId={eventId} />}
|
|
200
|
+
/>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**With Navigation:**
|
|
204
|
+
```tsx
|
|
205
|
+
<PublicPageHeader
|
|
206
|
+
title="Event Name"
|
|
207
|
+
description="Event description"
|
|
208
|
+
logo={<EventLogo eventId={eventId} />}
|
|
209
|
+
navigation={[
|
|
210
|
+
{ label: 'Home', href: '/' },
|
|
211
|
+
{ label: 'Schedule', href: '/schedule' },
|
|
212
|
+
{ label: 'Speakers', href: '/speakers' }
|
|
213
|
+
]}
|
|
214
|
+
/>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### EventLogo
|
|
218
|
+
|
|
219
|
+
A component for displaying event logos with automatic loading and caching.
|
|
220
|
+
|
|
221
|
+
#### Props
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
interface EventLogoProps {
|
|
225
|
+
eventId: string;
|
|
226
|
+
size?: 'small' | 'medium' | 'large' | 'xlarge';
|
|
227
|
+
variant?: 'default' | 'square' | 'wide';
|
|
228
|
+
className?: string;
|
|
229
|
+
fallback?: React.ReactNode;
|
|
230
|
+
showLoading?: boolean;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
#### Usage Examples
|
|
235
|
+
|
|
236
|
+
**Basic Logo:**
|
|
237
|
+
```tsx
|
|
238
|
+
<EventLogo eventId={eventId} size="medium" />
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Custom Styling:**
|
|
242
|
+
```tsx
|
|
243
|
+
<EventLogo
|
|
244
|
+
eventId={eventId}
|
|
245
|
+
size="large"
|
|
246
|
+
variant="square"
|
|
247
|
+
className="rounded-lg shadow-lg"
|
|
248
|
+
/>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**With Fallback:**
|
|
252
|
+
```tsx
|
|
253
|
+
<EventLogo
|
|
254
|
+
eventId={eventId}
|
|
255
|
+
size="medium"
|
|
256
|
+
fallback={
|
|
257
|
+
<div className="bg-sec-200 text-sec-500 p-4 rounded">
|
|
258
|
+
No Logo Available
|
|
259
|
+
</div>
|
|
260
|
+
}
|
|
261
|
+
/>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Hooks
|
|
265
|
+
|
|
266
|
+
### usePublicEvent
|
|
267
|
+
|
|
268
|
+
Hook for fetching public event data without authentication.
|
|
269
|
+
|
|
270
|
+
#### Interface
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
interface UsePublicEventReturn {
|
|
274
|
+
event: PublicEvent | null;
|
|
275
|
+
loading: boolean;
|
|
276
|
+
error: Error | null;
|
|
277
|
+
refetch: () => Promise<void>;
|
|
278
|
+
clearCache: () => void;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
interface PublicEvent {
|
|
282
|
+
id: string;
|
|
283
|
+
name: string;
|
|
284
|
+
description: string;
|
|
285
|
+
start_date: string;
|
|
286
|
+
end_date: string;
|
|
287
|
+
location: string;
|
|
288
|
+
status: 'draft' | 'published' | 'cancelled';
|
|
289
|
+
public_url?: string;
|
|
290
|
+
logo_url?: string;
|
|
291
|
+
metadata?: Record<string, any>;
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
#### Usage Examples
|
|
296
|
+
|
|
297
|
+
**Basic Event Loading:**
|
|
298
|
+
```tsx
|
|
299
|
+
import { usePublicEvent } from '@jmruthers/pace-core';
|
|
300
|
+
|
|
301
|
+
function EventPage({ eventId }: { eventId: string }) {
|
|
302
|
+
const { event, loading, error, refetch } = usePublicEvent(eventId);
|
|
303
|
+
|
|
304
|
+
if (loading) return <div>Loading event...</div>;
|
|
305
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
306
|
+
if (!event) return <div>Event not found</div>;
|
|
307
|
+
|
|
308
|
+
return (
|
|
309
|
+
<div>
|
|
310
|
+
<h1>{event.name}</h1>
|
|
311
|
+
<p>{event.description}</p>
|
|
312
|
+
<p>Date: {new Date(event.start_date).toLocaleDateString()}</p>
|
|
313
|
+
<p>Location: {event.location}</p>
|
|
314
|
+
|
|
315
|
+
<Button onClick={refetch}>Refresh Event</Button>
|
|
316
|
+
</div>
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**With Error Handling:**
|
|
322
|
+
```tsx
|
|
323
|
+
import { usePublicEvent } from '@jmruthers/pace-core';
|
|
324
|
+
|
|
325
|
+
function EventWithErrorHandling({ eventId }: { eventId: string }) {
|
|
326
|
+
const { event, loading, error, refetch } = usePublicEvent(eventId);
|
|
327
|
+
|
|
328
|
+
const handleRetry = async () => {
|
|
329
|
+
try {
|
|
330
|
+
await refetch();
|
|
331
|
+
} catch (err) {
|
|
332
|
+
console.error('Failed to refetch event:', err);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
if (loading) {
|
|
337
|
+
return (
|
|
338
|
+
<div className="flex items-center justify-center min-h-64">
|
|
339
|
+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-main-600"></div>
|
|
340
|
+
</div>
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (error) {
|
|
345
|
+
return (
|
|
346
|
+
<div className="text-center py-8">
|
|
347
|
+
<h2 className="text-xl font-semibold text-sec-800 mb-2">
|
|
348
|
+
Unable to load event
|
|
349
|
+
</h2>
|
|
350
|
+
<p className="text-sec-600 mb-4">{error.message}</p>
|
|
351
|
+
<Button onClick={handleRetry}>Try Again</Button>
|
|
352
|
+
</div>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (!event) {
|
|
357
|
+
return (
|
|
358
|
+
<div className="text-center py-8">
|
|
359
|
+
<h2 className="text-xl font-semibold text-sec-800">
|
|
360
|
+
Event not found
|
|
361
|
+
</h2>
|
|
362
|
+
<p className="text-sec-600">
|
|
363
|
+
The event you're looking for doesn't exist or isn't public.
|
|
364
|
+
</p>
|
|
365
|
+
</div>
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return (
|
|
370
|
+
<div>
|
|
371
|
+
<h1>{event.name}</h1>
|
|
372
|
+
<p>{event.description}</p>
|
|
373
|
+
{/* Event content */}
|
|
374
|
+
</div>
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### usePublicEventLogo
|
|
380
|
+
|
|
381
|
+
Hook for loading and caching event logos.
|
|
382
|
+
|
|
383
|
+
#### Interface
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
interface UsePublicEventLogoReturn {
|
|
387
|
+
logoUrl: string | null;
|
|
388
|
+
loading: boolean;
|
|
389
|
+
error: Error | null;
|
|
390
|
+
refetch: () => Promise<void>;
|
|
391
|
+
clearCache: () => void;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
interface LogoSize {
|
|
395
|
+
small: 'w-8 h-8';
|
|
396
|
+
medium: 'w-16 h-16';
|
|
397
|
+
large: 'w-32 h-32';
|
|
398
|
+
xlarge: 'w-48 h-48';
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### Usage Examples
|
|
403
|
+
|
|
404
|
+
**Basic Logo Loading:**
|
|
405
|
+
```tsx
|
|
406
|
+
import { usePublicEventLogo } from '@jmruthers/pace-core';
|
|
407
|
+
|
|
408
|
+
function EventLogoDisplay({ eventId }: { eventId: string }) {
|
|
409
|
+
const { logoUrl, loading, error } = usePublicEventLogo(eventId, 'large');
|
|
410
|
+
|
|
411
|
+
if (loading) {
|
|
412
|
+
return <div className="animate-pulse bg-sec-200 w-32 h-32 rounded"></div>;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (error) {
|
|
416
|
+
return <div className="text-sec-500">Logo unavailable</div>;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (!logoUrl) {
|
|
420
|
+
return <div className="bg-sec-200 text-sec-500 p-4 rounded">No logo</div>;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return (
|
|
424
|
+
<img
|
|
425
|
+
src={logoUrl}
|
|
426
|
+
alt="Event logo"
|
|
427
|
+
className="w-32 h-32 object-contain"
|
|
428
|
+
/>
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**With Caching Control:**
|
|
434
|
+
```tsx
|
|
435
|
+
import { usePublicEventLogo } from '@jmruthers/pace-core';
|
|
436
|
+
|
|
437
|
+
function LogoWithCacheControl({ eventId }: { eventId: string }) {
|
|
438
|
+
const {
|
|
439
|
+
logoUrl,
|
|
440
|
+
loading,
|
|
441
|
+
error,
|
|
442
|
+
refetch,
|
|
443
|
+
clearCache
|
|
444
|
+
} = usePublicEventLogo(eventId, 'medium');
|
|
445
|
+
|
|
446
|
+
const handleRefresh = async () => {
|
|
447
|
+
await refetch();
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
const handleClearCache = () => {
|
|
451
|
+
clearCache();
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
return (
|
|
455
|
+
<div className="space-y-4">
|
|
456
|
+
{loading && <div>Loading logo...</div>}
|
|
457
|
+
{error && <div>Error: {error.message}</div>}
|
|
458
|
+
{logoUrl && (
|
|
459
|
+
<img
|
|
460
|
+
src={logoUrl}
|
|
461
|
+
alt="Event logo"
|
|
462
|
+
className="w-16 h-16 object-contain"
|
|
463
|
+
/>
|
|
464
|
+
)}
|
|
465
|
+
|
|
466
|
+
<div className="space-x-2">
|
|
467
|
+
<Button onClick={handleRefresh} size="sm">
|
|
468
|
+
Refresh Logo
|
|
469
|
+
</Button>
|
|
470
|
+
<Button onClick={handleClearCache} size="sm" variant="outline">
|
|
471
|
+
Clear Cache
|
|
472
|
+
</Button>
|
|
473
|
+
</div>
|
|
474
|
+
</div>
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
## Caching Strategies
|
|
480
|
+
|
|
481
|
+
### Client-Side Caching
|
|
482
|
+
|
|
483
|
+
```tsx
|
|
484
|
+
import { usePublicEvent, clearPublicEventCache } from '@jmruthers/pace-core';
|
|
485
|
+
|
|
486
|
+
function CachedEventPage({ eventId }: { eventId: string }) {
|
|
487
|
+
const { event, loading } = usePublicEvent(eventId);
|
|
488
|
+
|
|
489
|
+
// Cache management
|
|
490
|
+
const handleClearCache = () => {
|
|
491
|
+
clearPublicEventCache();
|
|
492
|
+
console.log('Event cache cleared');
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
const handlePreloadEvent = async (newEventId: string) => {
|
|
496
|
+
// Preload another event
|
|
497
|
+
const { event: preloadedEvent } = usePublicEvent(newEventId);
|
|
498
|
+
console.log('Preloaded event:', preloadedEvent);
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
return (
|
|
502
|
+
<div>
|
|
503
|
+
<h1>{event?.name}</h1>
|
|
504
|
+
|
|
505
|
+
<div className="space-x-2">
|
|
506
|
+
<Button onClick={handleClearCache}>
|
|
507
|
+
Clear Cache
|
|
508
|
+
</Button>
|
|
509
|
+
<Button onClick={() => handlePreloadEvent('other-event-id')}>
|
|
510
|
+
Preload Other Event
|
|
511
|
+
</Button>
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Cache Statistics
|
|
519
|
+
|
|
520
|
+
```tsx
|
|
521
|
+
import { getPublicEventCacheStats, getPublicLogoCacheStats } from '@jmruthers/pace-core';
|
|
522
|
+
|
|
523
|
+
function CacheStats() {
|
|
524
|
+
const eventStats = getPublicEventCacheStats();
|
|
525
|
+
const logoStats = getPublicLogoCacheStats();
|
|
526
|
+
|
|
527
|
+
return (
|
|
528
|
+
<div className="bg-sec-50 p-4 rounded">
|
|
529
|
+
<h3>Cache Statistics</h3>
|
|
530
|
+
<div className="grid grid-cols-2 gap-4">
|
|
531
|
+
<div>
|
|
532
|
+
<h4>Event Cache</h4>
|
|
533
|
+
<p>Entries: {eventStats.entryCount}</p>
|
|
534
|
+
<p>Hit Rate: {eventStats.hitRate}%</p>
|
|
535
|
+
<p>Memory Usage: {eventStats.memoryUsage} bytes</p>
|
|
536
|
+
</div>
|
|
537
|
+
<div>
|
|
538
|
+
<h4>Logo Cache</h4>
|
|
539
|
+
<p>Entries: {logoStats.entryCount}</p>
|
|
540
|
+
<p>Hit Rate: {logoStats.hitRate}%</p>
|
|
541
|
+
<p>Memory Usage: {logoStats.memoryUsage} bytes</p>
|
|
542
|
+
</div>
|
|
543
|
+
</div>
|
|
544
|
+
</div>
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
## SEO Optimization
|
|
550
|
+
|
|
551
|
+
### Meta Tags and Structured Data
|
|
552
|
+
|
|
553
|
+
```tsx
|
|
554
|
+
import { usePublicEvent } from '@jmruthers/pace-core';
|
|
555
|
+
|
|
556
|
+
function SEOEventPage({ eventId }: { eventId: string }) {
|
|
557
|
+
const { event } = usePublicEvent(eventId);
|
|
558
|
+
|
|
559
|
+
// Generate structured data for search engines
|
|
560
|
+
const structuredData = event ? {
|
|
561
|
+
"@context": "https://schema.org",
|
|
562
|
+
"@type": "Event",
|
|
563
|
+
"name": event.name,
|
|
564
|
+
"description": event.description,
|
|
565
|
+
"startDate": event.start_date,
|
|
566
|
+
"endDate": event.end_date,
|
|
567
|
+
"location": {
|
|
568
|
+
"@type": "Place",
|
|
569
|
+
"name": event.location
|
|
570
|
+
},
|
|
571
|
+
"url": event.public_url,
|
|
572
|
+
"image": event.logo_url
|
|
573
|
+
} : null;
|
|
574
|
+
|
|
575
|
+
return (
|
|
576
|
+
<>
|
|
577
|
+
{/* Meta tags */}
|
|
578
|
+
<head>
|
|
579
|
+
<title>{event?.name || 'Event'}</title>
|
|
580
|
+
<meta name="description" content={event?.description} />
|
|
581
|
+
<meta property="og:title" content={event?.name} />
|
|
582
|
+
<meta property="og:description" content={event?.description} />
|
|
583
|
+
<meta property="og:image" content={event?.logo_url} />
|
|
584
|
+
<meta property="og:type" content="event" />
|
|
585
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
586
|
+
|
|
587
|
+
{/* Structured data */}
|
|
588
|
+
{structuredData && (
|
|
589
|
+
<script
|
|
590
|
+
type="application/ld+json"
|
|
591
|
+
dangerouslySetInnerHTML={{
|
|
592
|
+
__html: JSON.stringify(structuredData)
|
|
593
|
+
}}
|
|
594
|
+
/>
|
|
595
|
+
)}
|
|
596
|
+
</head>
|
|
597
|
+
|
|
598
|
+
<main>
|
|
599
|
+
<h1>{event?.name}</h1>
|
|
600
|
+
<p>{event?.description}</p>
|
|
601
|
+
{/* Event content */}
|
|
602
|
+
</main>
|
|
603
|
+
</>
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### Dynamic Routes and SSG
|
|
609
|
+
|
|
610
|
+
```tsx
|
|
611
|
+
// pages/events/[eventId].tsx
|
|
612
|
+
import { usePublicEvent } from '@jmruthers/pace-core';
|
|
613
|
+
|
|
614
|
+
export async function getStaticPaths() {
|
|
615
|
+
// Generate static paths for all public events
|
|
616
|
+
const { data: events } = await supabase
|
|
617
|
+
.from('events')
|
|
618
|
+
.select('id')
|
|
619
|
+
.eq('status', 'published')
|
|
620
|
+
.eq('is_public', true);
|
|
621
|
+
|
|
622
|
+
const paths = events?.map(event => ({
|
|
623
|
+
params: { eventId: event.id }
|
|
624
|
+
})) || [];
|
|
625
|
+
|
|
626
|
+
return {
|
|
627
|
+
paths,
|
|
628
|
+
fallback: 'blocking'
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export async function getStaticProps({ params }: { params: { eventId: string } }) {
|
|
633
|
+
// Pre-fetch event data at build time
|
|
634
|
+
const { data: event } = await supabase
|
|
635
|
+
.from('events')
|
|
636
|
+
.select('*')
|
|
637
|
+
.eq('id', params.eventId)
|
|
638
|
+
.eq('status', 'published')
|
|
639
|
+
.eq('is_public', true)
|
|
640
|
+
.single();
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
props: {
|
|
644
|
+
event,
|
|
645
|
+
eventId: params.eventId
|
|
646
|
+
},
|
|
647
|
+
revalidate: 3600 // Revalidate every hour
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function EventPage({ event, eventId }: { event: any; eventId: string }) {
|
|
652
|
+
const { event: liveEvent, loading } = usePublicEvent(eventId);
|
|
653
|
+
|
|
654
|
+
// Use pre-fetched data initially, then live data
|
|
655
|
+
const displayEvent = liveEvent || event;
|
|
656
|
+
|
|
657
|
+
return (
|
|
658
|
+
<div>
|
|
659
|
+
<h1>{displayEvent?.name}</h1>
|
|
660
|
+
<p>{displayEvent?.description}</p>
|
|
661
|
+
{loading && <div>Updating...</div>}
|
|
662
|
+
</div>
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
## Error Handling
|
|
668
|
+
|
|
669
|
+
### Graceful Degradation
|
|
670
|
+
|
|
671
|
+
```tsx
|
|
672
|
+
import { usePublicEvent, usePublicEventLogo } from '@jmruthers/pace-core';
|
|
673
|
+
|
|
674
|
+
function RobustEventPage({ eventId }: { eventId: string }) {
|
|
675
|
+
const {
|
|
676
|
+
event,
|
|
677
|
+
loading: eventLoading,
|
|
678
|
+
error: eventError
|
|
679
|
+
} = usePublicEvent(eventId);
|
|
680
|
+
|
|
681
|
+
const {
|
|
682
|
+
logoUrl,
|
|
683
|
+
loading: logoLoading,
|
|
684
|
+
error: logoError
|
|
685
|
+
} = usePublicEventLogo(eventId, 'large');
|
|
686
|
+
|
|
687
|
+
// Graceful degradation for missing data
|
|
688
|
+
const displayName = event?.name || 'Event';
|
|
689
|
+
const displayDescription = event?.description || 'Event details coming soon';
|
|
690
|
+
const displayLogo = logoUrl || '/default-event-logo.png';
|
|
691
|
+
|
|
692
|
+
if (eventLoading) {
|
|
693
|
+
return (
|
|
694
|
+
<div className="min-h-screen flex items-center justify-center">
|
|
695
|
+
<div className="text-center">
|
|
696
|
+
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-main-600 mx-auto"></div>
|
|
697
|
+
<p className="mt-4 text-sec-600">Loading event...</p>
|
|
698
|
+
</div>
|
|
699
|
+
</div>
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (eventError) {
|
|
704
|
+
return (
|
|
705
|
+
<div className="min-h-screen flex items-center justify-center">
|
|
706
|
+
<div className="text-center">
|
|
707
|
+
<h1 className="text-2xl font-bold text-sec-800 mb-2">
|
|
708
|
+
Unable to load event
|
|
709
|
+
</h1>
|
|
710
|
+
<p className="text-sec-600 mb-4">
|
|
711
|
+
{eventError.message}
|
|
712
|
+
</p>
|
|
713
|
+
<Button onClick={() => window.location.reload()}>
|
|
714
|
+
Try Again
|
|
715
|
+
</Button>
|
|
716
|
+
</div>
|
|
717
|
+
</div>
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
return (
|
|
722
|
+
<PublicPageLayout>
|
|
723
|
+
<header className="bg-main-50 py-8">
|
|
724
|
+
<div className="container mx-auto px-4 text-center">
|
|
725
|
+
{logoLoading ? (
|
|
726
|
+
<div className="animate-pulse bg-sec-200 h-32 w-32 mx-auto rounded"></div>
|
|
727
|
+
) : logoError ? (
|
|
728
|
+
<div className="bg-sec-200 text-sec-500 h-32 w-32 mx-auto rounded flex items-center justify-center">
|
|
729
|
+
No Logo
|
|
730
|
+
</div>
|
|
731
|
+
) : (
|
|
732
|
+
<img
|
|
733
|
+
src={displayLogo}
|
|
734
|
+
alt={`${displayName} logo`}
|
|
735
|
+
className="h-32 w-32 mx-auto object-contain"
|
|
736
|
+
/>
|
|
737
|
+
)}
|
|
738
|
+
|
|
739
|
+
<h1 className="text-3xl font-bold text-main-900 mt-4">
|
|
740
|
+
{displayName}
|
|
741
|
+
</h1>
|
|
742
|
+
<p className="text-main-700 mt-2">
|
|
743
|
+
{displayDescription}
|
|
744
|
+
</p>
|
|
745
|
+
</div>
|
|
746
|
+
</header>
|
|
747
|
+
|
|
748
|
+
<main className="container mx-auto px-4 py-8">
|
|
749
|
+
{/* Event content */}
|
|
750
|
+
</main>
|
|
751
|
+
</PublicPageLayout>
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
## Performance Optimization
|
|
757
|
+
|
|
758
|
+
### Lazy Loading
|
|
759
|
+
|
|
760
|
+
```tsx
|
|
761
|
+
import { lazy, Suspense } from 'react';
|
|
762
|
+
import { usePublicEvent } from '@jmruthers/pace-core';
|
|
763
|
+
|
|
764
|
+
// Lazy load heavy components
|
|
765
|
+
const EventSchedule = lazy(() => import('./EventSchedule'));
|
|
766
|
+
const EventSpeakers = lazy(() => import('./EventSpeakers'));
|
|
767
|
+
|
|
768
|
+
function OptimizedEventPage({ eventId }: { eventId: string }) {
|
|
769
|
+
const { event, loading } = usePublicEvent(eventId);
|
|
770
|
+
|
|
771
|
+
if (loading) return <div>Loading...</div>;
|
|
772
|
+
if (!event) return <div>Event not found</div>;
|
|
773
|
+
|
|
774
|
+
return (
|
|
775
|
+
<PublicPageLayout>
|
|
776
|
+
<header>
|
|
777
|
+
<h1>{event.name}</h1>
|
|
778
|
+
<p>{event.description}</p>
|
|
779
|
+
</header>
|
|
780
|
+
|
|
781
|
+
<main>
|
|
782
|
+
{/* Critical content loads immediately */}
|
|
783
|
+
<section>
|
|
784
|
+
<h2>Event Details</h2>
|
|
785
|
+
<p>Date: {new Date(event.start_date).toLocaleDateString()}</p>
|
|
786
|
+
<p>Location: {event.location}</p>
|
|
787
|
+
</section>
|
|
788
|
+
|
|
789
|
+
{/* Non-critical content loads lazily */}
|
|
790
|
+
<Suspense fallback={<div>Loading schedule...</div>}>
|
|
791
|
+
<EventSchedule eventId={eventId} />
|
|
792
|
+
</Suspense>
|
|
793
|
+
|
|
794
|
+
<Suspense fallback={<div>Loading speakers...</div>}>
|
|
795
|
+
<EventSpeakers eventId={eventId} />
|
|
796
|
+
</Suspense>
|
|
797
|
+
</main>
|
|
798
|
+
</PublicPageLayout>
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
### Image Optimization
|
|
804
|
+
|
|
805
|
+
```tsx
|
|
806
|
+
import { EventLogo } from '@jmruthers/pace-core';
|
|
807
|
+
|
|
808
|
+
function OptimizedEventLogo({ eventId }: { eventId: string }) {
|
|
809
|
+
return (
|
|
810
|
+
<EventLogo
|
|
811
|
+
eventId={eventId}
|
|
812
|
+
size="large"
|
|
813
|
+
className="w-full h-auto max-w-md mx-auto"
|
|
814
|
+
// The component handles:
|
|
815
|
+
// - WebP format when supported
|
|
816
|
+
// - Responsive sizing
|
|
817
|
+
// - Lazy loading
|
|
818
|
+
// - Error fallbacks
|
|
819
|
+
/>
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
## Complete Examples
|
|
825
|
+
|
|
826
|
+
### Full Event Landing Page
|
|
827
|
+
|
|
828
|
+
```tsx
|
|
829
|
+
import {
|
|
830
|
+
PublicPageLayout,
|
|
831
|
+
PublicPageHeader,
|
|
832
|
+
EventLogo,
|
|
833
|
+
usePublicEvent,
|
|
834
|
+
usePublicEventLogo
|
|
835
|
+
} from '@jmruthers/pace-core';
|
|
836
|
+
|
|
837
|
+
function EventLandingPage({ eventId }: { eventId: string }) {
|
|
838
|
+
const { event, loading: eventLoading, error: eventError } = usePublicEvent(eventId);
|
|
839
|
+
const { logoUrl, loading: logoLoading } = usePublicEventLogo(eventId, 'xlarge');
|
|
840
|
+
|
|
841
|
+
if (eventLoading) {
|
|
842
|
+
return (
|
|
843
|
+
<PublicPageLayout>
|
|
844
|
+
<div className="min-h-screen flex items-center justify-center">
|
|
845
|
+
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-main-600"></div>
|
|
846
|
+
</div>
|
|
847
|
+
</PublicPageLayout>
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
if (eventError || !event) {
|
|
852
|
+
return (
|
|
853
|
+
<PublicPageLayout>
|
|
854
|
+
<div className="min-h-screen flex items-center justify-center">
|
|
855
|
+
<div className="text-center">
|
|
856
|
+
<h1 className="text-2xl font-bold text-sec-800 mb-2">
|
|
857
|
+
Event Not Found
|
|
858
|
+
</h1>
|
|
859
|
+
<p className="text-sec-600">
|
|
860
|
+
The event you're looking for doesn't exist or isn't public.
|
|
861
|
+
</p>
|
|
862
|
+
</div>
|
|
863
|
+
</div>
|
|
864
|
+
</PublicPageLayout>
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
return (
|
|
869
|
+
<PublicPageLayout>
|
|
870
|
+
<PublicPageHeader
|
|
871
|
+
title={event.name}
|
|
872
|
+
description={event.description}
|
|
873
|
+
logo={
|
|
874
|
+
logoLoading ? (
|
|
875
|
+
<div className="animate-pulse bg-sec-200 h-48 w-48 rounded"></div>
|
|
876
|
+
) : (
|
|
877
|
+
<EventLogo eventId={eventId} size="xlarge" />
|
|
878
|
+
)
|
|
879
|
+
}
|
|
880
|
+
navigation={[
|
|
881
|
+
{ label: 'Home', href: '/' },
|
|
882
|
+
{ label: 'Schedule', href: '#schedule' },
|
|
883
|
+
{ label: 'Speakers', href: '#speakers' },
|
|
884
|
+
{ label: 'Register', href: '#register' }
|
|
885
|
+
]}
|
|
886
|
+
/>
|
|
887
|
+
|
|
888
|
+
<main className="container mx-auto px-4 py-8">
|
|
889
|
+
{/* Hero Section */}
|
|
890
|
+
<section className="text-center py-16">
|
|
891
|
+
<h1 className="text-4xl font-bold text-main-900 mb-4">
|
|
892
|
+
{event.name}
|
|
893
|
+
</h1>
|
|
894
|
+
<p className="text-xl text-main-700 mb-8 max-w-3xl mx-auto">
|
|
895
|
+
{event.description}
|
|
896
|
+
</p>
|
|
897
|
+
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
|
898
|
+
<Button size="lg" className="bg-main-600 text-main-50">
|
|
899
|
+
Register Now
|
|
900
|
+
</Button>
|
|
901
|
+
<Button size="lg" variant="outline">
|
|
902
|
+
View Schedule
|
|
903
|
+
</Button>
|
|
904
|
+
</div>
|
|
905
|
+
</section>
|
|
906
|
+
|
|
907
|
+
{/* Event Details */}
|
|
908
|
+
<section className="py-16">
|
|
909
|
+
<div className="grid md:grid-cols-2 gap-8">
|
|
910
|
+
<div>
|
|
911
|
+
<h2 className="text-2xl font-bold text-main-900 mb-4">
|
|
912
|
+
Event Information
|
|
913
|
+
</h2>
|
|
914
|
+
<div className="space-y-4">
|
|
915
|
+
<div>
|
|
916
|
+
<h3 className="font-semibold text-main-800">Date</h3>
|
|
917
|
+
<p className="text-main-600">
|
|
918
|
+
{new Date(event.start_date).toLocaleDateString('en-US', {
|
|
919
|
+
weekday: 'long',
|
|
920
|
+
year: 'numeric',
|
|
921
|
+
month: 'long',
|
|
922
|
+
day: 'numeric'
|
|
923
|
+
})}
|
|
924
|
+
</p>
|
|
925
|
+
</div>
|
|
926
|
+
<div>
|
|
927
|
+
<h3 className="font-semibold text-main-800">Location</h3>
|
|
928
|
+
<p className="text-main-600">{event.location}</p>
|
|
929
|
+
</div>
|
|
930
|
+
<div>
|
|
931
|
+
<h3 className="font-semibold text-main-800">Status</h3>
|
|
932
|
+
<span className={`px-3 py-1 rounded-full text-sm ${
|
|
933
|
+
event.status === 'published'
|
|
934
|
+
? 'bg-main-100 text-main-800'
|
|
935
|
+
: 'bg-sec-100 text-sec-800'
|
|
936
|
+
}`}>
|
|
937
|
+
{event.status}
|
|
938
|
+
</span>
|
|
939
|
+
</div>
|
|
940
|
+
</div>
|
|
941
|
+
</div>
|
|
942
|
+
|
|
943
|
+
<div>
|
|
944
|
+
<h2 className="text-2xl font-bold text-main-900 mb-4">
|
|
945
|
+
Event Logo
|
|
946
|
+
</h2>
|
|
947
|
+
{logoLoading ? (
|
|
948
|
+
<div className="animate-pulse bg-sec-200 h-48 w-48 rounded"></div>
|
|
949
|
+
) : (
|
|
950
|
+
<EventLogo
|
|
951
|
+
eventId={eventId}
|
|
952
|
+
size="large"
|
|
953
|
+
className="rounded-lg shadow-lg"
|
|
954
|
+
/>
|
|
955
|
+
)}
|
|
956
|
+
</div>
|
|
957
|
+
</div>
|
|
958
|
+
</section>
|
|
959
|
+
|
|
960
|
+
{/* Additional sections */}
|
|
961
|
+
<section id="schedule" className="py-16">
|
|
962
|
+
<h2 className="text-2xl font-bold text-main-900 mb-8">Schedule</h2>
|
|
963
|
+
{/* Schedule content */}
|
|
964
|
+
</section>
|
|
965
|
+
|
|
966
|
+
<section id="speakers" className="py-16">
|
|
967
|
+
<h2 className="text-2xl font-bold text-main-900 mb-8">Speakers</h2>
|
|
968
|
+
{/* Speakers content */}
|
|
969
|
+
</section>
|
|
970
|
+
</main>
|
|
971
|
+
</PublicPageLayout>
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
## Troubleshooting
|
|
977
|
+
|
|
978
|
+
### Common Issues
|
|
979
|
+
|
|
980
|
+
**Issue: Event not loading**
|
|
981
|
+
- Check that event exists and is published
|
|
982
|
+
- Verify `is_public` flag is set to true
|
|
983
|
+
- Check RLS policies allow public access
|
|
984
|
+
|
|
985
|
+
**Issue: Logo not displaying**
|
|
986
|
+
- Verify logo file exists in storage bucket
|
|
987
|
+
- Check storage bucket policies
|
|
988
|
+
- Ensure logo URL is correct
|
|
989
|
+
|
|
990
|
+
**Issue: Cache not working**
|
|
991
|
+
- Check browser storage permissions
|
|
992
|
+
- Verify cache configuration
|
|
993
|
+
- Clear browser cache and test
|
|
994
|
+
|
|
995
|
+
### Debug Mode
|
|
996
|
+
|
|
997
|
+
```tsx
|
|
998
|
+
import { usePublicEvent, usePublicEventLogo } from '@jmruthers/pace-core';
|
|
999
|
+
|
|
1000
|
+
function DebugPublicPage({ eventId }: { eventId: string }) {
|
|
1001
|
+
const { event, loading, error } = usePublicEvent(eventId);
|
|
1002
|
+
const { logoUrl, loading: logoLoading, error: logoError } = usePublicEventLogo(eventId, 'large');
|
|
1003
|
+
|
|
1004
|
+
console.log('Event data:', { event, loading, error });
|
|
1005
|
+
console.log('Logo data:', { logoUrl, logoLoading, logoError });
|
|
1006
|
+
|
|
1007
|
+
return (
|
|
1008
|
+
<div>
|
|
1009
|
+
<h1>Debug Information</h1>
|
|
1010
|
+
<pre>{JSON.stringify({ event, loading, error }, null, 2)}</pre>
|
|
1011
|
+
<pre>{JSON.stringify({ logoUrl, logoLoading, logoError }, null, 2)}</pre>
|
|
1012
|
+
</div>
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
## Next Steps
|
|
1018
|
+
|
|
1019
|
+
- **[Public Data Access](../api-reference/hooks.md#public-hooks)** - Public data hooks
|
|
1020
|
+
- **[SEO Best Practices](../best-practices/seo.md)** - Search engine optimization
|
|
1021
|
+
- **[Performance Optimization](./performance.md)** - Large page optimization
|
|
1022
|
+
- **[Caching Strategies](../best-practices/caching.md)** - Advanced caching patterns
|