@jmruthers/pace-core 0.5.135 → 0.5.136
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/dist/{DataTable-C7GaRZye.d.ts → DataTable-CWAZZcXC.d.ts} +1 -1
- package/dist/{DataTable-A36PJG6N.js → DataTable-CYOHOX3O.js} +25 -13
- package/dist/{PublicLoadingSpinner-CUAnTvcg.d.ts → EventLogo-801uofbR.d.ts} +51 -135
- package/dist/UnifiedAuthProvider-5E5TUNMS.js +17 -0
- package/dist/{UnifiedAuthProvider-BVKmQd9u.d.ts → UnifiedAuthProvider-DJxGTftH.d.ts} +1 -1
- package/dist/{api-TNIBJWLM.js → api-45XYYO2A.js} +4 -3
- package/dist/{audit-T36HM7IM.js → audit-64X3VJXB.js} +3 -2
- package/dist/{chunk-CTJRBUX2.js → chunk-2TWNJ46Y.js} +2 -2
- package/dist/{chunk-UJI6WSMD.js → chunk-444EZN6N.js} +3 -3
- package/dist/chunk-444EZN6N.js.map +1 -0
- package/dist/{chunk-3CG5L6RN.js → chunk-4MT5BGGL.js} +90 -73
- package/dist/chunk-4MT5BGGL.js.map +1 -0
- package/dist/{chunk-PYUXFQJ3.js → chunk-56XJ3TU6.js} +2 -2
- package/dist/chunk-56XJ3TU6.js.map +1 -0
- package/dist/chunk-5DPZ5EAT.js +60 -0
- package/dist/chunk-5DPZ5EAT.js.map +1 -0
- package/dist/{chunk-66C4BSAY.js → chunk-ANBQRTPX.js} +9 -2
- package/dist/chunk-ANBQRTPX.js.map +1 -0
- package/dist/chunk-APIBCTL2.js +670 -0
- package/dist/chunk-APIBCTL2.js.map +1 -0
- package/dist/{chunk-GKHF54DI.js → chunk-BESYRHQM.js} +10 -4
- package/dist/chunk-BESYRHQM.js.map +1 -0
- package/dist/{chunk-WP5I5GLN.js → chunk-BVYWGZVV.js} +112 -97
- package/dist/chunk-BVYWGZVV.js.map +1 -0
- package/dist/{chunk-GEVIB2UB.js → chunk-ERISIBYU.js} +14 -5
- package/dist/chunk-ERISIBYU.js.map +1 -0
- package/dist/{chunk-CQZU6TFE.js → chunk-FHWWBIHA.js} +100 -62
- package/dist/chunk-FHWWBIHA.js.map +1 -0
- package/dist/{chunk-O3NWNXDY.js → chunk-FMUCXFII.js} +2 -2
- package/dist/chunk-FMUCXFII.js.map +1 -0
- package/dist/{chunk-GVDR7WNV.js → chunk-HJGGOMQ6.js} +194 -518
- package/dist/chunk-HJGGOMQ6.js.map +1 -0
- package/dist/{chunk-BDZUMRBD.js → chunk-K2WWTH7O.js} +13 -6
- package/dist/chunk-K2WWTH7O.js.map +1 -0
- package/dist/{chunk-BYXRHAIF.js → chunk-L6PGMCMD.js} +23 -14
- package/dist/chunk-L6PGMCMD.js.map +1 -0
- package/dist/chunk-LMC26NLJ.js +84 -0
- package/dist/chunk-LMC26NLJ.js.map +1 -0
- package/dist/{chunk-M6DDYFUD.js → chunk-LS353YLY.js} +19 -16
- package/dist/chunk-LS353YLY.js.map +1 -0
- package/dist/{chunk-ZYZCRSBD.js → chunk-LTV3XIJJ.js} +16 -11
- package/dist/chunk-LTV3XIJJ.js.map +1 -0
- package/dist/{chunk-HMNOSGVA.js → chunk-NOHEVYVX.js} +377 -666
- package/dist/chunk-NOHEVYVX.js.map +1 -0
- package/dist/{chunk-JCQZ6LA7.js → chunk-Q5QRDWKI.js} +9 -3
- package/dist/chunk-Q5QRDWKI.js.map +1 -0
- package/dist/chunk-S5OFRT4M.js +94 -0
- package/dist/chunk-S5OFRT4M.js.map +1 -0
- package/dist/{chunk-3DBFLLLU.js → chunk-SBVILCCA.js} +14 -9
- package/dist/chunk-SBVILCCA.js.map +1 -0
- package/dist/{chunk-TGIY2AR2.js → chunk-SL2YQDR6.js} +4 -3
- package/dist/{chunk-TGIY2AR2.js.map → chunk-SL2YQDR6.js.map} +1 -1
- package/dist/{chunk-VZ5OR6HD.js → chunk-TVYPTYOY.js} +55 -179
- package/dist/chunk-TVYPTYOY.js.map +1 -0
- package/dist/{chunk-ZV77RZMU.js → chunk-XARJS7CD.js} +2 -2
- package/dist/chunk-XDNLUEXI.js +138 -0
- package/dist/chunk-XDNLUEXI.js.map +1 -0
- package/dist/{chunk-F64FFPOZ.js → chunk-YLKIDTUK.js} +26 -20
- package/dist/chunk-YLKIDTUK.js.map +1 -0
- package/dist/{chunk-5F3NDPJV.js → chunk-ZZ2SS7NI.js} +10 -5
- package/dist/chunk-ZZ2SS7NI.js.map +1 -0
- package/dist/components.d.ts +7 -287
- package/dist/components.js +26 -157
- package/dist/components.js.map +1 -1
- package/dist/{file-reference-C9isKNPn.d.ts → file-reference-C6Gkn77H.d.ts} +1 -1
- package/dist/{formatting-DFcCxUEk.d.ts → formatting-CvUXy2mF.d.ts} +1 -1
- package/dist/hooks.d.ts +3 -3
- package/dist/hooks.js +22 -16
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +101 -9
- package/dist/index.js +43 -31
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +5 -4
- package/dist/rbac/index.js +13 -12
- package/dist/styles/index.js +2 -1
- package/dist/theming/runtime.d.ts +2 -19
- package/dist/theming/runtime.js +2 -1
- package/dist/{types-D5rqZQXk.d.ts → types-Dfz9dmVH.d.ts} +12 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/dist/{useInactivityTracker-MRUU55XI.js → useInactivityTracker-TO6ZOF35.js} +3 -2
- package/dist/{usePublicRouteParams-Dyt1tzI9.d.ts → usePublicRouteParams-B7PabvuH.d.ts} +1 -1
- package/dist/utils.d.ts +195 -232
- package/dist/utils.js +173 -331
- package/dist/utils.js.map +1 -1
- package/dist/{validation-DnhrNMju.d.ts → validation-8npbysjg.d.ts} +26 -8
- package/dist/validation.d.ts +261 -10
- package/dist/validation.js +82 -440
- package/dist/validation.js.map +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +6 -6
- 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 +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +6 -6
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +7 -7
- 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 +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +4 -4
- 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 +29 -4
- package/docs/api/interfaces/DataAccessRecord.md +9 -9
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +18 -18
- package/docs/api/interfaces/DataTableColumn.md +61 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +14 -14
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +152 -0
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +8 -8
- package/docs/api/interfaces/FileDisplayProps.md +15 -15
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
- package/docs/api/interfaces/NavigationContextType.md +9 -9
- package/docs/api/interfaces/NavigationGuardProps.md +10 -10
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +7 -7
- 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 +27 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
- package/docs/api/interfaces/PageAccessRecord.md +8 -8
- package/docs/api/interfaces/PagePermissionContextType.md +8 -8
- package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
- package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
- package/docs/api/interfaces/PaletteData.md +4 -4
- package/docs/api/interfaces/PermissionEnforcerProps.md +11 -11
- package/docs/api/interfaces/ProtectedRouteProps.md +6 -6
- 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/RBACLogger.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
- package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +10 -10
- package/docs/api/interfaces/RouteConfig.md +10 -10
- package/docs/api/interfaces/SecureDataContextType.md +9 -9
- package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +21 -0
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +9 -9
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
- package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
- package/docs/api/interfaces/UsePublicEventReturn.md +5 -5
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +9 -9
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +4 -4
- package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
- package/docs/api/interfaces/UserEventAccess.md +11 -11
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +514 -212
- package/docs/api-reference/components.md +106 -26
- package/docs/architecture/README.md +0 -2
- package/docs/implementation-guides/data-tables.md +277 -13
- package/docs/implementation-guides/forms.md +1 -16
- package/docs/implementation-guides/permission-enforcement.md +8 -2
- package/examples/README.md +30 -14
- package/examples/STRUCTURE.md +125 -0
- package/examples/components/DataTable/HierarchicalActionsExample.tsx +421 -0
- package/examples/components/DataTable/HierarchicalExample.tsx +475 -0
- package/examples/components/DataTable/InitialPageSizeExample.tsx +177 -0
- package/examples/components/DataTable/PerformanceExample.tsx +506 -0
- package/examples/components/DataTable/index.ts +13 -0
- package/examples/components/Dialog/BasicHtmlTest.tsx +55 -0
- package/examples/components/Dialog/DebugHtmlExample.tsx +68 -0
- package/examples/components/Dialog/HtmlDialogExample.tsx +202 -0
- package/examples/components/Dialog/ScrollableDialogExample.tsx +290 -0
- package/examples/components/Dialog/SimpleHtmlTest.tsx +61 -0
- package/examples/components/Dialog/SmartDialogExample.tsx +322 -0
- package/examples/components/Dialog/index.ts +15 -0
- package/examples/components/index.ts +11 -0
- package/examples/features/index.ts +12 -0
- package/examples/{public-pages → features/public-pages}/CorrectPublicPageImplementation.tsx +1 -1
- package/examples/{public-pages → features/public-pages}/PublicEventPage.tsx +1 -1
- package/examples/{public-pages → features/public-pages}/PublicPageApp.tsx +1 -1
- package/examples/{public-pages → features/public-pages}/PublicPageUsageExample.tsx +1 -1
- package/examples/index.ts +11 -3
- package/package.json +30 -10
- package/src/components/Alert/Alert.tsx +1 -1
- package/src/components/Avatar/Avatar.tsx +1 -1
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/Card/Card.tsx +1 -1
- package/src/components/Checkbox/Checkbox.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +1 -1
- package/src/components/DataTable/DataTable.tsx +1 -30
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +562 -0
- package/src/components/DataTable/__tests__/styles.test.ts +2 -2
- package/src/components/DataTable/components/ActionButtons.tsx +0 -15
- package/src/components/DataTable/components/DataTableCore.tsx +4 -185
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +1 -1
- package/src/components/DataTable/components/DataTableModals.tsx +1 -27
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/ImportModal.tsx +2 -14
- package/src/components/DataTable/components/PaginationControls.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +109 -82
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +1 -1
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +1 -1
- package/src/components/DataTable/examples/GroupingAggregationExample.tsx +273 -0
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +1 -1
- package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +1 -1
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +1 -1
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +1 -1
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +2 -23
- package/src/components/DataTable/index.ts +4 -0
- package/src/components/DataTable/styles.ts +1 -1
- package/src/components/DataTable/types.ts +13 -0
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +1 -1
- package/src/components/DataTable/utils/aggregationUtils.ts +161 -0
- package/src/components/DataTable/utils/exportUtils.ts +1 -1
- package/src/components/DataTable/utils/flexibleImport.ts +1 -11
- package/src/components/DataTable/utils/index.ts +1 -0
- package/src/components/DataTable/utils/paginationUtils.ts +1 -1
- package/src/components/Dialog/Dialog.tsx +2 -2
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +35 -7
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +5 -4
- package/src/components/EventSelector/EventSelector.tsx +3 -2
- package/src/components/FileDisplay/FileDisplay.tsx +2 -36
- package/src/components/FileUpload/FileUpload.test.tsx +2 -2
- package/src/components/FileUpload/FileUpload.tsx +2 -2
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +4 -509
- package/src/components/Form/Form.tsx +1 -1
- package/src/components/Form/FormField.tsx +1 -1
- package/src/components/Form/index.ts +0 -12
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/Input/Input.tsx +1 -1
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoginForm/LoginForm.tsx +1 -1
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -3
- package/src/components/NavigationMenu/NavigationMenu.tsx +9 -8
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +4 -3
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +14 -12
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +0 -16
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +0 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +0 -9
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +35 -3
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +13 -12
- package/src/components/PasswordReset/PasswordChangeForm.tsx +1 -1
- package/src/components/PasswordReset/index.ts +0 -2
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +35 -8
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -2
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +1 -1
- package/src/components/PublicLayout/PublicPageContextChecker.tsx +44 -43
- package/src/components/PublicLayout/PublicPageFooter.tsx +1 -1
- package/src/components/PublicLayout/PublicPageHeader.tsx +1 -15
- package/src/components/PublicLayout/PublicPageProvider.tsx +3 -2
- package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +2 -0
- package/src/components/PublicLayout/index.ts +4 -2
- package/src/components/Select/Select.tsx +1 -1
- package/src/components/{SessionRestorationLoader.tsx → SessionRestorationLoader/SessionRestorationLoader.tsx} +3 -2
- package/src/components/SessionRestorationLoader/index.ts +3 -0
- package/src/components/Switch/Switch.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/index.ts +4 -10
- package/src/hooks/__tests__/hooks.integration.test.tsx +37 -22
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +33 -17
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +28 -3
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +36 -9
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +26 -2
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +19 -6
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +17 -4
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +17 -4
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +26 -6
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +16 -6
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +3 -3
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +17 -3
- package/src/hooks/public/usePublicEvent.ts +7 -6
- package/src/hooks/public/usePublicEventLogo.ts +7 -4
- package/src/hooks/public/usePublicFileDisplay.ts +6 -150
- package/src/hooks/useComponentPerformance.ts +4 -1
- package/src/hooks/useDataTablePerformance.ts +4 -3
- package/src/hooks/useEventTheme.test.ts +18 -5
- package/src/hooks/useEventTheme.ts +4 -1
- package/src/hooks/useEvents.ts +2 -0
- package/src/hooks/useFileDisplay.ts +9 -8
- package/src/hooks/useFileReference.ts +4 -1
- package/src/hooks/useFileUrl.ts +4 -1
- package/src/hooks/useInactivityTracker.ts +5 -4
- package/src/hooks/useOrganisationSecurity.test.ts +33 -12
- package/src/hooks/useOrganisationSecurity.ts +8 -7
- package/src/hooks/usePerformanceMonitor.ts +6 -3
- package/src/hooks/usePermissionCache.ts +13 -6
- package/src/hooks/useSecureDataAccess.test.ts +2 -2
- package/src/hooks/useSecureDataAccess.ts +9 -8
- package/src/hooks/useSessionRestoration.ts +4 -1
- package/src/hooks/useStorage.ts +4 -1
- package/src/index.ts +16 -7
- package/src/providers/services/AuthServiceProvider.tsx +3 -2
- package/src/providers/services/EventServiceProvider.tsx +2 -1
- package/src/providers/services/InactivityServiceProvider.tsx +2 -1
- package/src/providers/services/OrganisationServiceProvider.tsx +2 -1
- package/src/providers/services/UnifiedAuthProvider.tsx +4 -3
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +22 -2
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +24 -2
- package/src/rbac/__tests__/cache-invalidation.test.ts +20 -6
- package/src/rbac/api.ts +5 -2
- package/src/rbac/audit-enhanced.ts +6 -6
- package/src/rbac/audit.test.ts +60 -38
- package/src/rbac/audit.ts +8 -8
- package/src/rbac/cache-invalidation.ts +7 -4
- package/src/rbac/components/EnhancedNavigationMenu.tsx +11 -5
- package/src/rbac/components/NavigationGuard.tsx +7 -3
- package/src/rbac/components/NavigationProvider.tsx +6 -3
- package/src/rbac/components/PagePermissionGuard.tsx +28 -16
- package/src/rbac/components/PagePermissionProvider.tsx +4 -1
- package/src/rbac/components/PermissionEnforcer.tsx +9 -3
- package/src/rbac/components/RoleBasedRouter.tsx +3 -1
- package/src/rbac/components/SecureDataProvider.tsx +7 -3
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +87 -61
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +83 -33
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +36 -13
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +2 -2
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +22 -8
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +19 -6
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +43 -17
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +42 -17
- package/src/rbac/engine.ts +15 -7
- package/src/rbac/hooks/usePermissions.ts +7 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +2 -2
- package/src/rbac/hooks/useResolvedScope.ts +10 -7
- package/src/rbac/permissions.ts +5 -2
- package/src/rbac/security.test.ts +27 -16
- package/src/rbac/security.ts +5 -4
- package/src/services/AuthService.ts +22 -21
- package/src/services/EventService.ts +12 -12
- package/src/services/InactivityService.ts +5 -4
- package/src/services/OrganisationService.ts +26 -25
- package/src/services/__tests__/AuthService.test.ts +51 -19
- package/src/services/__tests__/EventService.test.ts +37 -5
- package/src/services/__tests__/InactivityService.test.ts +38 -4
- package/src/services/__tests__/OrganisationService.test.ts +3 -8
- package/src/services/base/BaseService.ts +3 -1
- package/src/theming/__tests__/runtime.test.ts +21 -12
- package/src/theming/parseEventColours.ts +5 -19
- package/src/theming/runtime.ts +8 -4
- package/src/types/validation.ts +2 -29
- package/src/utils/__tests__/appConfig.unit.test.ts +1 -1
- package/src/utils/__tests__/audit.unit.test.ts +1 -1
- package/src/utils/__tests__/auth-utils.unit.test.ts +1 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +19 -19
- package/src/utils/__tests__/cn.unit.test.ts +1 -1
- package/src/utils/__tests__/debugLogger.test.ts +1 -1
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +1 -1
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/formatting.unit.test.ts +1 -1
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +1 -1
- package/src/utils/__tests__/logger.unit.test.ts +1 -1
- package/src/utils/__tests__/organisationContext.unit.test.ts +1 -1
- package/src/utils/__tests__/performanceBenchmark.test.ts +1 -1
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +1 -1
- package/src/utils/__tests__/permissionTypes.unit.test.ts +1 -1
- package/src/utils/__tests__/permissionUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/sanitization.unit.test.ts +1 -1
- package/src/utils/__tests__/schemaUtils.unit.test.ts +1 -1
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +1 -1
- package/src/utils/__tests__/secureErrors.unit.test.ts +33 -15
- package/src/utils/__tests__/secureStorage.unit.test.ts +1 -1
- package/src/utils/__tests__/security.unit.test.ts +40 -18
- package/src/utils/__tests__/securityMonitor.unit.test.ts +1 -1
- package/src/utils/__tests__/sessionTracking.unit.test.ts +40 -29
- package/src/utils/__tests__/validationUtils.unit.test.ts +19 -6
- package/src/utils/{appIdResolver.test.ts → app/appIdResolver.test.ts} +28 -30
- package/src/utils/{appIdResolver.ts → app/appIdResolver.ts} +8 -5
- package/src/utils/{appNameResolver.test.ts → app/appNameResolver.test.ts} +1 -1
- package/src/utils/{appNameResolver.ts → app/appNameResolver.ts} +5 -1
- package/src/utils/{organisationContext.ts → context/organisationContext.ts} +6 -3
- package/src/utils/{sessionTracking.ts → context/sessionTracking.ts} +11 -12
- package/src/utils/{logger.ts → core/logger.ts} +4 -2
- package/src/utils/{deviceFingerprint.ts → device/deviceFingerprint.ts} +1 -1
- package/src/utils/{lazyLoad.tsx → dynamic/lazyLoad.tsx} +2 -2
- package/src/utils/{file-reference.test.ts → file-reference/__tests__/file-reference.test.ts} +5 -5
- package/src/utils/{file-reference.ts → file-reference/index.ts} +20 -38
- package/src/utils/index.ts +32 -54
- package/src/utils/{secureErrors.ts → security/secureErrors.ts} +6 -3
- package/src/utils/{security.ts → security/security.ts} +5 -2
- package/src/utils/storage/__tests__/helpers.unit.test.ts +1 -4
- package/src/utils/storage/helpers.ts +15 -8
- package/src/{components/Dialog/utils/__tests__/safeHtml.unit.test.ts → utils/validation/__tests__/htmlSanitization.unit.test.ts} +9 -15
- package/src/{validation → utils/validation}/csrf.ts +1 -1
- package/src/{components/Dialog/utils/safeHtml.ts → utils/validation/htmlSanitization.ts} +9 -10
- package/src/utils/validation/index.ts +79 -0
- package/src/utils/{sanitization.ts → validation/sanitization.ts} +71 -2
- package/src/{validation/schemaUtils.ts → utils/validation/schema.ts} +11 -6
- package/src/{validation → utils/validation}/sqlInjectionProtection.ts +2 -0
- package/src/utils/{validationUtils.ts → validation/validationUtils.ts} +4 -1
- package/src/validation/index.ts +3 -34
- package/dist/UnifiedAuthProvider-CQDZRJIS.js +0 -16
- package/dist/chunk-24MKLB7U.js +0 -81
- package/dist/chunk-24MKLB7U.js.map +0 -1
- package/dist/chunk-3CG5L6RN.js.map +0 -1
- package/dist/chunk-3DBFLLLU.js.map +0 -1
- package/dist/chunk-5F3NDPJV.js.map +0 -1
- package/dist/chunk-66C4BSAY.js.map +0 -1
- package/dist/chunk-BDZUMRBD.js.map +0 -1
- package/dist/chunk-BYXRHAIF.js.map +0 -1
- package/dist/chunk-CDQ3PX7L.js +0 -18
- package/dist/chunk-CDQ3PX7L.js.map +0 -1
- package/dist/chunk-CQZU6TFE.js.map +0 -1
- package/dist/chunk-F64FFPOZ.js.map +0 -1
- package/dist/chunk-GEVIB2UB.js.map +0 -1
- package/dist/chunk-GKHF54DI.js.map +0 -1
- package/dist/chunk-GVDR7WNV.js.map +0 -1
- package/dist/chunk-HMNOSGVA.js.map +0 -1
- package/dist/chunk-JCQZ6LA7.js.map +0 -1
- package/dist/chunk-M6DDYFUD.js.map +0 -1
- package/dist/chunk-O3NWNXDY.js.map +0 -1
- package/dist/chunk-PYUXFQJ3.js.map +0 -1
- package/dist/chunk-UJI6WSMD.js.map +0 -1
- package/dist/chunk-VZ5OR6HD.js.map +0 -1
- package/dist/chunk-WP5I5GLN.js.map +0 -1
- package/dist/chunk-ZYZCRSBD.js.map +0 -1
- package/src/components/Dialog/README.md +0 -804
- package/src/components/Form/FormErrorSummary.tsx +0 -113
- package/src/components/Form/FormFieldset.tsx +0 -127
- package/src/components/Form/FormLiveRegion.tsx +0 -198
- package/src/components/PasswordReset/PasswordResetForm.test.tsx +0 -597
- package/src/components/PasswordReset/PasswordResetForm.tsx +0 -201
- package/src/components/PublicLayout/PublicPageDebugger.tsx +0 -104
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +0 -162
- package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +0 -185
- package/src/examples/CorrectPublicPageImplementation.tsx +0 -304
- package/src/examples/PublicEventPage.tsx +0 -287
- package/src/examples/PublicPageApp.tsx +0 -321
- package/src/examples/PublicPageUsageExample.tsx +0 -218
- package/src/utils/schemaUtils.ts +0 -37
- package/src/validation/__tests__/common.unit.test.ts +0 -101
- package/src/validation/__tests__/csrf.unit.test.ts +0 -365
- package/src/validation/__tests__/passwordSchema.unit.test.ts +0 -203
- package/src/validation/__tests__/sanitization.unit.test.ts +0 -250
- package/src/validation/__tests__/schemaUtils.unit.test.ts +0 -451
- package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +0 -462
- package/src/validation/__tests__/user.unit.test.ts +0 -440
- package/src/validation/sanitization.ts +0 -96
- /package/dist/{DataTable-A36PJG6N.js.map → DataTable-CYOHOX3O.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-CQDZRJIS.js.map → UnifiedAuthProvider-5E5TUNMS.js.map} +0 -0
- /package/dist/{api-TNIBJWLM.js.map → api-45XYYO2A.js.map} +0 -0
- /package/dist/{audit-T36HM7IM.js.map → audit-64X3VJXB.js.map} +0 -0
- /package/dist/{chunk-CTJRBUX2.js.map → chunk-2TWNJ46Y.js.map} +0 -0
- /package/dist/{chunk-ZV77RZMU.js.map → chunk-XARJS7CD.js.map} +0 -0
- /package/dist/{useInactivityTracker-MRUU55XI.js.map → useInactivityTracker-TO6ZOF35.js.map} +0 -0
- /package/examples/{public-pages → features/public-pages}/index.ts +0 -0
- /package/examples/{RBAC → features/rbac}/CompleteRBACExample.tsx +0 -0
- /package/examples/{RBAC → features/rbac}/EventBasedApp.tsx +0 -0
- /package/examples/{RBAC → features/rbac}/PermissionExample.tsx +0 -0
- /package/examples/{RBAC → features/rbac}/index.ts +0 -0
- /package/src/utils/{appConfig.ts → app/appConfig.ts} +0 -0
- /package/src/utils/{appNameResolver.simple.test.ts → app/appNameResolver.simple.test.ts} +0 -0
- /package/src/utils/{audit.ts → audit/audit.ts} +0 -0
- /package/src/utils/{organisationContext.test.ts → context/organisationContext.test.ts} +0 -0
- /package/src/utils/{cn.ts → core/cn.ts} +0 -0
- /package/src/utils/{debugLogger.ts → core/debugLogger.ts} +0 -0
- /package/src/utils/{dynamicUtils.ts → dynamic/dynamicUtils.ts} +0 -0
- /package/src/utils/{formatDate.test.ts → formatting/formatDate.test.ts} +0 -0
- /package/src/utils/{formatting.ts → formatting/formatting.ts} +0 -0
- /package/src/utils/{bundleAnalysis.ts → performance/bundleAnalysis.ts} +0 -0
- /package/src/utils/{performanceBenchmark.ts → performance/performanceBenchmark.ts} +0 -0
- /package/src/utils/{performanceBudgets.ts → performance/performanceBudgets.ts} +0 -0
- /package/src/utils/{permissionTypes.ts → permissions/permissionTypes.ts} +0 -0
- /package/src/utils/{permissionUtils.test.ts → permissions/permissionUtils.test.ts} +0 -0
- /package/src/utils/{permissionUtils.ts → permissions/permissionUtils.ts} +0 -0
- /package/src/utils/{auth-utils.ts → security/auth-utils.ts} +0 -0
- /package/src/utils/{secureDataAccess.test.ts → security/secureDataAccess.test.ts} +0 -0
- /package/src/utils/{secureDataAccess.ts → security/secureDataAccess.ts} +0 -0
- /package/src/utils/{secureStorage.ts → security/secureStorage.ts} +0 -0
- /package/src/utils/{securityMonitor.ts → security/securityMonitor.ts} +0 -0
- /package/src/{validation → utils/validation}/common.ts +0 -0
- /package/src/{validation → utils/validation}/passwordSchema.ts +0 -0
- /package/src/{validation → utils/validation}/user.ts +0 -0
- /package/src/utils/{validation.ts → validation/validation.ts} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useInactivityTracker.ts"],"sourcesContent":["/**\n * @file Inactivity Tracker Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useInactivityTracker\n * @since 0.1.0\n *\n * A custom hook that tracks user inactivity and provides cross-tab synchronization.\n * Monitors various user interactions and manages inactivity timers with persistence.\n *\n * Features:\n * - Cross-tab synchronization using BroadcastChannel and localStorage\n * - Monitors keyboard, mouse, touch, scroll, and focus events\n * - Throttled event handling for performance\n * - Persistence of last activity time across page reloads\n * - SSR-safe implementation with proper cleanup\n * - Configurable timeout and warning thresholds\n * - Production-safe with dev-only escape hatches\n *\n * @example\n * ```tsx\n * const {\n * isIdle,\n * timeRemaining,\n * showWarning,\n * resetActivity,\n * startTracking,\n * stopTracking\n * } = useInactivityTracker({\n * idleTimeoutMs: 30 * 60 * 1000, // 30 minutes\n * warnBeforeMs: 60 * 1000, // 1 minute\n * onIdle: () => console.log('User is idle'),\n * onWarning: () => console.log('Warning should show'),\n * onActivity: () => console.log('User is active')\n * });\n * ```\n *\n * @accessibility\n * - No direct accessibility concerns (utility hook)\n * - Enables accessible inactivity warnings\n * - Supports screen reader friendly countdowns\n * - Maintains focus management during warnings\n *\n * @performance\n * - Throttled event handling (100ms intervals)\n * - Efficient timer management with cleanup\n * - Minimal re-renders with stable references\n * - Memory leak prevention\n * - Cross-tab optimization\n *\n * @dependencies\n * - React 18+ - Hooks and effects\n * - Browser APIs - BroadcastChannel, localStorage, timers\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\n\nexport interface UseInactivityTrackerOptions {\n /** Timeout in milliseconds before user is considered idle (default: 30 minutes) */\n idleTimeoutMs?: number;\n /** Time in milliseconds before idle timeout to show warning (default: 60 seconds) */\n warnBeforeMs?: number;\n /** Callback when user becomes idle */\n onIdle?: () => void;\n /** Callback when warning should be shown */\n onWarning?: () => void;\n /** Callback when user becomes active again */\n onActivity?: () => void;\n /** Whether tracking is enabled (default: true) */\n enabled?: boolean;\n /** Storage key for persistence (default: 'pace-core-inactivity') */\n storageKey?: string;\n /** Broadcast channel name for cross-tab sync (default: 'pace-core-inactivity') */\n channelName?: string;\n}\n\nexport interface UseInactivityTrackerReturn {\n /** Whether the user is currently idle */\n isIdle: boolean;\n /** Time remaining in milliseconds before idle timeout */\n timeRemaining: number;\n /** Whether warning should be shown */\n showWarning: boolean;\n /** Reset the activity timer */\n resetActivity: () => void;\n /** Start tracking inactivity */\n startTracking: () => void;\n /** Stop tracking inactivity */\n stopTracking: () => void;\n /** Whether tracking is currently active */\n isTracking: boolean;\n}\n\n// Events that indicate user activity\nconst ACTIVITY_EVENTS = [\n 'mousedown',\n 'mousemove',\n 'mouseup',\n 'click',\n 'scroll',\n 'wheel',\n 'touchstart',\n 'touchmove',\n 'touchend',\n 'keydown',\n 'keyup',\n 'keypress',\n 'focus',\n 'blur',\n 'visibilitychange'\n] as const;\n\n// Throttle function to limit event handler frequency\nfunction throttle<T extends (...args: any[]) => void>(\n func: T,\n limit: number\n): (...args: Parameters<T>) => void {\n let inThrottle: boolean;\n return function (this: any, ...args: Parameters<T>) {\n if (!inThrottle) {\n func.apply(this, args);\n inThrottle = true;\n setTimeout(() => (inThrottle = false), limit);\n }\n };\n}\n\nexport function useInactivityTracker({\n idleTimeoutMs = 30 * 60 * 1000, // 30 minutes\n warnBeforeMs = 60 * 1000, // 1 minute\n onIdle,\n onWarning,\n onActivity,\n enabled = true,\n storageKey = 'pace-core-inactivity',\n channelName = 'pace-core-inactivity'\n}: UseInactivityTrackerOptions = {}): UseInactivityTrackerReturn {\n const [isIdle, setIsIdle] = useState(false);\n const [timeRemaining, setTimeRemaining] = useState(idleTimeoutMs);\n const [showWarning, setShowWarning] = useState(false);\n const [isTracking, setIsTracking] = useState(false);\n\n // Reset tracking state when enabled changes\n useEffect(() => {\n if (!enabled) {\n setIsTracking(false);\n setIsIdle(false);\n setShowWarning(false);\n setTimeRemaining(idleTimeoutMs);\n }\n }, [enabled, idleTimeoutMs]);\n\n const timeoutRef = useRef<NodeJS.Timeout | null>(null);\n const warningTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const countdownIntervalRef = useRef<NodeJS.Timeout | null>(null);\n const lastActivityRef = useRef<number>(Date.now());\n const channelRef = useRef<BroadcastChannel | null>(null);\n\n // Clear all timers\n const clearTimers = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n if (warningTimeoutRef.current) {\n clearTimeout(warningTimeoutRef.current);\n warningTimeoutRef.current = null;\n }\n if (countdownIntervalRef.current) {\n clearInterval(countdownIntervalRef.current);\n countdownIntervalRef.current = null;\n }\n }, []);\n\n // Reset activity and restart timers\n const resetActivity = useCallback((skipActivityCallback = false) => {\n if (!enabled) return;\n\n const now = Date.now();\n lastActivityRef.current = now;\n\n // Clear existing timers\n clearTimers();\n\n // Reset state\n setIsIdle(false);\n setShowWarning(false);\n setTimeRemaining(idleTimeoutMs);\n\n // Notify activity callback (unless skipped for initial setup)\n if (!skipActivityCallback) {\n onActivity?.();\n }\n\n // Set up warning timer\n const warningTime = idleTimeoutMs - warnBeforeMs;\n if (warningTime > 0) {\n warningTimeoutRef.current = setTimeout(() => {\n setShowWarning(true);\n onWarning?.();\n }, warningTime);\n }\n\n // Set up idle timeout\n timeoutRef.current = setTimeout(() => {\n setIsIdle(true);\n onIdle?.();\n }, idleTimeoutMs);\n\n // Start countdown interval for time remaining\n countdownIntervalRef.current = setInterval(() => {\n const elapsed = Date.now() - lastActivityRef.current;\n const remaining = Math.max(0, idleTimeoutMs - elapsed);\n setTimeRemaining(remaining);\n\n if (remaining === 0) {\n clearTimers();\n }\n }, 1000);\n\n // Persist activity time\n try {\n localStorage.setItem(storageKey, now.toString());\n } catch (error) {\n console.warn('[useInactivityTracker] Failed to persist activity time:', error);\n }\n\n // Broadcast activity to other tabs\n try {\n if (channelRef.current) {\n channelRef.current.postMessage({ type: 'activity', timestamp: now });\n }\n } catch (error) {\n console.warn('[useInactivityTracker] Failed to broadcast activity:', error);\n }\n }, [enabled, idleTimeoutMs, warnBeforeMs, onIdle, onWarning, onActivity, storageKey, clearTimers]);\n\n // Start tracking\n const startTracking = useCallback(() => {\n if (!enabled) return;\n\n // Always reset state and start fresh\n setIsTracking(false);\n setIsIdle(false);\n setShowWarning(false);\n setTimeRemaining(idleTimeoutMs);\n \n // Clear any existing timers\n clearTimers();\n \n setIsTracking(true);\n\n // Set up cross-tab communication\n try {\n if (typeof BroadcastChannel !== 'undefined') {\n channelRef.current = new BroadcastChannel(channelName);\n channelRef.current.onmessage = (event) => {\n if (event.data.type === 'activity') {\n lastActivityRef.current = event.data.timestamp;\n resetActivity();\n }\n };\n }\n } catch (error) {\n console.warn('[useInactivityTracker] Failed to set up cross-tab communication:', error);\n }\n\n // Check for persisted activity time\n try {\n const persistedTime = localStorage.getItem(storageKey);\n if (persistedTime) {\n const persistedTimestamp = parseInt(persistedTime, 10);\n const elapsed = Date.now() - persistedTimestamp;\n \n if (elapsed < idleTimeoutMs) {\n // User was active recently, continue from where we left off\n lastActivityRef.current = persistedTimestamp;\n const remaining = idleTimeoutMs - elapsed;\n setTimeRemaining(remaining);\n \n if (remaining <= warnBeforeMs) {\n setShowWarning(true);\n onWarning?.();\n }\n \n if (remaining <= 0) {\n setIsIdle(true);\n onIdle?.();\n return;\n }\n }\n }\n } catch (error) {\n console.warn('[useInactivityTracker] Failed to check persisted activity time:', error);\n }\n\n // Set up throttled activity handler\n const throttledResetActivity = throttle((event) => {\n resetActivity();\n }, 100);\n\n // Add event listeners\n const addEventListeners = () => {\n ACTIVITY_EVENTS.forEach(event => {\n document.addEventListener(event, throttledResetActivity, { passive: true });\n });\n };\n\n // Remove event listeners\n const removeEventListeners = () => {\n ACTIVITY_EVENTS.forEach(event => {\n document.removeEventListener(event, throttledResetActivity);\n });\n };\n\n // Add listeners\n addEventListeners();\n\n // Start the timer (skip activity callback for initial setup)\n resetActivity(true);\n\n // Cleanup function\n return () => {\n removeEventListeners();\n clearTimers();\n if (channelRef.current) {\n channelRef.current.close();\n channelRef.current = null;\n }\n };\n }, [enabled, isTracking, channelName, storageKey, idleTimeoutMs, warnBeforeMs, onIdle, onWarning]);\n\n // Stop tracking\n const stopTracking = useCallback(() => {\n setIsTracking(false);\n clearTimers();\n \n if (channelRef.current) {\n channelRef.current.close();\n channelRef.current = null;\n }\n }, [clearTimers]);\n\n // Effect to start/stop tracking based on enabled state\n useEffect(() => {\n if (enabled) {\n const cleanup = startTracking();\n return cleanup;\n } else {\n stopTracking();\n }\n }, [enabled, idleTimeoutMs, warnBeforeMs]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n clearTimers();\n if (channelRef.current) {\n channelRef.current.close();\n }\n };\n }, [clearTimers]);\n\n return {\n isIdle,\n timeRemaining,\n showWarning,\n resetActivity,\n startTracking,\n stopTracking,\n isTracking\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useComponentPerformance.ts"],"sourcesContent":["\nimport { useEffect, useRef } from 'react';\n\ninterface PerformanceConfig {\n componentName: string;\n enableLogging?: boolean;\n threshold?: number;\n}\n\nexport function useComponentPerformance({ \n componentName, \n enableLogging = import.meta.env.MODE === 'development',\n threshold = 16 // 60fps = 16ms per frame\n}: PerformanceConfig) {\n const renderCount = useRef(0);\n const lastRenderTime = useRef<number>(0);\n\n useEffect(() => {\n if (!enableLogging) return;\n\n const now = performance.now();\n const renderTime = now - lastRenderTime.current;\n renderCount.current += 1;\n\n if (renderCount.current > 1 && renderTime > threshold) {\n console.warn(\n `Performance warning: ${componentName} rendered in ${renderTime.toFixed(2)}ms ` +\n `(render #${renderCount.current}). Consider optimizing.`\n );\n }\n\n lastRenderTime.current = now;\n });\n\n return {\n renderCount: renderCount.current,\n componentName\n };\n}\n"],"mappings":";AACA,SAAS,WAAW,cAAc;AAQ3B,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA,gBAAgB,YAAY,IAAI,SAAS;AAAA,EACzC,YAAY;AAAA;AACd,GAAsB;AACpB,QAAM,cAAc,OAAO,CAAC;AAC5B,QAAM,iBAAiB,OAAe,CAAC;AAEvC,YAAU,MAAM;AACd,QAAI,CAAC,cAAe;AAEpB,UAAM,MAAM,YAAY,IAAI;AAC5B,UAAM,aAAa,MAAM,eAAe;AACxC,gBAAY,WAAW;AAEvB,QAAI,YAAY,UAAU,KAAK,aAAa,WAAW;AACrD,cAAQ;AAAA,QACN,wBAAwB,aAAa,gBAAgB,WAAW,QAAQ,CAAC,CAAC,eAC9D,YAAY,OAAO;AAAA,MACjC;AAAA,IACF;AAEA,mBAAe,UAAU;AAAA,EAC3B,CAAC;AAED,SAAO;AAAA,IACL,aAAa,YAAY;AAAA,IACzB;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/organisationContext.ts"],"sourcesContent":["/**\n * @file Organisation Context Utility\n * @package @jmruthers/pace-core\n * @module Utils/OrganisationContext\n * @since 0.4.0\n *\n * Utility functions for managing organisation context in database sessions.\n * Provides fallback mechanisms for when database functions are not available.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\n\n/**\n * Set organisation context in the database session\n * \n * This function attempts to set the organisation context using a database function.\n * If the function is not available, it falls back gracefully without throwing errors.\n * \n * @param supabase - Supabase client instance\n * @param organisationId - The organisation ID to set as context\n * @returns Promise that resolves when context is set (or falls back gracefully)\n */\nexport async function setOrganisationContext(\n supabase: SupabaseClient,\n organisationId: string\n): Promise<void> {\n if (!supabase || !organisationId) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n // Add timeout to prevent hanging RPC calls\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => reject(new Error('RPC timeout after 3 seconds')), 3000);\n });\n \n // Call the database function to set organisation context\n const rpcPromise = supabase.rpc('set_organisation_context', {\n org_id: organisationId\n });\n\n const { error } = await Promise.race([rpcPromise, timeoutPromise]) as any;\n\n if (error) {\n // Function might not exist yet - this is expected during migration\n // Silent fail - will fall back to client-side filtering\n console.log('[organisationContext] RPC function not available or failed, continuing without database context');\n } else {\n console.log('[organisationContext] Organisation context set in database successfully');\n }\n } catch (error) {\n // Handle any other errors gracefully\n // Silent fail - will fall back to client-side filtering\n console.log('[organisationContext] Failed to set database context, continuing without it:', error);\n }\n}\n\n/**\n * Clear organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves when context is cleared\n */\nexport async function clearOrganisationContext(\n supabase: SupabaseClient\n): Promise<void> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n const { error } = await supabase.rpc('rbac_audit_log', {\n p_event_type: 'organisation_switched',\n p_metadata: { action: 'clear_context' }\n });\n \n if (error) {\n // Silent fail - function not available\n // TODO: Replace with proper logging service integration\n } else {\n // TODO: Replace with proper logging service integration\n }\n } catch (error) {\n // Silent fail - error occurred\n // TODO: Replace with proper logging service integration\n }\n}\n\n/**\n * Get current organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to the current organisation ID or null\n */\nexport async function getOrganisationContext(\n supabase: SupabaseClient\n): Promise<string | null> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n\n try {\n // For now, return null since we're not using database context\n // This will be replaced with proper context management\n const data = null;\n const error = null;\n \n if (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n \n // Validate that data is a string (allow empty strings)\n if (typeof data === 'string') {\n return data;\n }\n \n // Return null for invalid data formats\n return null;\n } catch (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n}\n\n/**\n * Check if organisation context functions are available in the database\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to true if functions are available\n */\nexport async function isOrganisationContextAvailable(\n supabase: SupabaseClient\n): Promise<boolean> {\n if (!supabase) {\n return false;\n }\n\n try {\n const { error } = await supabase.rpc('get_organisation_context');\n \n if (error) {\n return false;\n }\n \n return true;\n } catch (error) {\n return false;\n }\n} "],"mappings":";;;;;AAsBA,eAAsB,uBACpB,UACA,gBACe;AACf,MAAI,CAAC,YAAY,CAAC,gBAAgB;AAEhC;AAAA,EACF;AAEA,MAAI;AAEF,UAAM,iBAAiB,IAAI,QAAQ,CAAC,GAAG,WAAW;AAChD,iBAAW,MAAM,OAAO,IAAI,MAAM,6BAA6B,CAAC,GAAG,GAAI;AAAA,IACzE,CAAC;AAGD,UAAM,aAAa,SAAS,IAAI,4BAA4B;AAAA,MAC1D,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,EAAE,MAAM,IAAI,MAAM,QAAQ,KAAK,CAAC,YAAY,cAAc,CAAC;AAEjE,QAAI,OAAO;AAGT,cAAQ,IAAI,iGAAiG;AAAA,IAC/G,OAAO;AACL,cAAQ,IAAI,yEAAyE;AAAA,IACvF;AAAA,EACF,SAAS,OAAO;AAGd,YAAQ,IAAI,gFAAgF,KAAK;AAAA,EACnG;AACF;AAQA,eAAsB,yBACpB,UACe;AACf,MAAI,CAAC,UAAU;AAEb;AAAA,EACF;AAEA,MAAI;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,kBAAkB;AAAA,MACrD,cAAc;AAAA,MACd,YAAY,EAAE,QAAQ,gBAAgB;AAAA,IACxC,CAAC;AAED,QAAI,OAAO;AAAA,IAGX,OAAO;AAAA,IAEP;AAAA,EACF,SAAS,OAAO;AAAA,EAGhB;AACF;AAQA,eAAsB,uBACpB,UACwB;AACxB,MAAI,CAAC,UAAU;AAEb,WAAO;AAAA,EACT;AAEA,MAAI;AAGF,UAAM,OAAO;AACb,UAAM,QAAQ;AAEd,QAAI,OAAO;AAET,aAAO;AAAA,IACT;AAGA,QAAI,OAAO,SAAS,UAAU;AAC5B,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,EACT,SAAS,OAAO;AAEd,WAAO;AAAA,EACT;AACF;AAQA,eAAsB,+BACpB,UACkB;AAClB,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,0BAA0B;AAE/D,QAAI,OAAO;AACT,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO;AAAA,EACT;AACF;AAxJA;AAAA;AAAA;AAAA;AAAA;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/rbac/hooks/useRBAC.ts","../src/rbac/hooks/useResolvedScope.ts","../src/rbac/utils/eventContext.ts","../src/rbac/hooks/usePermissions.ts","../src/rbac/hooks/useRoleManagement.ts"],"sourcesContent":["/**\n * @file RBAC Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 0.3.0\n *\n * A React hook that provides access to the RBAC (Role-Based Access Control) system\n * through the hardened RBAC engine API. The hook defers all permission and role\n * resolution to the shared engine to ensure consistent security behaviour across\n * applications.\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';\nimport { useOrganisations } from '../../hooks/useOrganisations';\nimport { useEvents } from '../../hooks/useEvents';\nimport {\n getPermissionMap,\n getAccessLevel,\n resolveAppContext,\n getRoleContext,\n} from '../api';\nimport { getRBACLogger } from '../config';\nimport type {\n UserRBACContext,\n GlobalRole,\n OrganisationRole,\n EventAppRole,\n Permission,\n Scope,\n PermissionMap,\n UUID,\n} from '../types';\n\nfunction mapAccessLevelToEventRole(level: string | null): EventAppRole | null {\n switch (level) {\n case 'viewer':\n return 'viewer';\n case 'participant':\n return 'participant';\n case 'planner':\n return 'planner';\n case 'admin':\n case 'super':\n return 'event_admin';\n default:\n return null;\n }\n}\n\nexport function useRBAC(pageId?: string): UserRBACContext {\n const logger = getRBACLogger();\n const { user, session, appName } = useUnifiedAuth();\n const { selectedOrganisation } = useOrganisations();\n\n let selectedEvent: { event_id: string } | null = null;\n try {\n const eventsContext = useEvents();\n selectedEvent = eventsContext.selectedEvent;\n } catch (error) {\n logger.debug('[useRBAC] Event provider not available, continuing without event context', error);\n }\n\n const [globalRole, setGlobalRole] = useState<GlobalRole | null>(null);\n const [organisationRole, setOrganisationRole] = useState<OrganisationRole | null>(null);\n const [eventAppRole, setEventAppRole] = useState<EventAppRole | null>(null);\n const [permissionMap, setPermissionMap] = useState<PermissionMap>({} as PermissionMap);\n const [currentScope, setCurrentScope] = useState<Scope | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const resetState = useCallback(() => {\n setGlobalRole(null);\n setOrganisationRole(null);\n setEventAppRole(null);\n setPermissionMap({} as PermissionMap);\n setCurrentScope(null);\n }, []);\n\n const loadRBACContext = useCallback(async () => {\n if (!user || !session) {\n resetState();\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n let appId: UUID | undefined;\n if (appName) {\n const resolved = await resolveAppContext({ userId: user.id as UUID, appName });\n if (!resolved || !resolved.hasAccess) {\n throw new Error(`User does not have access to app \"${appName}\"`);\n }\n appId = resolved.appId;\n }\n\n const scope: Scope = {\n organisationId: selectedOrganisation?.id,\n eventId: selectedEvent?.event_id || undefined,\n appId,\n };\n setCurrentScope(scope);\n\n const [map, roleContext, accessLevel] = await Promise.all([\n getPermissionMap({ userId: user.id as UUID, scope }),\n getRoleContext({ userId: user.id as UUID, scope }),\n getAccessLevel({ userId: user.id as UUID, scope }),\n ]);\n\n setPermissionMap(map);\n setGlobalRole(roleContext.globalRole);\n setOrganisationRole(roleContext.organisationRole);\n setEventAppRole(roleContext.eventAppRole || mapAccessLevelToEventRole(accessLevel));\n } catch (err) {\n const handledError = err instanceof Error ? err : new Error('Failed to load RBAC context');\n logger.error('[useRBAC] Error loading RBAC context:', handledError);\n setError(handledError);\n resetState();\n } finally {\n setIsLoading(false);\n }\n }, [appName, logger, resetState, selectedEvent?.event_id, selectedOrganisation?.id, session, user]);\n\n const hasGlobalPermission = useCallback(\n (permission: string): boolean => {\n if (globalRole === 'super_admin' || permissionMap['*']) {\n return true;\n }\n\n if (permission === 'super_admin') {\n return globalRole === 'super_admin';\n }\n\n if (permission === 'org_admin') {\n return organisationRole === 'org_admin';\n }\n\n return permissionMap[permission as Permission] === true;\n },\n [globalRole, organisationRole, permissionMap],\n );\n\n const isSuperAdmin = useMemo(() => globalRole === 'super_admin' || permissionMap['*'] === true, [globalRole, permissionMap]);\n const isOrgAdmin = useMemo(() => organisationRole === 'org_admin' || isSuperAdmin, [organisationRole, isSuperAdmin]);\n const isEventAdmin = useMemo(() => eventAppRole === 'event_admin' || isSuperAdmin, [eventAppRole, isSuperAdmin]);\n const canManageOrganisation = useMemo(() => isSuperAdmin || organisationRole === 'org_admin', [isSuperAdmin, organisationRole]);\n const canManageEvent = useMemo(() => isSuperAdmin || eventAppRole === 'event_admin', [isSuperAdmin, eventAppRole]);\n\n useEffect(() => {\n loadRBACContext();\n }, [loadRBACContext]);\n\n return {\n user,\n globalRole,\n organisationRole,\n eventAppRole,\n hasGlobalPermission,\n isSuperAdmin,\n isOrgAdmin,\n isEventAdmin,\n canManageOrganisation,\n canManageEvent,\n isLoading,\n error,\n };\n}\n","/**\n * @file useResolvedScope Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n * \n * Shared hook for resolving RBAC scope from various contexts.\n * This hook is used by both DataTable and PagePermissionGuard to ensure\n * consistent scope resolution logic.\n */\n\nimport { useEffect, useState, useRef } from 'react';\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport type { Scope } from '../types';\nimport { createScopeFromEvent } from '../utils/eventContext';\nimport { getCurrentAppName } from '../../utils/appNameResolver';\n\nexport interface UseResolvedScopeOptions {\n /** Supabase client instance */\n supabase: SupabaseClient<Database> | null;\n /** Selected organisation ID */\n selectedOrganisationId: string | null;\n /** Selected event ID */\n selectedEventId: string | null;\n}\n\nexport interface UseResolvedScopeReturn {\n /** Resolved scope, or null if not yet resolved */\n resolvedScope: Scope | null;\n /** Whether the scope resolution is in progress */\n isLoading: boolean;\n /** Error if scope resolution failed */\n error: Error | null;\n}\n\n/**\n * Resolves RBAC scope from organisation and event context\n * \n * This hook handles the complex logic of determining the correct RBAC scope\n * based on available context (organisation, event, app). It ensures consistent\n * scope resolution across the application.\n * \n * @param options - Hook options\n * @returns Resolved scope and loading state\n * \n * @example\n * ```tsx\n * const { resolvedScope, isLoading } = useResolvedScope({\n * supabase,\n * selectedOrganisationId,\n * selectedEventId\n * });\n * \n * if (isLoading) return <Loading />;\n * if (!resolvedScope) return <Error />;\n * \n * const permission = useCan(userId, resolvedScope, permission);\n * ```\n */\nexport function useResolvedScope({\n supabase,\n selectedOrganisationId,\n selectedEventId\n}: UseResolvedScopeOptions): UseResolvedScopeReturn {\n const [resolvedScope, setResolvedScope] = useState<Scope | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n \n // Use a ref to track the stable scope and only update it when it actually changes\n const stableScopeRef = useRef<{ organisationId: string; appId: string; eventId: string | undefined }>({ \n organisationId: '', \n appId: '', \n eventId: undefined \n });\n \n // Update stable scope ref in useEffect to avoid updates during render\n useEffect(() => {\n if (resolvedScope && resolvedScope.organisationId) {\n const newScope = {\n organisationId: resolvedScope.organisationId,\n appId: resolvedScope.appId,\n eventId: resolvedScope.eventId\n };\n \n // Only update if the scope has actually changed\n if (stableScopeRef.current.organisationId !== newScope.organisationId ||\n stableScopeRef.current.eventId !== newScope.eventId ||\n stableScopeRef.current.appId !== newScope.appId) {\n stableScopeRef.current = {\n organisationId: newScope.organisationId,\n appId: newScope.appId || '',\n eventId: newScope.eventId\n };\n }\n } else if (!resolvedScope) {\n // Reset to empty scope when no resolved scope\n stableScopeRef.current = { organisationId: '', appId: '', eventId: undefined };\n }\n }, [resolvedScope]);\n \n const stableScope = stableScopeRef.current;\n \n useEffect(() => {\n let cancelled = false;\n \n const resolveScope = async () => {\n setIsLoading(true);\n setError(null);\n \n try {\n // Get app ID from package.json or environment\n let appId: string | undefined = undefined;\n \n // Try to resolve from database\n if (supabase) {\n const appName = getCurrentAppName();\n if (appName) {\n try {\n const { data: app, error } = await supabase\n .from('rbac_apps')\n .select('id, name, is_active')\n .eq('name', appName)\n .eq('is_active', true)\n .single() as { data: { id: string; name: string; is_active: boolean } | null; error: any };\n \n if (error) {\n // Check if app exists but is inactive\n const { data: inactiveApp } = await supabase\n .from('rbac_apps')\n .select('id, name, is_active')\n .eq('name', appName)\n .single() as { data: { id: string; name: string; is_active: boolean } | null };\n \n if (inactiveApp) {\n console.error(`[useResolvedScope] App \"${appName}\" exists but is inactive (is_active: ${inactiveApp.is_active})`);\n } else {\n console.error(`[useResolvedScope] App \"${appName}\" not found in rbac_apps table`);\n }\n } else if (app) {\n appId = app.id;\n }\n } catch (error) {\n console.error('[useResolvedScope] Unexpected error resolving app ID:', error);\n }\n }\n }\n\n // Resolve scope based on available context\n\n // If we have both organisation and event, use them directly\n if (selectedOrganisationId && selectedEventId) {\n if (!cancelled) {\n setResolvedScope({\n organisationId: selectedOrganisationId,\n eventId: selectedEventId,\n appId: appId\n });\n setIsLoading(false);\n }\n return;\n }\n\n // If we only have organisation, use it\n if (selectedOrganisationId) {\n if (!cancelled) {\n setResolvedScope({\n organisationId: selectedOrganisationId,\n eventId: selectedEventId || undefined,\n appId: appId\n });\n setIsLoading(false);\n }\n return;\n }\n\n // If we only have event, resolve organisation from event\n if (selectedEventId && supabase) {\n try {\n const eventScope = await createScopeFromEvent(supabase, selectedEventId, appId);\n if (!eventScope) {\n console.error('[useResolvedScope] Could not resolve organization from event context');\n if (!cancelled) {\n setResolvedScope(null);\n setError(new Error('Could not resolve organisation from event context'));\n setIsLoading(false);\n }\n return;\n }\n // Preserve the resolved app ID\n if (!cancelled) {\n setResolvedScope({\n ...eventScope,\n appId: appId || eventScope.appId\n });\n setIsLoading(false);\n }\n } catch (err) {\n console.error('[useResolvedScope] Error resolving scope from event:', err);\n if (!cancelled) {\n setResolvedScope(null);\n setError(err as Error);\n setIsLoading(false);\n }\n }\n return;\n }\n\n // No context available\n console.error('[useResolvedScope] No organisation or event context available');\n if (!cancelled) {\n setResolvedScope(null);\n setError(new Error('No organisation or event context available'));\n setIsLoading(false);\n }\n } catch (err) {\n if (!cancelled) {\n setError(err as Error);\n setIsLoading(false);\n }\n }\n };\n\n resolveScope();\n \n return () => {\n cancelled = true;\n };\n }, [selectedOrganisationId, selectedEventId, supabase]);\n \n return {\n resolvedScope: stableScope.organisationId ? stableScope as Scope : null,\n isLoading,\n error\n };\n}\n","/**\n * Event Context Utilities for RBAC\n * @package @jmruthers/pace-core\n * @module RBAC/EventContext\n * @since 1.0.0\n * \n * This module provides utilities for event-based RBAC operations where\n * the organization context is derived from the event context.\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { Database } from '../../types/database';\nimport { UUID, Scope } from '../types';\n\n/**\n * Get organization ID from event ID\n * \n * @param supabase - Supabase client\n * @param eventId - Event ID\n * @returns Promise resolving to organization ID or null\n */\nexport async function getOrganisationFromEvent(\n supabase: SupabaseClient<Database>,\n eventId: string\n): Promise<UUID | null> {\n const { data, error } = await supabase\n .from('event')\n .select('organisation_id')\n .eq('event_id', eventId)\n .single() as { data: { organisation_id: string } | null; error: any };\n\n if (error || !data) {\n return null;\n }\n\n return data.organisation_id;\n}\n\n/**\n * Create a complete scope from event context\n * \n * @param supabase - Supabase client\n * @param eventId - Event ID\n * @param appId - Optional app ID\n * @returns Promise resolving to complete scope\n */\nexport async function createScopeFromEvent(\n supabase: SupabaseClient<Database>,\n eventId: string,\n appId?: UUID\n): Promise<Scope | null> {\n const organisationId = await getOrganisationFromEvent(supabase, eventId);\n \n if (!organisationId) {\n return null;\n }\n\n return {\n organisationId,\n eventId,\n appId\n };\n}\n\n/**\n * Check if a scope is event-based (has eventId but no explicit organisationId)\n * \n * @param scope - Permission scope\n * @returns True if scope is event-based\n */\nexport function isEventBasedScope(scope: Scope): boolean {\n return !scope.organisationId && !!scope.eventId;\n}\n\n/**\n * Validate that an event-based scope has the required context\n * \n * @param scope - Permission scope\n * @returns True if scope is valid for event-based operations\n */\nexport function isValidEventBasedScope(scope: Scope): boolean {\n return isEventBasedScope(scope) && !!scope.eventId;\n}\n","/**\n * @file RBAC Permission Hooks\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 1.0.0\n * \n * This module provides React hooks for RBAC functionality.\n */\n\nimport { useState, useEffect, useCallback, useMemo, useRef } from 'react';\nimport { \n UUID, \n Scope, \n Permission, \n PermissionMap\n} from '../types';\nimport { AccessLevel as AccessLevelType } from '../types';\nimport { \n getAccessLevel, \n getPermissionMap, \n isPermitted,\n isPermittedCached \n} from '../api';\n\n/**\n * Hook to get user's permissions in a scope\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @returns Permission state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { permissions, isLoading, error } = usePermissions(userId, scope);\n * \n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {permissions['read:users'] && <UserList />}\n * {permissions['create:users'] && <CreateUserButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePermissions(userId: UUID, scope: Scope) {\n const [permissions, setPermissions] = useState<PermissionMap>({} as PermissionMap);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const isFetchingRef = useRef(false);\n\n // Add timeout for missing organisation context (3 seconds)\n useEffect(() => {\n if (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.organisationId.trim() === '')) {\n const timeoutId = setTimeout(() => {\n setError(new Error('Organisation context is required for permission checks'));\n setIsLoading(false);\n }, 3000); // 3 seconds - typical permission check is < 1 second\n \n return () => clearTimeout(timeoutId);\n }\n // Clear error if organisation context becomes available\n if (error?.message === 'Organisation context is required for permission checks') {\n setError(null);\n }\n }, [scope.organisationId, error]);\n\n useEffect(() => {\n const fetchPermissions = async () => {\n // Prevent multiple simultaneous fetches\n if (isFetchingRef.current) {\n return;\n }\n\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n // Don't fetch permissions if scope is invalid (e.g., organisationId is null/empty)\n // Wait for organisation context to resolve\n // IMPORTANT: Don't clear existing permissions here - keep them until we have new ones\n if (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.organisationId.trim() === '')) {\n // Keep existing permissions, just mark as loading\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n \n // Fetch new permissions - don't clear old ones until we have new ones\n const permissionMap = await getPermissionMap({ userId, scope });\n \n // Only update permissions if fetch was successful\n setPermissions(permissionMap);\n } catch (err) {\n // On error, keep existing permissions but set error state\n // This prevents the UI from losing all items when there's a transient error\n console.error('[usePermissions] Failed to fetch permissions:', err);\n setError(err instanceof Error ? err : new Error('Failed to fetch permissions'));\n // Don't clear permissions on error - keep what we had\n } finally {\n setIsLoading(false);\n isFetchingRef.current = false;\n }\n };\n\n fetchPermissions();\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n const hasPermission = useCallback((permission: Permission): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissions[permission] === true;\n }, [permissions]);\n\n const hasAnyPermission = useCallback((permissionList: Permission[]): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissionList.some(p => permissions[p] === true);\n }, [permissions]);\n\n const hasAllPermissions = useCallback((permissionList: Permission[]): boolean => {\n if (permissions['*']) {\n return true;\n }\n return permissionList.every(p => permissions[p] === true);\n }, [permissions]);\n\n const refetch = useCallback(async () => {\n // Prevent multiple simultaneous fetches\n if (isFetchingRef.current) {\n return;\n }\n\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n // Don't fetch permissions if scope is invalid (e.g., organisationId is null/empty)\n // IMPORTANT: Don't clear existing permissions - keep them until we have new ones\n if (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.organisationId.trim() === '')) {\n // Keep existing permissions, just mark as loading\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n \n // Fetch new permissions - don't clear old ones until we have new ones\n const permissionMap = await getPermissionMap({ userId, scope });\n \n // Only update permissions if fetch was successful\n setPermissions(permissionMap);\n } catch (err) {\n // On error, keep existing permissions but set error state\n // This prevents the UI from losing all items when there's a transient error\n console.error('[usePermissions] Failed to refetch permissions:', err);\n setError(err instanceof Error ? err : new Error('Failed to fetch permissions'));\n // Don't clear permissions on error - keep what we had\n } finally {\n setIsLoading(false);\n isFetchingRef.current = false;\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n permissions,\n isLoading,\n error,\n hasPermission,\n hasAnyPermission,\n hasAllPermissions,\n refetch\n }), [permissions, isLoading, error, hasPermission, hasAnyPermission, hasAllPermissions, refetch]);\n}\n\n/**\n * Hook to check if user can perform an action\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permission - Permission to check\n * @param pageId - Optional page ID\n * @param useCache - Whether to use cached results\n * @returns Permission check state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { can, isLoading, error } = useCan(userId, scope, 'read:users');\n * \n * if (isLoading) return <div>Checking permission...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return can ? <UserList /> : <div>Access denied</div>;\n * }\n * ```\n */\nexport function useCan(\n userId: UUID, \n scope: Scope, \n permission: Permission, \n pageId?: UUID,\n useCache: boolean = true\n) {\n const [can, setCan] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Add timeout for missing organisation context (3 seconds)\n useEffect(() => {\n if (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.organisationId.trim() === '')) {\n const timeoutId = setTimeout(() => {\n setError(new Error('Organisation context is required for permission checks'));\n setIsLoading(false);\n setCan(false);\n }, 3000); // 3 seconds - typical permission check is < 1 second\n \n return () => clearTimeout(timeoutId);\n }\n // Clear error if organisation context becomes available\n if (error?.message === 'Organisation context is required for permission checks') {\n setError(null);\n }\n }, [scope.organisationId, error]);\n\n // Use refs to track the last values to prevent unnecessary re-runs\n const lastUserIdRef = useRef<UUID | null>(null);\n const lastScopeRef = useRef<string | null>(null);\n const lastPermissionRef = useRef<Permission | null>(null);\n const lastPageIdRef = useRef<UUID | undefined | null>(null);\n const lastUseCacheRef = useRef<boolean | null>(null);\n\n useEffect(() => {\n // Create a scope key to track changes\n const scopeKey = `${scope.organisationId}-${scope.eventId}-${scope.appId}`;\n \n // Only run if something has actually changed\n if (\n lastUserIdRef.current !== userId ||\n lastScopeRef.current !== scopeKey ||\n lastPermissionRef.current !== permission ||\n lastPageIdRef.current !== pageId ||\n lastUseCacheRef.current !== useCache\n ) {\n lastUserIdRef.current = userId;\n lastScopeRef.current = scopeKey;\n lastPermissionRef.current = permission;\n lastPageIdRef.current = pageId;\n lastUseCacheRef.current = useCache;\n \n // Inline the permission check logic to avoid useCallback dependency issues\n const checkPermission = async () => {\n if (!userId) {\n setCan(false);\n setIsLoading(false);\n return;\n }\n\n // Don't check permissions if scope is invalid (e.g., organisationId is null/empty)\n // Wait for organisation context to resolve\n if (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.organisationId.trim() === '')) {\n setIsLoading(true);\n setCan(false);\n setError(null);\n // Timeout is handled in separate useEffect (Phase 1.4)\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const result = useCache \n ? await isPermittedCached({ userId, scope, permission, pageId })\n : await isPermitted({ userId, scope, permission, pageId });\n \n setCan(result);\n } catch (err) {\n console.error('[useCan] Permission check error:', { permission, error: err });\n setError(err instanceof Error ? err : new Error('Failed to check permission'));\n setCan(false);\n } finally {\n setIsLoading(false);\n }\n };\n \n checkPermission();\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permission, pageId, useCache]);\n\n const refetch = useCallback(async () => {\n if (!userId) {\n setCan(false);\n setIsLoading(false);\n return;\n }\n\n // Don't check permissions if scope is invalid (e.g., organisationId is null/empty)\n if (!scope.organisationId || scope.organisationId === null || (typeof scope.organisationId === 'string' && scope.organisationId.trim() === '')) {\n setCan(false);\n setIsLoading(true);\n setError(null);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const result = useCache \n ? await isPermittedCached({ userId, scope, permission, pageId })\n : await isPermitted({ userId, scope, permission, pageId });\n \n setCan(result);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permission'));\n setCan(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permission, pageId, useCache]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n can,\n isLoading,\n error,\n refetch\n }), [can, isLoading, error, refetch]);\n}\n\n/**\n * Hook to get user's access level in a scope\n * \n * @param userId - User ID\n * @param scope - Scope for access level checking\n * @returns Access level state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { accessLevel, isLoading, error } = useAccessLevel(userId, scope);\n * \n * if (isLoading) return <div>Loading access level...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * Access Level: {accessLevel}\n * {accessLevel >= AccessLevel.ADMIN && <AdminPanel />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useAccessLevel(userId: UUID, scope: Scope): {\n accessLevel: AccessLevelType;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [accessLevel, setAccessLevel] = useState<AccessLevelType>('viewer');\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchAccessLevel = useCallback(async () => {\n if (!userId) {\n setAccessLevel('viewer');\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const level = await getAccessLevel({ userId, scope });\n setAccessLevel(level);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch access level'));\n setAccessLevel('viewer');\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n useEffect(() => {\n fetchAccessLevel();\n }, [fetchAccessLevel]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n accessLevel,\n isLoading,\n error,\n refetch: fetchAccessLevel\n }), [accessLevel, isLoading, error, fetchAccessLevel]);\n}\n\n/**\n * Hook to check multiple permissions at once\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Multiple permission check results\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { results, isLoading, error } = useMultiplePermissions(\n * userId, \n * scope, \n * ['read:users', 'create:users', 'update:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {results['read:users'] && <UserList />}\n * {results['create:users'] && <CreateUserButton />}\n * {results['update:users'] && <EditUserButton />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMultiplePermissions(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n results: Record<Permission, boolean>;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [results, setResults] = useState<Record<Permission, boolean>>({} as Record<Permission, boolean>);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkPermissions = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setResults({} as Record<Permission, boolean>);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const permissionResults: Record<Permission, boolean> = {} as Record<Permission, boolean>;\n \n // Check each permission\n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n permissionResults[permission] = result;\n }\n \n setResults(permissionResults);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setResults({} as Record<Permission, boolean>);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkPermissions();\n }, [checkPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n results,\n isLoading,\n error,\n refetch: checkPermissions\n }), [results, isLoading, error, checkPermissions]);\n}\n\n/**\n * Hook to check if user has any of the specified permissions\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Whether user has any of the permissions\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { hasAny, isLoading, error } = useHasAnyPermission(\n * userId, \n * scope, \n * ['read:users', 'create:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return hasAny ? <UserManagementPanel /> : <div>No user permissions</div>;\n * }\n * ```\n */\nexport function useHasAnyPermission(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n hasAny: boolean;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [hasAny, setHasAny] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkAnyPermission = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setHasAny(false);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n let hasAnyPermission = false;\n \n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n \n if (result) {\n hasAnyPermission = true;\n break;\n }\n }\n \n setHasAny(hasAnyPermission);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setHasAny(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkAnyPermission();\n }, [checkAnyPermission]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n hasAny,\n isLoading,\n error,\n refetch: checkAnyPermission\n }), [hasAny, isLoading, error, checkAnyPermission]);\n}\n\n/**\n * Hook to check if user has all of the specified permissions\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @param permissions - Array of permissions to check\n * @param useCache - Whether to use cached results\n * @returns Whether user has all of the permissions\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { hasAll, isLoading, error } = useHasAllPermissions(\n * userId, \n * scope, \n * ['read:users', 'create:users', 'update:users']\n * );\n * \n * if (isLoading) return <div>Checking permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return hasAll ? <FullUserManagementPanel /> : <div>Insufficient permissions</div>;\n * }\n * ```\n */\nexport function useHasAllPermissions(\n userId: UUID, \n scope: Scope, \n permissions: Permission[],\n useCache: boolean = true\n): {\n hasAll: boolean;\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [hasAll, setHasAll] = useState<boolean>(false);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const checkAllPermissions = useCallback(async () => {\n if (!userId || permissions.length === 0) {\n setHasAll(false);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n let hasAllPermissions = true;\n \n for (const permission of permissions) {\n const result = useCache \n ? await isPermittedCached({ userId, scope, permission })\n : await isPermitted({ userId, scope, permission });\n \n if (!result) {\n hasAllPermissions = false;\n break;\n }\n }\n \n setHasAll(hasAllPermissions);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to check permissions'));\n setHasAll(false);\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId, permissions, useCache]);\n\n useEffect(() => {\n checkAllPermissions();\n }, [checkAllPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n hasAll,\n isLoading,\n error,\n refetch: checkAllPermissions\n }), [hasAll, isLoading, error, checkAllPermissions]);\n}\n\n/**\n * Hook to get cached permissions with TTL management\n * \n * @param userId - User ID\n * @param scope - Scope for permission checking\n * @returns Cached permission state and methods\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { permissions, isLoading, error, invalidateCache } = useCachedPermissions(userId, scope);\n * \n * if (isLoading) return <div>Loading cached permissions...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * \n * return (\n * <div>\n * {permissions['read:users'] && <UserList />}\n * <button onClick={invalidateCache}>Refresh Permissions</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useCachedPermissions(userId: UUID, scope: Scope): {\n permissions: PermissionMap;\n isLoading: boolean;\n error: Error | null;\n invalidateCache: () => void;\n refetch: () => Promise<void>;\n} {\n const [permissions, setPermissions] = useState<PermissionMap>({} as PermissionMap);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchCachedPermissions = useCallback(async () => {\n if (!userId) {\n setPermissions({} as PermissionMap);\n setIsLoading(false);\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n \n const permissionMap = await getPermissionMap({ userId, scope });\n setPermissions(permissionMap);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Failed to fetch cached permissions'));\n } finally {\n setIsLoading(false);\n }\n }, [userId, scope.organisationId, scope.eventId, scope.appId]);\n\n const invalidateCache = useCallback(() => {\n // This would typically invalidate the cache in the actual implementation\n // For now, we'll just refetch\n fetchCachedPermissions();\n }, [fetchCachedPermissions]);\n\n useEffect(() => {\n fetchCachedPermissions();\n }, [fetchCachedPermissions]);\n\n // Memoize the return object to prevent unnecessary re-renders\n return useMemo(() => ({\n permissions,\n isLoading,\n error,\n invalidateCache,\n refetch: fetchCachedPermissions\n }), [permissions, isLoading, error, invalidateCache, fetchCachedPermissions]);\n}\n","/**\n * @file RBAC Role Management Hook\n * @package @jmruthers/pace-core\n * @module RBAC/Hooks\n * @since 2.1.0\n *\n * React hook for managing RBAC roles safely using RPC functions.\n * This hook provides a secure, type-safe interface for granting and revoking roles\n * that ensures proper audit trails and security checks.\n *\n * @example\n * ```tsx\n * import { useRoleManagement } from '@jmruthers/pace-core/rbac';\n *\n * function UserRolesComponent() {\n * const { revokeEventAppRole, grantEventAppRole, isLoading, error } = useRoleManagement();\n *\n * const handleRevokeRole = async (roleId: string, roleData: EventAppRoleData) => {\n * const result = await revokeEventAppRole({\n * userId: roleData.user_id,\n * organisationId: roleData.organisation_id,\n * eventId: roleData.event_id,\n * appId: roleData.app_id,\n * role: roleData.role\n * });\n *\n * if (result.success) {\n * toast({ title: 'Role revoked successfully' });\n * } else {\n * toast({ title: 'Failed to revoke role', variant: 'destructive' });\n * }\n * };\n *\n * return <button onClick={() => handleRevokeRole(roleId, roleData)}>Revoke Role</button>;\n * }\n * ```\n */\n\nimport { useState, useCallback } from 'react';\nimport { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';\nimport type { UUID } from '../types';\n\nexport interface EventAppRoleData {\n user_id: UUID;\n organisation_id: UUID;\n event_id: string;\n app_id: UUID;\n role: 'viewer' | 'participant' | 'planner' | 'event_admin';\n}\n\nexport interface RevokeEventAppRoleParams extends EventAppRoleData {\n revoked_by?: UUID;\n}\n\nexport interface GrantEventAppRoleParams extends EventAppRoleData {\n granted_by?: UUID;\n valid_from?: string;\n valid_to?: string | null;\n}\n\nexport interface RoleManagementResult {\n success: boolean;\n message?: string;\n error?: string;\n roleId?: UUID;\n}\n\nexport function useRoleManagement() {\n const { user, supabase } = useUnifiedAuth();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n if (!supabase) {\n throw new Error('useRoleManagement requires a Supabase client. Ensure UnifiedAuthProvider is configured.');\n }\n\n /**\n * Revoke an event app role using the secure RPC function\n * \n * This function uses the `revoke_event_app_role` RPC which:\n * - Runs with SECURITY DEFINER privileges\n * - Includes proper permission checks\n * - Automatically populates audit fields (revoked_by, timestamps)\n * - Complies with Row-Level Security policies\n * \n * @param params - Role revocation parameters\n * @returns Promise resolving to operation result\n */\n const revokeEventAppRole = useCallback(async (\n params: RevokeEventAppRoleParams\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { data, error: rpcError } = await supabase.rpc('revoke_event_app_role', {\n p_user_id: params.user_id,\n p_organisation_id: params.organisation_id,\n p_event_id: params.event_id,\n p_app_id: params.app_id,\n p_role: params.role,\n p_revoked_by: params.revoked_by || user?.id || undefined\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to revoke role');\n }\n\n return {\n success: data === true,\n message: data === true ? 'Role revoked successfully' : 'No role found to revoke',\n error: data === false ? 'No matching role found' : undefined\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id]);\n\n /**\n * Grant an event app role using the secure RPC function\n * \n * This function uses the `grant_event_app_role` RPC which:\n * - Runs with SECURITY DEFINER privileges\n * - Includes proper permission checks\n * - Automatically populates audit fields (granted_by, timestamps)\n * - Complies with Row-Level Security policies\n * \n * @param params - Role grant parameters\n * @returns Promise resolving to operation result with role ID\n */\n const grantEventAppRole = useCallback(async (\n params: GrantEventAppRoleParams\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const { data, error: rpcError } = await supabase.rpc('grant_event_app_role', {\n p_user_id: params.user_id,\n p_organisation_id: params.organisation_id,\n p_event_id: params.event_id,\n p_app_id: params.app_id,\n p_role: params.role,\n p_granted_by: params.granted_by || user?.id || undefined,\n p_valid_from: params.valid_from,\n p_valid_to: params.valid_to\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to grant role');\n }\n\n if (!data) {\n return {\n success: false,\n error: 'Failed to grant role - no role ID returned'\n };\n }\n\n return {\n success: true,\n message: 'Role granted successfully',\n roleId: data as UUID\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id]);\n\n /**\n * Revoke an event app role by role ID (alternative method)\n * \n * This fetches the role by ID first to get the required context (role name, event_id, app_id),\n * then uses the unified `rbac_role_revoke` function to revoke it.\n * \n * @param roleId - The role ID to revoke\n * @returns Promise resolving to operation result\n */\n const revokeRoleById = useCallback(async (\n roleId: UUID\n ): Promise<RoleManagementResult> => {\n setIsLoading(true);\n setError(null);\n\n try {\n // First, fetch the role by ID to get the required context\n const { data: roleData, error: fetchError } = await supabase\n .from('rbac_event_app_roles')\n .select('user_id, role, event_id, app_id')\n .eq('id', roleId)\n .single();\n\n if (fetchError || !roleData) {\n throw new Error(fetchError?.message || 'Role not found');\n }\n\n // Construct context_id in the format required by rbac_role_revoke: \"event_id:app_id\"\n const contextId = `${roleData.event_id}:${roleData.app_id}`;\n\n // Now call rbac_role_revoke with the required parameters\n const { data, error: rpcError } = await supabase.rpc('rbac_role_revoke', {\n p_user_id: roleData.user_id,\n p_role_type: 'event_app',\n p_role_name: roleData.role,\n p_context_id: contextId,\n p_revoked_by: user?.id || undefined\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to revoke role');\n }\n\n // rbac_role_revoke returns a table with success, message, revoked_count, error_code\n const result = Array.isArray(data) && data.length > 0 ? data[0] : null;\n\n return {\n success: result?.success === true,\n message: result?.message || undefined,\n error: result?.success === false ? (result?.message || result?.error_code || 'Unknown error') : undefined\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';\n setError(err instanceof Error ? err : new Error(errorMessage));\n return {\n success: false,\n error: errorMessage\n };\n } finally {\n setIsLoading(false);\n }\n }, [user?.id, supabase]);\n\n return {\n revokeEventAppRole,\n grantEventAppRole,\n revokeRoleById,\n isLoading,\n error\n };\n}\n\n"],"mappings":"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","names":["error","useEffect","useState","useState","useEffect","error","useState","useEffect","useCallback","useMemo","useRef","useState","useRef","useEffect","useCallback","useMemo","useState","useCallback"]}
|
package/dist/chunk-CDQ3PX7L.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// src/components/LoadingSpinner/LoadingSpinner.tsx
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
var LoadingSpinner = ({
|
|
4
|
-
size = "md",
|
|
5
|
-
className = ""
|
|
6
|
-
}) => {
|
|
7
|
-
const sizeClasses = {
|
|
8
|
-
sm: "w-4 h-4",
|
|
9
|
-
md: "w-6 h-6",
|
|
10
|
-
lg: "w-8 h-8"
|
|
11
|
-
};
|
|
12
|
-
return /* @__PURE__ */ jsx("div", { className: `inline-block animate-spin rounded-full border-2 border-solid border-current border-r-transparent motion-reduce:animate-[spin_1.5s_linear_infinite] ${sizeClasses[size]} ${className}`, role: "status", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading..." }) });
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export {
|
|
16
|
-
LoadingSpinner
|
|
17
|
-
};
|
|
18
|
-
//# sourceMappingURL=chunk-CDQ3PX7L.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/LoadingSpinner/LoadingSpinner.tsx"],"sourcesContent":["/**\n * @file LoadingSpinner Component\n * @package @jmruthers/pace-core\n * @module Components/LoadingSpinner\n * @since 0.1.0\n *\n * A simple, accessible loading spinner component for indicating loading states.\n * Provides smooth animations with reduced motion support for accessibility.\n *\n * Features:\n * - Multiple size variants (sm, md, lg)\n * - Smooth CSS animations\n * - Reduced motion support for accessibility\n * - Screen reader friendly with proper ARIA attributes\n * - Customizable styling\n * - Lightweight and performant\n *\n * @example\n * ```tsx\n * // Basic loading spinner\n * <LoadingSpinner />\n * \n * // Different sizes\n * <LoadingSpinner size=\"sm\" />\n * <LoadingSpinner size=\"md\" />\n * <LoadingSpinner size=\"lg\" />\n * \n * // With custom styling\n * <LoadingSpinner \n * size=\"lg\" \n * className=\"text-main-500\" \n * />\n * \n * // In a button\n * <Button disabled>\n * <LoadingSpinner size=\"sm\" className=\"mr-2\" />\n * Loading...\n * </Button>\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Proper ARIA role=\"status\"\n * - Screen reader announcement with \"Loading...\" text\n * - Reduced motion support for users with vestibular disorders\n * - High contrast support\n *\n * @performance\n * - CSS-only animations for optimal performance\n * - No JavaScript dependencies\n * - Minimal DOM structure\n * - Efficient rendering\n *\n * @dependencies\n * - React 18+ - Component framework\n * - Tailwind CSS - Styling and animations\n */\n\nimport React from 'react';\n\n/**\n * Props for the LoadingSpinner component\n */\nexport interface LoadingSpinnerProps {\n /** Size variant of the spinner */\n size?: 'sm' | 'md' | 'lg';\n /** Additional CSS classes for styling */\n className?: string;\n}\n\n/**\n * LoadingSpinner component\n * A simple, accessible loading spinner for indicating loading states\n * \n * @param props - Spinner configuration and styling\n * @returns JSX.Element - The rendered loading spinner\n * \n * @example\n * ```tsx\n * <LoadingSpinner size=\"lg\" className=\"text-main-500\" />\n * ```\n */\nexport const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({ \n size = 'md', \n className = '' \n}) => {\n const sizeClasses = {\n sm: 'w-4 h-4',\n md: 'w-6 h-6',\n lg: 'w-8 h-8'\n };\n\n return (\n <div className={`inline-block animate-spin rounded-full border-2 border-solid border-current border-r-transparent motion-reduce:animate-[spin_1.5s_linear_infinite] ${sizeClasses[size]} ${className}`} role=\"status\">\n <span className=\"sr-only\">Loading...</span>\n </div>\n );\n};\n"],"mappings":";AA8FM;AAZC,IAAM,iBAAgD,CAAC;AAAA,EAC5D,OAAO;AAAA,EACP,YAAY;AACd,MAAM;AACJ,QAAM,cAAc;AAAA,IAClB,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,EACN;AAEA,SACE,oBAAC,SAAI,WAAW,sJAAsJ,YAAY,IAAI,CAAC,IAAI,SAAS,IAAI,MAAK,UAC3M,8BAAC,UAAK,WAAU,WAAU,wBAAU,GACtC;AAEJ;","names":[]}
|