@jmruthers/pace-core 0.5.193 → 0.6.2
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 +62 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +299 -0
- package/cursor-rules/01-standards-compliance.mdc +244 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +222 -0
- package/cursor-rules/04-testing-standards.mdc +268 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +309 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
- package/cursor-rules/08-markup-quality.mdc +452 -0
- package/cursor-rules/CHANGELOG.md +119 -0
- package/cursor-rules/README.md +192 -0
- package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
- package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
- package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
- package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
- package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
- package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
- package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
- package/dist/chunk-24UVZUZG.js.map +1 -0
- package/dist/{chunk-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
- package/dist/chunk-2UOI2FG5.js.map +1 -0
- package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
- package/dist/chunk-3XC4CPTD.js.map +1 -0
- package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
- package/dist/chunk-6J4GEEJR.js.map +1 -0
- package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
- package/dist/chunk-6SOIHG6Z.js.map +1 -0
- package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
- package/dist/chunk-EHMR7VYL.js.map +1 -0
- package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
- package/dist/chunk-F2IMUDXZ.js.map +1 -0
- package/dist/{chunk-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
- 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-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
- package/dist/chunk-L4OXEN46.js.map +1 -0
- package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
- package/dist/chunk-M43Y4SSO.js.map +1 -0
- package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
- package/dist/chunk-MMZ7JXPU.js.map +1 -0
- package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
- package/dist/chunk-NECFR5MM.js.map +1 -0
- package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
- package/dist/chunk-SFZUDBL5.js.map +1 -0
- package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
- package/dist/chunk-XWQCNGTQ.js.map +1 -0
- package/dist/components.d.ts +6 -6
- package/dist/components.js +15 -12
- package/dist/components.js.map +1 -1
- package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
- package/dist/hooks.d.ts +59 -126
- package/dist/hooks.js +19 -28
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +63 -16
- package/dist/index.js +23 -24
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +21 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +146 -115
- package/dist/rbac/index.js +8 -11
- package/dist/theming/runtime.d.ts +1 -13
- package/dist/theming/runtime.js +1 -1
- 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/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +4 -5
- package/dist/utils.js +15 -15
- package/dist/utils.js.map +1 -1
- package/docs/api/README.md +7 -1
- package/docs/api/classes/ColumnFactory.md +8 -8
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +4 -4
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- package/docs/api/classes/SecureSupabaseClient.md +18 -15
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +4 -4
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +9 -2
- package/docs/api/interfaces/ButtonProps.md +7 -4
- package/docs/api/interfaces/CalendarProps.md +8 -5
- package/docs/api/interfaces/CardProps.md +8 -5
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +24 -21
- package/docs/api/interfaces/DataTableColumn.md +31 -31
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
- package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
- package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +8 -8
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- 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 +26 -23
- package/docs/api/interfaces/FooterProps.md +10 -8
- package/docs/api/interfaces/FormFieldProps.md +10 -10
- package/docs/api/interfaces/FormProps.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 +7 -4
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +14 -11
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +11 -11
- package/docs/api/interfaces/NavigationMenuProps.md +15 -15
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
- package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
- package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
- package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
- package/docs/api/interfaces/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
- package/docs/api/interfaces/SetupIssue.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +3 -3
- package/docs/api/interfaces/TextareaProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +4 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +14 -11
- package/docs/api/interfaces/UserMenuProps.md +8 -6
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +575 -634
- package/docs/architecture/database-schema-requirements.md +161 -0
- package/docs/core-concepts/rbac-system.md +3 -3
- package/docs/documentation-index.md +2 -4
- package/docs/getting-started/cursor-rules.md +263 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
- package/docs/migration/MIGRATION_GUIDE.md +6 -28
- package/docs/migration/README.md +52 -6
- package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
- package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -0
- package/docs/migration/database-changes-december-2025.md +3 -3
- 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/standards/README.md +40 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +12 -6
- 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} +737 -691
- 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 +454 -0
- package/scripts/audit/core/checks/documentation.cjs +203 -0
- package/scripts/audit/core/checks/environment.cjs +128 -0
- package/scripts/audit/core/checks/error-handling.cjs +299 -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 +86 -0
- 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/install-cursor-rules.cjs +236 -0
- 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__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- 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 +16 -4
- package/src/components/Button/Button.tsx +27 -4
- package/src/components/Calendar/Calendar.tsx +9 -3
- 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/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +40 -6
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
- package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
- package/src/components/DataTable/components/ActionButtons.tsx +10 -7
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
- 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 +200 -561
- 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 +9 -1
- 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 +9 -1
- 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 +62 -852
- 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/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- 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 +14 -2
- 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 +124 -32
- package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- 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/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +8 -7
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
- package/src/components/ErrorBoundary/index.ts +27 -2
- package/src/components/EventSelector/EventSelector.tsx +4 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
- package/src/components/FileDisplay/FileDisplay.tsx +32 -18
- package/src/components/FileUpload/FileUpload.tsx +22 -2
- package/src/components/Footer/Footer.test.tsx +16 -16
- package/src/components/Footer/Footer.tsx +15 -12
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +31 -26
- package/src/components/Header/Header.tsx +22 -11
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.test.tsx +2 -2
- package/src/components/Input/Input.tsx +36 -34
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +12 -8
- package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
- package/src/components/NavigationMenu/types.ts +56 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceAppLayout/test-setup.tsx +1 -2
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
- package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
- package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
- package/src/components/Select/Select.tsx +95 -438
- 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 +5 -6
- package/src/components/Switch/Switch.tsx +4 -4
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +5 -1
- package/src/components/Tooltip/Tooltip.tsx +3 -3
- package/src/components/UserMenu/UserMenu.test.tsx +24 -11
- package/src/components/UserMenu/UserMenu.tsx +22 -19
- package/src/components/index.ts +2 -2
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/index.unit.test.ts +2 -5
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/index.ts +1 -2
- package/src/hooks/public/usePublicEvent.ts +5 -1
- package/src/hooks/public/usePublicEventLogo.ts +5 -1
- package/src/hooks/public/usePublicFileDisplay.ts +4 -0
- package/src/hooks/public/usePublicRouteParams.ts +5 -1
- 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/useDataTableState.ts +8 -18
- package/src/hooks/useDebounce.ts +9 -0
- package/src/hooks/useEventTheme.ts +6 -0
- package/src/hooks/useFileDisplay.ts +4 -0
- package/src/hooks/useFileReference.ts +25 -7
- package/src/hooks/useFileUrl.ts +11 -1
- package/src/hooks/useFocusManagement.ts +16 -2
- package/src/hooks/useFocusTrap.ts +7 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +4 -1
- 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 +8 -1
- 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 +3 -3
- package/src/index.ts +2 -1
- package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/AuthServiceProvider.tsx +18 -0
- package/src/providers/services/EventServiceProvider.tsx +18 -0
- package/src/providers/services/InactivityServiceProvider.tsx +18 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +58 -22
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
- package/src/rbac/adapters.tsx +14 -5
- package/src/rbac/api.ts +100 -67
- package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +1 -1
- package/src/rbac/components/NavigationProvider.tsx +5 -2
- package/src/rbac/components/PagePermissionGuard.tsx +158 -18
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +1 -1
- package/src/rbac/components/RoleBasedRouter.tsx +6 -2
- package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
- package/src/rbac/components/SecureDataProvider.tsx +21 -6
- 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/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 +347 -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 +71 -64
- package/src/rbac/hooks/usePermissions.ts +14 -995
- package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
- package/src/rbac/hooks/useResourcePermissions.ts +14 -4
- package/src/rbac/hooks/useSecureSupabase.ts +33 -13
- package/src/rbac/permissions.ts +0 -30
- package/src/rbac/secureClient.ts +212 -61
- package/src/rbac/types.ts +8 -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/file-reference/index.ts +53 -1
- package/src/utils/formatting/formatting.ts +8 -18
- package/src/utils/index.ts +0 -1
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js +0 -628
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js.map +0 -1
- package/dist/chunk-7FLMSG37.js 2.map +0 -1
- package/dist/chunk-7FLMSG37.js.map +0 -1
- package/dist/chunk-BC4IJKSL.js.map +0 -1
- package/dist/chunk-E3SPN4VZ.js +0 -12917
- package/dist/chunk-E3SPN4VZ.js.map +0 -1
- package/dist/chunk-E66EQZE6 5.js +0 -37
- package/dist/chunk-E66EQZE6.js 2.map +0 -1
- package/dist/chunk-HWIIPPNI.js.map +0 -1
- package/dist/chunk-I7PSE6JW 5.js +0 -191
- package/dist/chunk-I7PSE6JW.js 2.map +0 -1
- package/dist/chunk-I7PSE6JW.js.map +0 -1
- package/dist/chunk-IIELH4DL.js.map +0 -1
- package/dist/chunk-KNC55RTG.js 5.map +0 -1
- package/dist/chunk-KNC55RTG.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js 5.map +0 -1
- package/dist/chunk-LFNCN2SP.js +0 -412
- package/dist/chunk-LFNCN2SP.js 2.map +0 -1
- package/dist/chunk-LFNCN2SP.js.map +0 -1
- package/dist/chunk-LMC26NLJ 2.js +0 -84
- package/dist/chunk-NOAYCWCX.js +0 -4993
- package/dist/chunk-NOAYCWCX.js.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js.map +0 -1
- package/dist/chunk-QXHPKYJV 3.js +0 -113
- package/dist/chunk-R77UEZ4E.js +0 -68
- package/dist/chunk-R77UEZ4E.js.map +0 -1
- package/dist/chunk-SQGMNID3.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
- package/dist/chunk-XNXXZ43G.js.map +0 -1
- package/dist/chunk-ZSAAAMVR 6.js +0 -25
- package/dist/components.js 5.map +0 -1
- package/dist/styles/index 2.js +0 -12
- package/dist/styles/index.js 5.map +0 -1
- package/dist/theming/runtime 5.js +0 -19
- package/dist/theming/runtime.js 5.map +0 -1
- package/docs/api/classes/ErrorBoundary.md +0 -144
- package/docs/migration/quick-migration-guide.md +0 -356
- package/docs/migration/service-architecture.md +0 -281
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
- package/src/hooks/useSecureDataAccess.test.ts +0 -559
- package/src/hooks/useSecureDataAccess.ts +0 -666
- /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
- /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.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/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
* - Responsive design considerations
|
|
185
185
|
*
|
|
186
186
|
* @dependencies
|
|
187
|
-
* - React
|
|
187
|
+
* - React 19+ - Component framework and hooks
|
|
188
188
|
* - Lucide React - Icon components
|
|
189
189
|
* - Radix UI - Dropdown menu primitives
|
|
190
190
|
* - React Router (optional) - For navigation handling
|
|
@@ -192,70 +192,12 @@
|
|
|
192
192
|
*/
|
|
193
193
|
|
|
194
194
|
import * as React from "react";
|
|
195
|
-
import {
|
|
196
|
-
import { cn } from "../../utils/core/cn";
|
|
197
|
-
import { Button } from "../Button";
|
|
195
|
+
import { ChevronDown } from "lucide-react";
|
|
198
196
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../Select";
|
|
199
|
-
import { useUnifiedAuth } from "../../providers/services/UnifiedAuthProvider";
|
|
200
|
-
import { useRBAC } from "../../rbac/hooks/useRBAC";
|
|
201
|
-
import { useResolvedScope } from "../../rbac/hooks";
|
|
202
|
-
import { usePermissions } from "../../rbac/hooks/usePermissions";
|
|
203
|
-
import type { Permission, AccessLevel as RBACAccessLevel, UUID } from "../../rbac/types";
|
|
204
197
|
import { logger } from "../../utils/core/logger";
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
*/
|
|
209
|
-
export type NavigationMode = 'dropdown' | 'hierarchical';
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Navigation item metadata
|
|
213
|
-
*/
|
|
214
|
-
export interface NavigationItemMeta {
|
|
215
|
-
hidden?: boolean;
|
|
216
|
-
[key: string]: unknown;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Navigation item interface
|
|
221
|
-
*/
|
|
222
|
-
export interface NavigationItem {
|
|
223
|
-
id: string;
|
|
224
|
-
label: string;
|
|
225
|
-
href?: string;
|
|
226
|
-
icon?: string;
|
|
227
|
-
children?: NavigationItem[];
|
|
228
|
-
permissions?: (Permission | string)[];
|
|
229
|
-
roles?: string[];
|
|
230
|
-
accessLevel?: RBACAccessLevel | string;
|
|
231
|
-
meta?: NavigationItemMeta;
|
|
232
|
-
pageId?: string;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Navigation menu component props
|
|
237
|
-
*/
|
|
238
|
-
export interface NavigationMenuProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
239
|
-
items: NavigationItem[];
|
|
240
|
-
mode?: NavigationMode;
|
|
241
|
-
currentPath?: string;
|
|
242
|
-
onNavigate?: (item: NavigationItem) => void;
|
|
243
|
-
className?: string;
|
|
244
|
-
disabled?: boolean;
|
|
245
|
-
buttonText?: string;
|
|
246
|
-
showIcons?: boolean;
|
|
247
|
-
navigationLabel?: string;
|
|
248
|
-
strictMode?: boolean;
|
|
249
|
-
auditLog?: boolean;
|
|
250
|
-
onNavigationAccessDenied?: (item: NavigationItem) => void;
|
|
251
|
-
onStrictModeViolation?: (item: NavigationItem, reason: string) => void;
|
|
252
|
-
/**
|
|
253
|
-
* If true, indicates that items have already been filtered by the parent component (e.g., PaceAppLayout).
|
|
254
|
-
* When true, NavigationMenu will skip expensive permission checks and trust the provided items.
|
|
255
|
-
* This significantly improves performance when items are pre-filtered.
|
|
256
|
-
*/
|
|
257
|
-
itemsPreFiltered?: boolean;
|
|
258
|
-
}
|
|
198
|
+
import { useNavigationFiltering } from "./useNavigationFiltering";
|
|
199
|
+
import type { NavigationItem, NavigationMenuProps } from "./types";
|
|
200
|
+
import type { Permission } from "../../rbac/types";
|
|
259
201
|
|
|
260
202
|
/**
|
|
261
203
|
* Unified NavigationMenu component that supports both dropdown and hierarchical navigation modes.
|
|
@@ -472,456 +414,8 @@ export const NavigationMenu = React.forwardRef<
|
|
|
472
414
|
const [expandedItems, setExpandedItems] = React.useState<Set<string>>(new Set());
|
|
473
415
|
const buttonRef = React.useRef<HTMLButtonElement>(null);
|
|
474
416
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
try {
|
|
478
|
-
authContext = useUnifiedAuth();
|
|
479
|
-
} catch (error) {
|
|
480
|
-
// NavigationMenu is being used outside of UnifiedAuthProvider
|
|
481
|
-
logger.warn('NavigationMenu', 'useUnifiedAuth not available, running in unauthenticated mode');
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// Get RBAC context for permission and role checks
|
|
485
|
-
let rbacContext = null;
|
|
486
|
-
try {
|
|
487
|
-
rbacContext = useRBAC();
|
|
488
|
-
} catch (error) {
|
|
489
|
-
// RBAC not available - permission filtering will be disabled
|
|
490
|
-
logger.warn('NavigationMenu', 'useRBAC not available, permission filtering disabled');
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// Get event context state for checking readiness
|
|
494
|
-
// Store the raw value to check if it's undefined (tests without event provider)
|
|
495
|
-
const eventLoadingRaw = authContext?.eventLoading;
|
|
496
|
-
const eventLoading = eventLoadingRaw ?? false;
|
|
497
|
-
const selectedEvent = authContext?.selectedEvent || null;
|
|
498
|
-
// Check org context readiness: use isContextReady if available, otherwise fall back to checking selectedOrganisation
|
|
499
|
-
// This handles both production (with isContextReady) and test scenarios (without it)
|
|
500
|
-
const orgContextReady = authContext?.isContextReady ?? (authContext?.selectedOrganisation?.id ? true : false);
|
|
501
|
-
|
|
502
|
-
// Get resolved scope for permission checks
|
|
503
|
-
// Note: Always call useResolvedScope (hooks must be called unconditionally)
|
|
504
|
-
// However, if itemsPreFiltered is true, we'll skip using the results to avoid blocking
|
|
505
|
-
// Permission filtering is always enabled - scope is always needed (unless items are pre-filtered)
|
|
506
|
-
const { supabase } = authContext || {};
|
|
507
|
-
const { selectedOrganisation } = authContext || {};
|
|
508
|
-
const { resolvedScope, isLoading: scopeLoading, error: scopeError } = useResolvedScope({
|
|
509
|
-
supabase: itemsPreFiltered ? null : (supabase || null), // Skip expensive resolution if pre-filtered
|
|
510
|
-
selectedOrganisationId: itemsPreFiltered ? null : (selectedOrganisation?.id || null),
|
|
511
|
-
selectedEventId: itemsPreFiltered ? null : (selectedEvent?.event_id || null)
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
// Resolve appId when useResolvedScope fails but context is ready
|
|
515
|
-
// This is needed because usePermissions requires appId to fetch permissions correctly
|
|
516
|
-
const [resolvedAppId, setResolvedAppId] = React.useState<string | undefined>(undefined);
|
|
517
|
-
React.useEffect(() => {
|
|
518
|
-
// Only resolve appId if:
|
|
519
|
-
// 1. useResolvedScope errored or returned null
|
|
520
|
-
// 2. But we have organisation context ready
|
|
521
|
-
// 3. And we have appName from authContext
|
|
522
|
-
// 4. And we haven't resolved it yet
|
|
523
|
-
if (
|
|
524
|
-
!scopeLoading &&
|
|
525
|
-
!resolvedScope?.appId &&
|
|
526
|
-
selectedOrganisation?.id &&
|
|
527
|
-
authContext?.appName &&
|
|
528
|
-
authContext?.user?.id &&
|
|
529
|
-
!resolvedAppId
|
|
530
|
-
) {
|
|
531
|
-
// Resolve appId using the same method as useRBAC
|
|
532
|
-
// Double-check user exists (TypeScript narrowing) and capture values
|
|
533
|
-
if (!authContext.user || !authContext.appName) {
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
const userId = authContext.user.id;
|
|
537
|
-
const appName = authContext.appName;
|
|
538
|
-
import('../../rbac/api').then(({ resolveAppContext }) => {
|
|
539
|
-
resolveAppContext({
|
|
540
|
-
userId,
|
|
541
|
-
appName
|
|
542
|
-
}).then((result) => {
|
|
543
|
-
if (result?.appId) {
|
|
544
|
-
setResolvedAppId(result.appId);
|
|
545
|
-
}
|
|
546
|
-
}).catch((error) => {
|
|
547
|
-
// Silently fail - usePermissions will handle it
|
|
548
|
-
});
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
}, [scopeLoading, resolvedScope?.appId, selectedOrganisation?.id, authContext?.appName, authContext?.user?.id, resolvedAppId]);
|
|
552
|
-
|
|
553
|
-
// Build scope from resolvedScope if available, otherwise fall back to context values
|
|
554
|
-
// This handles the case where useResolvedScope errored initially but context is now ready
|
|
555
|
-
// IMPORTANT: We need appId for usePermissions to work correctly
|
|
556
|
-
// CRITICAL: Use selectedOrganisation?.id immediately when available, even during loading
|
|
557
|
-
// This prevents usePermissions from skipping fetch due to empty organisationId
|
|
558
|
-
const effectiveScope = React.useMemo(() => {
|
|
559
|
-
// Prefer resolvedScope if available (includes appId)
|
|
560
|
-
if (resolvedScope?.organisationId) {
|
|
561
|
-
return resolvedScope;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// Fall back to building scope from context if resolvedScope is not available yet
|
|
565
|
-
// Use selectedOrganisation?.id immediately when available, even if scopeLoading is true
|
|
566
|
-
// This prevents delay in permission fetching
|
|
567
|
-
if (selectedOrganisation?.id) {
|
|
568
|
-
const fallbackScope = {
|
|
569
|
-
organisationId: selectedOrganisation.id,
|
|
570
|
-
eventId: selectedEvent?.event_id || undefined,
|
|
571
|
-
appId: resolvedAppId
|
|
572
|
-
};
|
|
573
|
-
return fallbackScope;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
return null;
|
|
577
|
-
}, [resolvedScope, selectedOrganisation?.id, selectedEvent?.event_id, resolvedAppId]);
|
|
578
|
-
|
|
579
|
-
// Create a stable scope object that changes when effectiveScope changes
|
|
580
|
-
// This ensures usePermissions detects scope changes and re-runs
|
|
581
|
-
// We memoize it to prevent unnecessary re-renders while still triggering usePermissions when scope changes
|
|
582
|
-
// IMPORTANT: Use a string key for dependencies to ensure React detects changes
|
|
583
|
-
// This is more reliable than depending on the object itself
|
|
584
|
-
const scopeKey = effectiveScope
|
|
585
|
-
? `${effectiveScope.organisationId || ''}-${effectiveScope.eventId || ''}-${effectiveScope.appId || ''}`
|
|
586
|
-
: 'empty';
|
|
587
|
-
|
|
588
|
-
const stableScope = React.useMemo(() => {
|
|
589
|
-
if (effectiveScope?.organisationId) {
|
|
590
|
-
return {
|
|
591
|
-
organisationId: effectiveScope.organisationId,
|
|
592
|
-
eventId: effectiveScope.eventId,
|
|
593
|
-
appId: effectiveScope.appId
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
// Return empty scope object (not null) so usePermissions can handle it
|
|
597
|
-
return {
|
|
598
|
-
organisationId: '',
|
|
599
|
-
eventId: undefined,
|
|
600
|
-
appId: undefined
|
|
601
|
-
};
|
|
602
|
-
}, [scopeKey, effectiveScope]);
|
|
603
|
-
|
|
604
|
-
// Get permissions map for synchronous permission checks
|
|
605
|
-
// Skip expensive permission fetching if items are pre-filtered
|
|
606
|
-
const userId = (authContext?.user?.id || '') as UUID;
|
|
607
|
-
|
|
608
|
-
// Call usePermissions with primitive parameters
|
|
609
|
-
// If itemsPreFiltered is true, pass null/undefined to skip expensive permission fetching
|
|
610
|
-
// CRITICAL: Pass null for userId and undefined for organisationId to skip the 3-second timeout
|
|
611
|
-
// Empty string '' triggers the timeout, but null/undefined allows immediate early return
|
|
612
|
-
const { permissions: permissionMap, hasAnyPermission, isLoading: permissionsLoading, error: permissionsError } = usePermissions(
|
|
613
|
-
itemsPreFiltered ? (null as any) : userId, // Pass null to trigger early return (empty string would wait 3s)
|
|
614
|
-
itemsPreFiltered ? undefined : stableScope.organisationId, // Pass undefined to skip timeout
|
|
615
|
-
itemsPreFiltered ? undefined : stableScope.eventId, // Skip if pre-filtered
|
|
616
|
-
itemsPreFiltered ? undefined : stableScope.appId // Skip if pre-filtered
|
|
617
|
-
);
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
// NEW: Phase 2 - Enhanced Security Features
|
|
622
|
-
// Filter navigation items based on permissions using RBAC hooks
|
|
623
|
-
// Use ref to preserve previous filtered items during permission refetches (e.g., when event changes)
|
|
624
|
-
const previousFilteredItemsRef = React.useRef<NavigationItem[]>([]);
|
|
625
|
-
|
|
626
|
-
const filteredItems = React.useMemo(() => {
|
|
627
|
-
// PERFORMANCE OPTIMIZATION: If items are pre-filtered, skip all expensive permission checks
|
|
628
|
-
// This significantly improves load time when PaceAppLayout has already filtered items
|
|
629
|
-
if (itemsPreFiltered && items && items.length > 0) {
|
|
630
|
-
// Items are pre-filtered by parent (e.g., PaceAppLayout) - trust them and only filter hidden items
|
|
631
|
-
const visibleItems = (items || []).filter(item => !item.meta?.hidden);
|
|
632
|
-
previousFilteredItemsRef.current = visibleItems;
|
|
633
|
-
// Don't log here - logging happens in useEffect to avoid spam on re-renders
|
|
634
|
-
return visibleItems;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// SECURITY: Permission filtering is ALWAYS enabled - users only see navigation items they have permission to access
|
|
638
|
-
// Security: If we're missing required context or still loading, show NO items
|
|
639
|
-
// This prevents security risk of showing items before permissions are verified
|
|
640
|
-
|
|
641
|
-
// CRITICAL: Wait for BOTH organisation AND event context to be ready before checking permissions
|
|
642
|
-
// This prevents the error "No organisation or event context available" when event context is still loading
|
|
643
|
-
const isOrgContextReady = orgContextReady && selectedOrganisation?.id;
|
|
644
|
-
// Event context is ready when not loading
|
|
645
|
-
// If eventLoadingRaw is undefined (tests without event provider), consider it ready
|
|
646
|
-
// Only wait for event context if we're actually loading events
|
|
647
|
-
const isEventContextReady = eventLoadingRaw === undefined ? true : !eventLoading;
|
|
648
|
-
|
|
649
|
-
// Check if we have valid context for permission checking
|
|
650
|
-
// Use actual context values, not just resolvedScope, because resolvedScope might be null
|
|
651
|
-
// if useResolvedScope errored initially but context is now ready
|
|
652
|
-
const hasValidContext = isOrgContextReady && isEventContextReady;
|
|
653
|
-
|
|
654
|
-
// If scope is still loading or we don't have valid context yet, show nothing
|
|
655
|
-
// BUT: If scope errored but context is now ready, retry (don't block forever)
|
|
656
|
-
const shouldWaitForScope = scopeLoading || (!hasValidContext);
|
|
657
|
-
const shouldRetryAfterError = scopeError && hasValidContext && !scopeLoading;
|
|
658
|
-
|
|
659
|
-
// During initial load or when scope/context is loading, check if we can trust PaceAppLayout's filtering
|
|
660
|
-
// CRITICAL: If items are provided and we have valid context, trust PaceAppLayout's filtering
|
|
661
|
-
// This prevents delay - PaceAppLayout already filtered items, so show them immediately
|
|
662
|
-
// even if scope is still loading or rbacContext is not yet available
|
|
663
|
-
if (items && items.length > 0 && selectedOrganisation?.id) {
|
|
664
|
-
// Items are provided by PaceAppLayout - trust its filtering and show immediately
|
|
665
|
-
// This eliminates delay while usePermissions/useResolvedScope are still loading
|
|
666
|
-
// Items provided - trusting PaceAppLayout filtering (removed verbose logging)
|
|
667
|
-
// Filter out hidden items only - PaceAppLayout already did permission filtering
|
|
668
|
-
const visibleItems = (items || []).filter(item => !item.meta?.hidden);
|
|
669
|
-
// Store in ref for use during permission refetches
|
|
670
|
-
previousFilteredItemsRef.current = visibleItems;
|
|
671
|
-
return visibleItems;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
// If no items provided or no valid context, wait for context to be ready
|
|
675
|
-
if (!authContext || !rbacContext || (shouldWaitForScope && !shouldRetryAfterError)) {
|
|
676
|
-
// Still loading - show nothing to prevent security risk
|
|
677
|
-
// Note: We check both org and event context readiness to prevent premature permission checks
|
|
678
|
-
// Exception: If scope errored but context is now ready, we'll retry below
|
|
679
|
-
return [];
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
// If scope errored but context is now ready, we can proceed with permission checks
|
|
683
|
-
// The resolvedScope might be null, but we'll use the actual context values
|
|
684
|
-
// usePermissions will handle the scope resolution internally
|
|
685
|
-
|
|
686
|
-
// During permission refetch (after initial load), preserve previous items if we have them
|
|
687
|
-
// This prevents navigation from disappearing when switching events
|
|
688
|
-
if (permissionsLoading) {
|
|
689
|
-
// If we have previous items, keep showing them during refetch
|
|
690
|
-
// This prevents the navigation from disappearing when event changes
|
|
691
|
-
if (previousFilteredItemsRef.current.length > 0) {
|
|
692
|
-
return previousFilteredItemsRef.current;
|
|
693
|
-
}
|
|
694
|
-
// CRITICAL: If items are provided and we have valid scope, trust PaceAppLayout's filtering
|
|
695
|
-
// This prevents delay - PaceAppLayout already filtered items, so show them immediately
|
|
696
|
-
// even if usePermissions is still loading
|
|
697
|
-
if (items && items.length > 0 && stableScope.organisationId) {
|
|
698
|
-
// Permissions loading but items provided - trusting PaceAppLayout filtering (removed verbose logging)
|
|
699
|
-
// Filter out hidden items only - PaceAppLayout already did permission filtering
|
|
700
|
-
return (items || []).filter(item => !item.meta?.hidden);
|
|
701
|
-
}
|
|
702
|
-
// Otherwise, show nothing during initial load
|
|
703
|
-
return [];
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// If there's an error after loading, show nothing
|
|
707
|
-
// Security: Better to show nothing than risk showing unauthorized items
|
|
708
|
-
if (permissionsError) {
|
|
709
|
-
logger.warn('NavigationMenu', 'Permission check error - showing no items for security', {
|
|
710
|
-
permissionsError: permissionsError?.message
|
|
711
|
-
});
|
|
712
|
-
return [];
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
// If permission map is empty after loading, we need to handle this carefully:
|
|
716
|
-
// 1. If items were already filtered by PaceAppLayout (which uses getPermissionMap directly),
|
|
717
|
-
// we should trust those items and show them (they've already been filtered)
|
|
718
|
-
// 2. If NavigationMenu is used standalone, we need permissions to filter - show nothing
|
|
719
|
-
//
|
|
720
|
-
// Since PaceAppLayout filters items before passing to NavigationMenu, and NavigationMenu
|
|
721
|
-
// uses usePermissions hook which may return empty map while PaceAppLayout gets permissions,
|
|
722
|
-
// we should trust items that are provided if permission map is empty but we have valid scope.
|
|
723
|
-
// This is safe because PaceAppLayout already filtered them using getPermissionMap.
|
|
724
|
-
if (!permissionMap || Object.keys(permissionMap).length === 0) {
|
|
725
|
-
// If we have valid scope and items are provided, they were likely already filtered by PaceAppLayout
|
|
726
|
-
// In this case, trust the items and show them (they've already been filtered for security)
|
|
727
|
-
if (stableScope.organisationId && items && items.length > 0) {
|
|
728
|
-
// Permission map empty but items provided - trusting PaceAppLayout filtering (removed verbose logging)
|
|
729
|
-
// Items were already filtered by PaceAppLayout - trust them and only filter hidden items
|
|
730
|
-
return (items || []).filter(item => !item.meta?.hidden);
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
// If no items provided or no valid scope, show nothing (can't verify permissions)
|
|
734
|
-
if (stableScope.organisationId) {
|
|
735
|
-
logger.warn('NavigationMenu', 'Permission map is empty and no items provided - showing nothing', {
|
|
736
|
-
permissionMapSize: 0,
|
|
737
|
-
organisationId: stableScope.organisationId,
|
|
738
|
-
eventId: stableScope.eventId,
|
|
739
|
-
appId: stableScope.appId
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
return [];
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// SECURITY: Permission filtering is always enabled - filtering cannot be disabled
|
|
746
|
-
// Helper function to derive page ID from href
|
|
747
|
-
const getPageIdFromHref = (href?: string): string | null => {
|
|
748
|
-
if (!href) return null;
|
|
749
|
-
// Remove leading slash and any query params/hash
|
|
750
|
-
const path = href.split('?')[0].split('#')[0].replace(/^\//, '');
|
|
751
|
-
return path || 'home';
|
|
752
|
-
};
|
|
753
|
-
|
|
754
|
-
// Helper function to check if item has permission to be shown
|
|
755
|
-
const hasItemPermission = (item: NavigationItem): boolean => {
|
|
756
|
-
// If item has href, we'll check page permissions later - skip explicit permission check
|
|
757
|
-
// This allows items with href to be filtered by page permissions instead of explicit permissions
|
|
758
|
-
// Check permissions if available AND item doesn't have href
|
|
759
|
-
if (item.permissions && item.permissions.length > 0 && !item.href) {
|
|
760
|
-
// Convert string permissions to Permission type and check
|
|
761
|
-
const permissions = item.permissions
|
|
762
|
-
.filter((p): p is string => typeof p === 'string')
|
|
763
|
-
.map(p => p as Permission);
|
|
764
|
-
|
|
765
|
-
if (permissions.length > 0) {
|
|
766
|
-
const hasPermission = hasAnyPermission(permissions);
|
|
767
|
-
if (!hasPermission) {
|
|
768
|
-
return false;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
// Check roles if available
|
|
774
|
-
if (item.roles && item.roles.length > 0) {
|
|
775
|
-
const hasRole = item.roles.some(role => {
|
|
776
|
-
if (typeof role !== 'string') return true;
|
|
777
|
-
|
|
778
|
-
// Map role strings to RBAC role checks
|
|
779
|
-
switch (role.toLowerCase()) {
|
|
780
|
-
case 'super_admin':
|
|
781
|
-
case 'super admin':
|
|
782
|
-
return rbacContext.isSuperAdmin;
|
|
783
|
-
case 'org_admin':
|
|
784
|
-
case 'org admin':
|
|
785
|
-
case 'admin':
|
|
786
|
-
return rbacContext.isOrgAdmin || rbacContext.isSuperAdmin;
|
|
787
|
-
case 'event_admin':
|
|
788
|
-
case 'event admin':
|
|
789
|
-
return rbacContext.isEventAdmin || rbacContext.isSuperAdmin;
|
|
790
|
-
default:
|
|
791
|
-
// For other roles, check against organisationRole or eventAppRole
|
|
792
|
-
return (
|
|
793
|
-
rbacContext.organisationRole === role ||
|
|
794
|
-
rbacContext.eventAppRole === role ||
|
|
795
|
-
rbacContext.isSuperAdmin
|
|
796
|
-
);
|
|
797
|
-
}
|
|
798
|
-
});
|
|
799
|
-
if (!hasRole) {
|
|
800
|
-
return false;
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
// Check access level if available
|
|
805
|
-
if (item.accessLevel) {
|
|
806
|
-
if (typeof item.accessLevel === 'string') {
|
|
807
|
-
// Map access level string to RBAC access level
|
|
808
|
-
const accessLevel = item.accessLevel.toLowerCase() as RBACAccessLevel;
|
|
809
|
-
const userEventRole = rbacContext.eventAppRole;
|
|
810
|
-
|
|
811
|
-
// If user is super admin, they have all access levels
|
|
812
|
-
if (rbacContext.isSuperAdmin) {
|
|
813
|
-
// Super admin has access
|
|
814
|
-
} else {
|
|
815
|
-
// Map eventAppRole to access level for comparison
|
|
816
|
-
// eventAppRole: 'viewer' | 'participant' | 'planner' | 'event_admin'
|
|
817
|
-
const roleToAccessLevel: Record<string, RBACAccessLevel> = {
|
|
818
|
-
'viewer': 'viewer',
|
|
819
|
-
'participant': 'participant',
|
|
820
|
-
'planner': 'planner',
|
|
821
|
-
'event_admin': 'admin',
|
|
822
|
-
};
|
|
823
|
-
const userAccessLevel = userEventRole ? (roleToAccessLevel[userEventRole] || 'viewer') : null;
|
|
824
|
-
|
|
825
|
-
// Check if user's access level meets the required access level
|
|
826
|
-
const levelHierarchy: Record<RBACAccessLevel, number> = {
|
|
827
|
-
viewer: 1,
|
|
828
|
-
participant: 2,
|
|
829
|
-
planner: 3,
|
|
830
|
-
admin: 4,
|
|
831
|
-
super: 5
|
|
832
|
-
};
|
|
833
|
-
const requiredLevel = levelHierarchy[accessLevel] || 0;
|
|
834
|
-
const userLevel = userAccessLevel ? levelHierarchy[userAccessLevel] || 0 : 0;
|
|
835
|
-
|
|
836
|
-
if (userLevel < requiredLevel) {
|
|
837
|
-
return false;
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
// NEW: Auto-check page permissions for items with href
|
|
844
|
-
// Always check page permissions for items with href, even if they have explicit permissions/roles/accessLevel
|
|
845
|
-
// This ensures that items are filtered out if the user doesn't have access to the page itself
|
|
846
|
-
if (item.href) {
|
|
847
|
-
const pageId = item.pageId || getPageIdFromHref(item.href);
|
|
848
|
-
if (pageId) {
|
|
849
|
-
// Check for read permission on the page
|
|
850
|
-
const pagePermission: Permission = `read:page.${pageId}` as Permission;
|
|
851
|
-
|
|
852
|
-
// Check permission map (super admin has access to everything via '*' key)
|
|
853
|
-
// Only allow if permission is explicitly true (undefined/false means no access)
|
|
854
|
-
const isSuperAdmin = permissionMap['*'] === true;
|
|
855
|
-
const hasPagePermission = permissionMap[pagePermission] === true;
|
|
856
|
-
const finalHasPermission = isSuperAdmin || hasPagePermission;
|
|
857
|
-
|
|
858
|
-
if (!finalHasPermission) {
|
|
859
|
-
return false;
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
return true;
|
|
865
|
-
};
|
|
866
|
-
|
|
867
|
-
// Helper function to filter items recursively (creates new objects to avoid mutations)
|
|
868
|
-
const filterItem = (item: NavigationItem): NavigationItem | null => {
|
|
869
|
-
// Check if item should be hidden
|
|
870
|
-
if (item.meta?.hidden) return null;
|
|
871
|
-
|
|
872
|
-
// Check if item has permission
|
|
873
|
-
if (!hasItemPermission(item)) return null;
|
|
874
|
-
|
|
875
|
-
// Recursively filter children if present
|
|
876
|
-
let filteredChildren: NavigationItem[] | undefined;
|
|
877
|
-
if (item.children && item.children.length > 0) {
|
|
878
|
-
filteredChildren = item.children
|
|
879
|
-
.map(child => filterItem(child))
|
|
880
|
-
.filter((child): child is NavigationItem => child !== null);
|
|
881
|
-
|
|
882
|
-
// If parent has no accessible children, hide the parent too (unless it has its own href)
|
|
883
|
-
if (filteredChildren.length === 0 && !item.href) {
|
|
884
|
-
return null;
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
// Return filtered item (with filtered children if applicable)
|
|
889
|
-
return {
|
|
890
|
-
...item,
|
|
891
|
-
children: filteredChildren
|
|
892
|
-
};
|
|
893
|
-
};
|
|
894
|
-
|
|
895
|
-
// Filter items based on permissions - only show items with explicit permission
|
|
896
|
-
// Security: No fallback - if items don't have permission, they are hidden
|
|
897
|
-
const filtered = (items || [])
|
|
898
|
-
.map(item => filterItem(item))
|
|
899
|
-
.filter((item): item is NavigationItem => item !== null);
|
|
900
|
-
|
|
901
|
-
// Update the ref with the new filtered items so we can preserve them during refetches
|
|
902
|
-
previousFilteredItemsRef.current = filtered;
|
|
903
|
-
|
|
904
|
-
return filtered;
|
|
905
|
-
}, [
|
|
906
|
-
items,
|
|
907
|
-
itemsPreFiltered, // Add itemsPreFiltered to dependencies
|
|
908
|
-
authContext,
|
|
909
|
-
rbacContext,
|
|
910
|
-
permissionMap,
|
|
911
|
-
hasAnyPermission,
|
|
912
|
-
scopeLoading,
|
|
913
|
-
scopeError,
|
|
914
|
-
permissionsLoading,
|
|
915
|
-
resolvedScope,
|
|
916
|
-
effectiveScope,
|
|
917
|
-
auditLog,
|
|
918
|
-
// Add event context state to dependencies so we re-check permissions when event context becomes available
|
|
919
|
-
eventLoadingRaw,
|
|
920
|
-
eventLoading,
|
|
921
|
-
selectedEvent,
|
|
922
|
-
orgContextReady,
|
|
923
|
-
selectedOrganisation?.id
|
|
924
|
-
]);
|
|
417
|
+
const { authContext, rbacContext, filteredItems, permissionMap, hasAnyPermission } =
|
|
418
|
+
useNavigationFiltering({ items, itemsPreFiltered, auditLog });
|
|
925
419
|
|
|
926
420
|
// Note: Navigation access attempts are logged in handleItemClick, not here
|
|
927
421
|
// This prevents excessive logging on every render
|
|
@@ -1209,4 +703,11 @@ export const NavigationMenu = React.forwardRef<
|
|
|
1209
703
|
);
|
|
1210
704
|
});
|
|
1211
705
|
|
|
1212
|
-
NavigationMenu.displayName = "NavigationMenu";
|
|
706
|
+
NavigationMenu.displayName = "NavigationMenu";
|
|
707
|
+
|
|
708
|
+
export type {
|
|
709
|
+
NavigationMode,
|
|
710
|
+
NavigationItemMeta,
|
|
711
|
+
NavigationItem,
|
|
712
|
+
NavigationMenuProps,
|
|
713
|
+
} from "./types";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { Permission, AccessLevel as RBACAccessLevel } from "../../rbac/types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Navigation mode type
|
|
6
|
+
*/
|
|
7
|
+
export type NavigationMode = "dropdown" | "hierarchical";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Navigation item metadata
|
|
11
|
+
*/
|
|
12
|
+
export interface NavigationItemMeta {
|
|
13
|
+
hidden?: boolean;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Navigation item interface
|
|
19
|
+
*/
|
|
20
|
+
export interface NavigationItem {
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
href?: string;
|
|
24
|
+
icon?: string;
|
|
25
|
+
children?: NavigationItem[];
|
|
26
|
+
permissions?: (Permission | string)[];
|
|
27
|
+
roles?: string[];
|
|
28
|
+
accessLevel?: RBACAccessLevel | string;
|
|
29
|
+
meta?: NavigationItemMeta;
|
|
30
|
+
pageId?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Navigation menu component props
|
|
35
|
+
*/
|
|
36
|
+
export interface NavigationMenuProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
37
|
+
items: NavigationItem[];
|
|
38
|
+
mode?: NavigationMode;
|
|
39
|
+
currentPath?: string;
|
|
40
|
+
onNavigate?: (item: NavigationItem) => void;
|
|
41
|
+
className?: string;
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
buttonText?: string;
|
|
44
|
+
showIcons?: boolean;
|
|
45
|
+
navigationLabel?: string;
|
|
46
|
+
strictMode?: boolean;
|
|
47
|
+
auditLog?: boolean;
|
|
48
|
+
onNavigationAccessDenied?: (item: NavigationItem) => void;
|
|
49
|
+
onStrictModeViolation?: (item: NavigationItem, reason: string) => void;
|
|
50
|
+
/**
|
|
51
|
+
* If true, indicates that items have already been filtered by the parent component (e.g., PaceAppLayout).
|
|
52
|
+
* When true, NavigationMenu will skip expensive permission checks and trust the provided items.
|
|
53
|
+
* This significantly improves performance when items are pre-filtered.
|
|
54
|
+
*/
|
|
55
|
+
itemsPreFiltered?: boolean;
|
|
56
|
+
}
|