@jmruthers/pace-core 0.5.134 → 0.5.136
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/dist/{DataTable-C7GaRZye.d.ts → DataTable-CWAZZcXC.d.ts} +1 -1
- package/dist/{DataTable-A36PJG6N.js → DataTable-CYOHOX3O.js} +25 -13
- package/dist/{PublicLoadingSpinner-CUAnTvcg.d.ts → EventLogo-801uofbR.d.ts} +51 -135
- package/dist/UnifiedAuthProvider-5E5TUNMS.js +17 -0
- package/dist/{UnifiedAuthProvider-BVKmQd9u.d.ts → UnifiedAuthProvider-DJxGTftH.d.ts} +1 -1
- package/dist/{api-TNIBJWLM.js → api-45XYYO2A.js} +4 -3
- package/dist/{audit-T36HM7IM.js → audit-64X3VJXB.js} +3 -2
- package/dist/{chunk-CTJRBUX2.js → chunk-2TWNJ46Y.js} +2 -2
- package/dist/{chunk-UJI6WSMD.js → chunk-444EZN6N.js} +3 -3
- package/dist/chunk-444EZN6N.js.map +1 -0
- package/dist/{chunk-3CG5L6RN.js → chunk-4MT5BGGL.js} +90 -73
- package/dist/chunk-4MT5BGGL.js.map +1 -0
- package/dist/{chunk-PYUXFQJ3.js → chunk-56XJ3TU6.js} +2 -2
- package/dist/chunk-56XJ3TU6.js.map +1 -0
- package/dist/chunk-5DPZ5EAT.js +60 -0
- package/dist/chunk-5DPZ5EAT.js.map +1 -0
- package/dist/{chunk-66C4BSAY.js → chunk-ANBQRTPX.js} +9 -2
- package/dist/chunk-ANBQRTPX.js.map +1 -0
- package/dist/chunk-APIBCTL2.js +670 -0
- package/dist/chunk-APIBCTL2.js.map +1 -0
- package/dist/{chunk-GKHF54DI.js → chunk-BESYRHQM.js} +10 -4
- package/dist/chunk-BESYRHQM.js.map +1 -0
- package/dist/{chunk-WP5I5GLN.js → chunk-BVYWGZVV.js} +112 -97
- package/dist/chunk-BVYWGZVV.js.map +1 -0
- package/dist/{chunk-GEVIB2UB.js → chunk-ERISIBYU.js} +14 -5
- package/dist/chunk-ERISIBYU.js.map +1 -0
- package/dist/{chunk-CQZU6TFE.js → chunk-FHWWBIHA.js} +100 -62
- package/dist/chunk-FHWWBIHA.js.map +1 -0
- package/dist/{chunk-O3NWNXDY.js → chunk-FMUCXFII.js} +2 -2
- package/dist/chunk-FMUCXFII.js.map +1 -0
- package/dist/{chunk-GVDR7WNV.js → chunk-HJGGOMQ6.js} +194 -518
- package/dist/chunk-HJGGOMQ6.js.map +1 -0
- package/dist/{chunk-BDZUMRBD.js → chunk-K2WWTH7O.js} +13 -6
- package/dist/chunk-K2WWTH7O.js.map +1 -0
- package/dist/{chunk-BYXRHAIF.js → chunk-L6PGMCMD.js} +23 -14
- package/dist/chunk-L6PGMCMD.js.map +1 -0
- package/dist/chunk-LMC26NLJ.js +84 -0
- package/dist/chunk-LMC26NLJ.js.map +1 -0
- package/dist/{chunk-M6DDYFUD.js → chunk-LS353YLY.js} +19 -16
- package/dist/chunk-LS353YLY.js.map +1 -0
- package/dist/{chunk-ZYZCRSBD.js → chunk-LTV3XIJJ.js} +16 -11
- package/dist/chunk-LTV3XIJJ.js.map +1 -0
- package/dist/{chunk-HMNOSGVA.js → chunk-NOHEVYVX.js} +377 -666
- package/dist/chunk-NOHEVYVX.js.map +1 -0
- package/dist/{chunk-JCQZ6LA7.js → chunk-Q5QRDWKI.js} +9 -3
- package/dist/chunk-Q5QRDWKI.js.map +1 -0
- package/dist/chunk-S5OFRT4M.js +94 -0
- package/dist/chunk-S5OFRT4M.js.map +1 -0
- package/dist/{chunk-3DBFLLLU.js → chunk-SBVILCCA.js} +14 -9
- package/dist/chunk-SBVILCCA.js.map +1 -0
- package/dist/{chunk-TGIY2AR2.js → chunk-SL2YQDR6.js} +4 -3
- package/dist/{chunk-TGIY2AR2.js.map → chunk-SL2YQDR6.js.map} +1 -1
- package/dist/{chunk-VZ5OR6HD.js → chunk-TVYPTYOY.js} +55 -179
- package/dist/chunk-TVYPTYOY.js.map +1 -0
- package/dist/{chunk-ZV77RZMU.js → chunk-XARJS7CD.js} +2 -2
- package/dist/chunk-XDNLUEXI.js +138 -0
- package/dist/chunk-XDNLUEXI.js.map +1 -0
- package/dist/{chunk-F64FFPOZ.js → chunk-YLKIDTUK.js} +26 -20
- package/dist/chunk-YLKIDTUK.js.map +1 -0
- package/dist/{chunk-5F3NDPJV.js → chunk-ZZ2SS7NI.js} +10 -5
- package/dist/chunk-ZZ2SS7NI.js.map +1 -0
- package/dist/components.d.ts +7 -287
- package/dist/components.js +26 -157
- package/dist/components.js.map +1 -1
- package/dist/{file-reference-C9isKNPn.d.ts → file-reference-C6Gkn77H.d.ts} +1 -1
- package/dist/{formatting-DFcCxUEk.d.ts → formatting-CvUXy2mF.d.ts} +1 -1
- package/dist/hooks.d.ts +3 -3
- package/dist/hooks.js +22 -16
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +219 -9
- package/dist/index.js +49 -31
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +5 -4
- package/dist/rbac/index.js +13 -12
- package/dist/styles/index.js +2 -1
- package/dist/theming/runtime.d.ts +2 -19
- package/dist/theming/runtime.js +2 -1
- package/dist/{types-D5rqZQXk.d.ts → types-Dfz9dmVH.d.ts} +12 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/{useInactivityTracker-MRUU55XI.js → useInactivityTracker-TO6ZOF35.js} +3 -2
- package/dist/{usePublicRouteParams-Dyt1tzI9.d.ts → usePublicRouteParams-B7PabvuH.d.ts} +1 -1
- package/dist/utils.d.ts +195 -232
- package/dist/utils.js +173 -331
- package/dist/utils.js.map +1 -1
- package/dist/{validation-DnhrNMju.d.ts → validation-8npbysjg.d.ts} +26 -8
- package/dist/validation.d.ts +261 -10
- package/dist/validation.js +82 -440
- package/dist/validation.js.map +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +6 -6
- 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 +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +6 -6
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +7 -7
- 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 +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +4 -4
- 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 +29 -4
- package/docs/api/interfaces/DataAccessRecord.md +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +18 -18
- package/docs/api/interfaces/DataTableColumn.md +61 -1
- package/docs/api/interfaces/DataTableProps.md +3 -3
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +14 -14
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +152 -0
- package/docs/api/interfaces/ExportColumn.md +90 -0
- package/docs/api/interfaces/ExportOptions.md +126 -0
- package/docs/api/interfaces/FileDisplayProps.md +15 -15
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.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 +10 -10
- package/docs/api/interfaces/NavigationContextType.md +9 -9
- package/docs/api/interfaces/NavigationGuardProps.md +10 -10
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +7 -7
- 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 +27 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
- package/docs/api/interfaces/PageAccessRecord.md +8 -8
- package/docs/api/interfaces/PagePermissionContextType.md +8 -8
- package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
- package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
- package/docs/api/interfaces/PaletteData.md +4 -4
- package/docs/api/interfaces/PermissionEnforcerProps.md +11 -11
- package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
- 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/RBACLogger.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
- package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +10 -10
- package/docs/api/interfaces/RouteConfig.md +10 -10
- package/docs/api/interfaces/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +21 -0
- 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/SwitchProps.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 +53 -53
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +9 -9
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +5 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +9 -9
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
- package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
- package/docs/api/interfaces/UserEventAccess.md +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +648 -212
- package/docs/api-reference/components.md +106 -26
- package/docs/architecture/README.md +0 -2
- package/docs/implementation-guides/data-tables.md +277 -13
- package/docs/implementation-guides/forms.md +1 -16
- package/docs/implementation-guides/permission-enforcement.md +8 -2
- package/examples/README.md +30 -14
- package/examples/STRUCTURE.md +125 -0
- package/examples/components/DataTable/HierarchicalActionsExample.tsx +421 -0
- package/examples/components/DataTable/HierarchicalExample.tsx +475 -0
- package/examples/components/DataTable/InitialPageSizeExample.tsx +177 -0
- package/examples/components/DataTable/PerformanceExample.tsx +506 -0
- package/examples/components/DataTable/index.ts +13 -0
- package/examples/components/Dialog/BasicHtmlTest.tsx +55 -0
- package/examples/components/Dialog/DebugHtmlExample.tsx +68 -0
- package/examples/components/Dialog/HtmlDialogExample.tsx +202 -0
- package/examples/components/Dialog/ScrollableDialogExample.tsx +290 -0
- package/examples/components/Dialog/SimpleHtmlTest.tsx +61 -0
- package/examples/components/Dialog/SmartDialogExample.tsx +322 -0
- package/examples/components/Dialog/index.ts +15 -0
- package/examples/components/index.ts +11 -0
- package/examples/features/index.ts +12 -0
- package/examples/{public-pages → features/public-pages}/CorrectPublicPageImplementation.tsx +1 -1
- package/examples/{public-pages → features/public-pages}/PublicEventPage.tsx +1 -1
- package/examples/{public-pages → features/public-pages}/PublicPageApp.tsx +1 -1
- package/examples/{public-pages → features/public-pages}/PublicPageUsageExample.tsx +1 -1
- package/examples/index.ts +11 -3
- package/package.json +30 -10
- package/src/components/Alert/Alert.tsx +1 -1
- package/src/components/Avatar/Avatar.tsx +1 -1
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/Card/Card.tsx +1 -1
- package/src/components/Checkbox/Checkbox.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +1 -1
- package/src/components/DataTable/DataTable.tsx +1 -30
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +562 -0
- package/src/components/DataTable/__tests__/styles.test.ts +2 -2
- package/src/components/DataTable/components/ActionButtons.tsx +0 -15
- package/src/components/DataTable/components/DataTableCore.tsx +4 -185
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +1 -1
- package/src/components/DataTable/components/DataTableModals.tsx +1 -27
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/ImportModal.tsx +2 -14
- package/src/components/DataTable/components/PaginationControls.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +109 -82
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +1 -1
- package/src/components/DataTable/examples/GroupingAggregationExample.tsx +273 -0
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +1 -1
- package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +1 -1
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +1 -1
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +1 -1
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +2 -23
- package/src/components/DataTable/index.ts +4 -0
- package/src/components/DataTable/styles.ts +1 -1
- package/src/components/DataTable/types.ts +13 -0
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +1 -1
- package/src/components/DataTable/utils/aggregationUtils.ts +161 -0
- package/src/components/DataTable/utils/exportUtils.ts +1 -1
- package/src/components/DataTable/utils/flexibleImport.ts +1 -11
- package/src/components/DataTable/utils/index.ts +1 -0
- package/src/components/DataTable/utils/paginationUtils.ts +1 -1
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +35 -7
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +5 -4
- package/src/components/EventSelector/EventSelector.tsx +3 -2
- package/src/components/FileDisplay/FileDisplay.tsx +2 -36
- package/src/components/FileUpload/FileUpload.test.tsx +2 -2
- package/src/components/FileUpload/FileUpload.tsx +2 -2
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +4 -509
- package/src/components/Form/Form.tsx +1 -1
- package/src/components/Form/FormField.tsx +1 -1
- package/src/components/Form/index.ts +0 -12
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/Input/Input.tsx +1 -1
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoginForm/LoginForm.tsx +1 -1
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -3
- package/src/components/NavigationMenu/NavigationMenu.tsx +9 -8
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +4 -3
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -12
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +0 -16
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +0 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +0 -9
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +35 -3
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +13 -12
- package/src/components/PasswordReset/PasswordChangeForm.tsx +1 -1
- package/src/components/PasswordReset/index.ts +0 -2
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +35 -8
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -2
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +1 -1
- package/src/components/PublicLayout/PublicPageContextChecker.tsx +44 -43
- package/src/components/PublicLayout/PublicPageFooter.tsx +1 -1
- package/src/components/PublicLayout/PublicPageHeader.tsx +1 -15
- package/src/components/PublicLayout/PublicPageProvider.tsx +3 -2
- package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +2 -0
- package/src/components/PublicLayout/index.ts +4 -2
- package/src/components/Select/Select.tsx +1 -1
- package/src/components/{SessionRestorationLoader.tsx → SessionRestorationLoader/SessionRestorationLoader.tsx} +3 -2
- package/src/components/SessionRestorationLoader/index.ts +3 -0
- package/src/components/Switch/Switch.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/index.ts +4 -10
- package/src/hooks/__tests__/hooks.integration.test.tsx +37 -22
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +33 -17
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +28 -3
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +36 -9
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +26 -2
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +19 -6
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +17 -4
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +17 -4
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +26 -6
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +16 -6
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +3 -3
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +17 -3
- package/src/hooks/public/usePublicEvent.ts +7 -6
- package/src/hooks/public/usePublicEventLogo.ts +7 -4
- package/src/hooks/public/usePublicFileDisplay.ts +6 -150
- package/src/hooks/useComponentPerformance.ts +4 -1
- package/src/hooks/useDataTablePerformance.ts +4 -3
- package/src/hooks/useEventTheme.test.ts +18 -5
- package/src/hooks/useEventTheme.ts +4 -1
- package/src/hooks/useEvents.ts +2 -0
- package/src/hooks/useFileDisplay.ts +9 -8
- package/src/hooks/useFileReference.ts +4 -1
- package/src/hooks/useFileUrl.ts +4 -1
- package/src/hooks/useInactivityTracker.ts +5 -4
- package/src/hooks/useOrganisationSecurity.test.ts +33 -12
- package/src/hooks/useOrganisationSecurity.ts +8 -7
- package/src/hooks/usePerformanceMonitor.ts +6 -3
- package/src/hooks/usePermissionCache.ts +13 -6
- package/src/hooks/useSecureDataAccess.test.ts +2 -2
- package/src/hooks/useSecureDataAccess.ts +9 -8
- package/src/hooks/useSessionRestoration.ts +4 -1
- package/src/hooks/useStorage.ts +4 -1
- package/src/index.ts +25 -8
- package/src/providers/services/AuthServiceProvider.tsx +3 -2
- package/src/providers/services/EventServiceProvider.tsx +2 -1
- package/src/providers/services/InactivityServiceProvider.tsx +2 -1
- package/src/providers/services/OrganisationServiceProvider.tsx +2 -1
- package/src/providers/services/UnifiedAuthProvider.tsx +4 -3
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +22 -2
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +24 -2
- package/src/rbac/__tests__/cache-invalidation.test.ts +20 -6
- package/src/rbac/api.ts +5 -2
- package/src/rbac/audit-enhanced.ts +6 -6
- package/src/rbac/audit.test.ts +60 -38
- package/src/rbac/audit.ts +8 -8
- package/src/rbac/cache-invalidation.ts +7 -4
- package/src/rbac/components/EnhancedNavigationMenu.tsx +11 -5
- package/src/rbac/components/NavigationGuard.tsx +7 -3
- package/src/rbac/components/NavigationProvider.tsx +6 -3
- package/src/rbac/components/PagePermissionGuard.tsx +28 -16
- package/src/rbac/components/PagePermissionProvider.tsx +4 -1
- package/src/rbac/components/PermissionEnforcer.tsx +9 -3
- package/src/rbac/components/RoleBasedRouter.tsx +3 -1
- package/src/rbac/components/SecureDataProvider.tsx +7 -3
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +87 -61
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +83 -33
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +36 -13
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +2 -2
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +22 -8
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +19 -6
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +43 -17
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +42 -17
- package/src/rbac/engine.ts +15 -7
- package/src/rbac/hooks/usePermissions.ts +7 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +2 -2
- package/src/rbac/hooks/useResolvedScope.ts +10 -7
- package/src/rbac/permissions.ts +5 -2
- package/src/rbac/security.test.ts +27 -16
- package/src/rbac/security.ts +5 -4
- package/src/services/AuthService.ts +22 -21
- package/src/services/EventService.ts +12 -12
- package/src/services/InactivityService.ts +5 -4
- package/src/services/OrganisationService.ts +26 -25
- package/src/services/__tests__/AuthService.test.ts +51 -19
- package/src/services/__tests__/EventService.test.ts +37 -5
- package/src/services/__tests__/InactivityService.test.ts +38 -4
- package/src/services/__tests__/OrganisationService.test.ts +3 -8
- package/src/services/base/BaseService.ts +3 -1
- package/src/theming/__tests__/runtime.test.ts +21 -12
- package/src/theming/parseEventColours.ts +5 -19
- package/src/theming/runtime.ts +8 -4
- package/src/types/validation.ts +2 -29
- package/src/utils/__tests__/appConfig.unit.test.ts +1 -1
- package/src/utils/__tests__/audit.unit.test.ts +1 -1
- package/src/utils/__tests__/auth-utils.unit.test.ts +1 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +19 -19
- package/src/utils/__tests__/cn.unit.test.ts +1 -1
- package/src/utils/__tests__/debugLogger.test.ts +1 -1
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +1 -1
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/formatting.unit.test.ts +1 -1
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +1 -1
- package/src/utils/__tests__/logger.unit.test.ts +1 -1
- package/src/utils/__tests__/organisationContext.unit.test.ts +1 -1
- package/src/utils/__tests__/performanceBenchmark.test.ts +1 -1
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +1 -1
- package/src/utils/__tests__/permissionTypes.unit.test.ts +1 -1
- package/src/utils/__tests__/permissionUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/sanitization.unit.test.ts +1 -1
- package/src/utils/__tests__/schemaUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +1 -1
- package/src/utils/__tests__/secureErrors.unit.test.ts +33 -15
- package/src/utils/__tests__/secureStorage.unit.test.ts +1 -1
- package/src/utils/__tests__/security.unit.test.ts +40 -18
- package/src/utils/__tests__/securityMonitor.unit.test.ts +1 -1
- package/src/utils/__tests__/sessionTracking.unit.test.ts +40 -29
- package/src/utils/__tests__/validationUtils.unit.test.ts +19 -6
- package/src/utils/{appIdResolver.test.ts → app/appIdResolver.test.ts} +28 -30
- package/src/utils/{appIdResolver.ts → app/appIdResolver.ts} +8 -5
- package/src/utils/{appNameResolver.test.ts → app/appNameResolver.test.ts} +1 -1
- package/src/utils/{appNameResolver.ts → app/appNameResolver.ts} +5 -1
- package/src/utils/{organisationContext.ts → context/organisationContext.ts} +6 -3
- package/src/utils/{sessionTracking.ts → context/sessionTracking.ts} +11 -12
- package/src/utils/{logger.ts → core/logger.ts} +4 -2
- package/src/utils/{deviceFingerprint.ts → device/deviceFingerprint.ts} +1 -1
- package/src/utils/{lazyLoad.tsx → dynamic/lazyLoad.tsx} +2 -2
- package/src/utils/{file-reference.test.ts → file-reference/__tests__/file-reference.test.ts} +5 -5
- package/src/utils/{file-reference.ts → file-reference/index.ts} +20 -38
- package/src/utils/index.ts +32 -54
- package/src/utils/{secureErrors.ts → security/secureErrors.ts} +6 -3
- package/src/utils/{security.ts → security/security.ts} +5 -2
- package/src/utils/storage/__tests__/helpers.unit.test.ts +1 -4
- package/src/utils/storage/helpers.ts +15 -8
- package/src/{components/Dialog/utils/__tests__/safeHtml.unit.test.ts → utils/validation/__tests__/htmlSanitization.unit.test.ts} +9 -15
- package/src/{validation → utils/validation}/csrf.ts +1 -1
- package/src/{components/Dialog/utils/safeHtml.ts → utils/validation/htmlSanitization.ts} +9 -10
- package/src/utils/validation/index.ts +79 -0
- package/src/utils/{sanitization.ts → validation/sanitization.ts} +71 -2
- package/src/{validation/schemaUtils.ts → utils/validation/schema.ts} +11 -6
- package/src/{validation → utils/validation}/sqlInjectionProtection.ts +2 -0
- package/src/utils/{validationUtils.ts → validation/validationUtils.ts} +4 -1
- package/src/validation/index.ts +3 -34
- package/dist/UnifiedAuthProvider-CQDZRJIS.js +0 -16
- package/dist/chunk-24MKLB7U.js +0 -81
- package/dist/chunk-24MKLB7U.js.map +0 -1
- package/dist/chunk-3CG5L6RN.js.map +0 -1
- package/dist/chunk-3DBFLLLU.js.map +0 -1
- package/dist/chunk-5F3NDPJV.js.map +0 -1
- package/dist/chunk-66C4BSAY.js.map +0 -1
- package/dist/chunk-BDZUMRBD.js.map +0 -1
- package/dist/chunk-BYXRHAIF.js.map +0 -1
- package/dist/chunk-CDQ3PX7L.js +0 -18
- package/dist/chunk-CDQ3PX7L.js.map +0 -1
- package/dist/chunk-CQZU6TFE.js.map +0 -1
- package/dist/chunk-F64FFPOZ.js.map +0 -1
- package/dist/chunk-GEVIB2UB.js.map +0 -1
- package/dist/chunk-GKHF54DI.js.map +0 -1
- package/dist/chunk-GVDR7WNV.js.map +0 -1
- package/dist/chunk-HMNOSGVA.js.map +0 -1
- package/dist/chunk-JCQZ6LA7.js.map +0 -1
- package/dist/chunk-M6DDYFUD.js.map +0 -1
- package/dist/chunk-O3NWNXDY.js.map +0 -1
- package/dist/chunk-PYUXFQJ3.js.map +0 -1
- package/dist/chunk-UJI6WSMD.js.map +0 -1
- package/dist/chunk-VZ5OR6HD.js.map +0 -1
- package/dist/chunk-WP5I5GLN.js.map +0 -1
- package/dist/chunk-ZYZCRSBD.js.map +0 -1
- package/src/components/Dialog/README.md +0 -804
- package/src/components/Form/FormErrorSummary.tsx +0 -113
- package/src/components/Form/FormFieldset.tsx +0 -127
- package/src/components/Form/FormLiveRegion.tsx +0 -198
- package/src/components/PasswordReset/PasswordResetForm.test.tsx +0 -597
- package/src/components/PasswordReset/PasswordResetForm.tsx +0 -201
- package/src/components/PublicLayout/PublicPageDebugger.tsx +0 -104
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +0 -162
- package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +0 -185
- package/src/examples/CorrectPublicPageImplementation.tsx +0 -304
- package/src/examples/PublicEventPage.tsx +0 -287
- package/src/examples/PublicPageApp.tsx +0 -321
- package/src/examples/PublicPageUsageExample.tsx +0 -218
- package/src/utils/schemaUtils.ts +0 -37
- package/src/validation/__tests__/common.unit.test.ts +0 -101
- package/src/validation/__tests__/csrf.unit.test.ts +0 -365
- package/src/validation/__tests__/passwordSchema.unit.test.ts +0 -203
- package/src/validation/__tests__/sanitization.unit.test.ts +0 -250
- package/src/validation/__tests__/schemaUtils.unit.test.ts +0 -451
- package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +0 -462
- package/src/validation/__tests__/user.unit.test.ts +0 -440
- package/src/validation/sanitization.ts +0 -96
- /package/dist/{DataTable-A36PJG6N.js.map → DataTable-CYOHOX3O.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-CQDZRJIS.js.map → UnifiedAuthProvider-5E5TUNMS.js.map} +0 -0
- /package/dist/{api-TNIBJWLM.js.map → api-45XYYO2A.js.map} +0 -0
- /package/dist/{audit-T36HM7IM.js.map → audit-64X3VJXB.js.map} +0 -0
- /package/dist/{chunk-CTJRBUX2.js.map → chunk-2TWNJ46Y.js.map} +0 -0
- /package/dist/{chunk-ZV77RZMU.js.map → chunk-XARJS7CD.js.map} +0 -0
- /package/dist/{useInactivityTracker-MRUU55XI.js.map → useInactivityTracker-TO6ZOF35.js.map} +0 -0
- /package/examples/{public-pages → features/public-pages}/index.ts +0 -0
- /package/examples/{RBAC → features/rbac}/CompleteRBACExample.tsx +0 -0
- /package/examples/{RBAC → features/rbac}/EventBasedApp.tsx +0 -0
- /package/examples/{RBAC → features/rbac}/PermissionExample.tsx +0 -0
- /package/examples/{RBAC → features/rbac}/index.ts +0 -0
- /package/src/utils/{appConfig.ts → app/appConfig.ts} +0 -0
- /package/src/utils/{appNameResolver.simple.test.ts → app/appNameResolver.simple.test.ts} +0 -0
- /package/src/utils/{audit.ts → audit/audit.ts} +0 -0
- /package/src/utils/{organisationContext.test.ts → context/organisationContext.test.ts} +0 -0
- /package/src/utils/{cn.ts → core/cn.ts} +0 -0
- /package/src/utils/{debugLogger.ts → core/debugLogger.ts} +0 -0
- /package/src/utils/{dynamicUtils.ts → dynamic/dynamicUtils.ts} +0 -0
- /package/src/utils/{formatDate.test.ts → formatting/formatDate.test.ts} +0 -0
- /package/src/utils/{formatting.ts → formatting/formatting.ts} +0 -0
- /package/src/utils/{bundleAnalysis.ts → performance/bundleAnalysis.ts} +0 -0
- /package/src/utils/{performanceBenchmark.ts → performance/performanceBenchmark.ts} +0 -0
- /package/src/utils/{performanceBudgets.ts → performance/performanceBudgets.ts} +0 -0
- /package/src/utils/{permissionTypes.ts → permissions/permissionTypes.ts} +0 -0
- /package/src/utils/{permissionUtils.test.ts → permissions/permissionUtils.test.ts} +0 -0
- /package/src/utils/{permissionUtils.ts → permissions/permissionUtils.ts} +0 -0
- /package/src/utils/{auth-utils.ts → security/auth-utils.ts} +0 -0
- /package/src/utils/{secureDataAccess.test.ts → security/secureDataAccess.test.ts} +0 -0
- /package/src/utils/{secureDataAccess.ts → security/secureDataAccess.ts} +0 -0
- /package/src/utils/{secureStorage.ts → security/secureStorage.ts} +0 -0
- /package/src/utils/{securityMonitor.ts → security/securityMonitor.ts} +0 -0
- /package/src/{validation → utils/validation}/common.ts +0 -0
- /package/src/{validation → utils/validation}/passwordSchema.ts +0 -0
- /package/src/{validation → utils/validation}/user.ts +0 -0
- /package/src/utils/{validation.ts → validation/validation.ts} +0 -0
|
@@ -193,7 +193,7 @@
|
|
|
193
193
|
|
|
194
194
|
import * as React from "react";
|
|
195
195
|
import { Menu, ChevronDown } from "lucide-react";
|
|
196
|
-
import { cn } from "../../utils/cn";
|
|
196
|
+
import { cn } from "../../utils/core/cn";
|
|
197
197
|
import { NavigationMenuProps, NavigationItem } from "./types";
|
|
198
198
|
import { Button } from "../Button";
|
|
199
199
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../Select";
|
|
@@ -202,6 +202,7 @@ import { useRBAC } from "../../rbac/hooks/useRBAC";
|
|
|
202
202
|
import { useResolvedScope } from "../../rbac/hooks";
|
|
203
203
|
import { usePermissions } from "../../rbac/hooks/usePermissions";
|
|
204
204
|
import type { Permission, AccessLevel as RBACAccessLevel } from "../../rbac/types";
|
|
205
|
+
import { logger } from "../../utils/core/logger";
|
|
205
206
|
|
|
206
207
|
/**
|
|
207
208
|
* Unified NavigationMenu component that supports both dropdown and hierarchical navigation modes.
|
|
@@ -424,7 +425,7 @@ export const NavigationMenu = React.forwardRef<
|
|
|
424
425
|
authContext = useUnifiedAuth();
|
|
425
426
|
} catch (error) {
|
|
426
427
|
// NavigationMenu is being used outside of UnifiedAuthProvider
|
|
427
|
-
|
|
428
|
+
logger.warn('NavigationMenu', 'useUnifiedAuth not available, running in unauthenticated mode');
|
|
428
429
|
}
|
|
429
430
|
|
|
430
431
|
// Get RBAC context for permission and role checks
|
|
@@ -433,7 +434,7 @@ export const NavigationMenu = React.forwardRef<
|
|
|
433
434
|
rbacContext = useRBAC();
|
|
434
435
|
} catch (error) {
|
|
435
436
|
// RBAC not available - permission filtering will be disabled
|
|
436
|
-
|
|
437
|
+
logger.warn('NavigationMenu', 'useRBAC not available, permission filtering disabled');
|
|
437
438
|
}
|
|
438
439
|
|
|
439
440
|
// Get resolved scope for permission checks
|
|
@@ -524,7 +525,7 @@ export const NavigationMenu = React.forwardRef<
|
|
|
524
525
|
// If there's an error or empty permission map after loading, show nothing
|
|
525
526
|
// Security: Better to show nothing than risk showing unauthorized items
|
|
526
527
|
if (permissionsError || !permissionMap || Object.keys(permissionMap).length === 0) {
|
|
527
|
-
|
|
528
|
+
logger.warn('NavigationMenu', 'Permission map is empty or has error - showing no items for security', {
|
|
528
529
|
permissionsError: permissionsError?.message,
|
|
529
530
|
permissionMapSize: permissionMap ? Object.keys(permissionMap).length : 0
|
|
530
531
|
});
|
|
@@ -643,7 +644,7 @@ export const NavigationMenu = React.forwardRef<
|
|
|
643
644
|
|
|
644
645
|
if (!finalHasPermission) {
|
|
645
646
|
if (auditLog) {
|
|
646
|
-
|
|
647
|
+
logger.debug('NavigationMenu', `Filtering out navigation item "${item.label}" - no page permission:`, {
|
|
647
648
|
itemId: item.id,
|
|
648
649
|
href: item.href,
|
|
649
650
|
pageId,
|
|
@@ -716,7 +717,7 @@ export const NavigationMenu = React.forwardRef<
|
|
|
716
717
|
// Log navigation access attempts for debugging
|
|
717
718
|
React.useEffect(() => {
|
|
718
719
|
if (auditLog && authContext) {
|
|
719
|
-
|
|
720
|
+
logger.debug('NavigationMenu', 'Navigation access attempt:', {
|
|
720
721
|
itemId: 'navigation-menu',
|
|
721
722
|
label: 'Navigation Menu',
|
|
722
723
|
href: currentPath,
|
|
@@ -766,7 +767,7 @@ export const NavigationMenu = React.forwardRef<
|
|
|
766
767
|
// NEW: Phase 2 - Enhanced Security Features
|
|
767
768
|
// Log navigation access attempt for audit
|
|
768
769
|
if (auditLog) {
|
|
769
|
-
|
|
770
|
+
logger.debug('NavigationMenu', 'Navigation access attempt:', {
|
|
770
771
|
itemId: item.id,
|
|
771
772
|
label: item.label,
|
|
772
773
|
href: item.href,
|
|
@@ -842,7 +843,7 @@ export const NavigationMenu = React.forwardRef<
|
|
|
842
843
|
}
|
|
843
844
|
|
|
844
845
|
if (strictMode) {
|
|
845
|
-
|
|
846
|
+
logger.error('NavigationMenu', 'STRICT MODE VIOLATION: User attempted to access protected navigation item without permission', {
|
|
846
847
|
itemId: item.id,
|
|
847
848
|
label: item.label,
|
|
848
849
|
href: item.href,
|
|
@@ -61,6 +61,7 @@ import { LoadingSpinner } from '../LoadingSpinner/LoadingSpinner';
|
|
|
61
61
|
import { RefreshCw, AlertCircle, Building2, Shield } from 'lucide-react';
|
|
62
62
|
import { useOrganisations } from '../../providers/OrganisationProvider';
|
|
63
63
|
import type { Organisation } from '../../types/organisation';
|
|
64
|
+
import { logger } from '../../utils/core/logger';
|
|
64
65
|
|
|
65
66
|
export interface OrganisationSelectorProps {
|
|
66
67
|
/** Placeholder text for the dropdown */
|
|
@@ -137,9 +138,9 @@ export function OrganisationSelector({
|
|
|
137
138
|
onOrganisationChange(newOrganisation);
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
|
|
141
|
+
logger.debug('OrganisationSelector', 'Successfully switched to organisation:', orgId);
|
|
141
142
|
} catch (error) {
|
|
142
|
-
|
|
143
|
+
logger.error('OrganisationSelector', 'Failed to switch organisation:', error);
|
|
143
144
|
setSwitchError(error instanceof Error ? error.message : 'Failed to switch organisation');
|
|
144
145
|
} finally {
|
|
145
146
|
setIsLoading(false);
|
|
@@ -159,7 +160,7 @@ export function OrganisationSelector({
|
|
|
159
160
|
try {
|
|
160
161
|
await refreshOrganisations();
|
|
161
162
|
} catch (error) {
|
|
162
|
-
|
|
163
|
+
logger.error('OrganisationSelector', 'Failed to refresh organisations:', error);
|
|
163
164
|
setSwitchError('Failed to refresh organisations');
|
|
164
165
|
} finally {
|
|
165
166
|
setIsLoading(false);
|
|
@@ -105,8 +105,9 @@ import { useEvents } from '../../hooks/useEvents';
|
|
|
105
105
|
import { useEventTheme } from '../../hooks/useEventTheme';
|
|
106
106
|
import { useCan, useResolvedScope } from '../../rbac/hooks';
|
|
107
107
|
import { createScopeFromEvent } from '../../rbac/utils/eventContext';
|
|
108
|
-
import { getCurrentAppName } from '../../utils/appNameResolver';
|
|
108
|
+
import { getCurrentAppName } from '../../utils/app/appNameResolver';
|
|
109
109
|
import { isSuperAdmin } from '../../rbac/api';
|
|
110
|
+
import { logger } from '../../utils/core/logger';
|
|
110
111
|
import type { Permission, Scope } from '../../rbac/types';
|
|
111
112
|
|
|
112
113
|
// Stable empty objects to prevent infinite loops
|
|
@@ -465,7 +466,7 @@ export function PaceAppLayout({
|
|
|
465
466
|
const superAdminStatus = await isSuperAdmin(user.id);
|
|
466
467
|
setIsSuperAdminUser(superAdminStatus);
|
|
467
468
|
} catch (error) {
|
|
468
|
-
|
|
469
|
+
logger.error('PaceAppLayout', 'Error checking super admin status', { userId: user?.id, error });
|
|
469
470
|
setIsSuperAdminUser(false);
|
|
470
471
|
} finally {
|
|
471
472
|
setIsCheckingSuperAdmin(false);
|
|
@@ -506,7 +507,7 @@ export function PaceAppLayout({
|
|
|
506
507
|
// NEW: Phase 1 - Enhanced Security Features
|
|
507
508
|
// Handle strict mode violations - skip for super admins
|
|
508
509
|
if (strictMode && !isSuperAdminUser && !can) {
|
|
509
|
-
|
|
510
|
+
logger.error('PaceAppLayout', 'STRICT MODE VIOLATION: User attempted to access protected page without permission', {
|
|
510
511
|
pageName: currentPageId,
|
|
511
512
|
operation: currentRoutePermission,
|
|
512
513
|
userId: user?.id,
|
|
@@ -626,7 +627,7 @@ export function PaceAppLayout({
|
|
|
626
627
|
} catch (error) {
|
|
627
628
|
// On error, fall back to showing all items (graceful degradation)
|
|
628
629
|
// This prevents navigation from being empty if permission checks fail
|
|
629
|
-
|
|
630
|
+
logger.error('PaceAppLayout', 'Failed to load permission map for navigation filtering', { userId: user?.id, error });
|
|
630
631
|
if (isMounted) {
|
|
631
632
|
setFilteredMenuItems(baseMenuItems);
|
|
632
633
|
}
|
|
@@ -654,7 +655,7 @@ export function PaceAppLayout({
|
|
|
654
655
|
if (!currentRoute) {
|
|
655
656
|
// Route not found in configuration
|
|
656
657
|
if (strictMode) {
|
|
657
|
-
|
|
658
|
+
logger.error('PaceAppLayout', 'STRICT MODE VIOLATION: Route not found in configuration', {
|
|
658
659
|
route: currentPath,
|
|
659
660
|
userId: user?.id,
|
|
660
661
|
timestamp: new Date().toISOString()
|
|
@@ -685,7 +686,7 @@ export function PaceAppLayout({
|
|
|
685
686
|
if (!isMounted) return;
|
|
686
687
|
hasAccess = hasPagePermission;
|
|
687
688
|
} catch (error) {
|
|
688
|
-
|
|
689
|
+
logger.error('PaceAppLayout', 'Failed to check page permission', { route: currentPath, pageId: currentRoute.pageId, error });
|
|
689
690
|
if (!isMounted) return;
|
|
690
691
|
hasAccess = false;
|
|
691
692
|
}
|
|
@@ -709,7 +710,7 @@ export function PaceAppLayout({
|
|
|
709
710
|
}
|
|
710
711
|
|
|
711
712
|
if (strictMode) {
|
|
712
|
-
|
|
713
|
+
logger.error('PaceAppLayout', 'STRICT MODE VIOLATION: User attempted to access protected route without permission', {
|
|
713
714
|
route: currentPath,
|
|
714
715
|
userId: user?.id,
|
|
715
716
|
permissions: currentRoute.permissions,
|
|
@@ -745,15 +746,16 @@ export function PaceAppLayout({
|
|
|
745
746
|
const result = await updatePassword(newPassword);
|
|
746
747
|
if (result?.error) {
|
|
747
748
|
// The form will display the error message
|
|
748
|
-
|
|
749
|
+
logger.error('PaceAppLayout', 'Failed to change password', { error: result.error.message });
|
|
749
750
|
}
|
|
750
751
|
// The form will handle closing the modal on success
|
|
751
752
|
return result || { error: null };
|
|
752
753
|
};
|
|
753
754
|
|
|
754
755
|
// Show loading state while checking permissions or super admin status
|
|
755
|
-
//
|
|
756
|
-
|
|
756
|
+
// Keep loading active until BOTH checks complete to prevent exposing protected content
|
|
757
|
+
// This ensures we don't render the main layout when permission check failed but super admin check is pending
|
|
758
|
+
if (enforcePermissions && (isCheckingSuperAdmin || isCheckingPermission)) {
|
|
757
759
|
return (
|
|
758
760
|
<div className="flex items-center justify-center min-h-screen">
|
|
759
761
|
<div className="text-center">
|
|
@@ -764,9 +766,9 @@ export function PaceAppLayout({
|
|
|
764
766
|
);
|
|
765
767
|
}
|
|
766
768
|
|
|
767
|
-
// Show permission error (
|
|
769
|
+
// Show permission error (only after BOTH checks are complete)
|
|
768
770
|
// Super admins bypass all permission checks, so don't show errors for them
|
|
769
|
-
if (enforcePermissions && permissionError && !
|
|
771
|
+
if (enforcePermissions && permissionError && !isSuperAdminUser) {
|
|
770
772
|
return (
|
|
771
773
|
<div className="flex items-center justify-center min-h-screen">
|
|
772
774
|
<div className="text-center">
|
|
@@ -102,14 +102,6 @@ const mockIsSuperAdmin = vi.fn().mockResolvedValue(false);
|
|
|
102
102
|
|
|
103
103
|
vi.mock('../../../rbac/api', () => ({
|
|
104
104
|
isPermitted: vi.fn().mockImplementation((input) => {
|
|
105
|
-
console.log('[PaceAppLayout] Page access attempt:', {
|
|
106
|
-
pageName: input.pageId || 'unknown',
|
|
107
|
-
operation: input.permission,
|
|
108
|
-
userId: input.userId,
|
|
109
|
-
allowed: true,
|
|
110
|
-
strictMode: true,
|
|
111
|
-
timestamp: new Date().toISOString()
|
|
112
|
-
});
|
|
113
105
|
return Promise.resolve(true);
|
|
114
106
|
}),
|
|
115
107
|
getPermissionMap: vi.fn().mockResolvedValue({}),
|
|
@@ -288,14 +280,6 @@ describe('PaceAppLayout Integration', () => {
|
|
|
288
280
|
|
|
289
281
|
// Reset mockIsPermitted to default implementation
|
|
290
282
|
mockIsPermitted.mockImplementation((input) => {
|
|
291
|
-
console.log('[PaceAppLayout] Page access attempt:', {
|
|
292
|
-
pageName: input.pageId || 'unknown',
|
|
293
|
-
operation: input.permission,
|
|
294
|
-
userId: input.userId,
|
|
295
|
-
allowed: true,
|
|
296
|
-
strictMode: true,
|
|
297
|
-
timestamp: new Date().toISOString()
|
|
298
|
-
});
|
|
299
283
|
return Promise.resolve(true);
|
|
300
284
|
});
|
|
301
285
|
|
|
@@ -655,7 +655,6 @@ describe('PaceAppLayout Security', () => {
|
|
|
655
655
|
// Simulate a component that might try to execute malicious code
|
|
656
656
|
React.useEffect(() => {
|
|
657
657
|
// This would be dangerous in a real scenario
|
|
658
|
-
console.log('Component mounted');
|
|
659
658
|
}, []);
|
|
660
659
|
|
|
661
660
|
return <div data-testid="malicious-component">Malicious Component</div>;
|
|
@@ -99,14 +99,6 @@ vi.mock('../../../hooks/useEventTheme', () => ({
|
|
|
99
99
|
|
|
100
100
|
// Mock the new RBAC system
|
|
101
101
|
const mockIsPermitted = vi.fn().mockImplementation((input) => {
|
|
102
|
-
console.log('[PaceAppLayout] Page access attempt:', {
|
|
103
|
-
pageName: input.pageId || 'unknown',
|
|
104
|
-
operation: input.permission,
|
|
105
|
-
userId: input.userId,
|
|
106
|
-
allowed: true,
|
|
107
|
-
strictMode: true,
|
|
108
|
-
timestamp: new Date().toISOString()
|
|
109
|
-
});
|
|
110
102
|
return Promise.resolve(true);
|
|
111
103
|
});
|
|
112
104
|
|
|
@@ -902,7 +894,6 @@ describe('PaceAppLayout Component', () => {
|
|
|
902
894
|
it('filters navigation when filterNavigationByPermissions is true', async () => {
|
|
903
895
|
// Mock checkPermission to return false for some items
|
|
904
896
|
mockIsPermitted.mockImplementation((input) => {
|
|
905
|
-
console.log('[TEST] mockIsPermitted called with:', input);
|
|
906
897
|
if (input.pageId === 'dashboard') return Promise.resolve(false);
|
|
907
898
|
return Promise.resolve(true);
|
|
908
899
|
});
|
|
@@ -9,6 +9,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
9
9
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
10
10
|
import { PaceLoginPage } from './PaceLoginPage';
|
|
11
11
|
import { renderWithProviders } from '../../__tests__/helpers/test-utils';
|
|
12
|
+
import { Logger, LogLevel } from '../../utils/core/logger';
|
|
12
13
|
|
|
13
14
|
// Mock React Router
|
|
14
15
|
const mockNavigate = vi.fn();
|
|
@@ -83,9 +84,29 @@ import { isSuperAdmin } from '../../rbac/api';
|
|
|
83
84
|
const originalConsoleError = console.error;
|
|
84
85
|
|
|
85
86
|
describe('PaceLoginPage Component', () => {
|
|
86
|
-
|
|
87
|
+
let originalMode: string | undefined;
|
|
88
|
+
|
|
89
|
+
beforeEach(() => {
|
|
90
|
+
// Ensure logger is enabled by setting MODE to development
|
|
91
|
+
originalMode = import.meta.env.MODE;
|
|
92
|
+
(import.meta.env as any).MODE = 'development';
|
|
93
|
+
|
|
94
|
+
// Configure logger to ensure it logs in test environment
|
|
95
|
+
Logger.configure({
|
|
96
|
+
level: LogLevel.DEBUG,
|
|
97
|
+
includeTimestamp: false,
|
|
98
|
+
includeComponent: true,
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
afterEach(() => {
|
|
87
103
|
// Restore console methods
|
|
88
104
|
console.error = originalConsoleError;
|
|
105
|
+
|
|
106
|
+
// Restore original mode
|
|
107
|
+
if (originalMode !== undefined) {
|
|
108
|
+
(import.meta.env as any).MODE = originalMode;
|
|
109
|
+
}
|
|
89
110
|
});
|
|
90
111
|
|
|
91
112
|
// Basic rendering tests
|
|
@@ -330,7 +351,11 @@ describe('PaceLoginPage Component', () => {
|
|
|
330
351
|
await user.click(submitButton);
|
|
331
352
|
|
|
332
353
|
await waitFor(() => {
|
|
333
|
-
|
|
354
|
+
// Logger.error formats the message with component name and log level
|
|
355
|
+
expect(console.error).toHaveBeenCalledWith(
|
|
356
|
+
expect.stringContaining('[PaceLoginPage] Navigation error after sign-in:'),
|
|
357
|
+
expect.any(Error)
|
|
358
|
+
);
|
|
334
359
|
});
|
|
335
360
|
});
|
|
336
361
|
|
|
@@ -387,6 +412,9 @@ describe('PaceLoginPage Component', () => {
|
|
|
387
412
|
const user = userEvent.setup();
|
|
388
413
|
mockAuthContext.signIn.mockRejectedValue(new Error('Network error'));
|
|
389
414
|
|
|
415
|
+
// Mock console.error to catch the error
|
|
416
|
+
console.error = vi.fn();
|
|
417
|
+
|
|
390
418
|
renderWithProviders(<PaceLoginPage appName="Test App" />);
|
|
391
419
|
|
|
392
420
|
const emailInput = screen.getByLabelText('Email');
|
|
@@ -398,7 +426,11 @@ describe('PaceLoginPage Component', () => {
|
|
|
398
426
|
await user.click(submitButton);
|
|
399
427
|
|
|
400
428
|
await waitFor(() => {
|
|
401
|
-
|
|
429
|
+
// Logger.error formats the message with component name and log level
|
|
430
|
+
expect(console.error).toHaveBeenCalledWith(
|
|
431
|
+
expect.stringContaining('[PaceLoginPage] Login error:'),
|
|
432
|
+
expect.any(Error)
|
|
433
|
+
);
|
|
402
434
|
});
|
|
403
435
|
});
|
|
404
436
|
});
|
|
@@ -129,6 +129,7 @@ import { LoginForm } from '../LoginForm';
|
|
|
129
129
|
import { Button, Input, Label } from '..';
|
|
130
130
|
import { clearPalette } from '../../theming/runtime';
|
|
131
131
|
import { EventServiceContext } from '../../providers/services/EventServiceProvider';
|
|
132
|
+
import { logger } from '../../utils/core/logger';
|
|
132
133
|
|
|
133
134
|
export interface PaceLoginPageProps {
|
|
134
135
|
/** The name of the application to be displayed on the login form. */
|
|
@@ -207,7 +208,7 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
207
208
|
}
|
|
208
209
|
} catch (error) {
|
|
209
210
|
// Service may not be available yet or events not loaded - that's okay
|
|
210
|
-
|
|
211
|
+
logger.debug('PaceLoginPage', 'Could not restore persisted event (service may not be ready):', error);
|
|
211
212
|
}
|
|
212
213
|
};
|
|
213
214
|
|
|
@@ -231,13 +232,13 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
231
232
|
|
|
232
233
|
try {
|
|
233
234
|
const userId = user.id;
|
|
234
|
-
|
|
235
|
+
logger.debug('PaceLoginPage', 'Checking app access using RBAC:', { appName, userId });
|
|
235
236
|
|
|
236
237
|
// Step 1: Check if user is super admin (they have unrestricted access)
|
|
237
238
|
const superAdminCheck = await isSuperAdmin(userId);
|
|
238
239
|
|
|
239
240
|
if (superAdminCheck) {
|
|
240
|
-
|
|
241
|
+
logger.debug('PaceLoginPage', 'User is super admin, granting access');
|
|
241
242
|
setIsCheckingAccess(false);
|
|
242
243
|
navigate(onSuccessRedirectPath, { replace: true });
|
|
243
244
|
return;
|
|
@@ -252,7 +253,7 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
252
253
|
.single();
|
|
253
254
|
|
|
254
255
|
if (appError || !appData) {
|
|
255
|
-
|
|
256
|
+
logger.error('PaceLoginPage', 'App not found:', { appName, error: appError });
|
|
256
257
|
setAccessError(`Application "${appName}" is not configured. Please contact your administrator.`);
|
|
257
258
|
setIsCheckingAccess(false);
|
|
258
259
|
return;
|
|
@@ -265,7 +266,7 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
265
266
|
.eq('app_id', appData.id);
|
|
266
267
|
|
|
267
268
|
if (pagesError || !pagesData || pagesData.length === 0) {
|
|
268
|
-
|
|
269
|
+
logger.debug('PaceLoginPage', 'No pages configured for app:', appName);
|
|
269
270
|
setAccessError(`You do not have permission to access ${appName}. This application is currently unavailable. Please contact your administrator if you believe you should have access.`);
|
|
270
271
|
setIsCheckingAccess(false);
|
|
271
272
|
return;
|
|
@@ -282,7 +283,7 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
282
283
|
.single();
|
|
283
284
|
|
|
284
285
|
if (!orgData) {
|
|
285
|
-
|
|
286
|
+
logger.debug('PaceLoginPage', 'User has no organisation access');
|
|
286
287
|
setAccessError(`You do not have permission to access ${appName}. You are not assigned to any organisation. Please contact your administrator.`);
|
|
287
288
|
setIsCheckingAccess(false);
|
|
288
289
|
return;
|
|
@@ -302,7 +303,7 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
302
303
|
p_page_id: page.page_name // Page name to resolve to UUID
|
|
303
304
|
});
|
|
304
305
|
|
|
305
|
-
|
|
306
|
+
logger.debug('PaceLoginPage', 'Permission check for page:', { pageName: page.page_name, hasPermission, error: permError });
|
|
306
307
|
|
|
307
308
|
if (!permError && hasPermission === true) {
|
|
308
309
|
hasAnyAccess = true;
|
|
@@ -311,18 +312,18 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
311
312
|
}
|
|
312
313
|
|
|
313
314
|
if (hasAnyAccess) {
|
|
314
|
-
|
|
315
|
+
logger.debug('PaceLoginPage', 'User has access to app');
|
|
315
316
|
setIsCheckingAccess(false);
|
|
316
317
|
navigate(onSuccessRedirectPath, { replace: true });
|
|
317
318
|
return;
|
|
318
319
|
}
|
|
319
320
|
|
|
320
321
|
// No access - deny
|
|
321
|
-
|
|
322
|
+
logger.debug('PaceLoginPage', 'Access denied - no permissions');
|
|
322
323
|
setAccessError(`You do not have permission to access ${appName}. This application is restricted to authorized users only. Please contact your administrator if you believe you should have access.`);
|
|
323
324
|
setIsCheckingAccess(false);
|
|
324
325
|
} catch (error) {
|
|
325
|
-
|
|
326
|
+
logger.error('PaceLoginPage', 'Error checking app access:', error);
|
|
326
327
|
setAccessError('An error occurred while checking your permissions. Please try again or contact support.');
|
|
327
328
|
setIsCheckingAccess(false);
|
|
328
329
|
}
|
|
@@ -349,7 +350,7 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
349
350
|
try {
|
|
350
351
|
navigate(onSuccessRedirectPath, { replace: true });
|
|
351
352
|
} catch (navError) {
|
|
352
|
-
|
|
353
|
+
logger.error('PaceLoginPage', 'Navigation error after sign-in:', navError);
|
|
353
354
|
}
|
|
354
355
|
}
|
|
355
356
|
} finally {
|
|
@@ -371,7 +372,7 @@ export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
|
371
372
|
isLoading={isSigningIn}
|
|
372
373
|
onError={(error) => {
|
|
373
374
|
// LoginForm will handle display of the error
|
|
374
|
-
|
|
375
|
+
logger.error('PaceLoginPage', 'Login error:', error);
|
|
375
376
|
}}
|
|
376
377
|
/>
|
|
377
378
|
{(() => {
|
|
@@ -101,7 +101,7 @@ import React, { useState } from 'react';
|
|
|
101
101
|
import { Button } from '../Button/Button';
|
|
102
102
|
import { Input } from '../Input/Input';
|
|
103
103
|
import { Label } from '../Label';
|
|
104
|
-
import { cn } from '../../utils/cn';
|
|
104
|
+
import { cn } from '../../utils/core/cn';
|
|
105
105
|
|
|
106
106
|
export interface PasswordChangeFormValues {
|
|
107
107
|
newPassword: string;
|
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
export { PasswordResetForm } from './PasswordResetForm';
|
|
2
|
-
export type { PasswordResetFormProps } from './PasswordResetForm';
|
|
3
1
|
export { PasswordChangeForm } from './PasswordChangeForm';
|
|
4
2
|
export type { PasswordChangeFormProps, PasswordChangeFormValues } from './PasswordChangeForm';
|
|
@@ -98,7 +98,21 @@ describe('ProtectedRoute Component', () => {
|
|
|
98
98
|
isLoading: false,
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
+
let originalMode: string | undefined;
|
|
102
|
+
|
|
101
103
|
beforeEach(() => {
|
|
104
|
+
// Ensure logger is enabled by setting MODE to development
|
|
105
|
+
originalMode = import.meta.env.MODE;
|
|
106
|
+
(import.meta.env as any).MODE = 'development';
|
|
107
|
+
|
|
108
|
+
import('../../utils/core/logger').then(({ Logger, LogLevel }) => {
|
|
109
|
+
Logger.configure({
|
|
110
|
+
level: LogLevel.DEBUG,
|
|
111
|
+
includeTimestamp: false,
|
|
112
|
+
includeComponent: true,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
102
116
|
vi.clearAllMocks();
|
|
103
117
|
console.debug = vi.fn();
|
|
104
118
|
console.warn = vi.fn();
|
|
@@ -112,6 +126,11 @@ describe('ProtectedRoute Component', () => {
|
|
|
112
126
|
afterEach(() => {
|
|
113
127
|
console.debug = originalConsoleDebug;
|
|
114
128
|
console.warn = originalConsoleWarn;
|
|
129
|
+
|
|
130
|
+
// Restore original mode
|
|
131
|
+
if (originalMode !== undefined) {
|
|
132
|
+
(import.meta.env as any).MODE = originalMode;
|
|
133
|
+
}
|
|
115
134
|
});
|
|
116
135
|
|
|
117
136
|
describe('Rendering', () => {
|
|
@@ -124,6 +143,8 @@ describe('ProtectedRoute Component', () => {
|
|
|
124
143
|
});
|
|
125
144
|
|
|
126
145
|
it('renders outlet when events exist but none selected (allows event selector visibility)', () => {
|
|
146
|
+
const consoleDebugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
|
|
147
|
+
|
|
127
148
|
mockUseEvents.mockReturnValue({
|
|
128
149
|
selectedEvent: null,
|
|
129
150
|
events: [{ id: 'event-1', name: 'Test Event' }],
|
|
@@ -133,8 +154,8 @@ describe('ProtectedRoute Component', () => {
|
|
|
133
154
|
renderWithProviders(<ProtectedRoute />);
|
|
134
155
|
|
|
135
156
|
expect(screen.getByTestId('outlet')).toBeInTheDocument();
|
|
136
|
-
expect(
|
|
137
|
-
'[ProtectedRoute] Events available but none selected - allowing render so selector is visible'
|
|
157
|
+
expect(consoleDebugSpy).toHaveBeenCalledWith(
|
|
158
|
+
expect.stringContaining('[DEBUG] [ProtectedRoute] Events available but none selected - allowing render so selector is visible')
|
|
138
159
|
);
|
|
139
160
|
});
|
|
140
161
|
|
|
@@ -222,6 +243,8 @@ describe('ProtectedRoute Component', () => {
|
|
|
222
243
|
});
|
|
223
244
|
|
|
224
245
|
it('warns when redirecting due to session restoration timeout', () => {
|
|
246
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
247
|
+
|
|
225
248
|
mockUseUnifiedAuth.mockReturnValue({
|
|
226
249
|
isAuthenticated: false,
|
|
227
250
|
authLoading: false,
|
|
@@ -236,8 +259,8 @@ describe('ProtectedRoute Component', () => {
|
|
|
236
259
|
|
|
237
260
|
renderWithProviders(<ProtectedRoute />);
|
|
238
261
|
|
|
239
|
-
expect(
|
|
240
|
-
'[ProtectedRoute] Session restoration failed, redirecting to login',
|
|
262
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
|
263
|
+
expect.stringContaining('[WARN] [ProtectedRoute] Session restoration failed, redirecting to login'),
|
|
241
264
|
expect.objectContaining({
|
|
242
265
|
timedOut: true,
|
|
243
266
|
})
|
|
@@ -259,10 +282,12 @@ describe('ProtectedRoute Component', () => {
|
|
|
259
282
|
hasTimedOut: false,
|
|
260
283
|
});
|
|
261
284
|
|
|
285
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
286
|
+
|
|
262
287
|
renderWithProviders(<ProtectedRoute />);
|
|
263
288
|
|
|
264
|
-
expect(
|
|
265
|
-
'[ProtectedRoute] Session restoration failed, redirecting to login',
|
|
289
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
|
290
|
+
expect.stringContaining('[WARN] [ProtectedRoute] Session restoration failed, redirecting to login'),
|
|
266
291
|
expect.objectContaining({
|
|
267
292
|
error: 'Session restoration failed',
|
|
268
293
|
timedOut: false,
|
|
@@ -408,11 +433,13 @@ describe('ProtectedRoute Component', () => {
|
|
|
408
433
|
isLoading: false,
|
|
409
434
|
});
|
|
410
435
|
|
|
436
|
+
const consoleDebugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
|
|
437
|
+
|
|
411
438
|
renderWithProviders(<ProtectedRoute requireEvent={true} />);
|
|
412
439
|
|
|
413
440
|
expect(screen.getByTestId('outlet')).toBeInTheDocument();
|
|
414
|
-
expect(
|
|
415
|
-
'[ProtectedRoute] Events available but none selected - allowing render so selector is visible'
|
|
441
|
+
expect(consoleDebugSpy).toHaveBeenCalledWith(
|
|
442
|
+
expect.stringContaining('[DEBUG] [ProtectedRoute] Events available but none selected - allowing render so selector is visible')
|
|
416
443
|
);
|
|
417
444
|
});
|
|
418
445
|
|
|
@@ -76,6 +76,7 @@ import { useEvents } from '../../hooks/useEvents';
|
|
|
76
76
|
import { LoadingSpinner } from '../LoadingSpinner/LoadingSpinner';
|
|
77
77
|
import { SessionRestorationLoader } from '../SessionRestorationLoader';
|
|
78
78
|
import { Alert, AlertDescription, AlertTitle } from '../Alert/Alert';
|
|
79
|
+
import { logger } from '../../utils/core/logger';
|
|
79
80
|
|
|
80
81
|
export interface ProtectedRouteProps {
|
|
81
82
|
/**
|
|
@@ -175,7 +176,7 @@ export function ProtectedRoute({
|
|
|
175
176
|
// Redirect to login if not authenticated
|
|
176
177
|
if (!isAuthenticated) {
|
|
177
178
|
if (sessionRestoration.hasTimedOut || sessionRestoration.restorationError) {
|
|
178
|
-
|
|
179
|
+
logger.warn('ProtectedRoute', 'Session restoration failed, redirecting to login', {
|
|
179
180
|
timedOut: sessionRestoration.hasTimedOut,
|
|
180
181
|
error: sessionRestoration.restorationError?.message
|
|
181
182
|
});
|
|
@@ -217,7 +218,7 @@ export function ProtectedRoute({
|
|
|
217
218
|
// The event selector will be visible and user can select, or auto-selection will kick in
|
|
218
219
|
if (!selectedEvent) {
|
|
219
220
|
// Log for debugging - this is expected behavior, not an error
|
|
220
|
-
|
|
221
|
+
logger.debug('ProtectedRoute', 'Events available but none selected - allowing render so selector is visible');
|
|
221
222
|
return <Outlet />;
|
|
222
223
|
}
|
|
223
224
|
|