@jmruthers/pace-core 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -10
- package/cursor-rules/00-pace-core-compliance.mdc +46 -87
- package/cursor-rules/01-standards-compliance.mdc +16 -47
- package/cursor-rules/02-project-structure.mdc +4 -4
- package/cursor-rules/03-solid-principles.mdc +45 -164
- package/cursor-rules/04-testing-standards.mdc +22 -69
- package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
- package/cursor-rules/06-code-quality.mdc +42 -125
- package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
- package/cursor-rules/08-markup-quality.mdc +452 -0
- package/cursor-rules/CHANGELOG.md +18 -0
- package/cursor-rules/README.md +2 -1
- package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-Cb34EQs3.d.ts} +63 -1
- package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
- package/dist/{DataTable-DQ7RSOHE.js → DataTable-THFPBKTP.js} +12 -10
- package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DEMpysFR.d.ts} +394 -171
- package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +30 -8
- package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-KAGUYQ4J.js} +5 -4
- package/dist/{api-N774RPUA.js → api-IAGWF3ZG.js} +10 -10
- package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
- package/dist/{chunk-JBKQ3SAO.js → chunk-2T2IG7T7.js} +107 -57
- package/dist/chunk-2T2IG7T7.js.map +1 -0
- package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
- package/dist/chunk-6SOIHG6Z.js.map +1 -0
- package/dist/{chunk-3XTALGJF.js → chunk-6Z7LTB3D.js} +69 -240
- package/dist/chunk-6Z7LTB3D.js.map +1 -0
- package/dist/{chunk-4ZC4GX36.js → chunk-CNCQDFLN.js} +199 -46
- package/dist/chunk-CNCQDFLN.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-BYFSK72L.js → chunk-DWUBLJJM.js} +361 -187
- package/dist/chunk-DWUBLJJM.js.map +1 -0
- package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
- package/dist/chunk-FFQEQTNW.js.map +1 -0
- package/dist/chunk-FMUCXFII.js +76 -0
- package/dist/chunk-FMUCXFII.js.map +1 -0
- package/dist/{chunk-4N5C5XZU.js → chunk-HFZBI76P.js} +4 -4
- package/dist/chunk-HFZBI76P.js.map +1 -0
- package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
- package/dist/chunk-L4OXEN46.js.map +1 -0
- package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
- package/dist/chunk-M43Y4SSO.js.map +1 -0
- package/dist/{chunk-I7PSE6JW.js → chunk-M7MPQISP.js} +3 -76
- package/dist/chunk-M7MPQISP.js.map +1 -0
- package/dist/chunk-PQBSKX33.js +7793 -0
- package/dist/chunk-PQBSKX33.js.map +1 -0
- package/dist/chunk-QRPVRXYT.js +226 -0
- package/dist/chunk-QRPVRXYT.js.map +1 -0
- package/dist/{chunk-KNC55RTG.js → chunk-RWEBCB47.js} +194 -416
- package/dist/chunk-RWEBCB47.js.map +1 -0
- package/dist/{chunk-XM25TVIE.js → chunk-YDQHOZNA.js} +843 -388
- package/dist/chunk-YDQHOZNA.js.map +1 -0
- package/dist/{chunk-GLK6VM3F.js → chunk-ZNIWI3UC.js} +739 -737
- package/dist/chunk-ZNIWI3UC.js.map +1 -0
- package/dist/components.d.ts +5 -5
- package/dist/components.js +18 -16
- package/dist/components.js.map +1 -1
- package/dist/contextValidator-3JNZKUTX.js +9 -0
- package/dist/contextValidator-3JNZKUTX.js.map +1 -0
- package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
- package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
- package/dist/hooks.d.ts +55 -122
- package/dist/hooks.js +10 -13
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +60 -13
- package/dist/index.js +30 -25
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +21 -3
- package/dist/providers.js +4 -3
- package/dist/rbac/index.d.ts +210 -139
- package/dist/rbac/index.js +17 -13
- package/dist/styles/index.js +1 -1
- package/dist/theming/runtime.d.ts +1 -13
- package/dist/theming/runtime.js +2 -2
- package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
- package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
- package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +38 -17
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +17 -19
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +21 -17
- package/docs/api/modules.md +4191 -2967
- package/docs/architecture/database-schema-requirements.md +161 -0
- package/docs/components/context-selector.md +126 -0
- package/docs/core-concepts/rbac-system.md +3 -3
- package/docs/documentation-index.md +2 -4
- package/docs/getting-started/cursor-rules.md +2 -1
- package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
- package/docs/migration/MIGRATION_GUIDE.md +2 -24
- package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
- package/docs/migration/README.md +52 -6
- package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
- package/docs/migration/database-changes-december-2025.md +3 -3
- package/docs/pace-mint-fix-auto-selection.md +218 -0
- package/docs/pace-mint-rbac-setup.md +391 -0
- package/docs/rbac/event-based-apps.md +1 -1
- package/docs/rbac/getting-started.md +1 -1
- package/docs/rbac/quick-start.md +1 -1
- package/docs/rbac/secure-client-protection.md +330 -0
- package/docs/standards/README.md +1 -0
- package/package.json +4 -3
- package/scripts/audit/core/checks/accessibility.cjs +197 -0
- package/scripts/audit/core/checks/api-usage.cjs +191 -0
- package/scripts/audit/core/checks/bundle.cjs +142 -0
- package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +784 -685
- package/scripts/audit/core/checks/config.cjs +54 -0
- package/scripts/audit/core/checks/coverage.cjs +84 -0
- package/scripts/audit/core/checks/dependencies.cjs +985 -0
- package/scripts/audit/core/checks/documentation.cjs +268 -0
- package/scripts/audit/core/checks/environment.cjs +116 -0
- package/scripts/audit/core/checks/error-handling.cjs +340 -0
- package/scripts/audit/core/checks/forms.cjs +172 -0
- package/scripts/audit/core/checks/heuristics.cjs +68 -0
- package/scripts/audit/core/checks/hooks.cjs +334 -0
- package/scripts/audit/core/checks/imports.cjs +244 -0
- package/scripts/audit/core/checks/performance.cjs +325 -0
- package/scripts/audit/core/checks/routes.cjs +117 -0
- package/scripts/audit/core/checks/state.cjs +130 -0
- package/scripts/audit/core/checks/structure.cjs +65 -0
- package/scripts/audit/core/checks/style.cjs +584 -0
- package/scripts/audit/core/checks/testing.cjs +122 -0
- package/scripts/audit/core/checks/typescript.cjs +61 -0
- package/scripts/audit/core/scanner.cjs +199 -0
- package/scripts/audit/core/utils.cjs +137 -0
- package/scripts/audit/index.cjs +223 -0
- package/scripts/audit/reporters/console.cjs +151 -0
- package/scripts/audit/reporters/json.cjs +54 -0
- package/scripts/audit/reporters/markdown.cjs +124 -0
- package/scripts/audit-consuming-app.cjs +61 -936
- package/scripts/build-docs/build-decision.js +240 -0
- package/scripts/build-docs/cache-utils.js +105 -0
- package/scripts/build-docs/content-normalization.js +150 -0
- package/scripts/build-docs/file-utils.js +105 -0
- package/scripts/build-docs/git-utils.js +86 -0
- package/scripts/build-docs/hash-utils.js +116 -0
- package/scripts/build-docs/typedoc-runner.js +220 -0
- package/scripts/build-docs-incremental.js +77 -913
- package/scripts/utils/command-runner.js +16 -11
- package/scripts/validate-formats.js +61 -56
- package/scripts/validate-master.js +74 -69
- package/scripts/validate-pre-publish.js +70 -65
- package/src/__tests__/hooks/usePermissions.test.ts +2 -2
- package/src/components/Alert/Alert.test.tsx +12 -18
- package/src/components/Alert/Alert.tsx +5 -7
- package/src/components/Avatar/Avatar.test.tsx +4 -4
- package/src/components/Badge/Badge.tsx +14 -0
- package/src/components/Button/Button.tsx +22 -0
- package/src/components/Calendar/Calendar.tsx +8 -2
- package/src/components/Card/Card.tsx +4 -0
- package/src/components/Checkbox/Checkbox.test.tsx +12 -12
- package/src/components/Checkbox/Checkbox.tsx +2 -2
- package/src/components/ContextSelector/ContextSelector.tsx +384 -0
- package/src/components/ContextSelector/index.ts +3 -0
- package/src/components/DataTable/DataTable.tsx +38 -4
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
- package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
- package/src/components/DataTable/components/ActionButtons.tsx +10 -7
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
- package/src/components/DataTable/components/DataTableBody.tsx +8 -0
- package/src/components/DataTable/components/DataTableCore.tsx +196 -554
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
- package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
- package/src/components/DataTable/components/DataTableModals.tsx +8 -0
- package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
- package/src/components/DataTable/components/EditFields.tsx +307 -0
- package/src/components/DataTable/components/EditableRow.tsx +8 -0
- package/src/components/DataTable/components/EmptyState.tsx +10 -0
- package/src/components/DataTable/components/FilterRow.tsx +12 -0
- package/src/components/DataTable/components/GroupHeader.tsx +12 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
- package/src/components/DataTable/components/ImportModal.tsx +7 -0
- package/src/components/DataTable/components/LoadingState.tsx +6 -0
- package/src/components/DataTable/components/PaginationControls.tsx +16 -1
- package/src/components/DataTable/components/RowComponent.tsx +391 -0
- package/src/components/DataTable/components/UnifiedTableBody.tsx +63 -851
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
- package/src/components/DataTable/components/cellValueUtils.ts +40 -0
- package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
- package/src/components/DataTable/context/DataTableContext.tsx +50 -0
- package/src/components/DataTable/core/ColumnFactory.ts +31 -0
- package/src/components/DataTable/core/DataTableContext.tsx +32 -1
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
- package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +127 -33
- package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
- package/src/components/DataTable/styles.ts +6 -6
- package/src/components/DataTable/types.ts +6 -10
- package/src/components/DataTable/utils/a11yUtils.ts +7 -0
- package/src/components/DataTable/utils/debugTools.ts +18 -113
- package/src/components/DataTable/utils/errorHandling.ts +12 -0
- package/src/components/DataTable/utils/exportUtils.ts +9 -0
- package/src/components/DataTable/utils/flexibleImport.ts +12 -48
- package/src/components/DataTable/utils/paginationUtils.ts +8 -0
- package/src/components/DataTable/utils/performanceUtils.ts +5 -1
- package/src/components/Dialog/Dialog.tsx +31 -3
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
- package/src/components/ErrorBoundary/index.ts +27 -2
- package/src/components/FileDisplay/FileDisplay.tsx +74 -28
- package/src/components/FileUpload/FileUpload.tsx +22 -2
- package/src/components/Footer/Footer.test.tsx +16 -16
- package/src/components/Footer/Footer.tsx +14 -11
- package/src/components/Form/Form.tsx +1 -0
- package/src/components/Header/Header.test.tsx +43 -73
- package/src/components/Header/Header.tsx +59 -49
- package/src/components/Input/Input.test.tsx +2 -2
- package/src/components/Input/Input.tsx +8 -4
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
- package/src/components/LoginForm/LoginForm.tsx +4 -0
- package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
- package/src/components/NavigationMenu/types.ts +56 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +13 -11
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +167 -44
- package/src/components/PaceAppLayout/README.md +14 -17
- package/src/components/PaceAppLayout/test-setup.tsx +3 -4
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
- package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
- package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
- package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
- package/src/components/Select/Select.tsx +80 -434
- package/src/components/Select/context.ts +23 -0
- package/src/components/Select/hooks/useSelectEvents.ts +87 -0
- package/src/components/Select/hooks/useSelectSearch.ts +91 -0
- package/src/components/Select/hooks/useSelectState.ts +104 -0
- package/src/components/Select/index.ts +9 -1
- package/src/components/Select/types.ts +123 -0
- package/src/components/Select/utils/text.ts +26 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
- package/src/components/Switch/Switch.tsx +4 -4
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Toast/Toast.tsx +4 -0
- package/src/components/Tooltip/Tooltip.tsx +2 -2
- package/src/components/UserMenu/UserMenu.test.tsx +24 -11
- package/src/components/UserMenu/UserMenu.tsx +21 -18
- package/src/components/index.ts +7 -7
- package/src/eslint-rules/pace-core-compliance.cjs +106 -0
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
- package/src/hooks/index.ts +1 -2
- package/src/hooks/public/usePublicEvent.ts +4 -0
- package/src/hooks/public/usePublicEventLogo.ts +4 -0
- package/src/hooks/public/usePublicFileDisplay.ts +4 -0
- package/src/hooks/public/usePublicRouteParams.ts +4 -0
- package/src/hooks/services/useAuth.ts +32 -0
- package/src/hooks/services/useCurrentEvent.ts +6 -0
- package/src/hooks/services/useCurrentOrganisation.ts +6 -0
- package/src/hooks/useAppConfig.ts +15 -30
- package/src/hooks/useDebounce.ts +9 -0
- package/src/hooks/useEventTheme.ts +6 -0
- package/src/hooks/useFileDisplay.ts +81 -50
- package/src/hooks/useFileReference.ts +25 -7
- package/src/hooks/useFileUrl.ts +11 -1
- package/src/hooks/useFocusManagement.ts +14 -0
- package/src/hooks/useFocusTrap.ts +3 -0
- package/src/hooks/useInactivityTracker.ts +3 -0
- package/src/hooks/useKeyboardShortcuts.ts +4 -0
- package/src/hooks/useOrganisationPermissions.ts +4 -0
- package/src/hooks/useOrganisationSecurity.ts +4 -0
- package/src/hooks/usePerformanceMonitor.ts +4 -0
- package/src/hooks/usePermissionCache.ts +7 -0
- package/src/hooks/useQueryCache.ts +12 -1
- package/src/hooks/useSessionRestoration.ts +4 -0
- package/src/hooks/useStorage.ts +4 -0
- package/src/hooks/useToast.ts +1 -1
- package/src/index.ts +6 -6
- package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
- package/src/providers/services/AuthServiceProvider.tsx +35 -7
- package/src/providers/services/EventServiceProvider.tsx +51 -5
- package/src/providers/services/InactivityServiceProvider.tsx +18 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +126 -134
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
- package/src/rbac/adapters.tsx +12 -3
- package/src/rbac/api.test.ts +59 -51
- package/src/rbac/api.ts +246 -167
- package/src/rbac/components/NavigationProvider.tsx +4 -1
- package/src/rbac/components/PagePermissionGuard.tsx +185 -17
- package/src/rbac/components/RoleBasedRouter.tsx +5 -1
- package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
- package/src/rbac/components/SecureDataProvider.tsx +20 -5
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
- package/src/rbac/engine.ts +38 -14
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
- package/src/rbac/hooks/permissions/index.ts +7 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
- package/src/rbac/hooks/permissions/useCan.ts +377 -0
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
- package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
- package/src/rbac/hooks/useCan.test.ts +64 -66
- package/src/rbac/hooks/usePermissions.ts +14 -995
- package/src/rbac/hooks/useRBAC.test.ts +1 -5
- package/src/rbac/hooks/useRBAC.ts +36 -37
- package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
- package/src/rbac/hooks/useResolvedScope.ts +35 -40
- package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
- package/src/rbac/hooks/useResourcePermissions.ts +14 -4
- package/src/rbac/hooks/useSecureSupabase.ts +27 -7
- package/src/rbac/index.ts +7 -0
- package/src/rbac/permissions.ts +0 -30
- package/src/rbac/secureClient.test.ts +22 -18
- package/src/rbac/secureClient.ts +294 -68
- package/src/rbac/security.ts +0 -17
- package/src/rbac/types.ts +9 -0
- package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
- package/src/rbac/utils/clientSecurity.ts +93 -0
- package/src/rbac/utils/contextValidator.ts +77 -168
- package/src/services/AuthService.ts +39 -7
- package/src/services/EventService.ts +186 -54
- package/src/services/OrganisationService.ts +81 -14
- package/src/services/__tests__/EventService.test.ts +1 -2
- package/src/services/base/BaseService.ts +3 -0
- package/src/theming/__tests__/parseEventColours.test.ts +6 -9
- package/src/theming/parseEventColours.ts +5 -19
- package/src/types/vitest-globals.d.ts +51 -26
- package/src/utils/__mocks__/supabaseMock.ts +1 -3
- package/src/utils/__tests__/formatting.unit.test.ts +4 -4
- package/src/utils/__tests__/index.unit.test.ts +2 -2
- package/src/utils/audit/audit.ts +0 -3
- package/src/utils/core/cn.ts +1 -1
- package/src/utils/dynamic/dynamicUtils.ts +7 -4
- package/src/utils/file-reference/index.ts +53 -1
- package/src/utils/formatting/formatting.ts +8 -18
- package/src/utils/index.ts +0 -1
- package/dist/chunk-3QRJFVBR.js.map +0 -1
- package/dist/chunk-3XTALGJF.js.map +0 -1
- package/dist/chunk-4N5C5XZU.js.map +0 -1
- package/dist/chunk-4ZC4GX36.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js +0 -38
- package/dist/chunk-BYFSK72L.js.map +0 -1
- package/dist/chunk-EXUD6RNJ.js +0 -451
- package/dist/chunk-EXUD6RNJ.js.map +0 -1
- package/dist/chunk-GLK6VM3F.js.map +0 -1
- package/dist/chunk-I7PSE6JW.js.map +0 -1
- package/dist/chunk-JBKQ3SAO.js.map +0 -1
- package/dist/chunk-KNC55RTG.js.map +0 -1
- package/dist/chunk-LXQLPRQ2.js.map +0 -1
- package/dist/chunk-R77UEZ4E.js.map +0 -1
- package/dist/chunk-SQGMNID3.js.map +0 -1
- package/dist/chunk-T33XF5ZC.js +0 -12922
- package/dist/chunk-T33XF5ZC.js.map +0 -1
- package/dist/chunk-XM25TVIE.js.map +0 -1
- package/docs/api/classes/ColumnFactory.md +0 -243
- package/docs/api/classes/ErrorBoundary.md +0 -144
- package/docs/api/classes/InvalidScopeError.md +0 -73
- package/docs/api/classes/Logger.md +0 -178
- package/docs/api/classes/MissingUserContextError.md +0 -66
- package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
- package/docs/api/classes/PermissionDeniedError.md +0 -73
- package/docs/api/classes/RBACAuditManager.md +0 -297
- package/docs/api/classes/RBACCache.md +0 -322
- package/docs/api/classes/RBACEngine.md +0 -171
- package/docs/api/classes/RBACError.md +0 -76
- package/docs/api/classes/RBACNotInitializedError.md +0 -66
- package/docs/api/classes/SecureSupabaseClient.md +0 -160
- package/docs/api/classes/StorageUtils.md +0 -328
- package/docs/api/enums/FileCategory.md +0 -184
- package/docs/api/enums/LogLevel.md +0 -54
- package/docs/api/enums/RBACErrorCode.md +0 -228
- package/docs/api/enums/RPCFunction.md +0 -118
- package/docs/api/interfaces/AddressFieldProps.md +0 -241
- package/docs/api/interfaces/AddressFieldRef.md +0 -94
- package/docs/api/interfaces/AggregateConfig.md +0 -43
- package/docs/api/interfaces/AutocompleteOptions.md +0 -75
- package/docs/api/interfaces/AvatarProps.md +0 -128
- package/docs/api/interfaces/BadgeProps.md +0 -27
- package/docs/api/interfaces/ButtonProps.md +0 -53
- package/docs/api/interfaces/CalendarProps.md +0 -70
- package/docs/api/interfaces/CardProps.md +0 -66
- package/docs/api/interfaces/ColorPalette.md +0 -7
- package/docs/api/interfaces/ColorShade.md +0 -66
- package/docs/api/interfaces/ComplianceResult.md +0 -30
- package/docs/api/interfaces/DataAccessRecord.md +0 -96
- package/docs/api/interfaces/DataRecord.md +0 -11
- package/docs/api/interfaces/DataTableAction.md +0 -249
- package/docs/api/interfaces/DataTableColumn.md +0 -504
- package/docs/api/interfaces/DataTableProps.md +0 -625
- package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
- package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
- package/docs/api/interfaces/DatabaseIssue.md +0 -41
- package/docs/api/interfaces/EmptyStateConfig.md +0 -61
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
- package/docs/api/interfaces/EventAppRoleData.md +0 -71
- package/docs/api/interfaces/ExportColumn.md +0 -90
- package/docs/api/interfaces/ExportOptions.md +0 -126
- package/docs/api/interfaces/FileDisplayProps.md +0 -249
- package/docs/api/interfaces/FileMetadata.md +0 -129
- package/docs/api/interfaces/FileReference.md +0 -118
- package/docs/api/interfaces/FileSizeLimits.md +0 -7
- package/docs/api/interfaces/FileUploadOptions.md +0 -139
- package/docs/api/interfaces/FileUploadProps.md +0 -293
- package/docs/api/interfaces/FooterProps.md +0 -105
- package/docs/api/interfaces/FormFieldProps.md +0 -166
- package/docs/api/interfaces/FormProps.md +0 -113
- package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
- package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
- package/docs/api/interfaces/InputProps.md +0 -53
- package/docs/api/interfaces/LabelProps.md +0 -107
- package/docs/api/interfaces/LoggerConfig.md +0 -62
- package/docs/api/interfaces/LoginFormProps.md +0 -184
- package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
- package/docs/api/interfaces/NavigationContextType.md +0 -164
- package/docs/api/interfaces/NavigationGuardProps.md +0 -139
- package/docs/api/interfaces/NavigationItem.md +0 -120
- package/docs/api/interfaces/NavigationMenuProps.md +0 -221
- package/docs/api/interfaces/NavigationProviderProps.md +0 -117
- package/docs/api/interfaces/Organisation.md +0 -140
- package/docs/api/interfaces/OrganisationContextType.md +0 -388
- package/docs/api/interfaces/OrganisationMembership.md +0 -140
- package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
- package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
- package/docs/api/interfaces/PaceAppLayoutProps.md +0 -406
- package/docs/api/interfaces/PaceLoginPageProps.md +0 -47
- package/docs/api/interfaces/PageAccessRecord.md +0 -85
- package/docs/api/interfaces/PagePermissionContextType.md +0 -140
- package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
- package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
- package/docs/api/interfaces/PaletteData.md +0 -41
- package/docs/api/interfaces/ParsedAddress.md +0 -120
- package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
- package/docs/api/interfaces/ProgressProps.md +0 -42
- package/docs/api/interfaces/ProtectedRouteProps.md +0 -97
- package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
- package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
- package/docs/api/interfaces/PublicPageLayoutProps.md +0 -198
- package/docs/api/interfaces/QuickFix.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
- package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
- package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
- package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
- package/docs/api/interfaces/RBACConfig.md +0 -133
- package/docs/api/interfaces/RBACContext.md +0 -52
- package/docs/api/interfaces/RBACLogger.md +0 -112
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
- package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
- package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
- package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
- package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
- package/docs/api/interfaces/RBACResult.md +0 -58
- package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
- package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
- package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
- package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
- package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
- package/docs/api/interfaces/RBACRolesListParams.md +0 -52
- package/docs/api/interfaces/RBACRolesListResult.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
- package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
- package/docs/api/interfaces/ResourcePermissions.md +0 -155
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
- package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
- package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
- package/docs/api/interfaces/RoleManagementResult.md +0 -52
- package/docs/api/interfaces/RouteAccessRecord.md +0 -107
- package/docs/api/interfaces/RouteConfig.md +0 -134
- package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
- package/docs/api/interfaces/SecureDataContextType.md +0 -168
- package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
- package/docs/api/interfaces/SetupIssue.md +0 -41
- package/docs/api/interfaces/StorageConfig.md +0 -41
- package/docs/api/interfaces/StorageFileInfo.md +0 -74
- package/docs/api/interfaces/StorageFileMetadata.md +0 -151
- package/docs/api/interfaces/StorageListOptions.md +0 -99
- package/docs/api/interfaces/StorageListResult.md +0 -41
- package/docs/api/interfaces/StorageUploadOptions.md +0 -101
- package/docs/api/interfaces/StorageUploadResult.md +0 -63
- package/docs/api/interfaces/StorageUrlOptions.md +0 -60
- package/docs/api/interfaces/StyleImport.md +0 -19
- package/docs/api/interfaces/SwitchProps.md +0 -34
- package/docs/api/interfaces/TabsContentProps.md +0 -9
- package/docs/api/interfaces/TabsListProps.md +0 -9
- package/docs/api/interfaces/TabsProps.md +0 -9
- package/docs/api/interfaces/TabsTriggerProps.md +0 -50
- package/docs/api/interfaces/TextareaProps.md +0 -53
- package/docs/api/interfaces/ToastActionElement.md +0 -9
- package/docs/api/interfaces/ToastProps.md +0 -9
- package/docs/api/interfaces/UnifiedAuthContextType.md +0 -820
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -171
- package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
- package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -136
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -81
- package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
- package/docs/api/interfaces/UsePublicEventReturn.md +0 -68
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -120
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -94
- package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
- package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
- package/docs/api/interfaces/UserEventAccess.md +0 -118
- package/docs/api/interfaces/UserMenuProps.md +0 -86
- package/docs/api/interfaces/UserProfile.md +0 -63
- package/docs/migration/quick-migration-guide.md +0 -356
- package/docs/migration/service-architecture.md +0 -281
- package/src/components/EventSelector/EventSelector.test.tsx +0 -720
- package/src/components/EventSelector/EventSelector.tsx +0 -420
- package/src/components/EventSelector/index.ts +0 -3
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -324
- package/src/components/OrganisationSelector/index.ts +0 -9
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
- package/src/hooks/useSecureDataAccess.test.ts +0 -559
- package/src/hooks/useSecureDataAccess.ts +0 -681
- /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-THFPBKTP.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-KAGUYQ4J.js.map} +0 -0
- /package/dist/{api-N774RPUA.js.map → api-IAGWF3ZG.js.map} +0 -0
- /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
- /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
- /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
- /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
- /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
|
@@ -121,15 +121,21 @@ import type { PasswordChangeFormError } from '../PasswordChange/PasswordChangeFo
|
|
|
121
121
|
// Define Operation type locally since old RBAC types are removed
|
|
122
122
|
type Operation = 'read' | 'create' | 'update' | 'delete' | 'manage';
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Props for the PaceAppLayout component.
|
|
126
|
+
* Configures the application layout including navigation, header, and footer.
|
|
127
|
+
*/
|
|
124
128
|
export interface PaceAppLayoutProps {
|
|
125
129
|
/** The name of the application to be displayed in the header. */
|
|
126
130
|
appName: string;
|
|
127
131
|
/** Optional navigation items for the header menu. If not provided, uses default navigation. */
|
|
128
132
|
navItems?: NavigationItem[];
|
|
129
|
-
/** Show/hide
|
|
130
|
-
|
|
131
|
-
/** Show
|
|
132
|
-
|
|
133
|
+
/** Show/hide unified context selector (shows all accessible orgs and events) - default: true */
|
|
134
|
+
showContextSelector?: boolean;
|
|
135
|
+
/** Show organisations in context selector - default: true */
|
|
136
|
+
showOrganisations?: boolean;
|
|
137
|
+
/** Show events in context selector - default: true */
|
|
138
|
+
showEvents?: boolean;
|
|
133
139
|
/** Custom actions to display in the header (between event selector and user menu) */
|
|
134
140
|
headerActions?: React.ReactNode;
|
|
135
141
|
/** Custom logo component (overrides default logo) */
|
|
@@ -345,8 +351,9 @@ export interface PaceAppLayoutProps {
|
|
|
345
351
|
export function PaceAppLayout({
|
|
346
352
|
appName,
|
|
347
353
|
navItems,
|
|
348
|
-
|
|
349
|
-
|
|
354
|
+
showContextSelector = true,
|
|
355
|
+
showOrganisations = true,
|
|
356
|
+
showEvents = true,
|
|
350
357
|
headerActions,
|
|
351
358
|
customLogo,
|
|
352
359
|
logoHref = '/dashboard',
|
|
@@ -524,12 +531,15 @@ export function PaceAppLayout({
|
|
|
524
531
|
// Pass appName to useCan so it can be passed to isPermitted for PORTAL/ADMIN special case
|
|
525
532
|
// Only check permissions if enforcePermissions is true and we have a valid permission string
|
|
526
533
|
const shouldCheckPermission = enforcePermissions && !!currentPermission && !!currentPageId;
|
|
534
|
+
// Pass super admin status to avoid duplicate check - use null if still checking, false/true if known
|
|
535
|
+
const superAdminStatus = isSuperAdminFromRBAC ? true : (isCheckingSuperAdminDirect ? null : isSuperAdminDirect);
|
|
527
536
|
const { can: canFromHook, isLoading: isCheckingPermission, error: permissionError } = useCan(
|
|
528
537
|
user?.id || '',
|
|
529
538
|
scope,
|
|
530
539
|
shouldCheckPermission ? currentPermission : ('' as Permission),
|
|
531
540
|
shouldCheckPermission ? currentPageId : '',
|
|
532
541
|
true, // useCache
|
|
542
|
+
superAdminStatus, // Pass super admin status to avoid duplicate check
|
|
533
543
|
appName // Pass appName for PORTAL/ADMIN special case
|
|
534
544
|
);
|
|
535
545
|
|
|
@@ -607,9 +617,9 @@ export function PaceAppLayout({
|
|
|
607
617
|
const hasOrganisationContext = currentScope.organisationId;
|
|
608
618
|
const hasUser = !!user?.id;
|
|
609
619
|
|
|
610
|
-
//
|
|
611
|
-
//
|
|
612
|
-
// This prevents navigation from disappearing after initial render while waiting for
|
|
620
|
+
// Show navigation optimistically while waiting for context selection.
|
|
621
|
+
// Only hide navigation if user is not loaded.
|
|
622
|
+
// This prevents navigation from disappearing after initial render while waiting for context.
|
|
613
623
|
if (!hasUser) {
|
|
614
624
|
// User not loaded yet - show all items until user is ready
|
|
615
625
|
if (isMounted) {
|
|
@@ -618,9 +628,9 @@ export function PaceAppLayout({
|
|
|
618
628
|
return;
|
|
619
629
|
}
|
|
620
630
|
|
|
621
|
-
// If no organisation context yet, show items optimistically
|
|
622
|
-
// This allows users to see navigation while they select
|
|
623
|
-
// Only proceed with permission filtering once
|
|
631
|
+
// If no organisation context yet, show items optimistically
|
|
632
|
+
// This allows users to see navigation while they select a context
|
|
633
|
+
// Only proceed with permission filtering once context is selected
|
|
624
634
|
if (!hasOrganisationContext) {
|
|
625
635
|
if (isMounted) {
|
|
626
636
|
// Show items optimistically when org selector is enabled, otherwise show all items
|
|
@@ -675,32 +685,130 @@ export function PaceAppLayout({
|
|
|
675
685
|
scope: permissionScope,
|
|
676
686
|
});
|
|
677
687
|
|
|
678
|
-
// Filter items using the permission map
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
688
|
+
// Filter items using the permission map and scope type
|
|
689
|
+
// First, get scope types for all pages (batch if possible, or individual)
|
|
690
|
+
const { getPageScopeType } = await import('../../rbac/api');
|
|
691
|
+
const effectiveAppId = currentScope.appId || resolvedAppId;
|
|
692
|
+
const effectiveAppName = appName;
|
|
693
|
+
|
|
694
|
+
// Determine current context type for scope filtering
|
|
695
|
+
const hasEventContext = !!currentScope.eventId;
|
|
696
|
+
const hasOrgContext = !!currentScope.organisationId;
|
|
697
|
+
|
|
698
|
+
// Filter items using both permission map and scope type
|
|
699
|
+
const filtered = await Promise.all(
|
|
700
|
+
baseMenuItems.map(async (item) => {
|
|
701
|
+
if (!item.href) return { item, hasAccess: true, matchesScope: true, scopeCheckError: false };
|
|
702
|
+
|
|
703
|
+
// Extract page ID from href: remove leading slash, fallback to 'dashboard' for root
|
|
704
|
+
// This matches database page names in rbac_app_pages
|
|
705
|
+
const pageId = pageIdMapping[item.href] || (item.href === '/' ? 'dashboard' : item.href.slice(1)) || 'dashboard';
|
|
706
|
+
const permission = routePermissions[item.href] || defaultPermission;
|
|
707
|
+
const fullPermission: Permission = permission.includes(':')
|
|
708
|
+
? (permission as Permission)
|
|
709
|
+
: (pageId ? `${permission}:page.${pageId}` : permission) as Permission;
|
|
710
|
+
|
|
711
|
+
// Check permission map (super admin check already handled in getPermissionMap)
|
|
712
|
+
const hasAccess = permissionMap['*'] === true || permissionMap[fullPermission] === true;
|
|
713
|
+
|
|
714
|
+
// Check scope type compatibility
|
|
715
|
+
let matchesScope = true;
|
|
716
|
+
let scopeCheckError = false;
|
|
717
|
+
if (effectiveAppId || effectiveAppName) {
|
|
718
|
+
try {
|
|
719
|
+
const pageScopeType = await getPageScopeType(
|
|
720
|
+
pageId,
|
|
721
|
+
effectiveAppId,
|
|
722
|
+
effectiveAppName
|
|
723
|
+
);
|
|
724
|
+
|
|
725
|
+
// Filter based on current context:
|
|
726
|
+
// - If event is selected: show only 'event' or 'both' pages
|
|
727
|
+
// - If only org is selected: show only 'organisation' or 'both' pages
|
|
728
|
+
if (hasEventContext) {
|
|
729
|
+
// Event context: show 'event' or 'both' pages only
|
|
730
|
+
matchesScope = pageScopeType === 'event' || pageScopeType === 'both';
|
|
731
|
+
} else if (hasOrgContext) {
|
|
732
|
+
// Organisation context only: show 'organisation' or 'both' pages only
|
|
733
|
+
matchesScope = pageScopeType === 'organisation' || pageScopeType === 'both';
|
|
734
|
+
} else {
|
|
735
|
+
// No context: show all pages (shouldn't happen, but safe fallback)
|
|
736
|
+
matchesScope = true;
|
|
737
|
+
}
|
|
738
|
+
} catch (error) {
|
|
739
|
+
// If we can't get scope type, allow the page (graceful degradation)
|
|
740
|
+
// This prevents navigation from disappearing if there's a database issue
|
|
741
|
+
scopeCheckError = true;
|
|
742
|
+
logger.warn('PaceAppLayout', 'Failed to get page scope type for navigation filtering', {
|
|
743
|
+
pageId,
|
|
744
|
+
href: item.href,
|
|
745
|
+
contextType: hasEventContext ? 'event' : (hasOrgContext ? 'organisation' : 'none'),
|
|
746
|
+
error: error instanceof Error ? error.message : String(error),
|
|
747
|
+
note: 'Allowing page to prevent navigation from disappearing - this may indicate missing scope_type in database'
|
|
748
|
+
});
|
|
749
|
+
matchesScope = true; // Default to allowing if we can't check
|
|
750
|
+
}
|
|
751
|
+
} else {
|
|
752
|
+
// No appId/appName available - can't check scope, allow the page
|
|
753
|
+
// This happens during initial load before app context is resolved
|
|
754
|
+
matchesScope = true;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
return { item, hasAccess, matchesScope, scopeCheckError };
|
|
758
|
+
})
|
|
759
|
+
);
|
|
695
760
|
|
|
696
761
|
if (!isMounted) return;
|
|
697
762
|
|
|
698
763
|
const accessibleItems = filtered
|
|
699
|
-
.filter(({ hasAccess }) => hasAccess)
|
|
764
|
+
.filter(({ hasAccess, matchesScope }) => hasAccess && matchesScope)
|
|
700
765
|
.map(({ item }) => item);
|
|
701
766
|
|
|
767
|
+
// If all items were filtered out, check if it's due to scope filtering
|
|
768
|
+
// This can happen if:
|
|
769
|
+
// 1. All pages are scoped for the other context type (expected behavior)
|
|
770
|
+
// 2. Scope type lookup failed for all pages (should fallback to showing items with permissions)
|
|
771
|
+
if (accessibleItems.length === 0 && filtered.length > 0) {
|
|
772
|
+
const itemsWithPermissions = filtered.filter(({ hasAccess }) => hasAccess);
|
|
773
|
+
const itemsFilteredByScope = filtered.filter(({ hasAccess, matchesScope }) => hasAccess && !matchesScope);
|
|
774
|
+
const itemsWithScopeErrors = filtered.filter(({ hasAccess, scopeCheckError }) => hasAccess && scopeCheckError);
|
|
775
|
+
|
|
776
|
+
// If scope checking failed for ALL items with permissions, fall back to showing them
|
|
777
|
+
// This prevents navigation from disappearing due to database/API issues
|
|
778
|
+
if (itemsWithPermissions.length > 0 &&
|
|
779
|
+
itemsWithScopeErrors.length === itemsWithPermissions.length) {
|
|
780
|
+
logger.warn('PaceAppLayout', 'Scope checking failed for all items - falling back to permission-only filtering', {
|
|
781
|
+
contextType: hasEventContext ? 'event' : (hasOrgContext ? 'organisation' : 'none'),
|
|
782
|
+
totalItems: baseMenuItems.length,
|
|
783
|
+
itemsWithPermissions: itemsWithPermissions.length,
|
|
784
|
+
scopeCheckErrorCount: itemsWithScopeErrors.length,
|
|
785
|
+
note: 'Showing items based on permissions only - scope filtering disabled due to errors. This may indicate database issues or missing scope_type values.'
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
// Fall back to showing items that have permissions (ignore scope check)
|
|
789
|
+
const fallbackItems = filtered
|
|
790
|
+
.filter(({ hasAccess }) => hasAccess)
|
|
791
|
+
.map(({ item }) => item);
|
|
792
|
+
|
|
793
|
+
if (isMounted && fallbackItems.length > 0) {
|
|
794
|
+
setFilteredMenuItems(fallbackItems);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
} else if (itemsWithPermissions.length > 0 && itemsFilteredByScope.length === itemsWithPermissions.length) {
|
|
798
|
+
// All items were filtered by scope (not errors) - this is expected if all pages are scoped for other context
|
|
799
|
+
logger.info('PaceAppLayout', 'All navigation items filtered out by scope type', {
|
|
800
|
+
contextType: hasEventContext ? 'event' : (hasOrgContext ? 'organisation' : 'none'),
|
|
801
|
+
totalItems: baseMenuItems.length,
|
|
802
|
+
itemsWithPermissions: itemsWithPermissions.length,
|
|
803
|
+
itemsFilteredByScope: itemsFilteredByScope.length,
|
|
804
|
+
note: 'All pages appear to be scoped for a different context type - this is expected behavior. Navigation will be empty until user selects the appropriate context.'
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
702
809
|
// SECURITY: Never show all items if permission check fails - this would be a security risk
|
|
703
810
|
// If all items are filtered out, it means the user doesn't have permission to see any navigation
|
|
811
|
+
// OR all pages are scoped for a different context type
|
|
704
812
|
// This is the correct behavior - better to show nothing than show unauthorized items
|
|
705
813
|
setFilteredMenuItems(accessibleItems);
|
|
706
814
|
} catch (error) {
|
|
@@ -718,7 +826,7 @@ export function PaceAppLayout({
|
|
|
718
826
|
return () => {
|
|
719
827
|
isMounted = false;
|
|
720
828
|
};
|
|
721
|
-
}, [baseMenuItems, pageIdMapping, routePermissions, defaultPermission, can, user?.id, scope, scopeLoading, contextAppId, resolvedScope?.appId, selectedOrganisation?.id]);
|
|
829
|
+
}, [baseMenuItems, pageIdMapping, routePermissions, defaultPermission, can, user?.id, scope, scopeLoading, contextAppId, resolvedScope?.appId, selectedOrganisation?.id, selectedEvent?.event_id, appName]);
|
|
722
830
|
|
|
723
831
|
// NEW: Phase 2 - Enhanced Routing Features
|
|
724
832
|
// Check route access for role-based routing
|
|
@@ -817,25 +925,39 @@ export function PaceAppLayout({
|
|
|
817
925
|
}, [roleBasedRouting, routeConfig, location.pathname, strictMode, user?.id, fallbackRoute, scope, navigate, auditLog, onRouteAccessDenied, onRouteStrictModeViolation]);
|
|
818
926
|
|
|
819
927
|
const handleSignOut = async () => {
|
|
820
|
-
|
|
928
|
+
try {
|
|
929
|
+
await signOut();
|
|
930
|
+
} catch (error) {
|
|
931
|
+
logger.error('PaceAppLayout', 'Failed to sign out', { error: error instanceof Error ? error.message : String(error) });
|
|
932
|
+
}
|
|
821
933
|
};
|
|
822
934
|
|
|
823
935
|
const handleChangePassword = async (newPassword: string, confirmPassword: string) => {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
936
|
+
try {
|
|
937
|
+
// The form component in UserMenu already checks for matching passwords
|
|
938
|
+
const result = await updatePassword(newPassword);
|
|
939
|
+
if (result?.error) {
|
|
940
|
+
// The form will display the error message
|
|
941
|
+
logger.error('PaceAppLayout', 'Failed to change password', { error: result.error.message });
|
|
942
|
+
// Convert AuthError to PasswordChangeFormError
|
|
943
|
+
return {
|
|
944
|
+
error: {
|
|
945
|
+
message: result.error.message,
|
|
946
|
+
code: result.error.name || 'PASSWORD_UPDATE_ERROR'
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
// The form will handle closing the modal on success
|
|
951
|
+
return {};
|
|
952
|
+
} catch (error) {
|
|
953
|
+
logger.error('PaceAppLayout', 'Failed to change password', { error: error instanceof Error ? error.message : String(error) });
|
|
830
954
|
return {
|
|
831
955
|
error: {
|
|
832
|
-
message:
|
|
833
|
-
code:
|
|
956
|
+
message: error instanceof Error ? error.message : 'An unexpected error occurred',
|
|
957
|
+
code: 'PASSWORD_UPDATE_ERROR'
|
|
834
958
|
}
|
|
835
959
|
};
|
|
836
960
|
}
|
|
837
|
-
// The form will handle closing the modal on success
|
|
838
|
-
return {};
|
|
839
961
|
};
|
|
840
962
|
|
|
841
963
|
// CRITICAL: Wait for organisation context to be ready before proceeding
|
|
@@ -953,8 +1075,9 @@ export function PaceAppLayout({
|
|
|
953
1075
|
navigate(item.href);
|
|
954
1076
|
}
|
|
955
1077
|
}}
|
|
956
|
-
|
|
957
|
-
|
|
1078
|
+
showContextSelector={showContextSelector}
|
|
1079
|
+
showOrganisations={showOrganisations}
|
|
1080
|
+
showEvents={showEvents}
|
|
958
1081
|
showUserMenu={showUserMenu}
|
|
959
1082
|
className={headerClassName || "sticky top-0 z-[40] w-full"}
|
|
960
1083
|
/>
|
|
@@ -27,8 +27,7 @@ A comprehensive application layout component that provides a consistent structur
|
|
|
27
27
|
|------|------|---------|-------------|
|
|
28
28
|
| `appName` | `string` | required | The name of the application to be displayed in the header |
|
|
29
29
|
| `navItems` | `NavigationItem[]` | optional | Navigation items for the header menu. If not provided, uses default navigation |
|
|
30
|
-
| `
|
|
31
|
-
| `showOrgSelector` | `boolean` | `false` | Show/hide organisation selector in the header |
|
|
30
|
+
| `showContextSelector` | `boolean` | `true` | Show/hide unified context selector (organisations and events) in the header |
|
|
32
31
|
| `headerActions` | `React.ReactNode` | optional | Custom actions to display in the header (between event selector and user menu) |
|
|
33
32
|
| `customLogo` | `React.ReactNode` | optional | Custom logo component (overrides default logo) |
|
|
34
33
|
| `logoHref` | `string` | `'/dashboard'` | URL to navigate to when logo is clicked (e.g., '/dashboard', '/home') |
|
|
@@ -162,7 +161,7 @@ function TeamApp() {
|
|
|
162
161
|
<PaceAppLayout
|
|
163
162
|
appName="TEAM"
|
|
164
163
|
navItems={teamNavItems}
|
|
165
|
-
|
|
164
|
+
showContextSelector={false} // Hide context selector
|
|
166
165
|
/>
|
|
167
166
|
}>
|
|
168
167
|
<Route index element={<ProfilePage />} />
|
|
@@ -231,26 +230,24 @@ function App() {
|
|
|
231
230
|
|
|
232
231
|
## When to Use Each Feature
|
|
233
232
|
|
|
234
|
-
### Show
|
|
233
|
+
### Show Context Selector (`showContextSelector={true}`) - Default
|
|
235
234
|
- Event management applications
|
|
236
235
|
- Apps with multi-tenant event contexts
|
|
237
|
-
-
|
|
236
|
+
- Multi-organisation applications
|
|
237
|
+
- Applications where users need to switch between events or organisations
|
|
238
238
|
- Event planning tools
|
|
239
239
|
- Event registration systems
|
|
240
|
+
- Hybrid apps (like pace-mint) that support both event and organisation contexts
|
|
241
|
+
- Organisation management tools
|
|
242
|
+
- Multi-tenant applications with organisation context
|
|
243
|
+
- Apps requiring organisation-scoped data access
|
|
240
244
|
|
|
241
|
-
### Hide
|
|
245
|
+
### Hide Context Selector (`showContextSelector={false}`)
|
|
242
246
|
- Personal profile management apps (like TEAM)
|
|
243
247
|
- User account settings applications
|
|
244
|
-
-
|
|
248
|
+
- Apps that don't require event or organisation context
|
|
245
249
|
- Single-tenant applications
|
|
246
|
-
- Administrative dashboards that don't depend on events
|
|
247
|
-
|
|
248
|
-
### Show Organisation Selector (`showOrgSelector={true}`)
|
|
249
|
-
- Multi-organisation applications
|
|
250
|
-
- Apps where users need to switch between organisations
|
|
251
|
-
- Organisation management tools
|
|
252
|
-
- Multi-tenant applications with organisation context
|
|
253
|
-
- Apps requiring organisation-scoped data access
|
|
250
|
+
- Administrative dashboards that don't depend on events or organisations
|
|
254
251
|
|
|
255
252
|
### Custom Header Components
|
|
256
253
|
- Applications requiring custom branding
|
|
@@ -267,7 +264,7 @@ function App() {
|
|
|
267
264
|
|
|
268
265
|
## Integration with UnifiedAuthProvider
|
|
269
266
|
|
|
270
|
-
The PaceAppLayout works seamlessly with the UnifiedAuthProvider. When `
|
|
267
|
+
The PaceAppLayout works seamlessly with the UnifiedAuthProvider. When `showContextSelector={false}`, the provider will:
|
|
271
268
|
|
|
272
269
|
- Still handle user authentication
|
|
273
270
|
- Provide user profile access
|
|
@@ -285,7 +282,7 @@ function App() {
|
|
|
285
282
|
<Route path="/" element={
|
|
286
283
|
<PaceAppLayout
|
|
287
284
|
appName="TEAM"
|
|
288
|
-
|
|
285
|
+
showContextSelector={false} // No context selection needed
|
|
289
286
|
/>
|
|
290
287
|
}>
|
|
291
288
|
<Route index element={<ProfilePage />} />
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="vitest/globals" />
|
|
1
2
|
/**
|
|
2
3
|
* @file Shared Test Setup for PaceAppLayout Tests
|
|
3
4
|
* @package @jmruthers/pace-core
|
|
@@ -10,8 +11,6 @@
|
|
|
10
11
|
* provides the shared mock data and reset functions that can be imported and used
|
|
11
12
|
* in the vi.mock() factory functions.
|
|
12
13
|
*/
|
|
13
|
-
|
|
14
|
-
import { vi } from 'vitest';
|
|
15
14
|
import React from 'react';
|
|
16
15
|
|
|
17
16
|
// === MOCK DATA ===
|
|
@@ -118,7 +117,7 @@ export const createMockHeader = () => ({
|
|
|
118
117
|
onChangePassword,
|
|
119
118
|
currentPath,
|
|
120
119
|
onNavigate,
|
|
121
|
-
|
|
120
|
+
showContextSelector,
|
|
122
121
|
showUserMenu,
|
|
123
122
|
className
|
|
124
123
|
}: any) => (
|
|
@@ -157,7 +156,7 @@ export const createMockHeader = () => ({
|
|
|
157
156
|
Navigate
|
|
158
157
|
</button>
|
|
159
158
|
<div data-testid="current-path">{currentPath}</div>
|
|
160
|
-
<div data-testid="show-
|
|
159
|
+
<div data-testid="show-context-selector">{showContextSelector !== false ? 'true' : 'false'}</div>
|
|
161
160
|
</header>
|
|
162
161
|
)
|
|
163
162
|
});
|
|
@@ -131,6 +131,9 @@ import { clearPalette } from '../../theming/runtime';
|
|
|
131
131
|
import { EventServiceContext } from '../../providers/services/EventServiceProvider';
|
|
132
132
|
import { logger } from '../../utils/core/logger';
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Props for the PaceLoginPage component.
|
|
136
|
+
*/
|
|
134
137
|
export interface PaceLoginPageProps {
|
|
135
138
|
/** The name of the application to be displayed on the login form. */
|
|
136
139
|
appName: string;
|
|
@@ -103,16 +103,25 @@ import { Input } from '../Input/Input';
|
|
|
103
103
|
import { Label } from '../Label';
|
|
104
104
|
import { cn } from '../../utils/core/cn';
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Form values for password change.
|
|
108
|
+
*/
|
|
106
109
|
export interface PasswordChangeFormValues {
|
|
107
110
|
newPassword: string;
|
|
108
111
|
confirmPassword: string;
|
|
109
112
|
}
|
|
110
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Error structure for password change form.
|
|
116
|
+
*/
|
|
111
117
|
export interface PasswordChangeFormError {
|
|
112
118
|
message?: string;
|
|
113
119
|
code?: string;
|
|
114
120
|
}
|
|
115
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Props for the PasswordChangeForm component.
|
|
124
|
+
*/
|
|
116
125
|
export interface PasswordChangeFormProps {
|
|
117
126
|
onSubmit: (values: PasswordChangeFormValues) => Promise<{ error?: PasswordChangeFormError }>;
|
|
118
127
|
className?: string;
|
|
@@ -79,6 +79,9 @@ import { Alert, AlertDescription, AlertTitle } from '../Alert/Alert';
|
|
|
79
79
|
import { logger } from '../../utils/core/logger';
|
|
80
80
|
import { usePreventTabReload } from '../../hooks/usePreventTabReload';
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Props for the ProtectedRoute component.
|
|
84
|
+
*/
|
|
82
85
|
export interface ProtectedRouteProps {
|
|
83
86
|
/**
|
|
84
87
|
* Whether an event is required for routes inside this component.
|
|
@@ -88,14 +91,6 @@ export interface ProtectedRouteProps {
|
|
|
88
91
|
*/
|
|
89
92
|
requireEvent?: boolean;
|
|
90
93
|
|
|
91
|
-
/**
|
|
92
|
-
* Whether super admins can bypass event requirement.
|
|
93
|
-
* Note: This feature requires additional RBAC setup. For simple bypass, set requireEvent={false} instead.
|
|
94
|
-
* @default false
|
|
95
|
-
* @deprecated Use requireEvent={false} for routes that don't need events
|
|
96
|
-
*/
|
|
97
|
-
allowSuperAdminBypass?: boolean;
|
|
98
|
-
|
|
99
94
|
/**
|
|
100
95
|
* Custom component to render when no events are available.
|
|
101
96
|
* If not provided, a default message is shown.
|
|
@@ -133,7 +128,6 @@ export interface ProtectedRouteProps {
|
|
|
133
128
|
*/
|
|
134
129
|
export function ProtectedRoute({
|
|
135
130
|
requireEvent = false,
|
|
136
|
-
allowSuperAdminBypass = false,
|
|
137
131
|
noEventsFallback,
|
|
138
132
|
loadingFallback,
|
|
139
133
|
loginPath = '/login'
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
* refetch={refetch}
|
|
36
36
|
* >
|
|
37
37
|
* <h1>Event Details</h1>
|
|
38
|
-
* <
|
|
38
|
+
* <main className="content">
|
|
39
39
|
* Your public page content
|
|
40
|
-
* </
|
|
40
|
+
* </main>
|
|
41
41
|
* </PublicPageLayout>
|
|
42
42
|
* );
|
|
43
43
|
* }
|
|
@@ -90,8 +90,6 @@ export interface PublicPageLayoutProps {
|
|
|
90
90
|
refetch?: () => Promise<void> | void;
|
|
91
91
|
/** Whether to show the footer (default: true) */
|
|
92
92
|
showFooter?: boolean;
|
|
93
|
-
/** @deprecated Custom CSS classes for the layout - no longer used as wrapper div was removed */
|
|
94
|
-
className?: string;
|
|
95
93
|
/** Custom error fallback component */
|
|
96
94
|
errorFallback?: React.ComponentType<{ error: Error; retry: () => void }>;
|
|
97
95
|
/** Custom loading fallback component */
|
|
@@ -294,7 +292,6 @@ export function PublicPageLayout({
|
|
|
294
292
|
error = null,
|
|
295
293
|
refetch,
|
|
296
294
|
showFooter = true,
|
|
297
|
-
className = '',
|
|
298
295
|
errorFallback: ErrorFallback,
|
|
299
296
|
loadingFallback: LoadingFallback,
|
|
300
297
|
customHeader,
|
|
@@ -50,6 +50,10 @@ interface PublicPageContextType {
|
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Context for public pages.
|
|
55
|
+
* Provides isolated context for public pages without authentication requirements.
|
|
56
|
+
*/
|
|
53
57
|
export const PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);
|
|
54
58
|
|
|
55
59
|
export interface PublicPageProviderProps {
|