@jmruthers/pace-core 0.5.135 → 0.5.137
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-A36PJG6N.js → DataTable-6M4L6BI2.js} +26 -13
- package/dist/{DataTable-C7GaRZye.d.ts → DataTable-CWAZZcXC.d.ts} +1 -1
- package/dist/{PublicLoadingSpinner-CUAnTvcg.d.ts → EventLogo-rFL_kRjk.d.ts} +123 -135
- package/dist/{UnifiedAuthProvider-BVKmQd9u.d.ts → UnifiedAuthProvider-DJxGTftH.d.ts} +1 -1
- package/dist/{UnifiedAuthProvider-CQDZRJIS.js → UnifiedAuthProvider-XIQQ7LVU.js} +5 -5
- package/dist/{api-TNIBJWLM.js → api-45XYYO2A.js} +4 -3
- package/dist/{audit-T36HM7IM.js → audit-64X3VJXB.js} +3 -2
- package/dist/{chunk-F64FFPOZ.js → chunk-22WKWKRX.js} +26 -20
- package/dist/chunk-22WKWKRX.js.map +1 -0
- package/dist/{chunk-VZ5OR6HD.js → chunk-4C7EXCAR.js} +62 -150
- package/dist/chunk-4C7EXCAR.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-CTJRBUX2.js → chunk-6LAAY47Q.js} +2 -2
- package/dist/{chunk-UJI6WSMD.js → chunk-7QCC6MCP.js} +90 -3
- package/dist/chunk-7QCC6MCP.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-CQZU6TFE.js → chunk-BCIBECNB.js} +100 -62
- package/dist/chunk-BCIBECNB.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-BJPBT3CU.js +21 -0
- package/dist/chunk-BJPBT3CU.js.map +1 -0
- package/dist/{chunk-BYXRHAIF.js → chunk-BLCXZEYF.js} +23 -14
- package/dist/chunk-BLCXZEYF.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-O3NWNXDY.js → chunk-FMUCXFII.js} +2 -2
- package/dist/chunk-FMUCXFII.js.map +1 -0
- package/dist/{chunk-GVDR7WNV.js → chunk-HAWZXGR2.js} +334 -614
- package/dist/chunk-HAWZXGR2.js.map +1 -0
- package/dist/{chunk-ZV77RZMU.js → chunk-INQLMHPF.js} +2 -2
- package/dist/chunk-JISYG63F.js +70 -0
- package/dist/chunk-JISYG63F.js.map +1 -0
- package/dist/{chunk-HMNOSGVA.js → chunk-KYRHUBIU.js} +576 -767
- package/dist/chunk-KYRHUBIU.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-TGIY2AR2.js → chunk-MA6EPSGZ.js} +4 -3
- package/dist/{chunk-TGIY2AR2.js.map → chunk-MA6EPSGZ.js.map} +1 -1
- package/dist/chunk-OWAG3GSU.js +58 -0
- package/dist/chunk-OWAG3GSU.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-ZYZCRSBD.js → chunk-T6JN6LH6.js} +16 -11
- package/dist/chunk-T6JN6LH6.js.map +1 -0
- package/dist/chunk-XDNLUEXI.js +138 -0
- package/dist/chunk-XDNLUEXI.js.map +1 -0
- package/dist/{chunk-3CG5L6RN.js → chunk-YCWDTTUK.js} +90 -75
- package/dist/chunk-YCWDTTUK.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 +27 -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 +21 -16
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +101 -9
- package/dist/index.js +44 -31
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +4 -4
- package/dist/rbac/index.js +12 -12
- package/dist/schema-DTDZQe2u.d.ts +28 -0
- 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 +153 -4
- package/dist/types.js +51 -16
- package/dist/types.js.map +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 +221 -173
- package/dist/utils.js +185 -225
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +24 -115
- package/dist/validation.js +19 -474
- 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/BadgeProps.md +27 -0
- 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 +1 -1
- 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 +1 -1
- package/docs/api/interfaces/ExportOptions.md +8 -8
- 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 +591 -220
- package/docs/api-reference/components.md +106 -26
- package/docs/architecture/README.md +0 -3
- 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/docs/styles/README.md +0 -2
- package/examples/README.md +30 -14
- package/examples/STRUCTURE.md +125 -0
- package/examples/components 2/DataTable/HierarchicalActionsExample.tsx +421 -0
- package/examples/components 2/DataTable/HierarchicalExample.tsx +475 -0
- package/examples/components 2/DataTable/InitialPageSizeExample.tsx +177 -0
- package/examples/components 2/DataTable/PerformanceExample.tsx +506 -0
- package/examples/components 2/DataTable/index.ts +13 -0
- package/examples/components 2/Dialog/BasicHtmlTest.tsx +55 -0
- package/examples/components 2/Dialog/DebugHtmlExample.tsx +68 -0
- package/examples/components 2/Dialog/HtmlDialogExample.tsx +202 -0
- package/examples/components 2/Dialog/ScrollableDialogExample.tsx +290 -0
- package/examples/components 2/Dialog/SimpleHtmlTest.tsx +61 -0
- package/examples/components 2/Dialog/SmartDialogExample.tsx +322 -0
- package/examples/components 2/Dialog/index.ts +15 -0
- package/examples/components 2/index.ts +11 -0
- package/examples/features/index.ts +12 -0
- package/{src/examples → examples/features/public-pages}/CorrectPublicPageImplementation.tsx +14 -17
- package/{src/examples → examples/features/public-pages}/PublicEventPage.tsx +14 -27
- package/{src/examples → examples/features/public-pages}/PublicPageApp.tsx +15 -28
- package/{src/examples → examples/features/public-pages}/PublicPageUsageExample.tsx +8 -10
- package/examples/features/public-pages/index.ts +14 -0
- package/examples/features/rbac/CompleteRBACExample.tsx +324 -0
- package/examples/features/rbac/EventBasedApp.tsx +239 -0
- package/examples/features/rbac/PermissionExample.tsx +151 -0
- package/examples/features/rbac/index.ts +13 -0
- package/examples/index.ts +11 -3
- package/package.json +30 -19
- package/src/__tests__/TEST_STANDARD.md +92 -0
- package/src/components/Alert/Alert.tsx +1 -1
- package/src/components/Avatar/Avatar.tsx +1 -1
- package/src/components/Badge/Badge.test.tsx +314 -0
- package/src/components/Badge/Badge.tsx +304 -0
- package/src/components/Badge/index.ts +3 -0
- 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__/DataTableCore.test-setup.ts +217 -0
- package/src/components/DataTable/__tests__/styles.test.ts +3 -3
- package/src/components/DataTable/components/ActionButtons.tsx +0 -15
- package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
- package/src/components/DataTable/components/DataTableBody.tsx +461 -0
- 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/DraggableColumnHeader.tsx +144 -0
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/FilterRow.tsx +9 -3
- package/src/components/DataTable/components/ImportModal.tsx +2 -14
- package/src/components/DataTable/components/PaginationControls.tsx +2 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +109 -82
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
- 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__/FilterRow.test.tsx +43 -0
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +1 -1
- package/src/components/DataTable/core/ActionManager.ts +235 -0
- package/src/components/DataTable/core/ColumnManager.ts +205 -0
- package/src/components/DataTable/core/DataManager.ts +188 -0
- package/src/components/DataTable/core/DataTableContext.tsx +181 -0
- package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
- package/src/components/DataTable/core/PluginRegistry.ts +229 -0
- package/src/components/DataTable/core/StateManager.ts +311 -0
- package/src/components/DataTable/core/interfaces.ts +338 -0
- 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 +28 -7
- package/src/components/DataTable/types.ts +13 -0
- package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -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/columnUtils.ts +40 -0
- package/src/components/DataTable/utils/debugTools.ts +609 -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 +2 -0
- package/src/components/DataTable/utils/paginationUtils.ts +1 -1
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +8 -1
- 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.test.tsx +1 -1
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +5 -510
- 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.test.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.performance.test.tsx +76 -10
- 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 +14 -13
- 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/__tests__/PublicPageFooter.test.tsx +1 -1
- package/src/components/PublicLayout/index.ts +4 -2
- package/src/components/Select/Select.test.tsx +1 -1
- package/src/components/Select/Select.tsx +21 -9
- 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/Table/__tests__/Table.test.tsx +1 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/index.ts +7 -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__/useFileUrl.unit.test.ts +83 -85
- 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 +20 -7
- 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/styles/core.css +3 -0
- 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/app/appConfig.ts +47 -0
- package/src/utils/app/appIdResolver.test.ts +497 -0
- package/src/utils/app/appIdResolver.ts +133 -0
- package/src/utils/app/appNameResolver.simple.test.ts +212 -0
- package/src/utils/app/appNameResolver.test.ts +121 -0
- package/src/utils/app/appNameResolver.ts +195 -0
- package/src/utils/audit/audit.ts +127 -0
- package/src/utils/context/organisationContext.test.ts +322 -0
- package/src/utils/context/organisationContext.ts +156 -0
- package/src/utils/context/sessionTracking.ts +125 -0
- package/src/utils/core/cn.ts +7 -0
- package/src/utils/core/debugLogger.ts +67 -0
- package/src/utils/core/logger.ts +181 -0
- package/src/utils/device/deviceFingerprint.ts +215 -0
- package/src/utils/dynamic/dynamicUtils.ts +105 -0
- package/src/utils/dynamic/lazyLoad.tsx +44 -0
- package/src/utils/file-reference/__tests__/file-reference.test.ts +788 -0
- package/src/utils/file-reference/index.ts +501 -0
- package/src/utils/formatting/formatDate.test.ts +237 -0
- package/src/utils/formatting/formatting.ts +133 -0
- package/src/utils/index.ts +39 -54
- package/src/utils/performance/bundleAnalysis.ts +129 -0
- package/src/utils/performance/performanceBenchmark.ts +64 -0
- package/src/utils/performance/performanceBudgets.ts +110 -0
- package/src/utils/permissions/permissionTypes.ts +37 -0
- package/src/utils/permissions/permissionUtils.test.ts +393 -0
- package/src/utils/permissions/permissionUtils.ts +34 -0
- package/src/utils/security/auth-utils.ts +96 -0
- package/src/utils/security/secureDataAccess.test.ts +711 -0
- package/src/utils/security/secureDataAccess.ts +377 -0
- package/src/utils/security/secureErrors.ts +82 -0
- package/src/utils/security/secureStorage.ts +244 -0
- package/src/utils/security/security.ts +159 -0
- package/src/utils/security/securityMonitor.ts +45 -0
- package/src/utils/storage/__tests__/helpers.unit.test.ts +1 -4
- package/src/utils/storage/helpers.ts +15 -8
- package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +598 -0
- package/src/{validation → utils/validation}/csrf.ts +1 -1
- package/src/utils/validation/htmlSanitization.ts +184 -0
- package/src/utils/validation/index.ts +79 -0
- package/src/utils/validation/sanitization.ts +333 -0
- package/src/{validation/schemaUtils.ts → utils/validation/schema.ts} +11 -6
- package/src/{validation → utils/validation}/sqlInjectionProtection.ts +2 -0
- package/src/utils/validation/validation.ts +111 -0
- package/src/utils/validation/validationUtils.ts +123 -0
- package/src/validation/index.ts +3 -34
- 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 +0 -87
- 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/dist/validation-DnhrNMju.d.ts +0 -159
- package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +0 -185
- 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-6M4L6BI2.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-CQDZRJIS.js.map → UnifiedAuthProvider-XIQQ7LVU.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-6LAAY47Q.js.map} +0 -0
- /package/dist/{chunk-ZV77RZMU.js.map → chunk-INQLMHPF.js.map} +0 -0
- /package/dist/{useInactivityTracker-MRUU55XI.js.map → useInactivityTracker-TO6ZOF35.js.map} +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
|
@@ -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
|
|
|
@@ -216,6 +216,9 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
216
216
|
);
|
|
217
217
|
|
|
218
218
|
// Performance thresholds
|
|
219
|
+
// NOTE: These thresholds are used for smoke tests to detect significant regressions.
|
|
220
|
+
// They are intentionally generous to avoid flakiness in CI/coverage environments.
|
|
221
|
+
// For more reliable performance testing, prefer behavioral assertions (waitFor) over timing.
|
|
219
222
|
const PERFORMANCE_THRESHOLDS = {
|
|
220
223
|
RENDER_TIME: 200, // ms - Increased due to migration changes requiring more complex organization loading
|
|
221
224
|
PERMISSION_CHECK_TIME: 110, // ms - Increased to account for timing variations
|
|
@@ -223,6 +226,9 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
223
226
|
RE_RENDER_COUNT: 3
|
|
224
227
|
};
|
|
225
228
|
|
|
229
|
+
// Skip performance timing tests in coverage mode (they're unreliable under instrumentation)
|
|
230
|
+
const shouldSkipTimingTests = process.env.COVERAGE === 'true' || process.env.VITEST_COVERAGE;
|
|
231
|
+
|
|
226
232
|
const originalPerformanceMemory = (performance as any).memory;
|
|
227
233
|
|
|
228
234
|
describe('PaceAppLayout Performance', () => {
|
|
@@ -336,7 +342,13 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
336
342
|
});
|
|
337
343
|
|
|
338
344
|
// Since we're only measuring after mount, the time should be very small
|
|
339
|
-
|
|
345
|
+
// Skip timing assertion in coverage mode (unreliable under instrumentation)
|
|
346
|
+
if (!shouldSkipTimingTests) {
|
|
347
|
+
expect(permissionCheckTime).toBeLessThan(PERFORMANCE_THRESHOLDS.PERMISSION_CHECK_TIME);
|
|
348
|
+
} else {
|
|
349
|
+
// In coverage mode, just verify the component rendered successfully
|
|
350
|
+
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
351
|
+
}
|
|
340
352
|
}, { timeout: 6000 });
|
|
341
353
|
|
|
342
354
|
it('handles multiple permission checks efficiently', async () => {
|
|
@@ -381,7 +393,13 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
381
393
|
});
|
|
382
394
|
|
|
383
395
|
// Since we're only measuring after mount, the time should be very small
|
|
384
|
-
|
|
396
|
+
// Skip timing assertion in coverage mode (unreliable under instrumentation)
|
|
397
|
+
if (!shouldSkipTimingTests) {
|
|
398
|
+
expect(permissionCheckTime).toBeLessThan(PERFORMANCE_THRESHOLDS.PERMISSION_CHECK_TIME);
|
|
399
|
+
} else {
|
|
400
|
+
// In coverage mode, just verify the component rendered successfully
|
|
401
|
+
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
402
|
+
}
|
|
385
403
|
}, { timeout: 6000 });
|
|
386
404
|
|
|
387
405
|
it('handles permission check errors efficiently', async () => {
|
|
@@ -427,7 +445,12 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
427
445
|
const memoryIncrease = finalMemory - initialMemory;
|
|
428
446
|
|
|
429
447
|
if (initialMemory > 0 && finalMemory > 0) {
|
|
430
|
-
|
|
448
|
+
// Skip memory assertions in coverage mode (unreliable under instrumentation)
|
|
449
|
+
if (!shouldSkipTimingTests) {
|
|
450
|
+
expect(memoryIncrease).toBeLessThan(PERFORMANCE_THRESHOLDS.MEMORY_USAGE_INCREASE);
|
|
451
|
+
}
|
|
452
|
+
// Always verify component still renders correctly (behavioral check)
|
|
453
|
+
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
431
454
|
}
|
|
432
455
|
});
|
|
433
456
|
|
|
@@ -450,7 +473,12 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
450
473
|
const memoryIncrease = finalMemory - initialMemory;
|
|
451
474
|
|
|
452
475
|
if (initialMemory > 0 && finalMemory > 0) {
|
|
453
|
-
|
|
476
|
+
// Skip memory assertions in coverage mode (unreliable under instrumentation)
|
|
477
|
+
if (!shouldSkipTimingTests) {
|
|
478
|
+
expect(memoryIncrease).toBeLessThan(PERFORMANCE_THRESHOLDS.MEMORY_USAGE_INCREASE);
|
|
479
|
+
}
|
|
480
|
+
// Always verify component still renders correctly (behavioral check)
|
|
481
|
+
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
454
482
|
}
|
|
455
483
|
});
|
|
456
484
|
});
|
|
@@ -482,7 +510,13 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
482
510
|
const totalTime = endTime - startTime;
|
|
483
511
|
const averageTime = totalTime / PERFORMANCE_THRESHOLDS.RE_RENDER_COUNT;
|
|
484
512
|
|
|
485
|
-
|
|
513
|
+
// Skip timing assertion in coverage mode (unreliable under instrumentation)
|
|
514
|
+
if (!shouldSkipTimingTests) {
|
|
515
|
+
expect(averageTime).toBeLessThan(PERFORMANCE_THRESHOLDS.RENDER_TIME);
|
|
516
|
+
} else {
|
|
517
|
+
// In coverage mode, verify component renders successfully (behavioral check)
|
|
518
|
+
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
519
|
+
}
|
|
486
520
|
});
|
|
487
521
|
|
|
488
522
|
it('handles permission enforcement toggles efficiently', async () => {
|
|
@@ -529,7 +563,13 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
529
563
|
return tick;
|
|
530
564
|
});
|
|
531
565
|
|
|
532
|
-
|
|
566
|
+
// Skip timing assertion in coverage mode (unreliable under instrumentation)
|
|
567
|
+
if (!shouldSkipTimingTests) {
|
|
568
|
+
expect(totalTime).toBeLessThan(PERFORMANCE_THRESHOLDS.PERMISSION_CHECK_TIME);
|
|
569
|
+
} else {
|
|
570
|
+
// In coverage mode, verify component rendered successfully (behavioral check)
|
|
571
|
+
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
572
|
+
}
|
|
533
573
|
});
|
|
534
574
|
});
|
|
535
575
|
|
|
@@ -554,7 +594,13 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
554
594
|
const endTime = performance.now();
|
|
555
595
|
const renderTime = endTime - startTime;
|
|
556
596
|
|
|
557
|
-
|
|
597
|
+
// Skip timing assertion in coverage mode (unreliable under instrumentation)
|
|
598
|
+
if (!shouldSkipTimingTests) {
|
|
599
|
+
expect(renderTime).toBeLessThan(PERFORMANCE_THRESHOLDS.RENDER_TIME);
|
|
600
|
+
} else {
|
|
601
|
+
// In coverage mode, verify component renders successfully (behavioral check)
|
|
602
|
+
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
603
|
+
}
|
|
558
604
|
|
|
559
605
|
// Rapidly click navigation buttons
|
|
560
606
|
// If not, this test will fail.
|
|
@@ -642,7 +688,14 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
642
688
|
return tick;
|
|
643
689
|
});
|
|
644
690
|
|
|
645
|
-
|
|
691
|
+
// Skip timing assertion in coverage mode (unreliable under instrumentation)
|
|
692
|
+
if (!shouldSkipTimingTests) {
|
|
693
|
+
expect(authTime).toBeLessThan(PERFORMANCE_THRESHOLDS.PERMISSION_CHECK_TIME);
|
|
694
|
+
} else {
|
|
695
|
+
// In coverage mode, verify actions completed successfully (behavioral check)
|
|
696
|
+
expect(mockSignOut).toHaveBeenCalled();
|
|
697
|
+
expect(mockUpdatePassword).toHaveBeenCalledWith('newpassword123');
|
|
698
|
+
}
|
|
646
699
|
expect(mockSignOut).toHaveBeenCalled();
|
|
647
700
|
expect(mockUpdatePassword).toHaveBeenCalledWith('newpassword123');
|
|
648
701
|
});
|
|
@@ -692,7 +745,14 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
692
745
|
return tick;
|
|
693
746
|
});
|
|
694
747
|
|
|
695
|
-
|
|
748
|
+
// Skip timing assertion in coverage mode (unreliable under instrumentation)
|
|
749
|
+
if (!shouldSkipTimingTests) {
|
|
750
|
+
expect(authTime).toBeLessThan(PERFORMANCE_THRESHOLDS.PERMISSION_CHECK_TIME);
|
|
751
|
+
} else {
|
|
752
|
+
// In coverage mode, verify actions completed successfully (behavioral check)
|
|
753
|
+
expect(mockSignOut).toHaveBeenCalled();
|
|
754
|
+
expect(mockUpdatePassword).toHaveBeenCalledWith('newpassword123');
|
|
755
|
+
}
|
|
696
756
|
});
|
|
697
757
|
});
|
|
698
758
|
|
|
@@ -760,7 +820,13 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
760
820
|
|
|
761
821
|
// Performance threshold adjusted - render time includes async operations and filtering
|
|
762
822
|
// Since enforcePermissions is false, permission checks are minimal, but navigation filtering may be async
|
|
763
|
-
|
|
823
|
+
// Skip timing assertion in coverage mode (unreliable under instrumentation)
|
|
824
|
+
if (!shouldSkipTimingTests) {
|
|
825
|
+
expect(renderTime).toBeLessThan(PERFORMANCE_THRESHOLDS.RENDER_TIME * 3); // Allow more time for complex config
|
|
826
|
+
} else {
|
|
827
|
+
// In coverage mode, verify component renders successfully (behavioral check)
|
|
828
|
+
expect(screen.getByTestId('mock-header')).toBeInTheDocument();
|
|
829
|
+
}
|
|
764
830
|
}, { timeout: 3000 });
|
|
765
831
|
});
|
|
766
832
|
});
|
|
@@ -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. */
|
|
@@ -165,7 +166,7 @@ export interface PaceLoginPageProps {
|
|
|
165
166
|
*/
|
|
166
167
|
export const PaceLoginPage: React.FC<PaceLoginPageProps> = ({
|
|
167
168
|
appName = 'Pace',
|
|
168
|
-
onSuccessRedirectPath = '/',
|
|
169
|
+
onSuccessRedirectPath = '/user-dashboard',
|
|
169
170
|
requireAppAccess = false
|
|
170
171
|
}) => {
|
|
171
172
|
const { signIn, isAuthenticated, isLoading, authError, user, supabase } = useUnifiedAuth();
|
|
@@ -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';
|