@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
|
@@ -25,10 +25,35 @@
|
|
|
25
25
|
* <MyComponent />
|
|
26
26
|
* </ErrorBoundary>
|
|
27
27
|
*
|
|
28
|
+
* // Using global error handler (recommended for applications)
|
|
29
|
+
* import { ErrorBoundaryProvider, ErrorBoundary } from '@jmruthers/pace-core';
|
|
30
|
+
*
|
|
31
|
+
* // Setup once in main.tsx
|
|
32
|
+
* <ErrorBoundaryProvider
|
|
33
|
+
* defaultErrorHandler={(error, errorInfo, errorId, componentName) => {
|
|
34
|
+
* if (import.meta.env.DEV) {
|
|
35
|
+
* console.error(`[${componentName}] ErrorBoundary caught error:`, error, errorInfo);
|
|
36
|
+
* }
|
|
37
|
+
* errorTracking.captureException(error, {
|
|
38
|
+
* componentStack: errorInfo.componentStack,
|
|
39
|
+
* errorBoundary: true,
|
|
40
|
+
* errorId,
|
|
41
|
+
* componentName,
|
|
42
|
+
* });
|
|
43
|
+
* }}
|
|
44
|
+
* >
|
|
45
|
+
* <App />
|
|
46
|
+
* </ErrorBoundaryProvider>
|
|
47
|
+
*
|
|
48
|
+
* // Then use ErrorBoundary without onError prop - uses global handler
|
|
49
|
+
* <ErrorBoundary componentName="AppRoot">
|
|
50
|
+
* <AppContent />
|
|
51
|
+
* </ErrorBoundary>
|
|
52
|
+
*
|
|
28
53
|
* // Error boundary with custom fallback
|
|
29
54
|
* <ErrorBoundary
|
|
30
55
|
* componentName="UserProfile"
|
|
31
|
-
* fallback={<
|
|
56
|
+
* fallback={<section>Something went wrong loading the profile.</section>}
|
|
32
57
|
* onError={(error, errorInfo, errorId) => {
|
|
33
58
|
* console.log('Error caught:', errorId, error);
|
|
34
59
|
* }}
|
|
@@ -58,6 +83,16 @@
|
|
|
58
83
|
* </ErrorBoundary>
|
|
59
84
|
* <Footer />
|
|
60
85
|
* </ErrorBoundary>
|
|
86
|
+
*
|
|
87
|
+
* // Per-instance onError overrides global handler
|
|
88
|
+
* <ErrorBoundary
|
|
89
|
+
* componentName="SpecialCase"
|
|
90
|
+
* onError={(error, errorInfo, errorId) => {
|
|
91
|
+
* // Custom handling for this specific case - global handler won't be called
|
|
92
|
+
* }}
|
|
93
|
+
* >
|
|
94
|
+
* <SpecialComponent />
|
|
95
|
+
* </ErrorBoundary>
|
|
61
96
|
* ```
|
|
62
97
|
*
|
|
63
98
|
* @accessibility
|
|
@@ -120,6 +155,8 @@ export interface ErrorBoundaryProps {
|
|
|
120
155
|
enableRetry?: boolean;
|
|
121
156
|
/** Whether to enable error reporting */
|
|
122
157
|
enableReporting?: boolean;
|
|
158
|
+
/** Internal: Global error handler from context (not part of public API) */
|
|
159
|
+
_globalErrorHandler?: (error: Error, errorInfo: React.ErrorInfo, errorId: string, componentName: string) => void;
|
|
123
160
|
}
|
|
124
161
|
|
|
125
162
|
/**
|
|
@@ -162,15 +199,16 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
162
199
|
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
|
163
200
|
const { componentName = 'Unknown Component', onError, enableReporting = true } = this.props;
|
|
164
201
|
const errorId = this.state.errorId!;
|
|
202
|
+
const componentNameForHandler = componentName || 'Unknown Component';
|
|
165
203
|
|
|
166
204
|
this.setState({ errorInfo });
|
|
167
205
|
|
|
168
206
|
// Enhanced logging with component name and error ID
|
|
169
|
-
logger.error('ErrorBoundary', `[${
|
|
207
|
+
logger.error('ErrorBoundary', `[${componentNameForHandler}] Caught error ${errorId}:`, error, errorInfo);
|
|
170
208
|
|
|
171
209
|
// Performance monitoring - track error occurrence
|
|
172
210
|
performanceBudgetMonitor.measure('ERROR_BOUNDARY_TRIGGER', 1, {
|
|
173
|
-
componentName,
|
|
211
|
+
componentName: componentNameForHandler,
|
|
174
212
|
errorId,
|
|
175
213
|
errorMessage: error.message,
|
|
176
214
|
stack: error.stack?.substring(0, 200), // Truncated stack trace
|
|
@@ -178,12 +216,14 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
|
|
|
178
216
|
|
|
179
217
|
// Report error if enabled
|
|
180
218
|
if (enableReporting) {
|
|
181
|
-
this.reportError(errorId,
|
|
219
|
+
this.reportError(errorId, componentNameForHandler);
|
|
182
220
|
}
|
|
183
221
|
|
|
184
|
-
// Call
|
|
222
|
+
// Call error handler: prefer prop onError, fall back to global handler from props
|
|
185
223
|
if (onError) {
|
|
186
224
|
onError(error, errorInfo, errorId);
|
|
225
|
+
} else if (this.props._globalErrorHandler) {
|
|
226
|
+
this.props._globalErrorHandler(error, errorInfo, errorId, componentNameForHandler);
|
|
187
227
|
}
|
|
188
228
|
}
|
|
189
229
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Error Boundary Context
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Components/ErrorBoundary
|
|
5
|
+
* @since 0.6.2
|
|
6
|
+
*
|
|
7
|
+
* Context provider for global error handler configuration in ErrorBoundary components.
|
|
8
|
+
* Allows applications to register a default error handler once, which is then used by
|
|
9
|
+
* all ErrorBoundary instances unless overridden.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* // Setup once in main.tsx
|
|
14
|
+
* <ErrorBoundaryProvider
|
|
15
|
+
* defaultErrorHandler={(error, errorInfo, errorId, componentName) => {
|
|
16
|
+
* if (import.meta.env.DEV) {
|
|
17
|
+
* console.error(`[${componentName}] ErrorBoundary caught error:`, error, errorInfo);
|
|
18
|
+
* }
|
|
19
|
+
* errorTracking.captureException(error, {
|
|
20
|
+
* componentStack: errorInfo.componentStack,
|
|
21
|
+
* errorBoundary: true,
|
|
22
|
+
* errorId,
|
|
23
|
+
* componentName,
|
|
24
|
+
* });
|
|
25
|
+
* }}
|
|
26
|
+
* >
|
|
27
|
+
* <App />
|
|
28
|
+
* </ErrorBoundaryProvider>
|
|
29
|
+
*
|
|
30
|
+
* // Usage - no onError needed, uses global handler
|
|
31
|
+
* <ErrorBoundary componentName="AppRoot">
|
|
32
|
+
* <AppContent />
|
|
33
|
+
* </ErrorBoundary>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import React, { createContext, useContext, ReactNode } from 'react';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Type definition for the global error handler function
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
export type GlobalErrorHandler = (
|
|
44
|
+
error: Error,
|
|
45
|
+
errorInfo: React.ErrorInfo,
|
|
46
|
+
errorId: string,
|
|
47
|
+
componentName: string
|
|
48
|
+
) => void;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Context value type for ErrorBoundary configuration
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
interface ErrorBoundaryContextType {
|
|
55
|
+
/** Global error handler that will be used by all ErrorBoundary instances */
|
|
56
|
+
defaultErrorHandler?: GlobalErrorHandler;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Context for ErrorBoundary global configuration
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
const ErrorBoundaryContext = createContext<ErrorBoundaryContextType | undefined>(undefined);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Props for ErrorBoundaryProvider component
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
export interface ErrorBoundaryProviderProps {
|
|
70
|
+
/** Children to wrap with the provider */
|
|
71
|
+
children: ReactNode;
|
|
72
|
+
/** Global error handler that will be used by all ErrorBoundary instances unless overridden */
|
|
73
|
+
defaultErrorHandler?: GlobalErrorHandler;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Provider component for ErrorBoundary global error handler configuration
|
|
78
|
+
*
|
|
79
|
+
* Wrap your application with this provider to set a default error handler that will
|
|
80
|
+
* be used by all ErrorBoundary instances unless they provide their own onError prop.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```tsx
|
|
84
|
+
* <ErrorBoundaryProvider
|
|
85
|
+
* defaultErrorHandler={(error, errorInfo, errorId, componentName) => {
|
|
86
|
+
* errorTracking.captureException(error, {
|
|
87
|
+
* componentStack: errorInfo.componentStack,
|
|
88
|
+
* errorBoundary: true,
|
|
89
|
+
* errorId,
|
|
90
|
+
* componentName,
|
|
91
|
+
* });
|
|
92
|
+
* }}
|
|
93
|
+
* >
|
|
94
|
+
* <App />
|
|
95
|
+
* </ErrorBoundaryProvider>
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @public
|
|
99
|
+
*/
|
|
100
|
+
/**
|
|
101
|
+
* Error boundary provider component.
|
|
102
|
+
* Provides global error handling configuration to child ErrorBoundary components.
|
|
103
|
+
*
|
|
104
|
+
* @param props - Error boundary provider configuration
|
|
105
|
+
* @returns The error boundary provider
|
|
106
|
+
*/
|
|
107
|
+
export function ErrorBoundaryProvider({
|
|
108
|
+
children,
|
|
109
|
+
defaultErrorHandler,
|
|
110
|
+
}: ErrorBoundaryProviderProps) {
|
|
111
|
+
const contextValue: ErrorBoundaryContextType = {
|
|
112
|
+
defaultErrorHandler,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<ErrorBoundaryContext.Provider value={contextValue}>
|
|
117
|
+
{children}
|
|
118
|
+
</ErrorBoundaryContext.Provider>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Hook to access ErrorBoundary context
|
|
124
|
+
* @internal
|
|
125
|
+
*/
|
|
126
|
+
export function useErrorBoundaryContext(): ErrorBoundaryContextType | undefined {
|
|
127
|
+
return useContext(ErrorBoundaryContext);
|
|
128
|
+
}
|
|
129
|
+
|
|
@@ -2,7 +2,32 @@
|
|
|
2
2
|
* @file Error Boundary exports
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
// Re-export ErrorBoundary with automatic context support
|
|
6
|
+
import { ErrorBoundary as ErrorBoundaryClass } from './ErrorBoundary';
|
|
7
|
+
import { useErrorBoundaryContext } from './ErrorBoundaryContext';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import type { ErrorBoundaryProps } from './ErrorBoundary';
|
|
10
|
+
|
|
11
|
+
// Export types and provider
|
|
6
12
|
export type { ErrorBoundaryProps, ErrorBoundaryState } from './ErrorBoundary';
|
|
7
|
-
|
|
13
|
+
export { ErrorBoundaryProvider } from './ErrorBoundaryContext';
|
|
14
|
+
export type { ErrorBoundaryProviderProps, GlobalErrorHandler } from './ErrorBoundaryContext';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* ErrorBoundary component with automatic context support
|
|
18
|
+
* This wrapper automatically uses the global error handler from ErrorBoundaryProvider
|
|
19
|
+
* if no onError prop is provided.
|
|
20
|
+
*/
|
|
21
|
+
export const ErrorBoundary = React.forwardRef<
|
|
22
|
+
ErrorBoundaryClass,
|
|
23
|
+
Omit<ErrorBoundaryProps, '_globalErrorHandler'>
|
|
24
|
+
>((props, ref) => {
|
|
25
|
+
const context = useErrorBoundaryContext();
|
|
26
|
+
const globalErrorHandler = context?.defaultErrorHandler;
|
|
27
|
+
|
|
28
|
+
return React.createElement(ErrorBoundaryClass, { ...props, ref, _globalErrorHandler: globalErrorHandler });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
ErrorBoundary.displayName = 'ErrorBoundary';
|
|
32
|
+
|
|
8
33
|
export default ErrorBoundary;
|
|
@@ -125,7 +125,7 @@ interface FileDisplayContentProps {
|
|
|
125
125
|
showMetadata?: boolean;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
function FileDisplayContent({
|
|
128
|
+
const FileDisplayContent = React.memo(function FileDisplayContent({
|
|
129
129
|
isLoading,
|
|
130
130
|
error,
|
|
131
131
|
fileUrl,
|
|
@@ -156,6 +156,29 @@ function FileDisplayContent({
|
|
|
156
156
|
const [internalFileUrls, setInternalFileUrls] = useState<Map<string, string>>(new Map(fileUrls));
|
|
157
157
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
158
158
|
const fileReferencesRef = useRef<FileReference[]>([]);
|
|
159
|
+
const imgRef = useRef<HTMLImageElement | null>(null);
|
|
160
|
+
const currentSrcRef = useRef<string | null>(null);
|
|
161
|
+
const isImageLoadingRef = useRef(false);
|
|
162
|
+
|
|
163
|
+
// Stabilize fileUrl to prevent unnecessary image reloads
|
|
164
|
+
// This prevents NS_BINDING_ABORTED errors when the component re-renders
|
|
165
|
+
const stableFileUrl = useMemo(() => fileUrl, [fileUrl]);
|
|
166
|
+
|
|
167
|
+
// Track when image starts loading to prevent cancellation
|
|
168
|
+
const handleImageLoadStart = useCallback(() => {
|
|
169
|
+
isImageLoadingRef.current = true;
|
|
170
|
+
if (stableFileUrl) {
|
|
171
|
+
currentSrcRef.current = stableFileUrl;
|
|
172
|
+
}
|
|
173
|
+
}, [stableFileUrl]);
|
|
174
|
+
|
|
175
|
+
// Track when image finishes loading
|
|
176
|
+
const handleImageLoad = useCallback(() => {
|
|
177
|
+
isImageLoadingRef.current = false;
|
|
178
|
+
if (stableFileUrl) {
|
|
179
|
+
currentSrcRef.current = stableFileUrl;
|
|
180
|
+
}
|
|
181
|
+
}, [stableFileUrl]);
|
|
159
182
|
|
|
160
183
|
// Compute fallback text
|
|
161
184
|
const computedFallbackText = useMemo(() => {
|
|
@@ -192,10 +215,15 @@ function FileDisplayContent({
|
|
|
192
215
|
|
|
193
216
|
const handleDeleteConfirm = async () => {
|
|
194
217
|
setDeleteDialogOpen(false);
|
|
195
|
-
|
|
196
|
-
|
|
218
|
+
try {
|
|
219
|
+
if (onDelete) {
|
|
220
|
+
await onDelete();
|
|
221
|
+
}
|
|
222
|
+
setImageError(false);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
// Error handling is delegated to onDelete callback
|
|
225
|
+
setImageError(false);
|
|
197
226
|
}
|
|
198
|
-
setImageError(false);
|
|
199
227
|
};
|
|
200
228
|
|
|
201
229
|
const handleImageError = () => {
|
|
@@ -323,7 +351,7 @@ function FileDisplayContent({
|
|
|
323
351
|
}
|
|
324
352
|
|
|
325
353
|
// Show loading skeleton if URL is not available yet
|
|
326
|
-
if (!
|
|
354
|
+
if (!stableFileUrl) {
|
|
327
355
|
return (
|
|
328
356
|
<figure className={className || "max-w-full h-48"} title="Loading">
|
|
329
357
|
<p className={fallbackClasses}>
|
|
@@ -336,10 +364,15 @@ function FileDisplayContent({
|
|
|
336
364
|
return (
|
|
337
365
|
<figure className={className || ""}>
|
|
338
366
|
<img
|
|
339
|
-
|
|
367
|
+
ref={imgRef}
|
|
368
|
+
key={fileReference.id}
|
|
369
|
+
src={stableFileUrl || undefined}
|
|
340
370
|
alt={fileReference.file_metadata.fileName || 'File'}
|
|
341
371
|
className={imgClassName || "object-cover size-full"}
|
|
342
372
|
onError={handleImageError}
|
|
373
|
+
onLoadStart={handleImageLoadStart}
|
|
374
|
+
onLoad={handleImageLoad}
|
|
375
|
+
loading="lazy"
|
|
343
376
|
/>
|
|
344
377
|
</figure>
|
|
345
378
|
);
|
|
@@ -347,14 +380,14 @@ function FileDisplayContent({
|
|
|
347
380
|
|
|
348
381
|
// Document link display when displayOnly is true and file is not an image
|
|
349
382
|
// Render non-image files as clickable links that open in a new tab
|
|
350
|
-
if (displayOnly && !isImage &&
|
|
383
|
+
if (displayOnly && !isImage && stableFileUrl && fileReference && !showDelete) {
|
|
351
384
|
const fileName = fileReference.file_metadata?.fileName || 'Document';
|
|
352
385
|
const ariaLabel = `Open ${fileName} in new tab`;
|
|
353
386
|
|
|
354
387
|
return (
|
|
355
388
|
<figure className={className}>
|
|
356
389
|
<a
|
|
357
|
-
href={
|
|
390
|
+
href={stableFileUrl || undefined}
|
|
358
391
|
target="_blank"
|
|
359
392
|
rel="noopener noreferrer"
|
|
360
393
|
aria-label={ariaLabel}
|
|
@@ -380,7 +413,7 @@ function FileDisplayContent({
|
|
|
380
413
|
|
|
381
414
|
// Standard single file display with wrapper
|
|
382
415
|
// For displayOnly mode, if fallback is enabled and there's no URL or image error, show fallback instead of folder icon
|
|
383
|
-
if (displayOnly && showFallback && (!
|
|
416
|
+
if (displayOnly && showFallback && (!stableFileUrl || imageError || !isImage)) {
|
|
384
417
|
return (
|
|
385
418
|
<figure className={className} title={fileReference.file_metadata.fileName || 'File'}>
|
|
386
419
|
<p className={fallbackClasses}>
|
|
@@ -392,13 +425,15 @@ function FileDisplayContent({
|
|
|
392
425
|
|
|
393
426
|
return (
|
|
394
427
|
<figure className={`relative ${className}`}>
|
|
395
|
-
{isImage &&
|
|
428
|
+
{isImage && stableFileUrl && !imageError ? (
|
|
396
429
|
<>
|
|
397
430
|
<img
|
|
398
|
-
|
|
431
|
+
key={fileReference.id}
|
|
432
|
+
src={stableFileUrl}
|
|
399
433
|
alt={fileReference.file_metadata.fileName || 'File'}
|
|
400
434
|
className={imgClassName || "object-cover size-full"}
|
|
401
435
|
onError={handleImageError}
|
|
436
|
+
loading="lazy"
|
|
402
437
|
/>
|
|
403
438
|
{showDelete && (
|
|
404
439
|
<>
|
|
@@ -536,10 +571,12 @@ function FileDisplayContent({
|
|
|
536
571
|
<figure key={fileRef.id} className="flex items-center space-x-3 p-3 bg-sec-50 rounded-lg border border-sec-200">
|
|
537
572
|
{isImage && fileUrl ? (
|
|
538
573
|
<img
|
|
539
|
-
|
|
574
|
+
key={fileRef.id}
|
|
575
|
+
src={fileUrl || undefined}
|
|
540
576
|
alt={fileRef.file_metadata.fileName || 'File'}
|
|
541
577
|
className={imgClassName || "object-cover size-full"}
|
|
542
578
|
onError={handleImageError}
|
|
579
|
+
loading="lazy"
|
|
543
580
|
/>
|
|
544
581
|
) : (
|
|
545
582
|
<span className="text-2xl">
|
|
@@ -591,7 +628,7 @@ function FileDisplayContent({
|
|
|
591
628
|
{children}
|
|
592
629
|
</figure>
|
|
593
630
|
);
|
|
594
|
-
}
|
|
631
|
+
});
|
|
595
632
|
|
|
596
633
|
/**
|
|
597
634
|
* Internal component for public page context
|
|
@@ -773,16 +810,10 @@ function FileDisplayAuthenticated({
|
|
|
773
810
|
}: FileDisplayProps) {
|
|
774
811
|
const { supabase } = useUnifiedAuth();
|
|
775
812
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
<figure className={className} title="Error">
|
|
779
|
-
<p className={getFallbackClasses(fallbackSize || 'md')}>
|
|
780
|
-
Supabase client not available in authenticated context
|
|
781
|
-
</p>
|
|
782
|
-
</figure>
|
|
783
|
-
);
|
|
784
|
-
}
|
|
813
|
+
// Consolidated state for displayOnly mode - must be before early return
|
|
814
|
+
const [displayOnlyFileReference, setDisplayOnlyFileReference] = useState<FileReference | null>(null);
|
|
785
815
|
|
|
816
|
+
// Call hooks before any early returns - hooks must handle null supabase gracefully
|
|
786
817
|
const {
|
|
787
818
|
fileUrl,
|
|
788
819
|
fileReference,
|
|
@@ -797,20 +828,17 @@ function FileDisplayAuthenticated({
|
|
|
797
828
|
record_id,
|
|
798
829
|
organisation_id,
|
|
799
830
|
category,
|
|
800
|
-
{ supabase }
|
|
831
|
+
{ supabase: supabase || null }
|
|
801
832
|
);
|
|
802
|
-
|
|
803
|
-
// Consolidated state for displayOnly mode
|
|
804
|
-
const [displayOnlyFileReference, setDisplayOnlyFileReference] = useState<FileReference | null>(null);
|
|
805
833
|
|
|
806
834
|
// Use fileUrls map if available, otherwise use useFileUrl hook
|
|
807
835
|
const displayOnlyFileUrlFromMap = displayOnlyFileReference ? fileUrls.get(displayOnlyFileReference.id) : null;
|
|
808
836
|
const displayOnlyFileUrlHook = useFileUrl(
|
|
809
837
|
displayOnlyFileReference && !displayOnlyFileUrlFromMap ? displayOnlyFileReference : null,
|
|
810
838
|
{
|
|
811
|
-
supabase,
|
|
839
|
+
supabase: supabase || null,
|
|
812
840
|
organisation_id,
|
|
813
|
-
autoLoad: !displayOnlyFileUrlFromMap && !!displayOnlyFileReference
|
|
841
|
+
autoLoad: !displayOnlyFileUrlFromMap && !!displayOnlyFileReference && !!supabase
|
|
814
842
|
}
|
|
815
843
|
);
|
|
816
844
|
const displayOnlyFileUrl = displayOnlyFileUrlFromMap || displayOnlyFileUrlHook.url;
|
|
@@ -831,6 +859,17 @@ function FileDisplayAuthenticated({
|
|
|
831
859
|
}
|
|
832
860
|
}, [displayOnly, category, fileReferences, fileUrls]);
|
|
833
861
|
|
|
862
|
+
// Early return check after all hooks are called
|
|
863
|
+
if (!supabase) {
|
|
864
|
+
return (
|
|
865
|
+
<figure className={className} title="Error">
|
|
866
|
+
<p className={getFallbackClasses(fallbackSize || 'md')}>
|
|
867
|
+
Supabase client not available in authenticated context
|
|
868
|
+
</p>
|
|
869
|
+
</figure>
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
|
|
834
873
|
// Delete operation - implementation pending
|
|
835
874
|
const handleDelete = async () => {
|
|
836
875
|
// TODO: Implement delete via FileReferenceService when delete functionality is needed
|
|
@@ -906,6 +945,13 @@ function FileDisplayAuthenticated({
|
|
|
906
945
|
* @param props.category - Optional category filter. When specified, only displays files matching this category and uses single file display variant.
|
|
907
946
|
* @returns React element with file display
|
|
908
947
|
*/
|
|
948
|
+
/**
|
|
949
|
+
* File display component.
|
|
950
|
+
* Renders files from the file reference system with support for previews, downloads, and public/private access.
|
|
951
|
+
*
|
|
952
|
+
* @param props - File display configuration
|
|
953
|
+
* @returns The rendered file display
|
|
954
|
+
*/
|
|
909
955
|
export function FileDisplay({
|
|
910
956
|
table_name,
|
|
911
957
|
record_id,
|
|
@@ -15,6 +15,10 @@ import { getCurrentAppName } from '../../utils/app/appNameResolver';
|
|
|
15
15
|
import { getAppId } from '../../utils/app/appIdResolver';
|
|
16
16
|
import { assertAppId } from '../../types/core';
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Props for the FileUpload component.
|
|
20
|
+
* Configures file upload behavior including storage location, validation, and callbacks.
|
|
21
|
+
*/
|
|
18
22
|
export interface FileUploadProps {
|
|
19
23
|
supabase: SupabaseClient;
|
|
20
24
|
table_name: string;
|
|
@@ -71,6 +75,15 @@ export function FileUpload({
|
|
|
71
75
|
onProgress,
|
|
72
76
|
children
|
|
73
77
|
}: FileUploadProps) {
|
|
78
|
+
// Validate pageContext early - required prop
|
|
79
|
+
if (!pageContext) {
|
|
80
|
+
const errorMsg = 'pageContext is required for FileUpload component. This is used for permission checks.';
|
|
81
|
+
if (import.meta.env.MODE === 'development') {
|
|
82
|
+
console.error('[FileUpload]', errorMsg);
|
|
83
|
+
}
|
|
84
|
+
throw new Error(errorMsg);
|
|
85
|
+
}
|
|
86
|
+
|
|
74
87
|
const [isDragging, setIsDragging] = useState(false);
|
|
75
88
|
const [uploadStates, setUploadStates] = useState<Map<string, FileUploadState>>(new Map());
|
|
76
89
|
const [resolvedAppId, setResolvedAppId] = useState<string | null>(app_id || null);
|
|
@@ -286,6 +299,12 @@ export function FileUpload({
|
|
|
286
299
|
throw new Error(errorMsg);
|
|
287
300
|
}
|
|
288
301
|
|
|
302
|
+
// Validate pageContext before upload
|
|
303
|
+
if (!pageContext) {
|
|
304
|
+
const errorMsg = 'pageContext is required for file upload. This is used for permission checks.';
|
|
305
|
+
throw new Error(errorMsg);
|
|
306
|
+
}
|
|
307
|
+
|
|
289
308
|
const result = await uploadFile({
|
|
290
309
|
table_name,
|
|
291
310
|
record_id,
|
|
@@ -294,7 +313,7 @@ export function FileUpload({
|
|
|
294
313
|
app_id: resolvedAppId ? assertAppId(resolvedAppId) : assertAppId(''),
|
|
295
314
|
category,
|
|
296
315
|
folder,
|
|
297
|
-
pageContext,
|
|
316
|
+
pageContext: pageContext,
|
|
298
317
|
event_id,
|
|
299
318
|
is_public: isPublic
|
|
300
319
|
}, file);
|
|
@@ -393,7 +412,7 @@ export function FileUpload({
|
|
|
393
412
|
onUploadError?.(errorMessage, file);
|
|
394
413
|
}
|
|
395
414
|
}
|
|
396
|
-
}, [uploadFile, table_name, record_id, organisation_id, resolvedAppId, category, folder, isPublic, maxSize, onUploadSuccess, onUploadError, onProgress, validateFile, generatePreview, showPreview, appIdError]);
|
|
415
|
+
}, [uploadFile, table_name, record_id, organisation_id, resolvedAppId, category, folder, isPublic, maxSize, onUploadSuccess, onUploadError, onProgress, validateFile, generatePreview, showPreview, appIdError, pageContext]);
|
|
397
416
|
|
|
398
417
|
const handleDragOver = useCallback((e: React.DragEvent) => {
|
|
399
418
|
e.preventDefault();
|
|
@@ -475,6 +494,7 @@ export function FileUpload({
|
|
|
475
494
|
className="hidden"
|
|
476
495
|
disabled={isDisabled}
|
|
477
496
|
data-testid="file-input"
|
|
497
|
+
aria-label={accept ? `Upload file${multiple ? 's' : ''} (${accept})` : `Upload file${multiple ? 's' : ''}`}
|
|
478
498
|
/>
|
|
479
499
|
<div className="text-sec-600">
|
|
480
500
|
{isResolvingAppId ? (
|
|
@@ -82,7 +82,7 @@ describe('Footer Component', () => {
|
|
|
82
82
|
it('renders with children content', () => {
|
|
83
83
|
renderWithProviders(
|
|
84
84
|
<Footer>
|
|
85
|
-
<
|
|
85
|
+
<section data-testid="custom-content">Custom footer content</section>
|
|
86
86
|
</Footer>
|
|
87
87
|
);
|
|
88
88
|
|
|
@@ -163,7 +163,7 @@ describe('Footer Component', () => {
|
|
|
163
163
|
it('renders content in proper semantic structure', () => {
|
|
164
164
|
renderWithProviders(
|
|
165
165
|
<Footer>
|
|
166
|
-
<
|
|
166
|
+
<section>Custom content</section>
|
|
167
167
|
</Footer>
|
|
168
168
|
);
|
|
169
169
|
|
|
@@ -175,7 +175,7 @@ describe('Footer Component', () => {
|
|
|
175
175
|
it('renders logo before children content', () => {
|
|
176
176
|
renderWithProviders(
|
|
177
177
|
<Footer logo="/logo.png">
|
|
178
|
-
<
|
|
178
|
+
<section data-testid="children">Children content</section>
|
|
179
179
|
</Footer>
|
|
180
180
|
);
|
|
181
181
|
|
|
@@ -189,7 +189,7 @@ describe('Footer Component', () => {
|
|
|
189
189
|
it('renders copyright text after children content', () => {
|
|
190
190
|
renderWithProviders(
|
|
191
191
|
<Footer>
|
|
192
|
-
<
|
|
192
|
+
<section data-testid="children">Children content</section>
|
|
193
193
|
</Footer>
|
|
194
194
|
);
|
|
195
195
|
|
|
@@ -380,7 +380,7 @@ describe('Footer Component', () => {
|
|
|
380
380
|
logo="/test-logo.png"
|
|
381
381
|
copyright="Custom copyright text"
|
|
382
382
|
>
|
|
383
|
-
<
|
|
383
|
+
<section>Test children</section>
|
|
384
384
|
</Footer>
|
|
385
385
|
);
|
|
386
386
|
|
|
@@ -398,27 +398,27 @@ describe('Footer Component', () => {
|
|
|
398
398
|
it('works with complex children content', () => {
|
|
399
399
|
renderWithProviders(
|
|
400
400
|
<Footer>
|
|
401
|
-
<
|
|
402
|
-
<
|
|
401
|
+
<section className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
|
|
402
|
+
<section>
|
|
403
403
|
<h3 className="font-semibold mb-2">About Us</h3>
|
|
404
404
|
<p className="text-sm text-muted-foreground">
|
|
405
405
|
We provide innovative solutions for modern businesses.
|
|
406
406
|
</p>
|
|
407
|
-
</
|
|
408
|
-
<
|
|
407
|
+
</section>
|
|
408
|
+
<section>
|
|
409
409
|
<h3 className="font-semibold mb-2">Contact</h3>
|
|
410
410
|
<p className="text-sm text-muted-foreground">
|
|
411
411
|
Email: info@company.com
|
|
412
412
|
</p>
|
|
413
|
-
</
|
|
414
|
-
<
|
|
413
|
+
</section>
|
|
414
|
+
<section>
|
|
415
415
|
<h3 className="font-semibold mb-2">Follow Us</h3>
|
|
416
|
-
<
|
|
416
|
+
<nav className="flex gap-2">
|
|
417
417
|
<a href="#" className="text-muted-foreground hover:text-foreground">Twitter</a>
|
|
418
418
|
<a href="#" className="text-muted-foreground hover:text-foreground">LinkedIn</a>
|
|
419
|
-
</
|
|
420
|
-
</
|
|
421
|
-
</
|
|
419
|
+
</nav>
|
|
420
|
+
</section>
|
|
421
|
+
</section>
|
|
422
422
|
</Footer>
|
|
423
423
|
);
|
|
424
424
|
|
|
@@ -463,7 +463,7 @@ describe('Footer Component', () => {
|
|
|
463
463
|
logo="/logo.png"
|
|
464
464
|
className="custom-footer"
|
|
465
465
|
>
|
|
466
|
-
<
|
|
466
|
+
<section data-testid="custom-content">Custom footer content</section>
|
|
467
467
|
</Footer>
|
|
468
468
|
);
|
|
469
469
|
|