@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
|
@@ -53,6 +53,16 @@ import { Tooltip } from '../Tooltip';
|
|
|
53
53
|
// BASE BUTTON COMPONENT
|
|
54
54
|
// ============================================================================
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Button component props
|
|
58
|
+
* Extends standard HTML button attributes with button-specific styling and behavior options.
|
|
59
|
+
*
|
|
60
|
+
* @interface ButtonProps
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* Props for the Button component.
|
|
64
|
+
* Extends standard button HTML attributes.
|
|
65
|
+
*/
|
|
56
66
|
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
57
67
|
/** Visual variant of the button */
|
|
58
68
|
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
|
|
@@ -127,6 +137,12 @@ Button.displayName = 'Button';
|
|
|
127
137
|
// BUTTON GROUP COMPONENT
|
|
128
138
|
// ============================================================================
|
|
129
139
|
|
|
140
|
+
/**
|
|
141
|
+
* ButtonGroup component props
|
|
142
|
+
* Configuration for grouping multiple buttons with consistent styling and spacing.
|
|
143
|
+
*
|
|
144
|
+
* @interface ButtonGroupProps
|
|
145
|
+
*/
|
|
130
146
|
export type ButtonGroupProps = {
|
|
131
147
|
/** Child elements to be rendered in the group */
|
|
132
148
|
children: React.ReactNode;
|
|
@@ -216,6 +232,12 @@ export function ButtonGroup({
|
|
|
216
232
|
// ICON BUTTON COMPONENT
|
|
217
233
|
// ============================================================================
|
|
218
234
|
|
|
235
|
+
/**
|
|
236
|
+
* IconButton component props
|
|
237
|
+
* Extends ButtonProps but requires an icon and aria-label instead of children.
|
|
238
|
+
*
|
|
239
|
+
* @interface IconButtonProps
|
|
240
|
+
*/
|
|
219
241
|
export type IconButtonProps = Omit<ButtonProps, 'children'> & {
|
|
220
242
|
/** The icon element to display in the button */
|
|
221
243
|
icon: React.ReactNode;
|
|
@@ -89,6 +89,10 @@ type RootProps = {
|
|
|
89
89
|
// CALENDAR COMPONENT
|
|
90
90
|
// ============================================================================
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Props for the Calendar component.
|
|
94
|
+
* Extends DayPickerProps with pace-core specific customizations.
|
|
95
|
+
*/
|
|
92
96
|
export interface CalendarProps extends Omit<DayPickerProps, 'className' | 'classNames' | 'styles' | 'onSelect'> {
|
|
93
97
|
/**
|
|
94
98
|
* Additional CSS classes to apply to the calendar table
|
|
@@ -416,8 +420,10 @@ const Calendar = React.forwardRef<HTMLTableElement, CalendarProps>(
|
|
|
416
420
|
</>
|
|
417
421
|
);
|
|
418
422
|
}
|
|
419
|
-
|
|
420
|
-
return child
|
|
423
|
+
|
|
424
|
+
return React.isValidElement(child)
|
|
425
|
+
? React.cloneElement(child, { key: child.key ?? `calendar-child-${index}` })
|
|
426
|
+
: child;
|
|
421
427
|
})}
|
|
422
428
|
</>
|
|
423
429
|
);
|
|
@@ -65,6 +65,10 @@ import * as React from "react"
|
|
|
65
65
|
import { useNavigate } from "react-router-dom"
|
|
66
66
|
import { cn } from "../../utils/core/cn"
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Props for the Card component.
|
|
70
|
+
* Extends standard HTML attributes for semantic HTML elements.
|
|
71
|
+
*/
|
|
68
72
|
export interface CardProps extends React.HTMLAttributes<HTMLElement> {
|
|
69
73
|
/** Visual variant of the card */
|
|
70
74
|
variant?: 'default' | 'outline' | 'ghost';
|
|
@@ -235,10 +235,10 @@ describe('Checkbox Component', () => {
|
|
|
235
235
|
|
|
236
236
|
it('supports aria-labelledby', () => {
|
|
237
237
|
renderWithProviders(
|
|
238
|
-
|
|
238
|
+
<>
|
|
239
239
|
<label id="terms-label">Accept terms and conditions</label>
|
|
240
240
|
<Checkbox aria-labelledby="terms-label" />
|
|
241
|
-
|
|
241
|
+
</>
|
|
242
242
|
);
|
|
243
243
|
const checkbox = screen.getByRole('checkbox', { name: 'Accept terms and conditions' });
|
|
244
244
|
expect(checkbox).toBeInTheDocument();
|
|
@@ -246,10 +246,10 @@ describe('Checkbox Component', () => {
|
|
|
246
246
|
|
|
247
247
|
it('supports aria-describedby', () => {
|
|
248
248
|
renderWithProviders(
|
|
249
|
-
|
|
249
|
+
<>
|
|
250
250
|
<Checkbox aria-describedby="terms-help" />
|
|
251
|
-
<
|
|
252
|
-
|
|
251
|
+
<span id="terms-help">Please read the terms carefully</span>
|
|
252
|
+
</>
|
|
253
253
|
);
|
|
254
254
|
const checkbox = screen.getByRole('checkbox');
|
|
255
255
|
expect(checkbox).toHaveAttribute('aria-describedby', 'terms-help');
|
|
@@ -319,10 +319,10 @@ describe('Checkbox Component', () => {
|
|
|
319
319
|
describe('Form Integration', () => {
|
|
320
320
|
it('works with form labels', () => {
|
|
321
321
|
renderWithProviders(
|
|
322
|
-
|
|
322
|
+
<>
|
|
323
323
|
<label htmlFor="terms-checkbox">Accept terms and conditions</label>
|
|
324
324
|
<Checkbox id="terms-checkbox" />
|
|
325
|
-
|
|
325
|
+
</>
|
|
326
326
|
);
|
|
327
327
|
|
|
328
328
|
const checkbox = screen.getByRole('checkbox');
|
|
@@ -388,11 +388,11 @@ describe('Checkbox Component', () => {
|
|
|
388
388
|
describe('Integration', () => {
|
|
389
389
|
it('works with multiple checkboxes', () => {
|
|
390
390
|
renderWithProviders(
|
|
391
|
-
|
|
391
|
+
<>
|
|
392
392
|
<Checkbox id="option1" />
|
|
393
393
|
<Checkbox id="option2" />
|
|
394
394
|
<Checkbox id="option3" />
|
|
395
|
-
|
|
395
|
+
</>
|
|
396
396
|
);
|
|
397
397
|
|
|
398
398
|
expect(screen.getAllByRole('checkbox')).toHaveLength(3);
|
|
@@ -420,13 +420,13 @@ describe('Checkbox Component', () => {
|
|
|
420
420
|
const TestComponent = () => {
|
|
421
421
|
const [checked, setChecked] = React.useState(false);
|
|
422
422
|
return (
|
|
423
|
-
|
|
423
|
+
<>
|
|
424
424
|
<Checkbox
|
|
425
425
|
checked={checked}
|
|
426
426
|
onCheckedChange={setChecked}
|
|
427
427
|
/>
|
|
428
428
|
<span data-testid="status">{checked ? 'checked' : 'unchecked'}</span>
|
|
429
|
-
|
|
429
|
+
</>
|
|
430
430
|
);
|
|
431
431
|
};
|
|
432
432
|
|
|
@@ -470,7 +470,7 @@ describe('Checkbox Component', () => {
|
|
|
470
470
|
));
|
|
471
471
|
|
|
472
472
|
const startTime = performance.now();
|
|
473
|
-
renderWithProviders(
|
|
473
|
+
renderWithProviders(<>{checkboxes}</>);
|
|
474
474
|
const endTime = performance.now();
|
|
475
475
|
|
|
476
476
|
// Performance test: verify rendering completes in reasonable time
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
* <Checkbox />
|
|
22
22
|
*
|
|
23
23
|
* // With label
|
|
24
|
-
*
|
|
24
|
+
* <>
|
|
25
25
|
* <Checkbox id="terms" />
|
|
26
26
|
* <label htmlFor="terms">Accept terms and conditions</label>
|
|
27
|
-
*
|
|
27
|
+
* </>
|
|
28
28
|
*
|
|
29
29
|
* // Disabled state
|
|
30
30
|
* <Checkbox disabled />
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Context Selector Component
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/ContextSelector
|
|
5
|
+
* @since 0.6.4
|
|
6
|
+
*
|
|
7
|
+
* A unified intelligent selector component that shows all organisations and events
|
|
8
|
+
* a user can access based on their roles and permissions. This is the single
|
|
9
|
+
* selector for all apps - it intelligently determines what to show based on:
|
|
10
|
+
* - User's organisation roles
|
|
11
|
+
* - User's event-app roles
|
|
12
|
+
* - Super admin status (shows all)
|
|
13
|
+
*
|
|
14
|
+
* Features:
|
|
15
|
+
* - Intelligent display of accessible organisations and events
|
|
16
|
+
* - Grouped display (Organisations / Events sections)
|
|
17
|
+
* - Visual distinction between orgs and events
|
|
18
|
+
* - Super admin support (shows all orgs and events)
|
|
19
|
+
* - Loading states and error handling
|
|
20
|
+
* - Accessible interface design
|
|
21
|
+
* - Integration with OrganisationProvider and EventService
|
|
22
|
+
* - Automatically shows superset of what user can access
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <ContextSelector
|
|
27
|
+
* onOrganisationSelect={(org) => {
|
|
28
|
+
* switchOrganisation(org.id);
|
|
29
|
+
* }}
|
|
30
|
+
* onEventSelect={(event) => {
|
|
31
|
+
* setSelectedEvent(event);
|
|
32
|
+
* }}
|
|
33
|
+
* />
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @accessibility
|
|
37
|
+
* - WCAG 2.1 AA compliant
|
|
38
|
+
* - Keyboard navigation support
|
|
39
|
+
* - Screen reader friendly
|
|
40
|
+
* - Focus management
|
|
41
|
+
* - ARIA labels and descriptions
|
|
42
|
+
* - High contrast support
|
|
43
|
+
*
|
|
44
|
+
* @security
|
|
45
|
+
* - Only shows organisations/events user has access to
|
|
46
|
+
* - Super admins see all items
|
|
47
|
+
* - Validates access before selection
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
import React, { useMemo } from 'react';
|
|
51
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SelectGroup, SelectLabel, SelectSeparator } from '../Select';
|
|
52
|
+
import { Alert, AlertDescription } from '../Alert/Alert';
|
|
53
|
+
import { Button } from '../Button/Button';
|
|
54
|
+
import { LoadingSpinner } from '../LoadingSpinner/LoadingSpinner';
|
|
55
|
+
import { RefreshCw, AlertCircle, Building2, Calendar } from 'lucide-react';
|
|
56
|
+
import { useOrganisations } from '../../hooks/useOrganisations';
|
|
57
|
+
import { useEvents } from '../../hooks/useEvents';
|
|
58
|
+
import { useRBAC } from '../../rbac/hooks/useRBAC';
|
|
59
|
+
import type { Organisation } from '../../types/organisation';
|
|
60
|
+
import type { Event } from '../../types/event';
|
|
61
|
+
import { logger } from '../../utils/core/logger';
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Props for the ContextSelector component.
|
|
65
|
+
*/
|
|
66
|
+
export interface ContextSelectorProps {
|
|
67
|
+
/** Placeholder text for the dropdown */
|
|
68
|
+
placeholder?: string;
|
|
69
|
+
/** Additional CSS classes */
|
|
70
|
+
className?: string;
|
|
71
|
+
/** Callback fired when an organisation is selected */
|
|
72
|
+
onOrganisationSelect?: (org: Organisation) => void;
|
|
73
|
+
/** Callback fired when an event is selected */
|
|
74
|
+
onEventSelect?: (event: Event) => void;
|
|
75
|
+
/** Show friendly message when no items available */
|
|
76
|
+
showNoItemsMessage?: boolean;
|
|
77
|
+
/** Show retry button on errors */
|
|
78
|
+
showRetryButton?: boolean;
|
|
79
|
+
/** Compact display mode */
|
|
80
|
+
compact?: boolean;
|
|
81
|
+
/** Disabled state */
|
|
82
|
+
disabled?: boolean;
|
|
83
|
+
/** Show organisations section (default: true) */
|
|
84
|
+
showOrganisations?: boolean;
|
|
85
|
+
/** Show events section (default: true) */
|
|
86
|
+
showEvents?: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* ContextSelector component for selecting organisations or events
|
|
91
|
+
*
|
|
92
|
+
* This is the unified intelligent selector that shows all organisations and events
|
|
93
|
+
* a user can access. It automatically determines what to show based on the user's
|
|
94
|
+
* roles and permissions - no need to configure separate org/event selectors.
|
|
95
|
+
*
|
|
96
|
+
* The selector intelligently shows:
|
|
97
|
+
* - All organisations the user has access to (via organisation roles)
|
|
98
|
+
* - All events the user has access to (via event-app roles or organisation membership)
|
|
99
|
+
* - Everything for super admins
|
|
100
|
+
*
|
|
101
|
+
* @component
|
|
102
|
+
* @example
|
|
103
|
+
* <ContextSelector
|
|
104
|
+
* onOrganisationSelect={(org) => switchOrganisation(org)}
|
|
105
|
+
* onEventSelect={(event) => setSelectedEvent(event)}
|
|
106
|
+
* />
|
|
107
|
+
*/
|
|
108
|
+
export function ContextSelector({
|
|
109
|
+
placeholder = "Select organisation or event",
|
|
110
|
+
className,
|
|
111
|
+
onOrganisationSelect,
|
|
112
|
+
onEventSelect,
|
|
113
|
+
showNoItemsMessage = true,
|
|
114
|
+
showRetryButton = true,
|
|
115
|
+
compact = false,
|
|
116
|
+
disabled = false,
|
|
117
|
+
showOrganisations = true,
|
|
118
|
+
showEvents = true
|
|
119
|
+
}: ContextSelectorProps) {
|
|
120
|
+
const {
|
|
121
|
+
organisations,
|
|
122
|
+
selectedOrganisation,
|
|
123
|
+
isLoading: orgLoading,
|
|
124
|
+
error: orgError,
|
|
125
|
+
refreshOrganisations
|
|
126
|
+
} = useOrganisations();
|
|
127
|
+
|
|
128
|
+
const {
|
|
129
|
+
events,
|
|
130
|
+
selectedEvent,
|
|
131
|
+
isLoading: eventLoading,
|
|
132
|
+
error: eventError,
|
|
133
|
+
refreshEvents
|
|
134
|
+
} = useEvents();
|
|
135
|
+
|
|
136
|
+
const { isSuperAdmin } = useRBAC();
|
|
137
|
+
|
|
138
|
+
const isLoading = (showOrganisations && orgLoading) || (showEvents && eventLoading);
|
|
139
|
+
const hasError = (showOrganisations && orgError) || (showEvents && eventError);
|
|
140
|
+
const hasItems =
|
|
141
|
+
(showOrganisations && (organisations?.length || 0) > 0) ||
|
|
142
|
+
(showEvents && (events?.length || 0) > 0);
|
|
143
|
+
|
|
144
|
+
// Determine current selection value
|
|
145
|
+
// Priority: Event selection takes precedence over organisation selection
|
|
146
|
+
// When an event is selected, show the event (even if an org is also selected)
|
|
147
|
+
// The organisation remains in context (derived from event) but selector shows the active selection
|
|
148
|
+
const currentValue = useMemo(() => {
|
|
149
|
+
if (showEvents && selectedEvent) {
|
|
150
|
+
return `event:${selectedEvent.event_id || selectedEvent.id}`;
|
|
151
|
+
}
|
|
152
|
+
if (showOrganisations && selectedOrganisation) {
|
|
153
|
+
return `org:${selectedOrganisation.id}`;
|
|
154
|
+
}
|
|
155
|
+
return '';
|
|
156
|
+
}, [showOrganisations, showEvents, selectedOrganisation?.id, selectedEvent]);
|
|
157
|
+
|
|
158
|
+
const handleValueChange = (value: string) => {
|
|
159
|
+
if (disabled || isLoading) return;
|
|
160
|
+
|
|
161
|
+
if (value.startsWith('org:') && showOrganisations) {
|
|
162
|
+
const orgId = value.replace('org:', '');
|
|
163
|
+
const org = organisations?.find(o => o.id === orgId);
|
|
164
|
+
if (org && onOrganisationSelect) {
|
|
165
|
+
onOrganisationSelect(org);
|
|
166
|
+
}
|
|
167
|
+
} else if (value.startsWith('event:') && showEvents) {
|
|
168
|
+
const eventId = value.replace('event:', '');
|
|
169
|
+
const event = events?.find(e => (e.event_id || e.id) === eventId);
|
|
170
|
+
if (event && onEventSelect) {
|
|
171
|
+
onEventSelect(event);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const handleRetry = async () => {
|
|
177
|
+
try {
|
|
178
|
+
if (showOrganisations && orgError) {
|
|
179
|
+
await refreshOrganisations();
|
|
180
|
+
}
|
|
181
|
+
if (showEvents && eventError) {
|
|
182
|
+
await refreshEvents();
|
|
183
|
+
}
|
|
184
|
+
} catch (error) {
|
|
185
|
+
logger.error('ContextSelector', 'Failed to refresh:', error);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Loading state - only show loading if we don't have any items yet
|
|
190
|
+
// This prevents the selector from being stuck in loading when data exists
|
|
191
|
+
if (isLoading && !hasItems) {
|
|
192
|
+
const loadingText = compact ? "Loading..." :
|
|
193
|
+
showOrganisations && showEvents ? "Loading organisations and events..." :
|
|
194
|
+
showOrganisations ? "Loading organisations..." :
|
|
195
|
+
"Loading events...";
|
|
196
|
+
return (
|
|
197
|
+
<div className={`flex items-center gap-2 ${className}`}>
|
|
198
|
+
<LoadingSpinner size="sm" />
|
|
199
|
+
<span className="text-sm text-muted-foreground">
|
|
200
|
+
{loadingText}
|
|
201
|
+
</span>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Error state
|
|
207
|
+
if (hasError) {
|
|
208
|
+
const errorMessages = [];
|
|
209
|
+
if (showOrganisations && orgError) {
|
|
210
|
+
errorMessages.push(`Failed to load organisations: ${orgError.message}`);
|
|
211
|
+
}
|
|
212
|
+
if (showEvents && eventError) {
|
|
213
|
+
errorMessages.push(`Failed to load events: ${eventError.message}`);
|
|
214
|
+
}
|
|
215
|
+
return (
|
|
216
|
+
<div className={`space-y-2 ${className}`}>
|
|
217
|
+
<Alert variant="destructive">
|
|
218
|
+
<AlertCircle className="size-4" />
|
|
219
|
+
<AlertDescription>
|
|
220
|
+
{errorMessages.join(' ')}
|
|
221
|
+
</AlertDescription>
|
|
222
|
+
</Alert>
|
|
223
|
+
{showRetryButton && (
|
|
224
|
+
<Button
|
|
225
|
+
variant="outline"
|
|
226
|
+
size="sm"
|
|
227
|
+
onClick={handleRetry}
|
|
228
|
+
disabled={isLoading}
|
|
229
|
+
className="w-full"
|
|
230
|
+
>
|
|
231
|
+
<RefreshCw className={`size-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} />
|
|
232
|
+
Retry
|
|
233
|
+
</Button>
|
|
234
|
+
)}
|
|
235
|
+
</div>
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// No items available
|
|
240
|
+
if (!hasItems) {
|
|
241
|
+
if (showNoItemsMessage) {
|
|
242
|
+
const noItemsText =
|
|
243
|
+
showOrganisations && showEvents ? "No organisations or events available. Please contact your administrator." :
|
|
244
|
+
showOrganisations ? "No organisations available. Please contact your administrator." :
|
|
245
|
+
"No events available. Please contact your administrator.";
|
|
246
|
+
return (
|
|
247
|
+
<div className={`space-y-2 ${className}`}>
|
|
248
|
+
<Alert>
|
|
249
|
+
<AlertCircle className="size-4" />
|
|
250
|
+
<AlertDescription>
|
|
251
|
+
{noItemsText}
|
|
252
|
+
</AlertDescription>
|
|
253
|
+
</Alert>
|
|
254
|
+
{showRetryButton && (
|
|
255
|
+
<Button
|
|
256
|
+
variant="outline"
|
|
257
|
+
size="sm"
|
|
258
|
+
onClick={handleRetry}
|
|
259
|
+
disabled={isLoading}
|
|
260
|
+
className="w-full"
|
|
261
|
+
>
|
|
262
|
+
<RefreshCw className={`size-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} />
|
|
263
|
+
Check Again
|
|
264
|
+
</Button>
|
|
265
|
+
)}
|
|
266
|
+
</div>
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Format display value
|
|
273
|
+
// Priority: Event selection takes precedence over organisation selection (matches currentValue)
|
|
274
|
+
const displayValue = useMemo(() => {
|
|
275
|
+
if (showEvents && selectedEvent) {
|
|
276
|
+
return (
|
|
277
|
+
<div className="flex items-center gap-2">
|
|
278
|
+
<Calendar className="size-4 flex-shrink-0" />
|
|
279
|
+
<span className="truncate">{selectedEvent.event_name}</span>
|
|
280
|
+
</div>
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (showOrganisations && selectedOrganisation) {
|
|
284
|
+
return (
|
|
285
|
+
<div className="flex items-center gap-2">
|
|
286
|
+
<Building2 className="size-4 flex-shrink-0" />
|
|
287
|
+
<span className="truncate">{selectedOrganisation.display_name}</span>
|
|
288
|
+
</div>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
return null;
|
|
292
|
+
}, [showOrganisations, showEvents, selectedOrganisation, selectedEvent]);
|
|
293
|
+
|
|
294
|
+
// Determine placeholder text based on what's shown
|
|
295
|
+
const effectivePlaceholder = useMemo(() => {
|
|
296
|
+
if (placeholder !== "Select organisation or event") {
|
|
297
|
+
return placeholder;
|
|
298
|
+
}
|
|
299
|
+
if (showOrganisations && showEvents) {
|
|
300
|
+
return "Select organisation or event";
|
|
301
|
+
}
|
|
302
|
+
if (showOrganisations) {
|
|
303
|
+
return "Select organisation";
|
|
304
|
+
}
|
|
305
|
+
if (showEvents) {
|
|
306
|
+
return "Select event";
|
|
307
|
+
}
|
|
308
|
+
return placeholder;
|
|
309
|
+
}, [placeholder, showOrganisations, showEvents]);
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<div className={className} data-testid="context-selector">
|
|
313
|
+
<Select
|
|
314
|
+
value={currentValue}
|
|
315
|
+
onValueChange={handleValueChange}
|
|
316
|
+
disabled={disabled || isLoading}
|
|
317
|
+
>
|
|
318
|
+
<SelectTrigger
|
|
319
|
+
className="text-left"
|
|
320
|
+
variant="outline"
|
|
321
|
+
>
|
|
322
|
+
<SelectValue placeholder={effectivePlaceholder}>
|
|
323
|
+
{displayValue}
|
|
324
|
+
</SelectValue>
|
|
325
|
+
</SelectTrigger>
|
|
326
|
+
<SelectContent>
|
|
327
|
+
{/* Organisations Section */}
|
|
328
|
+
{showOrganisations && organisations && organisations.length > 0 && (
|
|
329
|
+
<>
|
|
330
|
+
<SelectGroup>
|
|
331
|
+
<SelectLabel>Organisations</SelectLabel>
|
|
332
|
+
{organisations.map((org) => (
|
|
333
|
+
<SelectItem
|
|
334
|
+
key={org.id}
|
|
335
|
+
value={`org:${org.id}`}
|
|
336
|
+
>
|
|
337
|
+
<div className="flex items-center gap-2">
|
|
338
|
+
<Building2 className="size-4" />
|
|
339
|
+
<div className="flex flex-col">
|
|
340
|
+
<span className="font-medium">{org.display_name}</span>
|
|
341
|
+
{!compact && org.description && (
|
|
342
|
+
<span className="text-xs text-muted-foreground truncate max-w-40">
|
|
343
|
+
{org.description}
|
|
344
|
+
</span>
|
|
345
|
+
)}
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
</SelectItem>
|
|
349
|
+
))}
|
|
350
|
+
</SelectGroup>
|
|
351
|
+
{showEvents && events && events.length > 0 && <SelectSeparator />}
|
|
352
|
+
</>
|
|
353
|
+
)}
|
|
354
|
+
|
|
355
|
+
{/* Events Section */}
|
|
356
|
+
{showEvents && events && events.length > 0 && (
|
|
357
|
+
<SelectGroup>
|
|
358
|
+
<SelectLabel>Events</SelectLabel>
|
|
359
|
+
{events.map((event) => (
|
|
360
|
+
<SelectItem
|
|
361
|
+
key={event.event_id || event.id}
|
|
362
|
+
value={`event:${event.event_id || event.id}`}
|
|
363
|
+
>
|
|
364
|
+
<div className="flex items-center gap-2">
|
|
365
|
+
<Calendar className="size-4" />
|
|
366
|
+
<div className="flex flex-col">
|
|
367
|
+
<span className="font-medium">{event.event_name}</span>
|
|
368
|
+
{!compact && event.event_date && (
|
|
369
|
+
<span className="text-xs text-muted-foreground">
|
|
370
|
+
{new Date(event.event_date).toLocaleDateString()}
|
|
371
|
+
</span>
|
|
372
|
+
)}
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
</SelectItem>
|
|
376
|
+
))}
|
|
377
|
+
</SelectGroup>
|
|
378
|
+
)}
|
|
379
|
+
</SelectContent>
|
|
380
|
+
</Select>
|
|
381
|
+
</div>
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
@@ -462,6 +462,38 @@ export interface DataTableProps<TData extends DataRecord> {
|
|
|
462
462
|
*
|
|
463
463
|
* Features are configured through the unified `features` prop for maximum flexibility.
|
|
464
464
|
*/
|
|
465
|
+
/**
|
|
466
|
+
* Enhanced DataTable component with performance optimizations and comprehensive features.
|
|
467
|
+
*
|
|
468
|
+
* This is the main entry point for the DataTable component. It wraps DataTableCore
|
|
469
|
+
* and provides feature normalization and validation.
|
|
470
|
+
*
|
|
471
|
+
* @template TData - The type of data records in the table
|
|
472
|
+
* @param props - DataTable configuration including data, columns, features, and RBAC
|
|
473
|
+
* @returns The rendered DataTable component
|
|
474
|
+
*
|
|
475
|
+
* @example
|
|
476
|
+
* ```tsx
|
|
477
|
+
* <DataTable
|
|
478
|
+
* data={users}
|
|
479
|
+
* columns={userColumns}
|
|
480
|
+
* rbac={{ pageId: 'user-management' }}
|
|
481
|
+
* features={{
|
|
482
|
+
* search: true,
|
|
483
|
+
* pagination: true,
|
|
484
|
+
* sorting: true
|
|
485
|
+
* }}
|
|
486
|
+
* />
|
|
487
|
+
* ```
|
|
488
|
+
*/
|
|
489
|
+
/**
|
|
490
|
+
* Comprehensive, feature-rich data table component built on top of TanStack Table.
|
|
491
|
+
* Provides advanced data management capabilities with a clean, accessible interface.
|
|
492
|
+
*
|
|
493
|
+
* @template TData - The type of data records in the table
|
|
494
|
+
* @param props - DataTable configuration and props
|
|
495
|
+
* @returns The rendered DataTable component
|
|
496
|
+
*/
|
|
465
497
|
export function DataTable<TData extends DataRecord>(props: DataTableProps<TData>) {
|
|
466
498
|
const logger = createLogger('DataTable');
|
|
467
499
|
const { features, ...rest } = props;
|
|
@@ -485,11 +517,13 @@ export function DataTable<TData extends DataRecord>(props: DataTableProps<TData>
|
|
|
485
517
|
// ============================================================================
|
|
486
518
|
|
|
487
519
|
/**
|
|
488
|
-
* DataTable component
|
|
520
|
+
* DataTable component export
|
|
521
|
+
*
|
|
522
|
+
* Note: React.memo removed for React 19 compatibility. React 19's automatic
|
|
523
|
+
* memoization (React Compiler) handles optimization automatically, and manual
|
|
524
|
+
* memoization can interfere with prop updates in React 19.
|
|
489
525
|
*/
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
export { DataTableComponent as default };
|
|
526
|
+
export { DataTable as default };
|
|
493
527
|
|
|
494
528
|
// Re-export for convenience
|
|
495
529
|
export { DataTable as EnhancedDataTable };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="vitest/globals" />
|
|
1
2
|
/**
|
|
2
3
|
* @file DataTableCore Test Setup
|
|
3
4
|
* @package @jmruthers/pace-core
|
|
@@ -7,8 +8,6 @@
|
|
|
7
8
|
* Shared test setup and mocks for DataTableCore component tests.
|
|
8
9
|
* This file is imported by all DataTableCore test files to ensure consistent mocking.
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
-
import { vi } from 'vitest';
|
|
12
11
|
import React from 'react';
|
|
13
12
|
|
|
14
13
|
// Mock the RBAC hooks
|
|
@@ -132,7 +131,7 @@ vi.mock('../hooks/useColumnOrderPersistence', () => ({
|
|
|
132
131
|
// Mock the column factory
|
|
133
132
|
vi.mock('../core/ColumnFactory', () => ({
|
|
134
133
|
ColumnFactory: {
|
|
135
|
-
createColumns: vi.fn((columns) => columns),
|
|
134
|
+
createColumns: vi.fn((columns: unknown) => columns),
|
|
136
135
|
createActionColumn: vi.fn(() => ({
|
|
137
136
|
id: 'actions',
|
|
138
137
|
header: 'Actions',
|
|
@@ -144,11 +143,11 @@ vi.mock('../core/ColumnFactory', () => ({
|
|
|
144
143
|
// Mock the hierarchical utilities
|
|
145
144
|
vi.mock('../utils/hierarchicalUtils', () => ({
|
|
146
145
|
validateHierarchicalData: vi.fn(() => true),
|
|
147
|
-
sortHierarchicalData: vi.fn((data) => data),
|
|
146
|
+
sortHierarchicalData: vi.fn((data: unknown) => data),
|
|
148
147
|
}));
|
|
149
148
|
|
|
150
149
|
vi.mock('../utils/hierarchicalSorting', () => ({
|
|
151
|
-
sortHierarchicalData: vi.fn((data) => data),
|
|
150
|
+
sortHierarchicalData: vi.fn((data: unknown) => data),
|
|
152
151
|
}));
|
|
153
152
|
|
|
154
153
|
// Create a mock implementation that can be overridden
|
|
@@ -184,7 +183,7 @@ vi.mock('../hooks/useDataTablePermissions', () => ({
|
|
|
184
183
|
}));
|
|
185
184
|
|
|
186
185
|
vi.mock('../hooks/useTableColumns', () => ({
|
|
187
|
-
useTableColumns: vi.fn(({ columns }) => ({
|
|
186
|
+
useTableColumns: vi.fn(({ columns }: { columns?: unknown[] }) => ({
|
|
188
187
|
enhancedColumns: columns || []
|
|
189
188
|
}))
|
|
190
189
|
}));
|