@jmruthers/pace-core 0.5.76 → 0.5.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/{RBACService-C4udt_Zp.d.ts → AuthService-Df3IozMG.d.ts} +10 -118
- package/dist/{DataTable-ntgmhO2W.d.ts → DataTable-BE0OXZKQ.d.ts} +9 -2
- package/dist/{DataTable-4GAVPIEG.js → DataTable-ETGVF4Y5.js} +50 -13
- package/dist/{PublicLoadingSpinner-BiNER8F5.d.ts → PublicLoadingSpinner-CnUaz0vG.d.ts} +5 -2
- package/dist/{UnifiedAuthProvider-Bj6YCf7c.d.ts → UnifiedAuthProvider-B391Aqum.d.ts} +42 -45
- package/dist/{UnifiedAuthProvider-3NKDOSOK.js → UnifiedAuthProvider-P5SOJAQ6.js} +4 -5
- package/dist/{api-DDMUKIUD.js → api-KG4A2X7P.js} +9 -3
- package/dist/{audit-6TOCAMKO.js → audit-65VNHEV2.js} +2 -2
- package/dist/{chunk-K34IM5CT.js → chunk-2OGV6IRV.js} +196 -626
- package/dist/chunk-2OGV6IRV.js.map +1 -0
- package/dist/{chunk-NTNILOBC.js → chunk-5BO3MI5Y.js} +4 -4
- package/dist/{chunk-XLZ7U46Z.js → chunk-CVMVPYAL.js} +9 -60
- package/dist/chunk-CVMVPYAL.js.map +1 -0
- package/dist/{chunk-URUTVZ7N.js → chunk-FL4ZCQLD.js} +2 -2
- package/dist/{chunk-LW7MMEAQ.js → chunk-FT2M4R4F.js} +2 -2
- package/dist/{chunk-5BSLGBYI.js → chunk-JCQZ6LA7.js} +2 -8
- package/dist/{chunk-5BSLGBYI.js.map → chunk-JCQZ6LA7.js.map} +1 -1
- package/dist/{chunk-KHJS6VIA.js → chunk-LRQ6RBJC.js} +157 -112
- package/dist/chunk-LRQ6RBJC.js.map +1 -0
- package/dist/{chunk-WN6XJWOS.js → chunk-MNJXXD6C.js} +274 -743
- package/dist/chunk-MNJXXD6C.js.map +1 -0
- package/dist/{chunk-KK73ZB4E.js → chunk-PTR5PMPE.js} +153 -132
- package/dist/chunk-PTR5PMPE.js.map +1 -0
- package/dist/{chunk-B2WTCLCV.js → chunk-Q7APDV6H.js} +18 -8
- package/dist/chunk-Q7APDV6H.js.map +1 -0
- package/dist/{chunk-A4FUBC7B.js → chunk-QGVSOUJ2.js} +2 -4
- package/dist/{chunk-A4FUBC7B.js.map → chunk-QGVSOUJ2.js.map} +1 -1
- package/dist/{chunk-FGMFQSHX.js → chunk-S63MFSY6.js} +500 -551
- package/dist/chunk-S63MFSY6.js.map +1 -0
- package/dist/{chunk-AFGTSUAD.js → chunk-VSOKOFRF.js} +4 -4
- package/dist/chunk-WUXCWRL6.js +20 -0
- package/dist/chunk-WUXCWRL6.js.map +1 -0
- package/dist/{chunk-Y6TXWPJO.js → chunk-YVVGHRGI.js} +105 -31
- package/dist/chunk-YVVGHRGI.js.map +1 -0
- package/dist/{chunk-M5IWZRBT.js → chunk-ZMNXIJP4.js} +2187 -981
- package/dist/chunk-ZMNXIJP4.js.map +1 -0
- package/dist/components.d.ts +6 -6
- package/dist/components.js +14 -18
- package/dist/components.js.map +1 -1
- package/dist/{database-C3Szpi5J.d.ts → database-BXAfr2Y_.d.ts} +18 -0
- package/dist/hooks.d.ts +5 -5
- package/dist/hooks.js +8 -9
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +19 -27
- package/dist/index.js +21 -29
- package/dist/index.js.map +1 -1
- package/dist/{organisation-BtshODVF.d.ts → organisation-D6qRDtbF.d.ts} +1 -1
- package/dist/providers.d.ts +7 -21
- package/dist/providers.js +3 -10
- package/dist/rbac/index.d.ts +71 -221
- package/dist/rbac/index.js +15 -16
- package/dist/{types-CGX9Vyf5.d.ts → types-BDg1mAGG.d.ts} +36 -6
- package/dist/types.d.ts +3 -3
- package/dist/types.js +61 -18
- package/dist/types.js.map +1 -1
- package/dist/{unified-CM7T0aTK.d.ts → unified-DQ4VcT7H.d.ts} +1 -1
- package/dist/{usePublicRouteParams-B-CumWRc.d.ts → usePublicRouteParams-BlgwXweB.d.ts} +3 -3
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +52 -9
- package/dist/utils.js.map +1 -1
- package/docs/CONTENT_AUDIT_REPORT.md +253 -0
- package/docs/DOCUMENTATION_AUDIT.md +172 -0
- package/docs/README.md +142 -147
- package/docs/STYLE_GUIDE.md +37 -0
- package/docs/api/classes/ColumnFactory.md +17 -17
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +5 -5
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +8 -8
- package/docs/api/classes/RBACCache.md +35 -5
- package/docs/api/classes/RBACEngine.md +49 -20
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- 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 +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +11 -0
- package/docs/api/interfaces/DataTableAction.md +65 -29
- package/docs/api/interfaces/DataTableColumn.md +36 -23
- package/docs/api/interfaces/DataTableProps.md +80 -38
- package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
- package/docs/api/interfaces/EmptyStateConfig.md +5 -5
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- 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/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 +11 -11
- package/docs/api/interfaces/NavigationContextType.md +9 -9
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +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 +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +16 -3
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/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 +94 -521
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +16 -16
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- 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 +251 -269
- package/docs/api-reference/components.md +193 -0
- package/docs/api-reference/hooks.md +265 -0
- package/docs/api-reference/providers.md +6 -0
- package/docs/api-reference/types.md +6 -0
- package/docs/api-reference/utilities.md +207 -0
- package/docs/architecture/README.md +6 -0
- package/docs/{database-schema-requirements.md → architecture/database-schema-requirements.md} +6 -0
- package/docs/architecture/rbac-security-architecture.md +258 -0
- package/docs/architecture/services.md +9 -1
- package/docs/best-practices/README.md +6 -0
- package/docs/best-practices/accessibility.md +6 -0
- package/docs/{common-patterns.md → best-practices/common-patterns.md} +6 -0
- package/docs/best-practices/deployment.md +6 -0
- package/docs/best-practices/performance.md +475 -2
- package/docs/best-practices/security.md +6 -0
- package/docs/best-practices/testing.md +6 -0
- package/docs/core-concepts/authentication.md +6 -0
- package/docs/core-concepts/events.md +6 -0
- package/docs/core-concepts/organisations.md +6 -0
- package/docs/core-concepts/permissions.md +6 -0
- package/docs/core-concepts/rbac-system.md +8 -0
- package/docs/documentation-index.md +121 -182
- package/docs/{consuming-app-vite-config.md → getting-started/consuming-app-vite-config.md} +6 -0
- package/docs/getting-started/documentation-index.md +40 -0
- package/docs/getting-started/examples/README.md +878 -35
- package/docs/{faq.md → getting-started/faq.md} +7 -1
- package/docs/getting-started/installation-guide.md +6 -0
- package/docs/{quick-reference.md → getting-started/quick-reference.md} +6 -0
- package/docs/implementation-guides/app-layout.md +6 -0
- package/docs/implementation-guides/authentication.md +1021 -0
- package/docs/implementation-guides/component-styling.md +6 -0
- package/docs/implementation-guides/data-tables.md +1264 -2076
- package/docs/implementation-guides/dynamic-colors.md +6 -0
- package/docs/implementation-guides/event-theming-summary.md +6 -0
- package/docs/{file-reference-system.md → implementation-guides/file-reference-system.md} +6 -0
- package/docs/implementation-guides/file-upload-storage.md +6 -0
- package/docs/implementation-guides/forms.md +6 -0
- package/docs/implementation-guides/inactivity-tracking.md +6 -0
- package/docs/implementation-guides/navigation.md +6 -0
- package/docs/implementation-guides/organisation-security.md +6 -0
- package/docs/implementation-guides/permission-enforcement.md +6 -0
- package/docs/implementation-guides/public-pages-advanced.md +6 -0
- package/docs/implementation-guides/public-pages.md +6 -0
- package/docs/migration/MIGRATION_GUIDE.md +827 -351
- package/docs/migration/README.md +7 -1
- package/docs/migration/organisation-context-timing-fix.md +6 -0
- package/docs/migration/rbac-migration.md +44 -1
- package/docs/migration/service-architecture.md +6 -0
- package/docs/migration/v0.4.15-tailwind-scanning.md +6 -0
- package/docs/migration/v0.4.16-css-first-approach.md +6 -0
- package/docs/migration/v0.4.17-source-path-fix.md +6 -0
- package/docs/rbac/README-rbac-rls-integration.md +6 -0
- package/docs/rbac/README.md +6 -0
- package/docs/rbac/advanced-patterns.md +6 -0
- package/docs/rbac/api-reference.md +7 -1
- package/docs/rbac/breaking-changes-v3.md +222 -0
- package/docs/rbac/examples/rbac-rls-integration-example.md +6 -0
- package/docs/rbac/examples.md +6 -0
- package/docs/rbac/getting-started.md +6 -0
- package/docs/rbac/migration-guide.md +260 -0
- package/docs/rbac/quick-start.md +70 -13
- package/docs/rbac/rbac-rls-integration.md +6 -0
- package/docs/rbac/super-admin-guide.md +6 -0
- package/docs/rbac/troubleshooting.md +6 -0
- package/docs/security/README.md +6 -0
- package/docs/security/checklist.md +6 -0
- package/docs/styles/README.md +7 -1
- package/docs/{usage.md → styles/usage.md} +6 -0
- package/docs/testing/README.md +6 -0
- package/docs/{visual-testing.md → testing/visual-testing.md} +6 -0
- package/docs/troubleshooting/README.md +387 -5
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +6 -0
- package/docs/troubleshooting/common-issues.md +6 -0
- package/docs/troubleshooting/database-view-compatibility.md +6 -0
- package/docs/troubleshooting/organisation-context-setup.md +6 -0
- package/docs/troubleshooting/react-hooks-issue-analysis.md +6 -0
- package/docs/troubleshooting/styling-issues.md +6 -0
- package/docs/troubleshooting/tailwind-content-scanning.md +6 -0
- package/package.json +1 -1
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -1
- package/src/__tests__/helpers/test-providers.tsx +3 -53
- package/src/components/DataTable/DataTable.test.tsx +319 -0
- package/src/components/DataTable/DataTable.tsx +32 -11
- package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx.skip} +6 -4
- package/src/components/DataTable/__tests__/{DataTable.test.tsx → DataTable.test.tsx.skip} +6 -4
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +31 -9
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +601 -0
- package/src/components/DataTable/__tests__/keyboard.test.tsx +615 -0
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +639 -0
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx.skip +330 -0
- package/src/components/DataTable/components/AccessDeniedPage.tsx +2 -2
- package/src/components/DataTable/components/ActionButtons.tsx +88 -104
- package/src/components/DataTable/components/DataTableCore.tsx +309 -337
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +4 -2
- package/src/components/DataTable/components/DataTableModals.tsx +22 -1
- package/src/components/DataTable/components/EditableRow.tsx +69 -84
- package/src/components/DataTable/components/EmptyState.tsx +5 -1
- package/src/components/DataTable/components/ImportModal.tsx +65 -36
- package/src/components/DataTable/components/PaginationControls.tsx +40 -100
- package/src/components/DataTable/components/UnifiedTableBody.tsx +125 -148
- package/src/components/DataTable/context/DataTableContext.tsx +1 -1
- package/src/components/DataTable/core/ColumnFactory.ts +5 -0
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +12 -10
- package/src/components/DataTable/examples/HierarchicalExample.tsx +1 -1
- package/src/components/DataTable/examples/InitialPageSizeExample.tsx +1 -0
- package/src/components/DataTable/examples/PerformanceExample.tsx +1 -0
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +1 -5
- package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +167 -0
- package/src/components/DataTable/hooks/index.ts +7 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +32 -15
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +102 -0
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +89 -0
- package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +117 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +71 -27
- package/src/components/DataTable/hooks/useDataTableState.ts +39 -11
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +33 -0
- package/src/components/DataTable/hooks/useHierarchicalState.ts +15 -1
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +447 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +94 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +10 -7
- package/src/components/DataTable/hooks/useTableHandlers.ts +174 -0
- package/src/components/DataTable/index.ts +12 -3
- package/src/components/DataTable/types.ts +129 -9
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +159 -22
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +111 -0
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +15 -29
- package/src/components/DataTable/utils/a11yUtils.ts +244 -0
- package/src/components/DataTable/utils/debugTools.ts +609 -0
- package/src/components/DataTable/utils/exportUtils.ts +114 -16
- package/src/components/DataTable/utils/flexibleImport.ts +202 -32
- package/src/components/DataTable/utils/hierarchicalUtils.ts +1 -1
- package/src/components/DataTable/utils/index.ts +2 -0
- package/src/components/DataTable/utils/paginationUtils.ts +350 -0
- package/src/components/DataTable/utils/rowUtils.ts +6 -5
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -24
- package/src/components/NavigationMenu/NavigationMenu.tsx +19 -8
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +1 -23
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +56 -6
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +137 -13
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +1 -1
- package/src/components/Select/Select.tsx +1 -0
- package/src/components/examples/PermissionExample.tsx +173 -0
- package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
- package/src/examples/PublicEventPage.tsx +274 -0
- package/src/examples/PublicPageApp.tsx +308 -0
- package/src/examples/PublicPageUsageExample.tsx +216 -0
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +12 -1
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +129 -17
- package/src/hooks/__tests__/useRBAC.unit.test.ts +151 -846
- package/src/hooks/useOrganisationPermissions.test.ts +42 -18
- package/src/hooks/useOrganisationPermissions.ts +12 -6
- package/src/hooks/useOrganisationSecurity.test.ts +138 -85
- package/src/hooks/useOrganisationSecurity.ts +41 -10
- package/src/index.ts +0 -1
- package/src/providers/AuthProvider.simplified.tsx +880 -0
- package/src/providers/UnifiedAuthProvider.test.simple.tsx +8 -8
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +29 -19
- package/src/providers/index.ts +0 -1
- package/src/providers/services/EventServiceProvider.tsx +19 -15
- package/src/providers/services/InactivityServiceProvider.tsx +19 -15
- package/src/providers/services/OrganisationServiceProvider.tsx +19 -15
- package/src/providers/services/UnifiedAuthProvider.tsx +156 -127
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +1 -1
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -3
- package/src/rbac/README.md +1 -1
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +25 -27
- package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +313 -0
- package/src/rbac/__tests__/engine.comprehensive.test.ts +114 -348
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +28 -110
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +33 -85
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +2 -2
- package/src/rbac/adapters.tsx +26 -69
- package/src/rbac/api.test.ts +90 -27
- package/src/rbac/api.ts +61 -10
- package/src/rbac/audit.test.ts +33 -38
- package/src/rbac/audit.ts +21 -6
- package/src/rbac/cache.ts +33 -1
- package/src/rbac/components/NavigationGuard.tsx +11 -11
- package/src/rbac/components/NavigationProvider.test.tsx +11 -5
- package/src/rbac/components/NavigationProvider.tsx +37 -13
- package/src/rbac/components/PagePermissionGuard.tsx +111 -50
- package/src/rbac/components/PagePermissionProvider.tsx +5 -5
- package/src/rbac/components/PermissionEnforcer.tsx +11 -11
- package/src/rbac/components/RoleBasedRouter.tsx +5 -5
- package/src/rbac/components/SecureDataProvider.tsx +5 -5
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +8 -8
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +14 -14
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +12 -12
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +6 -6
- package/src/rbac/engine.test.simple.ts +19 -13
- package/src/rbac/engine.test.ts +1 -0
- package/src/rbac/engine.ts +330 -766
- package/src/rbac/errors.ts +156 -0
- package/src/rbac/hooks/usePermissions.ts +32 -10
- package/src/rbac/hooks/useRBAC.test.ts +126 -512
- package/src/rbac/hooks/useRBAC.ts +147 -193
- package/src/rbac/hooks/useResolvedScope.ts +12 -0
- package/src/rbac/index.ts +7 -4
- package/src/rbac/security.ts +109 -18
- package/src/rbac/types.ts +12 -1
- package/src/services/AuthService.ts +2 -15
- package/src/services/EventService.ts +43 -46
- package/src/services/OrganisationService.ts +51 -31
- package/src/services/__tests__/AuthService.test.ts +1 -1
- package/src/services/__tests__/EventService.test.ts +1 -1
- package/src/services/__tests__/OrganisationService.test.ts +1 -1
- package/src/services/base/BaseService.ts +8 -0
- package/src/styles/base.css +208 -0
- package/src/styles/semantic.css +24 -0
- package/src/types/database.generated.ts +7347 -0
- package/src/types/database.ts +20 -0
- package/src/utils/logger.ts +179 -0
- package/src/utils/organisationContext.ts +11 -4
- package/src/utils/storage/__tests__/helpers.unit.test.ts +6 -2
- package/dist/appNameResolver-UURKN7NF.js +0 -22
- package/dist/audit-6TOCAMKO.js.map +0 -1
- package/dist/chunk-B2WTCLCV.js.map +0 -1
- package/dist/chunk-FGMFQSHX.js.map +0 -1
- package/dist/chunk-K34IM5CT.js.map +0 -1
- package/dist/chunk-KHJS6VIA.js.map +0 -1
- package/dist/chunk-KK73ZB4E.js.map +0 -1
- package/dist/chunk-M5IWZRBT.js.map +0 -1
- package/dist/chunk-ULBI5JGB.js +0 -109
- package/dist/chunk-ULBI5JGB.js.map +0 -1
- package/dist/chunk-WN6XJWOS.js.map +0 -1
- package/dist/chunk-XLZ7U46Z.js.map +0 -1
- package/dist/chunk-Y6TXWPJO.js.map +0 -1
- package/docs/DOCUMENTATION_CHECKLIST.md +0 -281
- package/docs/TERMINOLOGY.md +0 -231
- package/docs/api/interfaces/RBACContextType.md +0 -468
- package/docs/api/interfaces/RBACProviderProps.md +0 -107
- package/docs/best-practices/performance-expansion.md +0 -473
- package/docs/breaking-changes.md +0 -179
- package/docs/consuming-app-example.md +0 -290
- package/docs/documentation-templates.md +0 -539
- package/docs/examples/navigation-menu-auth-fix.md +0 -344
- package/docs/getting-started/examples/basic-auth-app.md +0 -520
- package/docs/getting-started/examples/full-featured-app.md +0 -616
- package/docs/getting-started/quick-start.md +0 -376
- package/docs/implementation-guides/datatable-filtering.md +0 -313
- package/docs/implementation-guides/datatable-rbac-usage.md +0 -317
- package/docs/implementation-guides/hierarchical-datatable.md +0 -850
- package/docs/implementation-guides/large-datasets.md +0 -281
- package/docs/implementation-guides/performance.md +0 -403
- package/docs/migration/quick-migration-guide.md +0 -320
- package/docs/migration-guide.md +0 -193
- package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +0 -226
- package/docs/performance/README.md +0 -551
- package/docs/style-guide.md +0 -964
- package/docs/troubleshooting/authentication-issues.md +0 -334
- package/docs/troubleshooting/debugging.md +0 -1117
- package/docs/troubleshooting/migration.md +0 -918
- package/src/__tests__/hooks/usePermissions.test.ts +0 -261
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +0 -574
- package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -613
- package/src/hooks/services/__tests__/useServiceHooks.test.tsx +0 -137
- package/src/hooks/services/usePermissions.ts +0 -70
- package/src/hooks/services/useRBACService.ts +0 -30
- package/src/hooks/usePermissionCheck.ts +0 -150
- package/src/providers/__tests__/ServiceProviders.test.tsx +0 -477
- package/src/providers/services/RBACServiceProvider.tsx +0 -79
- package/src/rbac/__tests__/integration.authflow.test.tsx +0 -119
- package/src/rbac/__tests__/integration.navigation.test.tsx +0 -69
- package/src/rbac/__tests__/integration.securedata.test.tsx +0 -92
- package/src/rbac/__tests__/integration.smoke.test.tsx +0 -73
- package/src/rbac/providers/RBACProvider.tsx +0 -645
- package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +0 -688
- package/src/rbac/providers/__tests__/RBACProvider.test.tsx +0 -1186
- package/src/rbac/providers/index.ts +0 -11
- package/src/services/RBACService.ts +0 -522
- package/src/services/__tests__/RBACService.test.ts +0 -492
- package/src/services/interfaces/IRBACService.ts +0 -62
- package/src/utils/appNameResolver.test 2.ts +0 -494
- /package/dist/{DataTable-4GAVPIEG.js.map → DataTable-ETGVF4Y5.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-3NKDOSOK.js.map → UnifiedAuthProvider-P5SOJAQ6.js.map} +0 -0
- /package/dist/{api-DDMUKIUD.js.map → api-KG4A2X7P.js.map} +0 -0
- /package/dist/{appNameResolver-UURKN7NF.js.map → audit-65VNHEV2.js.map} +0 -0
- /package/dist/{chunk-NTNILOBC.js.map → chunk-5BO3MI5Y.js.map} +0 -0
- /package/dist/{chunk-URUTVZ7N.js.map → chunk-FL4ZCQLD.js.map} +0 -0
- /package/dist/{chunk-LW7MMEAQ.js.map → chunk-FT2M4R4F.js.map} +0 -0
- /package/dist/{chunk-AFGTSUAD.js.map → chunk-VSOKOFRF.js.map} +0 -0
- /package/docs/{app.css.example → styles/app.css.example} +0 -0
package/src/rbac/types.ts
CHANGED
|
@@ -40,7 +40,7 @@ export type PermissionCheck = {
|
|
|
40
40
|
pageId?: UUID | string;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
export type PermissionMap = Record<
|
|
43
|
+
export type PermissionMap = Record<Permission, boolean> & Partial<Record<'*', boolean>>;
|
|
44
44
|
|
|
45
45
|
// ============================================================================
|
|
46
46
|
// ROLE TYPES
|
|
@@ -162,6 +162,17 @@ export interface RBACAuditEvent {
|
|
|
162
162
|
created_at: string;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
export interface RBACAppContext {
|
|
166
|
+
appId: UUID;
|
|
167
|
+
hasAccess: boolean;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface RBACRoleContext {
|
|
171
|
+
globalRole: GlobalRole | null;
|
|
172
|
+
organisationRole: OrganisationRole | null;
|
|
173
|
+
eventAppRole: EventAppRole | null;
|
|
174
|
+
}
|
|
175
|
+
|
|
165
176
|
// ============================================================================
|
|
166
177
|
// CACHE TYPES
|
|
167
178
|
// ============================================================================
|
|
@@ -11,12 +11,11 @@
|
|
|
11
11
|
import { type SupabaseClient, type User, type Session, AuthError } from '@supabase/supabase-js';
|
|
12
12
|
import { BaseService } from './base/BaseService';
|
|
13
13
|
import { IAuthService, AuthResult } from './interfaces/IAuthService';
|
|
14
|
-
import { DebugLogger } from '../utils/debugLogger';
|
|
15
14
|
|
|
16
15
|
export class AuthService extends BaseService implements IAuthService {
|
|
17
16
|
private user: User | null = null;
|
|
18
17
|
private session: Session | null = null;
|
|
19
|
-
private authLoading =
|
|
18
|
+
private authLoading = false;
|
|
20
19
|
private authError: AuthError | null = null;
|
|
21
20
|
private supabaseClient: SupabaseClient | null = null;
|
|
22
21
|
private authStateSubscription: any = null;
|
|
@@ -307,16 +306,12 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
307
306
|
this.authStateSubscription = this.supabaseClient.auth.onAuthStateChange(
|
|
308
307
|
(event, session) => {
|
|
309
308
|
try {
|
|
310
|
-
DebugLogger.log('AuthService', 'Auth state changed:', event, session?.user?.email);
|
|
311
|
-
|
|
312
309
|
// Handle different auth events
|
|
313
310
|
if (event === 'SIGNED_OUT') {
|
|
314
|
-
DebugLogger.log('AuthService', 'User signed out, clearing all state');
|
|
315
311
|
this.session = null;
|
|
316
312
|
this.user = null;
|
|
317
313
|
this.authError = null;
|
|
318
314
|
} else if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {
|
|
319
|
-
DebugLogger.log('AuthService', 'User signed in or token refreshed');
|
|
320
315
|
this.session = session;
|
|
321
316
|
this.user = session?.user ?? null;
|
|
322
317
|
|
|
@@ -325,7 +320,6 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
325
320
|
this.authError = null;
|
|
326
321
|
}
|
|
327
322
|
} else if (event === 'INITIAL_SESSION') {
|
|
328
|
-
DebugLogger.log('AuthService', 'Initial session event');
|
|
329
323
|
if (session) {
|
|
330
324
|
this.session = session;
|
|
331
325
|
this.user = session.user ?? null;
|
|
@@ -357,8 +351,6 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
357
351
|
}
|
|
358
352
|
|
|
359
353
|
try {
|
|
360
|
-
DebugLogger.log('AuthService', 'Initializing authentication...');
|
|
361
|
-
|
|
362
354
|
// First, try to get the current session
|
|
363
355
|
const { data: { session: currentSession }, error: sessionError } = await this.supabaseClient.auth.getSession();
|
|
364
356
|
|
|
@@ -368,19 +360,14 @@ export class AuthService extends BaseService implements IAuthService {
|
|
|
368
360
|
}
|
|
369
361
|
|
|
370
362
|
if (currentSession) {
|
|
371
|
-
DebugLogger.log('AuthService', 'Session restored from storage:', currentSession.user?.email);
|
|
372
363
|
this.session = currentSession;
|
|
373
364
|
this.user = currentSession.user;
|
|
374
365
|
this.authError = null;
|
|
375
366
|
} else {
|
|
376
|
-
DebugLogger.log('AuthService', 'No session found in storage');
|
|
377
367
|
// Try to get user anyway (in case session is expired but user exists)
|
|
378
368
|
const { data: { user: currentUser }, error: userError } = await this.supabaseClient.auth.getUser();
|
|
379
369
|
|
|
380
|
-
if (userError) {
|
|
381
|
-
DebugLogger.log('AuthService', 'No user found:', userError.message);
|
|
382
|
-
} else if (currentUser) {
|
|
383
|
-
DebugLogger.log('AuthService', 'User found without session:', currentUser.email);
|
|
370
|
+
if (!userError && currentUser) {
|
|
384
371
|
this.user = currentUser;
|
|
385
372
|
// Don't set session if it's null - this prevents issues
|
|
386
373
|
}
|
|
@@ -8,23 +8,25 @@
|
|
|
8
8
|
* Handles event management and selection with organisation context validation.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { type SupabaseClient, type User, type Session } from '@supabase/supabase-js';
|
|
11
12
|
import { BaseService } from './base/BaseService';
|
|
12
13
|
import { IEventService } from './interfaces/IEventService';
|
|
13
14
|
import { Event } from '../types/unified';
|
|
15
|
+
import { Organisation } from '../types/organisation';
|
|
14
16
|
import { DebugLogger } from '../utils/debugLogger';
|
|
15
17
|
|
|
16
18
|
export class EventService extends BaseService implements IEventService {
|
|
17
19
|
private events: Event[] = [];
|
|
18
20
|
private selectedEvent: Event | null = null;
|
|
19
|
-
private _isLoading =
|
|
21
|
+
private _isLoading = false; // Start as false to avoid blocking UI
|
|
20
22
|
private error: Error | null = null;
|
|
21
23
|
|
|
22
24
|
// Dependencies
|
|
23
|
-
private supabaseClient:
|
|
24
|
-
private user:
|
|
25
|
-
private session:
|
|
25
|
+
private supabaseClient: SupabaseClient | null = null;
|
|
26
|
+
private user: User | null = null;
|
|
27
|
+
private session: Session | null = null;
|
|
26
28
|
private appName: string = '';
|
|
27
|
-
private selectedOrganisation:
|
|
29
|
+
private selectedOrganisation: Organisation | null = null;
|
|
28
30
|
private setSelectedEventId: ((eventId: string | null) => void) | null = null;
|
|
29
31
|
|
|
30
32
|
// Internal state management
|
|
@@ -34,11 +36,11 @@ export class EventService extends BaseService implements IEventService {
|
|
|
34
36
|
private userClearedEventRef = false;
|
|
35
37
|
|
|
36
38
|
constructor(
|
|
37
|
-
supabaseClient:
|
|
38
|
-
user:
|
|
39
|
-
session:
|
|
39
|
+
supabaseClient: SupabaseClient,
|
|
40
|
+
user: User | null,
|
|
41
|
+
session: Session | null,
|
|
40
42
|
appName: string,
|
|
41
|
-
selectedOrganisation:
|
|
43
|
+
selectedOrganisation: Organisation | null,
|
|
42
44
|
setSelectedEventId: (eventId: string | null) => void
|
|
43
45
|
) {
|
|
44
46
|
super();
|
|
@@ -52,19 +54,36 @@ export class EventService extends BaseService implements IEventService {
|
|
|
52
54
|
|
|
53
55
|
// Update dependencies
|
|
54
56
|
updateDependencies(
|
|
55
|
-
supabaseClient:
|
|
56
|
-
user:
|
|
57
|
-
session:
|
|
57
|
+
supabaseClient: SupabaseClient,
|
|
58
|
+
user: User | null,
|
|
59
|
+
session: Session | null,
|
|
58
60
|
appName: string,
|
|
59
|
-
selectedOrganisation:
|
|
61
|
+
selectedOrganisation: Organisation | null,
|
|
60
62
|
setSelectedEventId: (eventId: string | null) => void
|
|
61
63
|
): void {
|
|
64
|
+
const previousOrgId = this.selectedOrganisation?.id;
|
|
65
|
+
const newOrgId = selectedOrganisation?.id;
|
|
66
|
+
|
|
62
67
|
this.supabaseClient = supabaseClient;
|
|
63
68
|
this.user = user;
|
|
64
69
|
this.session = session;
|
|
65
70
|
this.appName = appName;
|
|
66
71
|
this.selectedOrganisation = selectedOrganisation;
|
|
67
72
|
this.setSelectedEventId = setSelectedEventId;
|
|
73
|
+
|
|
74
|
+
// If organisation changed (from null to value, or different org), reset initialization
|
|
75
|
+
// This ensures events are re-fetched when organisation context becomes available
|
|
76
|
+
if (previousOrgId !== newOrgId) {
|
|
77
|
+
this.resetInitialization(); // Reset BaseService's isInitialized flag
|
|
78
|
+
this.isInitializedRef = false;
|
|
79
|
+
this.isFetchingRef = false;
|
|
80
|
+
// Clear events when switching organisations
|
|
81
|
+
if (previousOrgId !== null && newOrgId !== previousOrgId) {
|
|
82
|
+
this.events = [];
|
|
83
|
+
this.selectedEvent = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
68
87
|
this.notify();
|
|
69
88
|
}
|
|
70
89
|
|
|
@@ -153,14 +172,12 @@ export class EventService extends BaseService implements IEventService {
|
|
|
153
172
|
|
|
154
173
|
if (persistedEventId && events.length > 0) {
|
|
155
174
|
const persistedEvent = events.find(event => event.event_id === persistedEventId);
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
DebugLogger.log('EventService', 'Persisted event not found in current events, clearing storage');
|
|
163
|
-
// Clear invalid persisted event
|
|
175
|
+
if (persistedEvent) {
|
|
176
|
+
this.selectedEvent = persistedEvent;
|
|
177
|
+
this.setSelectedEventId?.(persistedEventId);
|
|
178
|
+
return true;
|
|
179
|
+
} else {
|
|
180
|
+
// Clear invalid persisted event
|
|
164
181
|
sessionStorage.removeItem('pace-core-selected-event');
|
|
165
182
|
localStorage.removeItem('pace-core-selected-event');
|
|
166
183
|
}
|
|
@@ -198,7 +215,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
198
215
|
autoSelectNextEvent(events: Event[]): void {
|
|
199
216
|
const nextEvent = this.getNextEventByDate(events);
|
|
200
217
|
if (nextEvent) {
|
|
201
|
-
DebugLogger.log('EventService', 'Auto-selecting next event:', nextEvent.event_name);
|
|
202
218
|
this.selectedEvent = nextEvent;
|
|
203
219
|
this.setSelectedEventId?.(nextEvent.event_id);
|
|
204
220
|
this.persistEventSelection(nextEvent.event_id);
|
|
@@ -226,49 +242,31 @@ export class EventService extends BaseService implements IEventService {
|
|
|
226
242
|
|
|
227
243
|
private async fetchEvents(): Promise<void> {
|
|
228
244
|
if (!this.user || !this.session || !this.supabaseClient || !this.appName || !this.selectedOrganisation) {
|
|
229
|
-
|
|
230
|
-
this._isLoading = false;
|
|
245
|
+
// Already false from initialization, just notify
|
|
231
246
|
this.notify();
|
|
232
247
|
return;
|
|
233
248
|
}
|
|
249
|
+
|
|
250
|
+
// Only set loading to true if we actually have dependencies and are going to fetch
|
|
251
|
+
this._isLoading = true;
|
|
252
|
+
this.notify();
|
|
234
253
|
|
|
235
254
|
// Prevent multiple simultaneous fetches
|
|
236
255
|
if (this.isFetchingRef) {
|
|
237
|
-
DebugLogger.log('EventService', 'Already fetching events, skipping');
|
|
238
256
|
return;
|
|
239
257
|
}
|
|
240
258
|
|
|
241
|
-
DebugLogger.log('EventService', 'User and organisation found, fetching events for:', {
|
|
242
|
-
userId: this.user.id,
|
|
243
|
-
appName: this.appName,
|
|
244
|
-
organisationId: this.selectedOrganisation.id,
|
|
245
|
-
organisationName: this.selectedOrganisation.display_name
|
|
246
|
-
});
|
|
247
|
-
|
|
248
259
|
this.isFetchingRef = true;
|
|
249
260
|
let isMounted = true;
|
|
250
261
|
|
|
251
262
|
try {
|
|
252
263
|
// Call the RPC function following the established pattern
|
|
253
|
-
DebugLogger.log('EventService', 'Calling data_user_events_get RPC with:', {
|
|
254
|
-
user_id: this.user.id,
|
|
255
|
-
organisation_id: this.selectedOrganisation.id,
|
|
256
|
-
app_name: this.appName
|
|
257
|
-
});
|
|
258
|
-
|
|
259
264
|
const { data, error: rpcError } = await this.supabaseClient.rpc('data_user_events_get', {
|
|
260
265
|
p_user_id: this.user.id,
|
|
261
266
|
p_organisation_id: this.selectedOrganisation.id,
|
|
262
267
|
p_app_name: this.appName
|
|
263
268
|
});
|
|
264
269
|
|
|
265
|
-
DebugLogger.log('EventService', 'RPC response:', {
|
|
266
|
-
data,
|
|
267
|
-
error: rpcError,
|
|
268
|
-
dataLength: data?.length || 0,
|
|
269
|
-
organisationId: this.selectedOrganisation.id
|
|
270
|
-
});
|
|
271
|
-
|
|
272
270
|
if (rpcError) {
|
|
273
271
|
throw new Error(rpcError.message || 'Failed to fetch events');
|
|
274
272
|
}
|
|
@@ -312,7 +310,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
312
310
|
if (!persistedEventLoaded && !this.userClearedEventRef) {
|
|
313
311
|
const nextEvent = this.getNextEventByDate(transformedEvents);
|
|
314
312
|
if (nextEvent) {
|
|
315
|
-
DebugLogger.log('EventService', 'Auto-selecting next event after no persisted event found:', nextEvent.event_name);
|
|
316
313
|
this.hasAutoSelectedRef = true;
|
|
317
314
|
this.selectedEvent = nextEvent;
|
|
318
315
|
this.setSelectedEventId?.(nextEvent.event_id);
|
|
@@ -18,14 +18,13 @@ import type {
|
|
|
18
18
|
OrganisationHierarchy
|
|
19
19
|
} from '../types/organisation';
|
|
20
20
|
import { setOrganisationContext } from '../utils/organisationContext';
|
|
21
|
-
import { DebugLogger } from '../utils/debugLogger';
|
|
22
21
|
|
|
23
22
|
export class OrganisationService extends BaseService implements IOrganisationService {
|
|
24
23
|
private _selectedOrganisation: Organisation | null = null;
|
|
25
24
|
private _organisations: Organisation[] = [];
|
|
26
25
|
private _userMemberships: OrganisationMembership[] = [];
|
|
27
26
|
private _roleMapState: Map<string, string> = new Map();
|
|
28
|
-
private _isLoading =
|
|
27
|
+
private _isLoading = false;
|
|
29
28
|
private _error: Error | null = null;
|
|
30
29
|
private _isContextReady = false;
|
|
31
30
|
private retryCount = 0;
|
|
@@ -52,7 +51,9 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
52
51
|
getSelectedOrganisation(): Organisation | null { return this._selectedOrganisation; }
|
|
53
52
|
getOrganisations(): Organisation[] { return this._organisations; }
|
|
54
53
|
getUserMemberships(): OrganisationMembership[] { return this._userMemberships; }
|
|
55
|
-
isLoading(): boolean {
|
|
54
|
+
isLoading(): boolean {
|
|
55
|
+
return this._isLoading;
|
|
56
|
+
}
|
|
56
57
|
getError(): Error | null { return this._error; }
|
|
57
58
|
hasValidOrganisationContext(): boolean { return !!(this._selectedOrganisation && !this._isLoading && !this._error && this._isContextReady); }
|
|
58
59
|
isContextReady(): boolean { return this._isContextReady; }
|
|
@@ -101,15 +102,23 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
101
102
|
|
|
102
103
|
// Update dependencies
|
|
103
104
|
updateDependencies(user: User | null, session: Session | null): void {
|
|
105
|
+
const wasAuthenticated = !!(this.user && this.session);
|
|
106
|
+
const isAuthenticated = !!(user && session);
|
|
107
|
+
|
|
104
108
|
this.user = user;
|
|
105
109
|
this.session = session;
|
|
110
|
+
|
|
111
|
+
// If user logs out, allow re-initialization when they log back in
|
|
112
|
+
if (wasAuthenticated && !isAuthenticated) {
|
|
113
|
+
// Reset BaseService initialization state to allow re-initialization
|
|
114
|
+
(this as any).isInitialized = false;
|
|
115
|
+
}
|
|
116
|
+
|
|
106
117
|
this.notify();
|
|
107
118
|
}
|
|
108
119
|
|
|
109
120
|
// Organisation methods
|
|
110
121
|
async switchOrganisation(orgId: string): Promise<void> {
|
|
111
|
-
DebugLogger.log("OrganisationService", "Switching to organisation:", orgId);
|
|
112
|
-
|
|
113
122
|
// Validate access
|
|
114
123
|
if (!this.validateOrganisationAccess(orgId)) {
|
|
115
124
|
throw new Error(`User does not have access to organisation ${orgId}`) as OrganisationSecurityError;
|
|
@@ -128,7 +137,6 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
128
137
|
// Set database organisation context
|
|
129
138
|
await this.setDatabaseOrganisationContext(targetOrg);
|
|
130
139
|
|
|
131
|
-
DebugLogger.log("OrganisationService", "Switched to organisation:", targetOrg.display_name);
|
|
132
140
|
this.notify();
|
|
133
141
|
}
|
|
134
142
|
|
|
@@ -206,7 +214,11 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
206
214
|
// Lifecycle methods
|
|
207
215
|
async initialize(): Promise<void> {
|
|
208
216
|
await super.initialize();
|
|
209
|
-
|
|
217
|
+
|
|
218
|
+
// Don't load if already loading (prevents duplicate loads during rapid auth events)
|
|
219
|
+
if (!this.isLoadingRef) {
|
|
220
|
+
await this.loadUserOrganisations();
|
|
221
|
+
}
|
|
210
222
|
}
|
|
211
223
|
|
|
212
224
|
cleanup(): void {
|
|
@@ -214,9 +226,11 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
214
226
|
this.isLoadingRef = false;
|
|
215
227
|
this.hasFailedRef = false;
|
|
216
228
|
this.lastLoadTimeRef = 0;
|
|
217
|
-
//
|
|
229
|
+
// Don't abort pending requests - let them complete naturally
|
|
230
|
+
// Aborting causes React StrictMode issues where requests are cancelled mid-flight
|
|
231
|
+
// The requests will complete on their own and update state if needed
|
|
218
232
|
if (this.abortControllerRef) {
|
|
219
|
-
|
|
233
|
+
// Clear the reference but don't abort - let requests complete
|
|
220
234
|
this.abortControllerRef = null;
|
|
221
235
|
}
|
|
222
236
|
// Reset state
|
|
@@ -247,13 +261,25 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
247
261
|
}
|
|
248
262
|
|
|
249
263
|
try {
|
|
250
|
-
|
|
251
|
-
|
|
264
|
+
console.log('[OrganisationService] Setting database organisation context for:', organisation.id);
|
|
265
|
+
|
|
266
|
+
// Add timeout to prevent hanging
|
|
267
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
268
|
+
setTimeout(() => reject(new Error('Context setting timeout after 5 seconds')), 5000);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const contextPromise = setOrganisationContext(this.supabaseClient, organisation.id);
|
|
272
|
+
|
|
273
|
+
await Promise.race([contextPromise, timeoutPromise]);
|
|
274
|
+
|
|
275
|
+
console.log('[OrganisationService] Database organisation context set successfully');
|
|
252
276
|
this._isContextReady = true;
|
|
253
277
|
this.notify();
|
|
254
278
|
} catch (error) {
|
|
255
279
|
console.error('[OrganisationService] Failed to set database organisation context:', error);
|
|
256
|
-
this
|
|
280
|
+
// Set context ready to true anyway - this is a non-critical operation
|
|
281
|
+
// The app should still work without database context
|
|
282
|
+
this._isContextReady = true;
|
|
257
283
|
this.notify();
|
|
258
284
|
// Don't throw - this is a non-critical operation
|
|
259
285
|
}
|
|
@@ -266,7 +292,6 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
266
292
|
|
|
267
293
|
if (!this.user || !this.session || !this.supabaseClient) {
|
|
268
294
|
// Clear state when no user, session, or supabase client
|
|
269
|
-
DebugLogger.log('OrganisationService', 'Clearing organisation state - no user, session, or supabase client');
|
|
270
295
|
this._selectedOrganisation = null;
|
|
271
296
|
this._organisations = [];
|
|
272
297
|
this._userMemberships = [];
|
|
@@ -279,6 +304,9 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
279
304
|
// Additional check to prevent loading during auth state changes
|
|
280
305
|
if (this.isLoadingRef) {
|
|
281
306
|
console.log("OrganisationService", "Already loading, skipping duplicate load");
|
|
307
|
+
// Ensure loading state is correct
|
|
308
|
+
this._isLoading = true;
|
|
309
|
+
this.notify();
|
|
282
310
|
return;
|
|
283
311
|
}
|
|
284
312
|
|
|
@@ -286,6 +314,13 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
286
314
|
const now = Date.now();
|
|
287
315
|
if (now - this.lastLoadTimeRef < 2000) {
|
|
288
316
|
console.log("OrganisationService", "Too soon since last load, skipping");
|
|
317
|
+
// Ensure loading state is correct
|
|
318
|
+
if (this._organisations.length > 0 || this._selectedOrganisation) {
|
|
319
|
+
this._isLoading = false;
|
|
320
|
+
} else {
|
|
321
|
+
this._isLoading = true;
|
|
322
|
+
}
|
|
323
|
+
this.notify();
|
|
289
324
|
return;
|
|
290
325
|
}
|
|
291
326
|
|
|
@@ -305,9 +340,6 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
305
340
|
this.notify();
|
|
306
341
|
|
|
307
342
|
try {
|
|
308
|
-
DebugLogger.log("OrganisationService", "Loading organisations for user:", this.user.id);
|
|
309
|
-
|
|
310
|
-
// Debug: Log Supabase client configuration
|
|
311
343
|
console.log("[OrganisationService] Supabase client ready:", {
|
|
312
344
|
isConnected: !!this.supabaseClient,
|
|
313
345
|
hasAuth: !!this.supabaseClient.auth,
|
|
@@ -418,8 +450,6 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
418
450
|
}
|
|
419
451
|
}
|
|
420
452
|
|
|
421
|
-
DebugLogger.log("OrganisationService", "Raw memberships data:", memberships);
|
|
422
|
-
|
|
423
453
|
if (!memberships || memberships.length === 0) {
|
|
424
454
|
throw new Error('User has no active organisation memberships') as OrganisationSecurityError;
|
|
425
455
|
}
|
|
@@ -451,8 +481,6 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
451
481
|
throw new Error('No valid organisation IDs found in memberships') as OrganisationSecurityError;
|
|
452
482
|
}
|
|
453
483
|
|
|
454
|
-
DebugLogger.log("OrganisationService", "Valid organisation IDs:", organisationIds);
|
|
455
|
-
|
|
456
484
|
// Check if request was aborted before making organisations query
|
|
457
485
|
if (abortSignal.aborted) {
|
|
458
486
|
throw new Error('Request aborted');
|
|
@@ -486,8 +514,6 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
486
514
|
throw new Error('User has no access to active organisations') as OrganisationSecurityError;
|
|
487
515
|
}
|
|
488
516
|
|
|
489
|
-
DebugLogger.log("OrganisationService", "Active organisations:", activeOrgs);
|
|
490
|
-
|
|
491
517
|
this._organisations = activeOrgs;
|
|
492
518
|
this._userMemberships = memberships as OrganisationMembership[];
|
|
493
519
|
|
|
@@ -507,7 +533,6 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
507
533
|
const validPersistedOrg = activeOrgs.find(org => org.id === persistedOrg.id);
|
|
508
534
|
if (validPersistedOrg) {
|
|
509
535
|
initialOrg = validPersistedOrg;
|
|
510
|
-
DebugLogger.log("OrganisationService", "Restored persisted organisation:", initialOrg.display_name);
|
|
511
536
|
} else {
|
|
512
537
|
console.warn("[OrganisationService] Persisted organisation not found in active orgs, clearing cache");
|
|
513
538
|
localStorage.removeItem('pace-core-selected-organisation');
|
|
@@ -527,10 +552,9 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
527
552
|
if (!initialOrg) {
|
|
528
553
|
const adminMembership = memberships.find((m: any) => m.role === 'org_admin');
|
|
529
554
|
if (adminMembership) {
|
|
530
|
-
const foundOrg = organisations.find((org
|
|
555
|
+
const foundOrg = organisations.find((org) => org.id === adminMembership.organisation_id);
|
|
531
556
|
if (foundOrg) {
|
|
532
557
|
initialOrg = foundOrg;
|
|
533
|
-
DebugLogger.log("OrganisationService", "Selected org_admin organisation:", initialOrg.display_name);
|
|
534
558
|
}
|
|
535
559
|
}
|
|
536
560
|
}
|
|
@@ -538,7 +562,6 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
538
562
|
// 3. Fall back to first organisation
|
|
539
563
|
if (!initialOrg) {
|
|
540
564
|
initialOrg = activeOrgs[0];
|
|
541
|
-
DebugLogger.log("OrganisationService", "Selected first organisation:", initialOrg.display_name);
|
|
542
565
|
}
|
|
543
566
|
|
|
544
567
|
if (!initialOrg) {
|
|
@@ -550,11 +573,8 @@ export class OrganisationService extends BaseService implements IOrganisationSer
|
|
|
550
573
|
// Persist selection
|
|
551
574
|
localStorage.setItem('pace-core-selected-organisation', JSON.stringify(initialOrg));
|
|
552
575
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
totalOrganisations: activeOrgs.length,
|
|
556
|
-
userRole: roleMap.get(initialOrg.id)
|
|
557
|
-
});
|
|
576
|
+
// Set database organisation context
|
|
577
|
+
await this.setDatabaseOrganisationContext(initialOrg);
|
|
558
578
|
|
|
559
579
|
// Reset retry count and failed flag on success
|
|
560
580
|
this.retryCount = 0;
|
|
@@ -46,7 +46,7 @@ describe('AuthService', () => {
|
|
|
46
46
|
expect(authService.getUser()).toBeNull();
|
|
47
47
|
expect(authService.getSession()).toBeNull();
|
|
48
48
|
expect(authService.isAuthenticated()).toBe(false);
|
|
49
|
-
expect(authService.isLoading()).toBe(
|
|
49
|
+
expect(authService.isLoading()).toBe(false);
|
|
50
50
|
expect(authService.getError()).toBeNull();
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -95,7 +95,7 @@ describe('EventService', () => {
|
|
|
95
95
|
it('should initialize with default state', () => {
|
|
96
96
|
expect(eventService.getEvents()).toEqual([]);
|
|
97
97
|
expect(eventService.getSelectedEvent()).toBeNull();
|
|
98
|
-
expect(eventService.isLoading()).toBe(
|
|
98
|
+
expect(eventService.isLoading()).toBe(false);
|
|
99
99
|
expect(eventService.getError()).toBeNull();
|
|
100
100
|
});
|
|
101
101
|
|
|
@@ -126,7 +126,7 @@ describe('OrganisationService', () => {
|
|
|
126
126
|
expect(organisationService.getSelectedOrganisation()).toBeNull();
|
|
127
127
|
expect(organisationService.getOrganisations()).toEqual([]);
|
|
128
128
|
expect(organisationService.getUserMemberships()).toEqual([]);
|
|
129
|
-
expect(organisationService.isLoading()).toBe(
|
|
129
|
+
expect(organisationService.isLoading()).toBe(false);
|
|
130
130
|
expect(organisationService.getError()).toBeNull();
|
|
131
131
|
expect(organisationService.hasValidOrganisationContext()).toBe(false);
|
|
132
132
|
});
|
|
@@ -75,6 +75,14 @@ export abstract class BaseService {
|
|
|
75
75
|
return this.isInitialized;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Reset initialization state (allows re-initialization)
|
|
80
|
+
* Use when dependencies change and service needs to re-initialize
|
|
81
|
+
*/
|
|
82
|
+
protected resetInitialization(): void {
|
|
83
|
+
this.isInitialized = false;
|
|
84
|
+
}
|
|
85
|
+
|
|
78
86
|
/**
|
|
79
87
|
* Override in subclasses to implement initialization logic
|
|
80
88
|
*/
|