@jmruthers/pace-core 0.5.54 → 0.5.55
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/README.md +0 -4
- package/dist/{DataTable-7FMFXA7A.js → DataTable-4T627QFJ.js} +11 -11
- package/dist/{PublicLoadingSpinner-Bq_-BeK-.d.ts → PublicLoadingSpinner-SL8WaQN7.d.ts} +2 -21
- package/dist/{api-H5A3H4IR.js → api-LUNF5O6M.js} +3 -3
- package/dist/{appConfig-BVGyuvI7.d.ts → appConfig-DjpeG6P-.d.ts} +9 -1
- package/dist/{appNameResolver-7GHF5ED2.js → appNameResolver-UURKN7NF.js} +2 -2
- package/dist/{audit-BUW3LMJB.js → audit-6TOCAMKO.js} +2 -2
- package/dist/{chunk-MZBUOP4P.js → chunk-5BSLGBYI.js} +4 -3
- package/dist/chunk-5BSLGBYI.js.map +1 -0
- package/dist/{chunk-I5Z3QH5X.js → chunk-66C4BSAY.js} +2 -2
- package/dist/{chunk-I5Z3QH5X.js.map → chunk-66C4BSAY.js.map} +1 -1
- package/dist/{chunk-MYP2EGHX.js → chunk-AJ2KMES7.js} +21 -14
- package/dist/chunk-AJ2KMES7.js.map +1 -0
- package/dist/{chunk-EL2O4IUX.js → chunk-AQFRLC7K.js} +16 -24
- package/dist/{chunk-EL2O4IUX.js.map → chunk-AQFRLC7K.js.map} +1 -1
- package/dist/{chunk-7BNPOCLL.js → chunk-B2WTCLCV.js} +6 -2
- package/dist/chunk-B2WTCLCV.js.map +1 -0
- package/dist/{chunk-WJARTBCT.js → chunk-D7ARGIA3.js} +16 -7
- package/dist/chunk-D7ARGIA3.js.map +1 -0
- package/dist/{chunk-NRK4AIHQ.js → chunk-KBRACSJI.js} +3 -3
- package/dist/{chunk-NYUJ4FJR.js → chunk-KJDPSM64.js} +7 -7
- package/dist/chunk-KJDPSM64.js.map +1 -0
- package/dist/{chunk-GWSBHC4J.js → chunk-KLPVOPRI.js} +261 -38
- package/dist/chunk-KLPVOPRI.js.map +1 -0
- package/dist/{chunk-TRIZ7IB7.js → chunk-MPQDF75X.js} +148 -288
- package/dist/chunk-MPQDF75X.js.map +1 -0
- package/dist/{chunk-MSFACPQQ.js → chunk-PAEM3OWN.js} +11 -11
- package/dist/{chunk-MSFACPQQ.js.map → chunk-PAEM3OWN.js.map} +1 -1
- package/dist/{chunk-GIO7BFE7.js → chunk-RQD3D2CO.js} +66 -169
- package/dist/{chunk-GIO7BFE7.js.map → chunk-RQD3D2CO.js.map} +1 -1
- package/dist/{chunk-YDJW5XTN.js → chunk-STT7INZR.js} +25 -1
- package/dist/chunk-STT7INZR.js.map +1 -0
- package/dist/{chunk-6MTY77WU.js → chunk-TNMXZLDR.js} +3 -3
- package/dist/{chunk-BC3S53OZ.js → chunk-UQE2Y64H.js} +30 -14
- package/dist/chunk-UQE2Y64H.js.map +1 -0
- package/dist/{chunk-22KLBHPS.js → chunk-W66AZIOH.js} +2 -2
- package/dist/chunk-W66AZIOH.js.map +1 -0
- package/dist/{chunk-SS3E6QLB.js → chunk-YNUBMSMV.js} +2 -2
- package/dist/chunk-YNUBMSMV.js.map +1 -0
- package/dist/{chunk-NZ655MWE.js → chunk-ZOD2ZY6X.js} +5 -4
- package/dist/chunk-ZOD2ZY6X.js.map +1 -0
- package/dist/{chunk-74C6SNEC.js → chunk-ZPK5656W.js} +3 -3
- package/dist/{chunk-74C6SNEC.js.map → chunk-ZPK5656W.js.map} +1 -1
- package/dist/components.d.ts +22 -899
- package/dist/components.js +436 -3118
- package/dist/components.js.map +1 -1
- package/dist/file-reference-9xUOnwyt.d.ts +70 -0
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +10 -10
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +49 -9
- package/dist/index.js +190 -25
- package/dist/index.js.map +1 -1
- package/dist/{organisation-CO3Sh3_D.d.ts → organisation-t-vvQC3g.d.ts} +1 -8
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +5 -5
- package/dist/rbac/index.d.ts +65 -46
- package/dist/rbac/index.js +10 -12
- package/dist/styles/core.css +0 -125
- package/dist/types.d.ts +2 -1
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/dist/{usePublicRouteParams-B2OcAsur.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +1 -1
- package/dist/utils.d.ts +3 -4
- package/dist/utils.js +44 -13
- package/dist/utils.js.map +1 -1
- package/docs/FILE_REFERENCE_SYSTEM.md +440 -0
- package/docs/INDEX.md +7 -5
- package/docs/README.md +0 -1
- package/docs/api/README.md +0 -4
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +2 -2
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +12 -12
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +6 -6
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +281 -0
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +204 -200
- package/docs/api-reference/components.md +141 -163
- package/docs/api-reference/hooks.md +347 -0
- package/docs/core-concepts/rbac-system.md +69 -16
- package/docs/getting-started/examples/basic-auth-app.md +0 -1
- package/docs/implementation-guides/datatable-rbac-usage.md +12 -11
- package/docs/implementation-guides/file-upload-storage.md +733 -0
- package/docs/implementation-guides/inactivity-tracking.md +779 -0
- package/docs/implementation-guides/organisation-security.md +748 -0
- package/docs/implementation-guides/public-pages-advanced.md +1022 -0
- package/docs/migration/MIGRATION_GUIDE.md +684 -0
- package/docs/migration/README.md +13 -2
- package/docs/migration/rbac-migration.md +73 -0
- package/docs/rbac/examples/rbac-rls-integration-example.md +11 -13
- package/docs/style-guide.md +269 -1
- package/package.json +1 -1
- package/src/__tests__/TESTING_GUIDELINES.md +331 -18
- package/src/__tests__/helpers/supabaseMock.ts +99 -0
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -7
- package/src/__tests__/shared.ts +6 -0
- package/src/components/DataTable/components/ActionButtons.tsx +2 -2
- package/src/components/DataTable/components/DataTableCore.tsx +2 -2
- package/src/components/DataTable/components/UnifiedTableBody.tsx +1 -1
- package/src/components/DataTable/utils/debugTools.ts +2 -2
- package/src/components/Dialog/Dialog.test.tsx +12 -2
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +6 -6
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +2 -2
- package/src/components/FileDisplay.tsx +233 -0
- package/src/components/FileUpload.tsx +176 -0
- package/src/components/Footer/Footer.test.tsx +7 -7
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +13 -6
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +30 -3
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +558 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
- package/src/components/PublicLayout/PublicPageDebugger.tsx +2 -2
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +2 -2
- package/src/components/PublicLayout/PublicPageProvider.tsx +2 -2
- package/src/components/Select/Select.test.tsx +50 -15
- package/src/components/SuperAdminGuard.tsx +2 -2
- package/src/components/__tests__/SuperAdminGuard.test.tsx +559 -0
- package/src/components/index.ts +0 -183
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +1 -1
- package/src/hooks/__tests__/useRBAC.unit.test.ts +191 -138
- package/src/hooks/public/usePublicEvent.ts +2 -2
- package/src/hooks/useAppConfig.ts +3 -3
- package/src/hooks/useComponentPerformance.ts +1 -1
- package/src/hooks/useDataTablePerformance.ts +1 -1
- package/src/hooks/useFileReference.ts +232 -0
- package/src/hooks/useOrganisationPermissions.test.ts +254 -344
- package/src/hooks/useOrganisationPermissions.ts +15 -7
- package/src/hooks/useOrganisationSecurity.test.ts +390 -402
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePermissionCache.test.ts +264 -395
- package/src/hooks/usePermissionCache.ts +34 -4
- package/src/hooks/useSecureDataAccess.test.ts +486 -0
- package/src/hooks/useSecureDataAccess.ts +4 -1
- package/src/providers/InactivityProvider.tsx +2 -2
- package/src/providers/OrganisationProvider.test.simple.tsx +168 -0
- package/src/providers/OrganisationProvider.test.tsx +168 -0
- package/src/providers/OrganisationProvider.tsx +18 -31
- package/src/providers/UnifiedAuthProvider.test.simple.tsx +205 -0
- package/src/providers/UnifiedAuthProvider.test.tsx +128 -0
- package/src/providers/__tests__/InactivityProvider.test.tsx +3 -4
- package/src/providers/__tests__/OrganisationProvider.test.tsx +19 -14
- package/src/rbac/__tests__/integration.authflow.test.tsx +123 -0
- package/src/rbac/__tests__/integration.navigation.test.tsx +72 -0
- package/src/rbac/__tests__/integration.securedata.test.tsx +92 -0
- package/src/rbac/__tests__/integration.smoke.test.tsx +73 -0
- package/src/rbac/__tests__/rbac-core.test.tsx +26 -22
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +411 -0
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +285 -0
- package/src/rbac/__tests__/rbac-functions.test.ts +655 -0
- package/src/rbac/__tests__/rbac-integration.test.ts +532 -0
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +196 -0
- package/src/rbac/api.test.ts +6 -6
- package/src/rbac/api.ts +2 -2
- package/src/rbac/audit.test.ts +485 -0
- package/src/rbac/audit.ts +7 -1
- package/src/rbac/cache-invalidation.ts +318 -0
- package/src/rbac/cache.test.ts +286 -0
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +559 -0
- package/src/rbac/components/EnhancedNavigationMenu.tsx +29 -23
- package/src/rbac/components/NavigationProvider.test.tsx +449 -0
- package/src/rbac/components/PagePermissionGuard.tsx +4 -4
- package/src/rbac/components/PagePermissionProvider.test.tsx +479 -0
- package/src/rbac/components/SecureDataProvider.test.tsx +511 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +159 -430
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +4 -5
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +112 -118
- package/src/rbac/config.test.ts +410 -0
- package/src/rbac/engine.test.simple.ts +237 -0
- package/src/rbac/engine.test.ts +233 -0
- package/src/rbac/engine.ts +37 -41
- package/src/rbac/examples/CompleteRBACExample.tsx +3 -3
- package/src/rbac/examples/EventBasedApp.tsx +4 -4
- package/src/rbac/hooks/useRBAC.simple.test.ts +16 -0
- package/src/rbac/hooks/useRBAC.test.ts +207 -455
- package/src/rbac/hooks/useRBAC.ts +30 -22
- package/src/rbac/permissions.test.ts +128 -0
- package/src/rbac/permissions.ts +56 -141
- package/src/rbac/providers/RBACProvider.tsx +1 -1
- package/src/rbac/secureClient.test.ts +444 -0
- package/src/rbac/security.test.ts +390 -0
- package/src/rbac/security.ts +1 -1
- package/src/rbac/types.test.ts +382 -0
- package/src/rbac/types.ts +2 -2
- package/src/styles/core.css +0 -125
- package/src/types/file-reference.ts +77 -0
- package/src/types/rbac-functions.ts +290 -0
- package/src/types/supabase.ts +10 -28
- package/src/types/unified.ts +4 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +81 -55
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +21 -12
- package/src/utils/__tests__/organisationContext.unit.test.ts +13 -7
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +3 -3
- package/src/utils/__tests__/sessionTracking.unit.test.ts +32 -12
- package/src/utils/appConfig.ts +1 -1
- package/src/utils/appIdResolver.test.ts +503 -0
- package/src/utils/appIdResolver.ts +1 -1
- package/src/utils/appNameResolver.test.ts +494 -0
- package/src/utils/appNameResolver.ts +3 -2
- package/src/utils/bundleAnalysis.ts +3 -3
- package/src/utils/debugLogger.ts +1 -1
- package/src/utils/file-reference.ts +263 -0
- package/src/utils/formatDate.test.ts +2 -2
- package/src/utils/organisationContext.test.ts +340 -0
- package/src/utils/organisationContext.ts +19 -6
- package/src/utils/performanceBudgets.ts +2 -2
- package/src/utils/permissionUtils.test.ts +393 -0
- package/src/utils/permissionUtils.ts +5 -2
- package/src/utils/secureDataAccess.test.ts +715 -0
- package/src/utils/secureDataAccess.ts +21 -5
- package/src/utils/sessionTracking.ts +34 -4
- package/src/utils/storage/__tests__/helpers.unit.test.ts +328 -0
- package/src/utils/storage/__tests__/index.unit.test.ts +16 -0
- package/src/utils/storage/helpers.ts +20 -25
- package/src/utils/storage/index.ts +29 -1
- package/src/vite-env.d.ts +17 -0
- package/dist/chunk-22KLBHPS.js.map +0 -1
- package/dist/chunk-7BNPOCLL.js.map +0 -1
- package/dist/chunk-BC3S53OZ.js.map +0 -1
- package/dist/chunk-GWSBHC4J.js.map +0 -1
- package/dist/chunk-MYP2EGHX.js.map +0 -1
- package/dist/chunk-MZBUOP4P.js.map +0 -1
- package/dist/chunk-NYUJ4FJR.js.map +0 -1
- package/dist/chunk-NZ655MWE.js.map +0 -1
- package/dist/chunk-SS3E6QLB.js.map +0 -1
- package/dist/chunk-TRIZ7IB7.js.map +0 -1
- package/dist/chunk-WJARTBCT.js.map +0 -1
- package/dist/chunk-YDJW5XTN.js.map +0 -1
- package/docs/print-components/README.md +0 -258
- package/docs/print-components/api-reference.md +0 -636
- package/docs/print-components/examples/README.md +0 -204
- package/docs/print-components/examples/basic-report.tsx +0 -92
- package/docs/print-components/examples/card-catalog.tsx +0 -149
- package/docs/print-components/examples/cover-page-report.tsx +0 -163
- package/docs/print-components/quick-start.md +0 -363
- package/src/components/PrintButton/PrintButton.tsx +0 -321
- package/src/components/PrintButton/PrintButtonGroup.tsx +0 -84
- package/src/components/PrintButton/PrintToolbar.tsx +0 -94
- package/src/components/PrintButton/__tests__/PrintButton.test.tsx +0 -271
- package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +0 -438
- package/src/components/PrintButton/index.ts +0 -33
- package/src/components/PrintButton/types.ts +0 -173
- package/src/components/PrintCard/PrintCard.tsx +0 -154
- package/src/components/PrintCard/PrintCardContent.tsx +0 -57
- package/src/components/PrintCard/PrintCardFooter.tsx +0 -60
- package/src/components/PrintCard/PrintCardGrid.tsx +0 -91
- package/src/components/PrintCard/PrintCardHeader.tsx +0 -78
- package/src/components/PrintCard/PrintCardImage.tsx +0 -81
- package/src/components/PrintCard/examples/PrintCardShowcase.tsx +0 -239
- package/src/components/PrintCard/index.ts +0 -34
- package/src/components/PrintCard/types.ts +0 -171
- package/src/components/PrintDataTable/PrintDataTable.tsx +0 -215
- package/src/components/PrintDataTable/PrintTableGroup.tsx +0 -90
- package/src/components/PrintDataTable/PrintTableRow.tsx +0 -76
- package/src/components/PrintDataTable/index.ts +0 -25
- package/src/components/PrintDataTable/types.ts +0 -67
- package/src/components/PrintFooter/PrintFooter.tsx +0 -183
- package/src/components/PrintFooter/PrintFooterContent.tsx +0 -71
- package/src/components/PrintFooter/PrintFooterInfo.tsx +0 -86
- package/src/components/PrintFooter/PrintPageNumber.tsx +0 -90
- package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +0 -390
- package/src/components/PrintFooter/index.ts +0 -30
- package/src/components/PrintFooter/types.ts +0 -149
- package/src/components/PrintGrid/PrintGrid.tsx +0 -180
- package/src/components/PrintGrid/PrintGridBreakpoint.tsx +0 -109
- package/src/components/PrintGrid/PrintGridContainer.tsx +0 -128
- package/src/components/PrintGrid/PrintGridItem.tsx +0 -220
- package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +0 -359
- package/src/components/PrintGrid/index.ts +0 -31
- package/src/components/PrintGrid/types.ts +0 -159
- package/src/components/PrintHeader/PrintCoverHeader.tsx +0 -230
- package/src/components/PrintHeader/PrintHeader.tsx +0 -150
- package/src/components/PrintHeader/index.ts +0 -17
- package/src/components/PrintHeader/types.ts +0 -42
- package/src/components/PrintLayout/PrintLayout.tsx +0 -122
- package/src/components/PrintLayout/PrintLayoutContext.tsx +0 -66
- package/src/components/PrintLayout/PrintPageBreak.tsx +0 -52
- package/src/components/PrintLayout/examples/PrintShowcase.tsx +0 -230
- package/src/components/PrintLayout/index.ts +0 -19
- package/src/components/PrintLayout/types.ts +0 -37
- package/src/components/PrintPageBreak/PrintPageBreak.tsx +0 -120
- package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +0 -90
- package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +0 -112
- package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +0 -279
- package/src/components/PrintPageBreak/index.ts +0 -23
- package/src/components/PrintPageBreak/types.ts +0 -94
- package/src/components/PrintSection/PrintColumn.tsx +0 -104
- package/src/components/PrintSection/PrintDivider.tsx +0 -101
- package/src/components/PrintSection/PrintSection.tsx +0 -129
- package/src/components/PrintSection/PrintSectionContent.tsx +0 -75
- package/src/components/PrintSection/PrintSectionHeader.tsx +0 -97
- package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +0 -258
- package/src/components/PrintSection/index.ts +0 -33
- package/src/components/PrintSection/types.ts +0 -155
- package/src/components/PrintText/PrintText.tsx +0 -116
- package/src/components/PrintText/index.ts +0 -16
- package/src/components/PrintText/types.ts +0 -24
- package/src/rbac/__tests__/integration.test.tsx +0 -218
- package/src/utils/print/PrintDataProcessor.ts +0 -390
- package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +0 -397
- package/src/utils/print/index.ts +0 -29
- package/src/utils/print/types.ts +0 -196
- package/src/utils/print/usePrintOptimization.ts +0 -272
- /package/dist/{DataTable-7FMFXA7A.js.map → DataTable-4T627QFJ.js.map} +0 -0
- /package/dist/{api-H5A3H4IR.js.map → api-LUNF5O6M.js.map} +0 -0
- /package/dist/{appNameResolver-7GHF5ED2.js.map → appNameResolver-UURKN7NF.js.map} +0 -0
- /package/dist/{audit-BUW3LMJB.js.map → audit-6TOCAMKO.js.map} +0 -0
- /package/dist/{chunk-NRK4AIHQ.js.map → chunk-KBRACSJI.js.map} +0 -0
- /package/dist/{chunk-6MTY77WU.js.map → chunk-TNMXZLDR.js.map} +0 -0
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
# Inactivity Tracking
|
|
2
|
+
|
|
3
|
+
> **⏰ User Session Management** | [← Back to Documentation](../README.md) | [↑ Table of Contents](#table-of-contents)
|
|
4
|
+
|
|
5
|
+
Complete guide to implementing user inactivity tracking with automatic warnings and session management.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
PACE Core provides comprehensive inactivity tracking with:
|
|
10
|
+
- **Cross-tab Synchronization** - Activity tracked across all browser tabs
|
|
11
|
+
- **Configurable Timeouts** - Customizable idle and warning periods
|
|
12
|
+
- **Automatic Warnings** - User-friendly inactivity warnings
|
|
13
|
+
- **Session Management** - Integration with authentication systems
|
|
14
|
+
- **Accessibility Support** - Screen reader friendly warnings
|
|
15
|
+
- **Performance Optimized** - Throttled event handling
|
|
16
|
+
|
|
17
|
+
## Architecture
|
|
18
|
+
|
|
19
|
+
```mermaid
|
|
20
|
+
graph TD
|
|
21
|
+
A[User Activity] --> B[useInactivityTracker Hook]
|
|
22
|
+
B --> C[BroadcastChannel]
|
|
23
|
+
C --> D[Cross-tab Sync]
|
|
24
|
+
B --> E[Local Storage]
|
|
25
|
+
E --> F[Persistence]
|
|
26
|
+
B --> G[InactivityWarningModal]
|
|
27
|
+
G --> H[User Action]
|
|
28
|
+
H --> I[Reset Timer]
|
|
29
|
+
B --> J[onIdle Callback]
|
|
30
|
+
J --> K[Sign Out]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### 1. Basic Inactivity Tracking
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useInactivityTracker, InactivityWarningModal } from '@jmruthers/pace-core';
|
|
39
|
+
|
|
40
|
+
function App() {
|
|
41
|
+
const { isIdle, timeRemaining, showWarning, resetActivity } = useInactivityTracker({
|
|
42
|
+
idleTimeoutMs: 30 * 60 * 1000, // 30 minutes
|
|
43
|
+
warnBeforeMs: 5 * 60 * 1000, // 5 minutes warning
|
|
44
|
+
onIdle: () => {
|
|
45
|
+
console.log('User is idle - signing out');
|
|
46
|
+
signOut();
|
|
47
|
+
},
|
|
48
|
+
onWarning: () => {
|
|
49
|
+
console.log('Showing inactivity warning');
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div>
|
|
55
|
+
<h1>My App</h1>
|
|
56
|
+
<p>Idle: {isIdle ? 'Yes' : 'No'}</p>
|
|
57
|
+
<p>Time remaining: {Math.floor(timeRemaining / 1000)}s</p>
|
|
58
|
+
|
|
59
|
+
<InactivityWarningModal
|
|
60
|
+
isOpen={showWarning}
|
|
61
|
+
timeRemaining={timeRemaining}
|
|
62
|
+
onStayActive={resetActivity}
|
|
63
|
+
onSignOut={() => signOut()}
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2. Integration with Authentication
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { useInactivityTracker, InactivityWarningModal } from '@jmruthers/pace-core';
|
|
74
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core';
|
|
75
|
+
|
|
76
|
+
function AuthenticatedApp() {
|
|
77
|
+
const { signOut } = useUnifiedAuth();
|
|
78
|
+
|
|
79
|
+
const {
|
|
80
|
+
isIdle,
|
|
81
|
+
timeRemaining,
|
|
82
|
+
showWarning,
|
|
83
|
+
resetActivity,
|
|
84
|
+
startTracking,
|
|
85
|
+
stopTracking
|
|
86
|
+
} = useInactivityTracker({
|
|
87
|
+
idleTimeoutMs: 15 * 60 * 1000, // 15 minutes
|
|
88
|
+
warnBeforeMs: 2 * 60 * 1000, // 2 minutes warning
|
|
89
|
+
onIdle: () => {
|
|
90
|
+
console.log('User inactive - signing out');
|
|
91
|
+
signOut();
|
|
92
|
+
},
|
|
93
|
+
onWarning: () => {
|
|
94
|
+
console.log('Inactivity warning triggered');
|
|
95
|
+
},
|
|
96
|
+
onActivity: () => {
|
|
97
|
+
console.log('User activity detected');
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Start tracking when component mounts
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
startTracking();
|
|
104
|
+
return () => stopTracking();
|
|
105
|
+
}, [startTracking, stopTracking]);
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div>
|
|
109
|
+
<h1>Dashboard</h1>
|
|
110
|
+
<p>Status: {isIdle ? 'Idle' : 'Active'}</p>
|
|
111
|
+
|
|
112
|
+
<InactivityWarningModal
|
|
113
|
+
isOpen={showWarning}
|
|
114
|
+
timeRemaining={timeRemaining}
|
|
115
|
+
onStayActive={resetActivity}
|
|
116
|
+
onSignOut={signOut}
|
|
117
|
+
title="Session Timeout Warning"
|
|
118
|
+
message="You will be signed out due to inactivity."
|
|
119
|
+
stayActiveText="Stay Active"
|
|
120
|
+
signOutText="Sign Out Now"
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Components
|
|
128
|
+
|
|
129
|
+
### InactivityWarningModal
|
|
130
|
+
|
|
131
|
+
A modal component that displays inactivity warnings with countdown timer and action buttons.
|
|
132
|
+
|
|
133
|
+
#### Props
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
interface InactivityWarningModalProps {
|
|
137
|
+
isOpen: boolean;
|
|
138
|
+
timeRemaining: number;
|
|
139
|
+
onStayActive: () => void;
|
|
140
|
+
onSignOut: () => void;
|
|
141
|
+
title?: string;
|
|
142
|
+
message?: string;
|
|
143
|
+
stayActiveText?: string;
|
|
144
|
+
signOutText?: string;
|
|
145
|
+
className?: string;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### Usage Examples
|
|
150
|
+
|
|
151
|
+
**Basic Warning Modal:**
|
|
152
|
+
```tsx
|
|
153
|
+
<InactivityWarningModal
|
|
154
|
+
isOpen={showWarning}
|
|
155
|
+
timeRemaining={timeRemaining}
|
|
156
|
+
onStayActive={resetActivity}
|
|
157
|
+
onSignOut={signOut}
|
|
158
|
+
/>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Customized Warning Modal:**
|
|
162
|
+
```tsx
|
|
163
|
+
<InactivityWarningModal
|
|
164
|
+
isOpen={showWarning}
|
|
165
|
+
timeRemaining={timeRemaining}
|
|
166
|
+
onStayActive={resetActivity}
|
|
167
|
+
onSignOut={signOut}
|
|
168
|
+
title="Session Expiring Soon"
|
|
169
|
+
message="Your session will expire in {time} seconds due to inactivity."
|
|
170
|
+
stayActiveText="Continue Working"
|
|
171
|
+
signOutText="End Session"
|
|
172
|
+
className="custom-warning-modal"
|
|
173
|
+
/>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Custom Styling:**
|
|
177
|
+
```tsx
|
|
178
|
+
<InactivityWarningModal
|
|
179
|
+
isOpen={showWarning}
|
|
180
|
+
timeRemaining={timeRemaining}
|
|
181
|
+
onStayActive={resetActivity}
|
|
182
|
+
onSignOut={signOut}
|
|
183
|
+
className="bg-main-50 border-main-300"
|
|
184
|
+
/>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Hooks
|
|
188
|
+
|
|
189
|
+
### useInactivityTracker
|
|
190
|
+
|
|
191
|
+
Primary hook for tracking user inactivity with comprehensive configuration options.
|
|
192
|
+
|
|
193
|
+
#### Interface
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
interface UseInactivityTrackerOptions {
|
|
197
|
+
/** Timeout in milliseconds before user is considered idle (default: 30 minutes) */
|
|
198
|
+
idleTimeoutMs?: number;
|
|
199
|
+
|
|
200
|
+
/** Time in milliseconds before idle to show warning (default: 5 minutes) */
|
|
201
|
+
warnBeforeMs?: number;
|
|
202
|
+
|
|
203
|
+
/** Callback when user becomes idle */
|
|
204
|
+
onIdle?: () => void;
|
|
205
|
+
|
|
206
|
+
/** Callback when warning should be shown */
|
|
207
|
+
onWarning?: () => void;
|
|
208
|
+
|
|
209
|
+
/** Callback when user activity is detected */
|
|
210
|
+
onActivity?: () => void;
|
|
211
|
+
|
|
212
|
+
/** Events to track for activity (default: all) */
|
|
213
|
+
events?: string[];
|
|
214
|
+
|
|
215
|
+
/** Throttle interval for event handling (default: 100ms) */
|
|
216
|
+
throttleMs?: number;
|
|
217
|
+
|
|
218
|
+
/** Enable cross-tab synchronization (default: true) */
|
|
219
|
+
enableCrossTabSync?: boolean;
|
|
220
|
+
|
|
221
|
+
/** Storage key for persistence (default: 'pace-inactivity') */
|
|
222
|
+
storageKey?: string;
|
|
223
|
+
|
|
224
|
+
/** Enable debug logging (default: false) */
|
|
225
|
+
debug?: boolean;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
interface UseInactivityTrackerReturn {
|
|
229
|
+
/** Whether user is currently idle */
|
|
230
|
+
isIdle: boolean;
|
|
231
|
+
|
|
232
|
+
/** Time remaining before idle (in milliseconds) */
|
|
233
|
+
timeRemaining: number;
|
|
234
|
+
|
|
235
|
+
/** Whether warning should be shown */
|
|
236
|
+
showWarning: boolean;
|
|
237
|
+
|
|
238
|
+
/** Reset the inactivity timer */
|
|
239
|
+
resetActivity: () => void;
|
|
240
|
+
|
|
241
|
+
/** Start tracking activity */
|
|
242
|
+
startTracking: () => void;
|
|
243
|
+
|
|
244
|
+
/** Stop tracking activity */
|
|
245
|
+
stopTracking: () => void;
|
|
246
|
+
|
|
247
|
+
/** Get current activity status */
|
|
248
|
+
getActivityStatus: () => {
|
|
249
|
+
isIdle: boolean;
|
|
250
|
+
timeRemaining: number;
|
|
251
|
+
lastActivity: number;
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### Usage Examples
|
|
257
|
+
|
|
258
|
+
**Basic Configuration:**
|
|
259
|
+
```tsx
|
|
260
|
+
import { useInactivityTracker } from '@jmruthers/pace-core';
|
|
261
|
+
|
|
262
|
+
function MyComponent() {
|
|
263
|
+
const { isIdle, timeRemaining, showWarning, resetActivity } = useInactivityTracker({
|
|
264
|
+
idleTimeoutMs: 20 * 60 * 1000, // 20 minutes
|
|
265
|
+
warnBeforeMs: 3 * 60 * 1000, // 3 minutes warning
|
|
266
|
+
onIdle: () => {
|
|
267
|
+
console.log('User is idle');
|
|
268
|
+
// Handle idle state
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<div>
|
|
274
|
+
<p>Idle: {isIdle ? 'Yes' : 'No'}</p>
|
|
275
|
+
<p>Time remaining: {Math.floor(timeRemaining / 1000)}s</p>
|
|
276
|
+
</div>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Advanced Configuration:**
|
|
282
|
+
```tsx
|
|
283
|
+
import { useInactivityTracker } from '@jmruthers/pace-core';
|
|
284
|
+
|
|
285
|
+
function AdvancedComponent() {
|
|
286
|
+
const {
|
|
287
|
+
isIdle,
|
|
288
|
+
timeRemaining,
|
|
289
|
+
showWarning,
|
|
290
|
+
resetActivity,
|
|
291
|
+
startTracking,
|
|
292
|
+
stopTracking,
|
|
293
|
+
getActivityStatus
|
|
294
|
+
} = useInactivityTracker({
|
|
295
|
+
idleTimeoutMs: 45 * 60 * 1000, // 45 minutes
|
|
296
|
+
warnBeforeMs: 10 * 60 * 1000, // 10 minutes warning
|
|
297
|
+
events: ['mousedown', 'keydown', 'scroll', 'touchstart'], // Custom events
|
|
298
|
+
throttleMs: 200, // Throttle to 200ms
|
|
299
|
+
enableCrossTabSync: true,
|
|
300
|
+
storageKey: 'my-app-inactivity',
|
|
301
|
+
debug: process.env.NODE_ENV === 'development',
|
|
302
|
+
onIdle: () => {
|
|
303
|
+
console.log('User idle - performing cleanup');
|
|
304
|
+
// Save work, clear sensitive data, etc.
|
|
305
|
+
},
|
|
306
|
+
onWarning: () => {
|
|
307
|
+
console.log('Warning: User will be idle soon');
|
|
308
|
+
// Show notification, play sound, etc.
|
|
309
|
+
},
|
|
310
|
+
onActivity: () => {
|
|
311
|
+
console.log('User activity detected');
|
|
312
|
+
// Hide warnings, reset notifications, etc.
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// Manual control
|
|
317
|
+
const handlePauseTracking = () => {
|
|
318
|
+
stopTracking();
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const handleResumeTracking = () => {
|
|
322
|
+
startTracking();
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const handleCheckStatus = () => {
|
|
326
|
+
const status = getActivityStatus();
|
|
327
|
+
console.log('Current status:', status);
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
return (
|
|
331
|
+
<div>
|
|
332
|
+
<div className="space-y-2">
|
|
333
|
+
<p>Status: {isIdle ? 'Idle' : 'Active'}</p>
|
|
334
|
+
<p>Time remaining: {Math.floor(timeRemaining / 1000)}s</p>
|
|
335
|
+
<p>Warning: {showWarning ? 'Yes' : 'No'}</p>
|
|
336
|
+
</div>
|
|
337
|
+
|
|
338
|
+
<div className="space-x-2">
|
|
339
|
+
<Button onClick={resetActivity}>Reset Timer</Button>
|
|
340
|
+
<Button onClick={handlePauseTracking}>Pause Tracking</Button>
|
|
341
|
+
<Button onClick={handleResumeTracking}>Resume Tracking</Button>
|
|
342
|
+
<Button onClick={handleCheckStatus}>Check Status</Button>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Configuration Options
|
|
350
|
+
|
|
351
|
+
### Timeout Configuration
|
|
352
|
+
|
|
353
|
+
```tsx
|
|
354
|
+
// Short session (5 minutes idle, 1 minute warning)
|
|
355
|
+
const shortSession = useInactivityTracker({
|
|
356
|
+
idleTimeoutMs: 5 * 60 * 1000,
|
|
357
|
+
warnBeforeMs: 1 * 60 * 1000
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Long session (2 hours idle, 15 minutes warning)
|
|
361
|
+
const longSession = useInactivityTracker({
|
|
362
|
+
idleTimeoutMs: 2 * 60 * 60 * 1000,
|
|
363
|
+
warnBeforeMs: 15 * 60 * 1000
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// No warning (immediate idle)
|
|
367
|
+
const immediateIdle = useInactivityTracker({
|
|
368
|
+
idleTimeoutMs: 10 * 60 * 1000,
|
|
369
|
+
warnBeforeMs: 0
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Event Tracking
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
// Track only mouse and keyboard events
|
|
377
|
+
const basicTracking = useInactivityTracker({
|
|
378
|
+
events: ['mousedown', 'keydown']
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// Track all user interactions
|
|
382
|
+
const fullTracking = useInactivityTracker({
|
|
383
|
+
events: [
|
|
384
|
+
'mousedown',
|
|
385
|
+
'mousemove',
|
|
386
|
+
'keydown',
|
|
387
|
+
'keyup',
|
|
388
|
+
'scroll',
|
|
389
|
+
'touchstart',
|
|
390
|
+
'touchmove',
|
|
391
|
+
'focus',
|
|
392
|
+
'blur'
|
|
393
|
+
]
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// Custom event tracking
|
|
397
|
+
const customTracking = useInactivityTracker({
|
|
398
|
+
events: ['click', 'input', 'change', 'submit']
|
|
399
|
+
});
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Cross-Tab Synchronization
|
|
403
|
+
|
|
404
|
+
```tsx
|
|
405
|
+
// Enable cross-tab sync (default)
|
|
406
|
+
const syncedTracking = useInactivityTracker({
|
|
407
|
+
enableCrossTabSync: true
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// Disable cross-tab sync
|
|
411
|
+
const localTracking = useInactivityTracker({
|
|
412
|
+
enableCrossTabSync: false
|
|
413
|
+
});
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Integration Patterns
|
|
417
|
+
|
|
418
|
+
### Authentication Integration
|
|
419
|
+
|
|
420
|
+
```tsx
|
|
421
|
+
import { useInactivityTracker, InactivityWarningModal } from '@jmruthers/pace-core';
|
|
422
|
+
import { useUnifiedAuth } from '@jmruthers/pace-core';
|
|
423
|
+
|
|
424
|
+
function AuthenticatedApp() {
|
|
425
|
+
const { user, signOut, loading } = useUnifiedAuth();
|
|
426
|
+
|
|
427
|
+
const {
|
|
428
|
+
isIdle,
|
|
429
|
+
timeRemaining,
|
|
430
|
+
showWarning,
|
|
431
|
+
resetActivity
|
|
432
|
+
} = useInactivityTracker({
|
|
433
|
+
idleTimeoutMs: 30 * 60 * 1000,
|
|
434
|
+
warnBeforeMs: 5 * 60 * 1000,
|
|
435
|
+
onIdle: () => {
|
|
436
|
+
console.log('User idle - signing out');
|
|
437
|
+
signOut();
|
|
438
|
+
},
|
|
439
|
+
onWarning: () => {
|
|
440
|
+
console.log('Inactivity warning');
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// Don't track inactivity if not authenticated
|
|
445
|
+
if (loading || !user) {
|
|
446
|
+
return <div>Loading...</div>;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return (
|
|
450
|
+
<div>
|
|
451
|
+
<h1>Welcome, {user.email}</h1>
|
|
452
|
+
|
|
453
|
+
<InactivityWarningModal
|
|
454
|
+
isOpen={showWarning}
|
|
455
|
+
timeRemaining={timeRemaining}
|
|
456
|
+
onStayActive={resetActivity}
|
|
457
|
+
onSignOut={signOut}
|
|
458
|
+
/>
|
|
459
|
+
</div>
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Form Integration
|
|
465
|
+
|
|
466
|
+
```tsx
|
|
467
|
+
import { useInactivityTracker } from '@jmruthers/pace-core';
|
|
468
|
+
import { useForm } from 'react-hook-form';
|
|
469
|
+
|
|
470
|
+
function LongForm() {
|
|
471
|
+
const { formState: { isDirty } } = useForm();
|
|
472
|
+
|
|
473
|
+
const { isIdle, showWarning, resetActivity } = useInactivityTracker({
|
|
474
|
+
idleTimeoutMs: 10 * 60 * 1000, // 10 minutes
|
|
475
|
+
warnBeforeMs: 2 * 60 * 1000, // 2 minutes warning
|
|
476
|
+
onIdle: () => {
|
|
477
|
+
if (isDirty) {
|
|
478
|
+
// Save draft before signing out
|
|
479
|
+
saveDraft();
|
|
480
|
+
}
|
|
481
|
+
signOut();
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
return (
|
|
486
|
+
<form>
|
|
487
|
+
{/* Form fields */}
|
|
488
|
+
|
|
489
|
+
{showWarning && isDirty && (
|
|
490
|
+
<div className="bg-acc-100 border border-acc-300 rounded p-4">
|
|
491
|
+
<p>You have unsaved changes. Your session will expire soon.</p>
|
|
492
|
+
<Button onClick={resetActivity}>Continue Editing</Button>
|
|
493
|
+
</div>
|
|
494
|
+
)}
|
|
495
|
+
</form>
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Multi-Step Process Integration
|
|
501
|
+
|
|
502
|
+
```tsx
|
|
503
|
+
import { useInactivityTracker } from '@jmruthers/pace-core';
|
|
504
|
+
|
|
505
|
+
function MultiStepProcess() {
|
|
506
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
507
|
+
const [formData, setFormData] = useState({});
|
|
508
|
+
|
|
509
|
+
const { isIdle, showWarning, resetActivity } = useInactivityTracker({
|
|
510
|
+
idleTimeoutMs: 15 * 60 * 1000,
|
|
511
|
+
warnBeforeMs: 3 * 60 * 1000,
|
|
512
|
+
onIdle: () => {
|
|
513
|
+
// Save progress before signing out
|
|
514
|
+
saveProgress(formData, currentStep);
|
|
515
|
+
signOut();
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
return (
|
|
520
|
+
<div>
|
|
521
|
+
<h2>Step {currentStep + 1} of 5</h2>
|
|
522
|
+
|
|
523
|
+
{/* Step content */}
|
|
524
|
+
|
|
525
|
+
{showWarning && (
|
|
526
|
+
<div className="fixed bottom-4 right-4 bg-main-600 text-main-50 p-4 rounded">
|
|
527
|
+
<p>Your session will expire soon. Save your progress?</p>
|
|
528
|
+
<Button onClick={resetActivity}>Continue</Button>
|
|
529
|
+
</div>
|
|
530
|
+
)}
|
|
531
|
+
</div>
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
## Accessibility
|
|
537
|
+
|
|
538
|
+
### Screen Reader Support
|
|
539
|
+
|
|
540
|
+
The `InactivityWarningModal` includes proper ARIA attributes and announcements:
|
|
541
|
+
|
|
542
|
+
```tsx
|
|
543
|
+
<InactivityWarningModal
|
|
544
|
+
isOpen={showWarning}
|
|
545
|
+
timeRemaining={timeRemaining}
|
|
546
|
+
onStayActive={resetActivity}
|
|
547
|
+
onSignOut={signOut}
|
|
548
|
+
// Automatically includes:
|
|
549
|
+
// - role="alertdialog"
|
|
550
|
+
// - aria-labelledby
|
|
551
|
+
// - aria-describedby
|
|
552
|
+
// - Live region for countdown updates
|
|
553
|
+
/>
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### Keyboard Navigation
|
|
557
|
+
|
|
558
|
+
The modal supports full keyboard navigation:
|
|
559
|
+
- **Tab** - Navigate between buttons
|
|
560
|
+
- **Enter/Space** - Activate focused button
|
|
561
|
+
- **Escape** - Close modal (if configured)
|
|
562
|
+
|
|
563
|
+
### Focus Management
|
|
564
|
+
|
|
565
|
+
```tsx
|
|
566
|
+
import { useInactivityTracker } from '@jmruthers/pace-core';
|
|
567
|
+
|
|
568
|
+
function AccessibleApp() {
|
|
569
|
+
const { showWarning, resetActivity } = useInactivityTracker({
|
|
570
|
+
// ... configuration
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
// Focus management for accessibility
|
|
574
|
+
useEffect(() => {
|
|
575
|
+
if (showWarning) {
|
|
576
|
+
// Focus will be automatically managed by the modal
|
|
577
|
+
// The modal will trap focus and return it when closed
|
|
578
|
+
}
|
|
579
|
+
}, [showWarning]);
|
|
580
|
+
|
|
581
|
+
return (
|
|
582
|
+
<div>
|
|
583
|
+
{/* App content */}
|
|
584
|
+
|
|
585
|
+
<InactivityWarningModal
|
|
586
|
+
isOpen={showWarning}
|
|
587
|
+
timeRemaining={timeRemaining}
|
|
588
|
+
onStayActive={resetActivity}
|
|
589
|
+
onSignOut={signOut}
|
|
590
|
+
// Focus management is handled automatically
|
|
591
|
+
/>
|
|
592
|
+
</div>
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
## Performance Considerations
|
|
598
|
+
|
|
599
|
+
### Throttling
|
|
600
|
+
|
|
601
|
+
The hook uses throttling to prevent excessive event handling:
|
|
602
|
+
|
|
603
|
+
```tsx
|
|
604
|
+
// Default throttling (100ms)
|
|
605
|
+
const defaultThrottling = useInactivityTracker({
|
|
606
|
+
// Events are throttled to 100ms by default
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// Custom throttling
|
|
610
|
+
const customThrottling = useInactivityTracker({
|
|
611
|
+
throttleMs: 500 // Throttle to 500ms
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
// No throttling (not recommended for performance)
|
|
615
|
+
const noThrottling = useInactivityTracker({
|
|
616
|
+
throttleMs: 0
|
|
617
|
+
});
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
### Memory Management
|
|
621
|
+
|
|
622
|
+
The hook automatically cleans up event listeners and timers:
|
|
623
|
+
|
|
624
|
+
```tsx
|
|
625
|
+
function MyComponent() {
|
|
626
|
+
const inactivityTracker = useInactivityTracker({
|
|
627
|
+
// ... configuration
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
// Cleanup is automatic when component unmounts
|
|
631
|
+
// No manual cleanup required
|
|
632
|
+
}
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### Cross-Tab Optimization
|
|
636
|
+
|
|
637
|
+
Cross-tab synchronization is optimized to minimize performance impact:
|
|
638
|
+
|
|
639
|
+
```tsx
|
|
640
|
+
// Optimized cross-tab sync
|
|
641
|
+
const optimizedSync = useInactivityTracker({
|
|
642
|
+
enableCrossTabSync: true,
|
|
643
|
+
// Uses BroadcastChannel API for efficient communication
|
|
644
|
+
// Minimal memory footprint
|
|
645
|
+
// Automatic cleanup
|
|
646
|
+
});
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
## Troubleshooting
|
|
650
|
+
|
|
651
|
+
### Common Issues
|
|
652
|
+
|
|
653
|
+
**Issue: Timer not resetting on activity**
|
|
654
|
+
- Check that events are being tracked correctly
|
|
655
|
+
- Verify throttle settings aren't too high
|
|
656
|
+
- Ensure event listeners are properly attached
|
|
657
|
+
|
|
658
|
+
**Issue: Cross-tab sync not working**
|
|
659
|
+
- Check browser support for BroadcastChannel API
|
|
660
|
+
- Verify storage permissions
|
|
661
|
+
- Check for conflicting storage keys
|
|
662
|
+
|
|
663
|
+
**Issue: Modal not showing**
|
|
664
|
+
- Verify `showWarning` state is true
|
|
665
|
+
- Check that `timeRemaining` is greater than 0
|
|
666
|
+
- Ensure modal is rendered in the component tree
|
|
667
|
+
|
|
668
|
+
### Debug Mode
|
|
669
|
+
|
|
670
|
+
Enable debug logging to troubleshoot issues:
|
|
671
|
+
|
|
672
|
+
```tsx
|
|
673
|
+
const { isIdle, timeRemaining, showWarning } = useInactivityTracker({
|
|
674
|
+
debug: true, // Enable debug logging
|
|
675
|
+
// ... other options
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
// Debug output will show:
|
|
679
|
+
// - Event tracking
|
|
680
|
+
// - Timer updates
|
|
681
|
+
// - Cross-tab communication
|
|
682
|
+
// - State changes
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
### Manual Testing
|
|
686
|
+
|
|
687
|
+
```tsx
|
|
688
|
+
function DebugComponent() {
|
|
689
|
+
const {
|
|
690
|
+
isIdle,
|
|
691
|
+
timeRemaining,
|
|
692
|
+
showWarning,
|
|
693
|
+
resetActivity,
|
|
694
|
+
getActivityStatus
|
|
695
|
+
} = useInactivityTracker({
|
|
696
|
+
idleTimeoutMs: 10 * 1000, // 10 seconds for testing
|
|
697
|
+
warnBeforeMs: 5 * 1000, // 5 seconds warning
|
|
698
|
+
debug: true
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
const status = getActivityStatus();
|
|
702
|
+
|
|
703
|
+
return (
|
|
704
|
+
<div>
|
|
705
|
+
<h3>Debug Information</h3>
|
|
706
|
+
<p>Is Idle: {isIdle ? 'Yes' : 'No'}</p>
|
|
707
|
+
<p>Time Remaining: {timeRemaining}ms</p>
|
|
708
|
+
<p>Show Warning: {showWarning ? 'Yes' : 'No'}</p>
|
|
709
|
+
<p>Last Activity: {new Date(status.lastActivity).toLocaleTimeString()}</p>
|
|
710
|
+
|
|
711
|
+
<Button onClick={resetActivity}>Reset Timer</Button>
|
|
712
|
+
</div>
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
## Best Practices
|
|
718
|
+
|
|
719
|
+
### 1. Appropriate Timeouts
|
|
720
|
+
|
|
721
|
+
```tsx
|
|
722
|
+
// Short sessions (banking, admin)
|
|
723
|
+
const shortSession = useInactivityTracker({
|
|
724
|
+
idleTimeoutMs: 5 * 60 * 1000, // 5 minutes
|
|
725
|
+
warnBeforeMs: 1 * 60 * 1000 // 1 minute warning
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
// Medium sessions (general apps)
|
|
729
|
+
const mediumSession = useInactivityTracker({
|
|
730
|
+
idleTimeoutMs: 30 * 60 * 1000, // 30 minutes
|
|
731
|
+
warnBeforeMs: 5 * 60 * 1000 // 5 minutes warning
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
// Long sessions (content creation)
|
|
735
|
+
const longSession = useInactivityTracker({
|
|
736
|
+
idleTimeoutMs: 2 * 60 * 60 * 1000, // 2 hours
|
|
737
|
+
warnBeforeMs: 15 * 60 * 1000 // 15 minutes warning
|
|
738
|
+
});
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### 2. User Experience
|
|
742
|
+
|
|
743
|
+
```tsx
|
|
744
|
+
// Provide clear feedback
|
|
745
|
+
const userFriendly = useInactivityTracker({
|
|
746
|
+
onWarning: () => {
|
|
747
|
+
// Show toast notification
|
|
748
|
+
toast.info('Your session will expire soon');
|
|
749
|
+
},
|
|
750
|
+
onIdle: () => {
|
|
751
|
+
// Save work before signing out
|
|
752
|
+
saveUserWork();
|
|
753
|
+
signOut();
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
### 3. Security Considerations
|
|
759
|
+
|
|
760
|
+
```tsx
|
|
761
|
+
// Clear sensitive data on idle
|
|
762
|
+
const secure = useInactivityTracker({
|
|
763
|
+
onIdle: () => {
|
|
764
|
+
// Clear sensitive data from memory
|
|
765
|
+
clearSensitiveData();
|
|
766
|
+
// Clear local storage
|
|
767
|
+
localStorage.removeItem('sensitive-data');
|
|
768
|
+
// Sign out
|
|
769
|
+
signOut();
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
## Next Steps
|
|
775
|
+
|
|
776
|
+
- **[Authentication Integration](./permission-enforcement.md)** - Security patterns
|
|
777
|
+
- **[Performance Optimization](./performance.md)** - Large application considerations
|
|
778
|
+
- **[Accessibility Guidelines](../best-practices/accessibility.md)** - Accessibility best practices
|
|
779
|
+
- **[Security Best Practices](../best-practices/security.md)** - Security considerations
|