@jmruthers/pace-core 0.6.5 → 0.6.7
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 +104 -0
- package/README.md +5 -403
- package/audit-tool/00-dependencies.cjs +394 -0
- package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
- package/audit-tool/audits/02-project-structure.cjs +255 -0
- package/audit-tool/audits/03-architecture.cjs +196 -0
- package/audit-tool/audits/04-code-quality.cjs +149 -0
- package/audit-tool/audits/05-styling.cjs +224 -0
- package/audit-tool/audits/06-security-rbac.cjs +544 -0
- package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
- package/audit-tool/audits/08-testing-documentation.cjs +202 -0
- package/audit-tool/audits/09-operations.cjs +208 -0
- package/audit-tool/index.cjs +291 -0
- package/audit-tool/utils/code-utils.cjs +218 -0
- package/audit-tool/utils/file-utils.cjs +230 -0
- package/audit-tool/utils/report-utils.cjs +241 -0
- package/core-usage-manifest.json +93 -0
- package/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/01-pace-core-compliance.mdc +586 -0
- package/cursor-rules/02-project-structure.mdc +42 -4
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
- package/cursor-rules/06-security-rbac.mdc +518 -0
- package/cursor-rules/07-api-tech-stack.mdc +377 -0
- package/cursor-rules/08-testing-documentation.mdc +324 -0
- package/cursor-rules/09-operations.mdc +365 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/DataTable-7PMH7XN7.js +15 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
- package/dist/chunk-6F3IILHI.js +62 -0
- package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
- package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
- package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
- package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
- package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/chunk-GHYHJTYV.js +994 -0
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
- package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
- package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
- package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
- package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
- package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
- package/dist/components.d.ts +7 -5
- package/dist/components.js +46 -257
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +35 -0
- package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
- package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
- package/dist/eslint-rules/rules/05-styling.cjs +61 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/eslint-rules/utils/helpers.cjs +42 -0
- package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +62 -172
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +12 -11
- package/dist/index.js +67 -660
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +8 -35
- package/dist/rbac/eslint-rules.d.ts +46 -44
- package/dist/rbac/eslint-rules.js +7 -4
- package/dist/rbac/index.d.ts +109 -586
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- package/dist/theming/runtime.d.ts +14 -1
- package/dist/theming/runtime.js +3 -19
- package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
- package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +17 -7
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +301 -871
- package/docs/api-reference/components.md +21 -21
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/hooks.md +80 -80
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/api-reference/types.md +1 -1
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -1
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +6 -6
- package/docs/core-concepts/permissions.md +6 -6
- package/docs/documentation-index.md +12 -18
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +4 -4
- package/docs/getting-started/examples/full-featured-app.md +1 -1
- package/docs/getting-started/faq.md +2 -2
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-reference.md +4 -4
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/authentication.md +15 -15
- package/docs/implementation-guides/component-styling.md +1 -1
- package/docs/implementation-guides/data-tables.md +126 -33
- package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
- package/docs/implementation-guides/dynamic-colors.md +3 -3
- package/docs/implementation-guides/file-upload-storage.md +2 -2
- package/docs/implementation-guides/hierarchical-datatable.md +40 -60
- package/docs/implementation-guides/inactivity-tracking.md +3 -3
- package/docs/implementation-guides/large-datasets.md +3 -2
- package/docs/implementation-guides/organisation-security.md +2 -2
- package/docs/implementation-guides/performance.md +2 -2
- package/docs/implementation-guides/permission-enforcement.md +5 -1
- package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
- package/docs/migration/V0.4.0_rbac-migration.md +6 -6
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +17 -8
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/event-based-apps.md +3 -3
- package/docs/rbac/examples.md +41 -41
- package/docs/rbac/getting-started.md +37 -37
- package/docs/rbac/performance.md +1 -1
- package/docs/rbac/quick-start.md +52 -52
- package/docs/rbac/secure-client-protection.md +1 -35
- package/docs/rbac/troubleshooting.md +1 -1
- package/docs/security/README.md +5 -5
- package/docs/standards/0-standards-overview.md +220 -0
- package/docs/standards/1-pace-core-compliance-standards.md +986 -0
- package/docs/standards/2-project-structure-standards.md +949 -0
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/5-styling-standards.md +348 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
- package/docs/standards/7-api-tech-stack-standards.md +662 -0
- package/docs/standards/8-testing-documentation-standards.md +401 -0
- package/docs/standards/9-operations-standards.md +1102 -0
- package/docs/standards/README.md +185 -57
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/common-issues.md +2 -2
- package/docs/troubleshooting/debugging.md +9 -9
- package/docs/troubleshooting/migration.md +4 -4
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +33 -6
- package/package.json +35 -23
- package/scripts/install-cursor-rules.cjs +25 -6
- package/scripts/install-eslint-config.cjs +284 -0
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
- package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
- package/src/__tests__/templates/component.test.template.tsx +18 -15
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/Calendar/Calendar.tsx +201 -47
- package/src/components/ContextSelector/ContextSelector.tsx +106 -119
- package/src/components/DataTable/AUDIT_REPORT.md +293 -0
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
- package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +12 -9
- package/src/components/DataTable/components/EmptyState.tsx +10 -9
- package/src/components/DataTable/components/FilterRow.tsx +2 -4
- package/src/components/DataTable/components/ImportModal.tsx +124 -126
- package/src/components/DataTable/components/LoadingState.tsx +5 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/SortIndicator.tsx +50 -0
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/components/index.ts +2 -1
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
- package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
- package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
- package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
- package/src/components/DataTable/types.ts +5 -18
- package/src/components/DataTable/utils/a11yUtils.ts +17 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
- package/src/components/DateTimeField/DateTimeField.tsx +10 -9
- package/src/components/Dialog/Dialog.test.tsx +128 -104
- package/src/components/Dialog/Dialog.tsx +742 -24
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
- package/src/components/FileDisplay/FileDisplay.tsx +23 -17
- package/src/components/FileUpload/FileUpload.test.tsx +52 -14
- package/src/components/FileUpload/FileUpload.tsx +112 -130
- package/src/components/Form/Form.test.tsx +6 -8
- package/src/components/Form/Form.tsx +365 -4
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +109 -98
- package/src/components/Select/types.ts +4 -1
- package/src/components/UserMenu/UserMenu.tsx +9 -6
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
- package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
- package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
- package/src/hooks/public/usePublicEvent.ts +67 -195
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +24 -14
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +5 -5
- package/src/hooks/useAppConfig.ts +28 -26
- package/src/hooks/useEventTheme.test.ts +217 -239
- package/src/hooks/useEventTheme.ts +16 -28
- package/src/hooks/useFileDisplay.ts +2 -2
- package/src/hooks/useOrganisationPermissions.ts +5 -7
- package/src/hooks/useQueryCache.ts +0 -1
- package/src/hooks/useSessionDraft.ts +380 -0
- package/src/hooks/useSessionRestoration.ts +3 -1
- package/src/icons/index.ts +27 -0
- package/src/index.ts +5 -0
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
- package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
- package/src/providers/__tests__/EventProvider.test.tsx +61 -61
- package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
- package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
- package/src/rbac/adapters.tsx +7 -295
- package/src/rbac/api.test.ts +44 -56
- package/src/rbac/api.ts +10 -17
- package/src/rbac/cache-invalidation.ts +0 -1
- package/src/rbac/compliance/index.ts +10 -0
- package/src/rbac/compliance/pattern-detector.ts +553 -0
- package/src/rbac/compliance/runtime-compliance.ts +22 -0
- package/src/rbac/components/AccessDenied.tsx +150 -0
- package/src/rbac/components/NavigationGuard.tsx +12 -20
- package/src/rbac/components/PagePermissionGuard.tsx +4 -24
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
- package/src/rbac/components/index.ts +3 -41
- package/src/rbac/eslint-rules.js +1 -1
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/permissions/index.ts +0 -3
- package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
- package/src/rbac/hooks/usePermissions.ts +0 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
- package/src/rbac/hooks/useResolvedScope.ts +58 -140
- package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
- package/src/rbac/hooks/useResourcePermissions.ts +139 -48
- package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
- package/src/rbac/hooks/useRoleManagement.ts +147 -19
- package/src/rbac/hooks/useSecureSupabase.ts +4 -8
- package/src/rbac/index.ts +7 -9
- package/src/rbac/utils/contextValidator.ts +9 -7
- package/src/services/AuthService.ts +130 -18
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +16 -0
- package/src/services/OrganisationService.ts +7 -44
- package/src/services/__tests__/OrganisationService.test.ts +26 -8
- package/src/services/base/BaseService.ts +0 -3
- package/src/styles/core.css +7 -0
- package/src/theming/__tests__/parseEventColours.test.ts +9 -3
- package/src/theming/parseEventColours.ts +22 -10
- package/src/types/database.generated.ts +4733 -3809
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
- package/src/utils/context/organisationContext.test.ts +13 -28
- package/src/utils/context/organisationContext.ts +21 -52
- package/src/utils/dynamic/dynamicUtils.ts +1 -1
- package/src/utils/file-reference/index.ts +39 -15
- package/src/utils/formatting/formatDateTime.test.ts +3 -2
- package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
- package/src/utils/index.ts +4 -1
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
- package/src/utils/persistence/keyDerivation.ts +304 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
- package/src/utils/security/secureStorage.ts +5 -5
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/helpers.ts +3 -3
- package/src/utils/supabase/createBaseClient.ts +147 -0
- package/src/utils/timezone/timezone.test.ts +1 -2
- package/src/utils/timezone/timezone.ts +1 -1
- package/src/utils/validation/csrf.ts +4 -4
- package/cursor-rules/00-pace-core-compliance.mdc +0 -331
- package/cursor-rules/01-standards-compliance.mdc +0 -244
- package/cursor-rules/04-testing-standards.mdc +0 -268
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
- package/cursor-rules/06-code-quality.mdc +0 -309
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
- package/cursor-rules/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-AOVNCPTX.js +0 -175
- package/dist/DataTable-AOVNCPTX.js.map +0 -1
- package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
- package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
- package/dist/api-O6HTBX5Y.js +0 -52
- package/dist/api-O6HTBX5Y.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6COVEUS7.js.map +0 -1
- package/dist/chunk-AFVQODI2.js +0 -263
- package/dist/chunk-AFVQODI2.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.js.map +0 -1
- package/dist/chunk-EFN2EIMK.js.map +0 -1
- package/dist/chunk-FFQEQTNW.js.map +0 -1
- package/dist/chunk-FMUCXFII.js.map +0 -1
- package/dist/chunk-G37KK66H.js.map +0 -1
- package/dist/chunk-G7QEZTYQ.js +0 -2053
- package/dist/chunk-G7QEZTYQ.js.map +0 -1
- package/dist/chunk-HU2C6SSC.js.map +0 -1
- package/dist/chunk-IHB5DR3H.js.map +0 -1
- package/dist/chunk-IVOFDYWT.js.map +0 -1
- package/dist/chunk-J36DSWQK.js.map +0 -1
- package/dist/chunk-JGRYX5UX.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js +0 -1
- package/dist/chunk-KQCRWDSA.js.map +0 -1
- package/dist/chunk-L4OXEN46.js.map +0 -1
- package/dist/chunk-LMC26NLJ.js +0 -84
- package/dist/chunk-LMC26NLJ.js.map +0 -1
- package/dist/chunk-M43Y4SSO.js.map +0 -1
- package/dist/chunk-M7MPQISP.js.map +0 -1
- package/dist/chunk-NTM7ZSB6.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-RGAWHO7N.js.map +0 -1
- package/dist/chunk-UPPMRMYG.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-5OGXSPKS.js +0 -9
- package/dist/contextValidator-5OGXSPKS.js.map +0 -1
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
- package/dist/hooks.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/providers.js.map +0 -1
- package/dist/rbac/eslint-rules.js.map +0 -1
- package/dist/rbac/index.js.map +0 -1
- package/dist/styles/index.js.map +0 -1
- package/dist/theming/runtime.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -601
- package/docs/best-practices/common-patterns.md +0 -516
- package/docs/best-practices/deployment.md +0 -1103
- package/docs/best-practices/performance.md +0 -1328
- package/docs/best-practices/security.md +0 -940
- package/docs/best-practices/testing.md +0 -1034
- package/docs/rbac/compliance/compliance-guide.md +0 -544
- package/docs/standards/01-architecture-standard.md +0 -44
- package/docs/standards/02-api-and-rpc-standard.md +0 -39
- package/docs/standards/03-component-standard.md +0 -32
- package/docs/standards/04-code-style-standard.md +0 -32
- package/docs/standards/05-security-standard.md +0 -44
- package/docs/standards/06-testing-and-docs-standard.md +0 -29
- package/docs/standards/pace-core-compliance.md +0 -432
- package/scripts/audit/core/checks/accessibility.cjs +0 -197
- package/scripts/audit/core/checks/api-usage.cjs +0 -191
- package/scripts/audit/core/checks/bundle.cjs +0 -142
- package/scripts/audit/core/checks/compliance.cjs +0 -2706
- package/scripts/audit/core/checks/config.cjs +0 -54
- package/scripts/audit/core/checks/coverage.cjs +0 -84
- package/scripts/audit/core/checks/dependencies.cjs +0 -994
- package/scripts/audit/core/checks/documentation.cjs +0 -268
- package/scripts/audit/core/checks/environment.cjs +0 -116
- package/scripts/audit/core/checks/error-handling.cjs +0 -340
- package/scripts/audit/core/checks/forms.cjs +0 -172
- package/scripts/audit/core/checks/heuristics.cjs +0 -68
- package/scripts/audit/core/checks/hooks.cjs +0 -334
- package/scripts/audit/core/checks/imports.cjs +0 -244
- package/scripts/audit/core/checks/performance.cjs +0 -325
- package/scripts/audit/core/checks/routes.cjs +0 -117
- package/scripts/audit/core/checks/state.cjs +0 -130
- package/scripts/audit/core/checks/structure.cjs +0 -65
- package/scripts/audit/core/checks/style.cjs +0 -584
- package/scripts/audit/core/checks/testing.cjs +0 -122
- package/scripts/audit/core/checks/typescript.cjs +0 -61
- package/scripts/audit/core/scanner.cjs +0 -199
- package/scripts/audit/core/utils.cjs +0 -137
- package/scripts/audit/index.cjs +0 -223
- package/scripts/audit/reporters/console.cjs +0 -151
- package/scripts/audit/reporters/json.cjs +0 -54
- package/scripts/audit/reporters/markdown.cjs +0 -124
- package/scripts/audit-consuming-app.cjs +0 -86
- package/src/components/DataTable/components/DataTableBody.tsx +0 -454
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
- package/src/components/DataTable/components/ExpandButton.tsx +0 -113
- package/src/components/DataTable/components/GroupHeader.tsx +0 -54
- package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
- package/src/components/DataTable/core/DataTableContext.tsx +0 -216
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
- package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
- package/src/components/DataTable/utils/debugTools.ts +0 -514
- package/src/eslint-rules/pace-core-compliance.js +0 -638
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
- package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
- package/src/rbac/components/NavigationProvider.test.tsx +0 -481
- package/src/rbac/components/NavigationProvider.tsx +0 -345
- package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
- package/src/rbac/components/PagePermissionProvider.tsx +0 -279
- package/src/rbac/components/PermissionEnforcer.tsx +0 -312
- package/src/rbac/components/RoleBasedRouter.tsx +0 -440
- package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
- package/src/rbac/components/SecureDataProvider.tsx +0 -339
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
|
@@ -38,15 +38,12 @@
|
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
import { useEffect } from 'react';
|
|
41
|
+
import { useEffect, useContext } from 'react';
|
|
42
42
|
import { useLocation } from 'react-router-dom';
|
|
43
|
-
import {
|
|
43
|
+
import { EventServiceContext } from '../providers/services/EventServiceProvider';
|
|
44
44
|
import { applyPalette, clearPalette } from '../theming/runtime';
|
|
45
45
|
import { parseAndNormalizeEventColours } from '../theming/parseEventColours';
|
|
46
46
|
import type { Event } from '../types/event';
|
|
47
|
-
import { createLogger } from '../utils/core/logger';
|
|
48
|
-
|
|
49
|
-
const log = createLogger('useEventTheme');
|
|
50
47
|
|
|
51
48
|
/**
|
|
52
49
|
* Hook that automatically applies event-specific theming
|
|
@@ -99,30 +96,21 @@ const log = createLogger('useEventTheme');
|
|
|
99
96
|
* @param event - Optional event object for public page mode. If not provided, uses EventProvider context.
|
|
100
97
|
*/
|
|
101
98
|
export function useEventTheme(event?: Event | null): void {
|
|
99
|
+
// Call all hooks unconditionally at the top level
|
|
100
|
+
// Hooks must be called in the same order on every render
|
|
102
101
|
const location = useLocation();
|
|
103
102
|
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
} catch (error) {
|
|
117
|
-
// useEvents() throws if EventProvider is not available
|
|
118
|
-
// This is expected for public pages - use the event prop if provided
|
|
119
|
-
if (event !== undefined) {
|
|
120
|
-
selectedEvent = event;
|
|
121
|
-
} else {
|
|
122
|
-
// No event prop and no EventProvider - can't apply theme
|
|
123
|
-
selectedEvent = null;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
103
|
+
// Use EventServiceContext directly to handle missing provider gracefully
|
|
104
|
+
// This allows the hook to work in public pages without EventProvider
|
|
105
|
+
const eventServiceContext = useContext(EventServiceContext);
|
|
106
|
+
const eventsContextSelectedEvent = eventServiceContext?.eventService?.getSelectedEvent() ?? null;
|
|
107
|
+
|
|
108
|
+
// Determine which event to use: event prop (public mode) or EventProvider context (authenticated mode)
|
|
109
|
+
// If event prop is provided, use it (public page mode)
|
|
110
|
+
// Otherwise, use EventProvider context (authenticated mode)
|
|
111
|
+
const selectedEvent = event !== undefined
|
|
112
|
+
? event // Public page mode - use event prop
|
|
113
|
+
: eventsContextSelectedEvent; // Authenticated mode - use EventProvider context
|
|
126
114
|
|
|
127
115
|
useEffect(() => {
|
|
128
116
|
// Skip theme application when on login route
|
|
@@ -156,7 +144,7 @@ export function useEventTheme(event?: Event | null): void {
|
|
|
156
144
|
try {
|
|
157
145
|
applyPalette(normalized);
|
|
158
146
|
} catch (error) {
|
|
159
|
-
|
|
147
|
+
// Silently fail - theming is not critical
|
|
160
148
|
}
|
|
161
149
|
|
|
162
150
|
// Cleanup function to clear palette when component unmounts or event changes
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
* FileCategory.EVENT_LOGOS
|
|
27
27
|
* );
|
|
28
28
|
*
|
|
29
|
-
* if (isLoading) return <
|
|
30
|
-
* if (error) return <
|
|
29
|
+
* if (isLoading) return <p>Loading...</p>;
|
|
30
|
+
* if (error) return <p>Error: {error.message}</p>;
|
|
31
31
|
*
|
|
32
32
|
* return fileUrl ? <img src={fileUrl} alt="File" /> : null;
|
|
33
33
|
* }
|
|
@@ -106,13 +106,11 @@ export function useOrganisationPermissions(orgId?: string): UseOrganisationPermi
|
|
|
106
106
|
ensureOrganisationContext
|
|
107
107
|
} = useOrganisations();
|
|
108
108
|
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// Not available in this context, default to false
|
|
115
|
-
}
|
|
109
|
+
// Call hook unconditionally - if provider is missing, it will throw
|
|
110
|
+
// Errors should be handled by error boundaries at a higher level
|
|
111
|
+
// We cannot conditionally call hooks
|
|
112
|
+
const organisationSecurity = useOrganisationSecurity();
|
|
113
|
+
const superAdminContext = organisationSecurity.superAdminContext;
|
|
116
114
|
|
|
117
115
|
const organisationId = useMemo(() => {
|
|
118
116
|
if (orgId) {
|
|
@@ -50,7 +50,6 @@ function runCacheCleanup() {
|
|
|
50
50
|
// Initialize cleanup timer once
|
|
51
51
|
if (typeof window !== 'undefined' && !cleanupTimer) {
|
|
52
52
|
cleanupTimer = setInterval(runCacheCleanup, CLEANUP_INTERVAL_MS);
|
|
53
|
-
log.debug('Query cache cleanup initialized.');
|
|
54
53
|
|
|
55
54
|
// Cleanup on page unload to prevent memory leaks
|
|
56
55
|
window.addEventListener('beforeunload', () => {
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Session Draft Persistence Hook
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Hooks/Persistence
|
|
5
|
+
* @since 1.0.0
|
|
6
|
+
*
|
|
7
|
+
* A React hook for persisting component state to sessionStorage.
|
|
8
|
+
* Provides automatic persistence that survives tab switching but clears on tab close.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Debounced writes to prevent excessive storage operations
|
|
12
|
+
* - Automatic restoration on mount
|
|
13
|
+
* - Versioning support for schema migrations
|
|
14
|
+
* - Safe JSON serialization with error handling
|
|
15
|
+
* - Type-safe with generics
|
|
16
|
+
* - Handles storage quota errors gracefully
|
|
17
|
+
* - Optional cleanup on unmount
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* const { state, setState, clearDraft } = useSessionDraft('my-component', {
|
|
22
|
+
* filters: [],
|
|
23
|
+
* sortBy: 'name'
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // State is automatically persisted on changes
|
|
27
|
+
* setState({ filters: ['active'], sortBy: 'email' });
|
|
28
|
+
*
|
|
29
|
+
* // Clear draft when done
|
|
30
|
+
* clearDraft();
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @dependencies
|
|
34
|
+
* - React 19+ - Hooks and effects
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Schema version for stored drafts
|
|
41
|
+
* Increment when the data structure changes
|
|
42
|
+
*/
|
|
43
|
+
const DRAFT_SCHEMA_VERSION = 1;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Default debounce delay in milliseconds
|
|
47
|
+
*/
|
|
48
|
+
const DEFAULT_DEBOUNCE_MS = 300;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Stored draft payload structure
|
|
52
|
+
*/
|
|
53
|
+
interface StoredDraft<T> {
|
|
54
|
+
version: number;
|
|
55
|
+
data: T;
|
|
56
|
+
timestamp: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Options for useSessionDraft hook
|
|
61
|
+
*/
|
|
62
|
+
export interface UseSessionDraftOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Debounce delay in milliseconds
|
|
65
|
+
* @default 300
|
|
66
|
+
*/
|
|
67
|
+
debounceMs?: number;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Whether to clear draft on component unmount
|
|
71
|
+
* @default false
|
|
72
|
+
*/
|
|
73
|
+
clearOnUnmount?: boolean;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Whether to enable persistence (useful for conditional persistence)
|
|
77
|
+
* @default true
|
|
78
|
+
*/
|
|
79
|
+
enabled?: boolean;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Custom storage key prefix
|
|
83
|
+
* @default 'pace-core:draft'
|
|
84
|
+
*/
|
|
85
|
+
keyPrefix?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Return value of useSessionDraft hook
|
|
90
|
+
*/
|
|
91
|
+
export interface UseSessionDraftReturn<T> {
|
|
92
|
+
/**
|
|
93
|
+
* Current draft state
|
|
94
|
+
*/
|
|
95
|
+
state: T;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Update draft state (triggers debounced save)
|
|
99
|
+
*/
|
|
100
|
+
setState: (newState: T | ((prev: T) => T)) => void;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Clear the draft from storage
|
|
104
|
+
*/
|
|
105
|
+
clearDraft: () => void;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Force immediate save (bypasses debounce)
|
|
109
|
+
*/
|
|
110
|
+
saveImmediately: () => void;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Whether the draft was restored from storage
|
|
114
|
+
*/
|
|
115
|
+
wasRestored: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Debounce function that matches lodash debounce API
|
|
120
|
+
*/
|
|
121
|
+
function debounce<T extends (...args: any[]) => void>(
|
|
122
|
+
func: T,
|
|
123
|
+
wait: number
|
|
124
|
+
): T & { cancel: () => void } {
|
|
125
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
126
|
+
|
|
127
|
+
const debounced = ((...args: Parameters<T>) => {
|
|
128
|
+
if (timeoutId !== null) {
|
|
129
|
+
clearTimeout(timeoutId);
|
|
130
|
+
}
|
|
131
|
+
timeoutId = setTimeout(() => {
|
|
132
|
+
func(...args);
|
|
133
|
+
}, wait);
|
|
134
|
+
}) as T & { cancel: () => void };
|
|
135
|
+
|
|
136
|
+
debounced.cancel = () => {
|
|
137
|
+
if (timeoutId !== null) {
|
|
138
|
+
clearTimeout(timeoutId);
|
|
139
|
+
timeoutId = null;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return debounced;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get the full storage key with namespace
|
|
148
|
+
*/
|
|
149
|
+
function getStorageKey(key: string, prefix: string): string {
|
|
150
|
+
return `${prefix}:${key}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Save draft to sessionStorage
|
|
155
|
+
*/
|
|
156
|
+
function saveDraft<T>(storageKey: string, data: T): boolean {
|
|
157
|
+
if (typeof window === 'undefined' || !window.sessionStorage) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
const payload: StoredDraft<T> = {
|
|
163
|
+
version: DRAFT_SCHEMA_VERSION,
|
|
164
|
+
data,
|
|
165
|
+
timestamp: Date.now(),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const serialized = JSON.stringify(payload);
|
|
169
|
+
window.sessionStorage.setItem(storageKey, serialized);
|
|
170
|
+
return true;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
// Handle quota exceeded or other storage errors
|
|
173
|
+
if (error instanceof DOMException && error.name === 'QuotaExceededError') {
|
|
174
|
+
console.warn(`[useSessionDraft] Storage quota exceeded for key: ${storageKey}`);
|
|
175
|
+
} else {
|
|
176
|
+
console.warn(`[useSessionDraft] Failed to save draft for key: ${storageKey}`, error);
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Load draft from sessionStorage
|
|
184
|
+
*/
|
|
185
|
+
function loadDraft<T>(storageKey: string): T | null {
|
|
186
|
+
if (typeof window === 'undefined' || !window.sessionStorage) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const stored = window.sessionStorage.getItem(storageKey);
|
|
192
|
+
if (!stored) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const parsed = JSON.parse(stored) as StoredDraft<T>;
|
|
197
|
+
|
|
198
|
+
// Validate version
|
|
199
|
+
if (parsed.version !== DRAFT_SCHEMA_VERSION) {
|
|
200
|
+
console.warn(
|
|
201
|
+
`[useSessionDraft] Schema version mismatch for key: ${storageKey}. Expected ${DRAFT_SCHEMA_VERSION}, got ${parsed.version}`
|
|
202
|
+
);
|
|
203
|
+
// Clear outdated draft
|
|
204
|
+
window.sessionStorage.removeItem(storageKey);
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Validate structure
|
|
209
|
+
if (!parsed.data) {
|
|
210
|
+
console.warn(`[useSessionDraft] Invalid draft structure for key: ${storageKey}`);
|
|
211
|
+
window.sessionStorage.removeItem(storageKey);
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return parsed.data;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.warn(`[useSessionDraft] Failed to load draft for key: ${storageKey}`, error);
|
|
218
|
+
// Clear corrupted draft
|
|
219
|
+
try {
|
|
220
|
+
if (typeof window !== 'undefined' && window.sessionStorage) {
|
|
221
|
+
window.sessionStorage.removeItem(storageKey);
|
|
222
|
+
}
|
|
223
|
+
} catch {
|
|
224
|
+
// Ignore cleanup errors
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Remove draft from sessionStorage
|
|
232
|
+
*/
|
|
233
|
+
function removeDraft(storageKey: string): void {
|
|
234
|
+
if (typeof window === 'undefined' || !window.sessionStorage) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
window.sessionStorage.removeItem(storageKey);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.warn(`[useSessionDraft] Failed to remove draft for key: ${storageKey}`, error);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Hook for persisting component state to sessionStorage
|
|
247
|
+
*
|
|
248
|
+
* Automatically saves state changes with debouncing and restores state on mount.
|
|
249
|
+
* Data persists across tab switches but clears when the tab closes.
|
|
250
|
+
*
|
|
251
|
+
* @template T - The type of state to persist
|
|
252
|
+
* @param key - Unique key for this draft (will be namespaced automatically)
|
|
253
|
+
* @param initialState - Initial state if no draft exists
|
|
254
|
+
* @param options - Configuration options
|
|
255
|
+
* @returns Object containing state, setState, clearDraft, and utility functions
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```tsx
|
|
259
|
+
* const { state, setState } = useSessionDraft('user-filters', {
|
|
260
|
+
* search: '',
|
|
261
|
+
* status: 'all'
|
|
262
|
+
* });
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
export function useSessionDraft<T>(
|
|
266
|
+
key: string,
|
|
267
|
+
initialState: T,
|
|
268
|
+
options: UseSessionDraftOptions = {}
|
|
269
|
+
): UseSessionDraftReturn<T> {
|
|
270
|
+
const {
|
|
271
|
+
debounceMs = DEFAULT_DEBOUNCE_MS,
|
|
272
|
+
clearOnUnmount = false,
|
|
273
|
+
enabled = true,
|
|
274
|
+
keyPrefix = 'pace-core:draft',
|
|
275
|
+
} = options;
|
|
276
|
+
|
|
277
|
+
const storageKey = getStorageKey(key, keyPrefix);
|
|
278
|
+
const wasRestoredRef = useRef(false);
|
|
279
|
+
|
|
280
|
+
// Load draft on mount
|
|
281
|
+
const [state, setStateInternal] = useState<T>(() => {
|
|
282
|
+
if (!enabled) {
|
|
283
|
+
return initialState;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const restored = loadDraft<T>(storageKey);
|
|
287
|
+
if (restored !== null) {
|
|
288
|
+
wasRestoredRef.current = true;
|
|
289
|
+
return restored;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return initialState;
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Create debounced save function
|
|
296
|
+
const debouncedSaveRef = useRef<ReturnType<typeof debounce> | null>(null);
|
|
297
|
+
|
|
298
|
+
useEffect(() => {
|
|
299
|
+
if (!enabled) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Create debounced save function
|
|
304
|
+
const debouncedSave = debounce((data: T) => {
|
|
305
|
+
saveDraft(storageKey, data);
|
|
306
|
+
}, debounceMs);
|
|
307
|
+
|
|
308
|
+
debouncedSaveRef.current = debouncedSave;
|
|
309
|
+
|
|
310
|
+
return () => {
|
|
311
|
+
// Cancel pending saves on unmount
|
|
312
|
+
debouncedSave.cancel();
|
|
313
|
+
};
|
|
314
|
+
}, [storageKey, debounceMs, enabled]);
|
|
315
|
+
|
|
316
|
+
// Save state changes
|
|
317
|
+
const setState = useCallback(
|
|
318
|
+
(newState: T | ((prev: T) => T)) => {
|
|
319
|
+
setStateInternal((prev) => {
|
|
320
|
+
const updated = typeof newState === 'function' ? (newState as (prev: T) => T)(prev) : newState;
|
|
321
|
+
|
|
322
|
+
// Save to storage (debounced)
|
|
323
|
+
if (enabled && debouncedSaveRef.current) {
|
|
324
|
+
debouncedSaveRef.current(updated);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return updated;
|
|
328
|
+
});
|
|
329
|
+
},
|
|
330
|
+
[enabled]
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
// Force immediate save
|
|
334
|
+
const saveImmediately = useCallback(() => {
|
|
335
|
+
if (!enabled) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Cancel any pending debounced save
|
|
340
|
+
if (debouncedSaveRef.current) {
|
|
341
|
+
debouncedSaveRef.current.cancel();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Save immediately
|
|
345
|
+
saveDraft(storageKey, state);
|
|
346
|
+
}, [storageKey, state, enabled]);
|
|
347
|
+
|
|
348
|
+
// Clear draft
|
|
349
|
+
const clearDraft = useCallback(() => {
|
|
350
|
+
removeDraft(storageKey);
|
|
351
|
+
setStateInternal(initialState);
|
|
352
|
+
wasRestoredRef.current = false;
|
|
353
|
+
}, [storageKey, initialState]);
|
|
354
|
+
|
|
355
|
+
// Cleanup on unmount if configured
|
|
356
|
+
useEffect(() => {
|
|
357
|
+
if (!enabled || !clearOnUnmount) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return () => {
|
|
362
|
+
// Cancel pending saves
|
|
363
|
+
if (debouncedSaveRef.current) {
|
|
364
|
+
debouncedSaveRef.current.cancel();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Clear draft
|
|
368
|
+
removeDraft(storageKey);
|
|
369
|
+
};
|
|
370
|
+
}, [storageKey, clearOnUnmount, enabled]);
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
state,
|
|
374
|
+
setState,
|
|
375
|
+
clearDraft,
|
|
376
|
+
saveImmediately,
|
|
377
|
+
wasRestored: wasRestoredRef.current,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
@@ -16,7 +16,9 @@ import { createLogger } from '../utils/core/logger';
|
|
|
16
16
|
|
|
17
17
|
const log = createLogger('useSessionRestoration');
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// Increased timeout to handle hard refresh scenarios where localStorage access
|
|
20
|
+
// and session restoration may take longer, especially with organisation/event context
|
|
21
|
+
const SESSION_RESTORATION_TIMEOUT_MS = 10000; // 10 seconds (increased from 5)
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Return value of the useSessionRestoration hook.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Icon Exports
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Icons
|
|
5
|
+
* @since 0.6.6
|
|
6
|
+
*
|
|
7
|
+
* Icon exports from lucide-react.
|
|
8
|
+
* Import icons from '@jmruthers/pace-core/icons' instead of 'lucide-react'.
|
|
9
|
+
*
|
|
10
|
+
* All lucide-react icons are exported to ensure consuming apps have access
|
|
11
|
+
* to all icons while enforcing consistent usage through pace-core.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* // ✅ CORRECT: Import icons from pace-core
|
|
16
|
+
* import { ChevronDown, Edit, Trash } from '@jmruthers/pace-core/icons';
|
|
17
|
+
*
|
|
18
|
+
* // ❌ WRONG: Direct import from lucide-react (will fail - lucide-react is not a peer dependency)
|
|
19
|
+
* import { ChevronDown } from 'lucide-react';
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
// Export all icons from lucide-react
|
|
24
|
+
// This ensures consuming apps have access to all icons while enforcing
|
|
25
|
+
// consistent usage through pace-core's icon export system
|
|
26
|
+
export * from 'lucide-react';
|
|
27
|
+
|
package/src/index.ts
CHANGED
|
@@ -212,6 +212,8 @@ export { z } from 'zod';
|
|
|
212
212
|
// Common validation schemas
|
|
213
213
|
export { emailSchema, nameSchema, phoneSchema, urlSchema } from './utils/validation/common';
|
|
214
214
|
export { passwordSchema } from './utils/validation/passwordSchema';
|
|
215
|
+
// Sanitization utilities
|
|
216
|
+
export { sanitizeUserInput, sanitizeFormData, sanitizeHtml } from './utils/validation/sanitization';
|
|
215
217
|
|
|
216
218
|
// LAYOUT COMPONENTS
|
|
217
219
|
export { Header } from './components/Header/Header';
|
|
@@ -260,6 +262,9 @@ export { useEventTheme } from './hooks/useEventTheme';
|
|
|
260
262
|
export { cn } from './utils/core/cn';
|
|
261
263
|
export { setAppConfig, getAppConfig, getCurrentAppName, getCurrentAppId } from './utils/app/appConfig';
|
|
262
264
|
|
|
265
|
+
// Supabase client creation (restricted wrapper - ONLY for base client creation)
|
|
266
|
+
export { createBaseClient } from './utils/supabase/createBaseClient';
|
|
267
|
+
|
|
263
268
|
// LOGGING UTILITIES
|
|
264
269
|
export { Logger, logger, createLogger, LogLevel } from './utils/core/logger';
|
|
265
270
|
export type { LoggerConfig } from './utils/core/logger';
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
* for backward compatibility or standalone usage.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import React from 'react';
|
|
17
|
-
import {
|
|
18
|
-
import { OrganisationServiceProvider } from './services/OrganisationServiceProvider';
|
|
16
|
+
import React, { useContext } from 'react';
|
|
17
|
+
import { UnifiedAuthContext } from '../providers/services/UnifiedAuthProvider';
|
|
18
|
+
import { OrganisationServiceProvider, OrganisationServiceContext } from './services/OrganisationServiceProvider';
|
|
19
19
|
import type { OrganisationProviderProps as BaseOrganisationProviderProps } from '../types/organisation';
|
|
20
20
|
|
|
21
21
|
export interface OrganisationProviderProps extends BaseOrganisationProviderProps {
|
|
@@ -45,19 +45,29 @@ export function OrganisationProvider({
|
|
|
45
45
|
autoSelectPrimaryOrganisation,
|
|
46
46
|
onOrganisationChange
|
|
47
47
|
}: OrganisationProviderProps) {
|
|
48
|
+
// Call all hooks unconditionally at the top level
|
|
49
|
+
// Hooks must be called in the same order on every render
|
|
50
|
+
// Check if we're already inside OrganisationServiceProvider (e.g., from UnifiedAuthProvider)
|
|
51
|
+
// If so, just pass through children to avoid double-wrapping
|
|
52
|
+
const existingOrganisationService = useContext(OrganisationServiceContext);
|
|
53
|
+
|
|
48
54
|
// Get auth context from UnifiedAuthProvider
|
|
49
|
-
|
|
55
|
+
// Use useContext directly instead of useUnifiedAuth() to avoid throwing
|
|
56
|
+
// if context isn't available yet (e.g., during initial render)
|
|
57
|
+
const authContext = useContext(UnifiedAuthContext);
|
|
50
58
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// If no auth context, we can't provide organisation service
|
|
56
|
-
// This might happen if used outside UnifiedAuthProvider
|
|
57
|
-
// In that case, we should probably throw an error or show a message
|
|
58
|
-
console.warn('OrganisationProvider: No auth context available. Make sure OrganisationProvider is used inside UnifiedAuthProvider.');
|
|
59
|
+
// Early return after all hooks have been called
|
|
60
|
+
if (existingOrganisationService) {
|
|
61
|
+
// We're already inside UnifiedAuthProvider which includes OrganisationServiceProvider
|
|
62
|
+
// Just pass through children - no need to wrap again
|
|
59
63
|
return <>{children}</>;
|
|
60
64
|
}
|
|
65
|
+
|
|
66
|
+
// If no auth context, we can't provide organisation service
|
|
67
|
+
// This might happen if used outside UnifiedAuthProvider
|
|
68
|
+
if (!authContext) {
|
|
69
|
+
throw new Error('OrganisationProvider must be used inside UnifiedAuthProvider, or OrganisationServiceProvider must be provided separately');
|
|
70
|
+
}
|
|
61
71
|
|
|
62
72
|
const { supabase, user, session } = authContext;
|
|
63
73
|
|
|
@@ -66,8 +76,7 @@ export function OrganisationProvider({
|
|
|
66
76
|
// are handled by the OrganisationService internally, not by the provider
|
|
67
77
|
// Note: supabase is mapped to supabaseClient for OrganisationServiceProvider
|
|
68
78
|
if (!supabase) {
|
|
69
|
-
|
|
70
|
-
return <>{children}</>;
|
|
79
|
+
throw new Error('OrganisationProvider: No supabase client available from UnifiedAuthProvider');
|
|
71
80
|
}
|
|
72
81
|
|
|
73
82
|
return (
|