@jmruthers/pace-core 0.5.191 → 0.5.193
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/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
- package/dist/{DataTable-WKRZD47S.js → DataTable-5FU7IESH.js} +7 -6
- package/dist/{PublicPageProvider-ULXC_u6U.d.ts → PublicPageProvider-C0Sm_e5k.d.ts} +3 -1
- package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
- package/dist/{UnifiedAuthProvider-FTSG5XH7.js → UnifiedAuthProvider-RGJTDE2C.js} +3 -3
- package/dist/{api-IHKALJZD.js → api-N774RPUA.js} +2 -2
- package/dist/chunk-6C4YBBJM 5.js +628 -0
- package/dist/chunk-7D4SUZUM.js 2.map +1 -0
- package/dist/{chunk-LOMZXPSN.js → chunk-7EQTDTTJ.js} +47 -74
- package/dist/chunk-7EQTDTTJ.js 2.map +1 -0
- package/dist/chunk-7EQTDTTJ.js.map +1 -0
- package/dist/{chunk-6LTQQAT6.js → chunk-7FLMSG37.js} +336 -137
- package/dist/chunk-7FLMSG37.js 2.map +1 -0
- package/dist/chunk-7FLMSG37.js.map +1 -0
- package/dist/{chunk-XNYQOL3Z.js → chunk-BC4IJKSL.js} +9 -18
- package/dist/chunk-BC4IJKSL.js.map +1 -0
- package/dist/{chunk-ULHIJK66.js → chunk-E3SPN4VZ 5.js } +146 -36
- package/dist/chunk-E3SPN4VZ.js +12917 -0
- package/dist/{chunk-ULHIJK66.js.map → chunk-E3SPN4VZ.js.map} +1 -1
- package/dist/chunk-E66EQZE6 5.js +37 -0
- package/dist/chunk-E66EQZE6.js 2.map +1 -0
- package/dist/{chunk-6TQDD426.js → chunk-HWIIPPNI.js} +40 -221
- package/dist/chunk-HWIIPPNI.js.map +1 -0
- package/dist/chunk-I7PSE6JW 5.js +191 -0
- package/dist/chunk-I7PSE6JW.js 2.map +1 -0
- package/dist/{chunk-OETXORNB.js → chunk-IIELH4DL.js} +211 -136
- package/dist/chunk-IIELH4DL.js.map +1 -0
- package/dist/{chunk-ROXMHMY2.js → chunk-KNC55RTG.js} +13 -3
- package/dist/{chunk-ROXMHMY2.js.map → chunk-KNC55RTG.js 5.map } +1 -1
- package/dist/chunk-KNC55RTG.js.map +1 -0
- package/dist/chunk-KQCRWDSA.js 5.map +1 -0
- package/dist/{chunk-XYXSXPUK.js → chunk-LFNCN2SP.js} +7 -6
- package/dist/chunk-LFNCN2SP.js 2.map +1 -0
- package/dist/chunk-LFNCN2SP.js.map +1 -0
- package/dist/chunk-LMC26NLJ 2.js +84 -0
- package/dist/{chunk-VKB2CO4Z.js → chunk-NOAYCWCX 5.js } +84 -87
- package/dist/chunk-NOAYCWCX.js +4993 -0
- package/dist/chunk-NOAYCWCX.js.map +1 -0
- package/dist/chunk-QWWZ5CAQ.js 3.map +1 -0
- package/dist/chunk-QXHPKYJV 3.js +113 -0
- package/dist/chunk-R77UEZ4E 3.js +68 -0
- package/dist/chunk-VBXEHIUJ.js 6.map +1 -0
- package/dist/{chunk-VRGWKHDB.js → chunk-XNXXZ43G.js} +77 -33
- package/dist/chunk-XNXXZ43G.js.map +1 -0
- package/dist/chunk-ZSAAAMVR 6.js +25 -0
- package/dist/components.d.ts +2 -2
- package/dist/components.js +7 -7
- package/dist/components.js 5.map +1 -0
- package/dist/hooks.js +8 -8
- package/dist/index.d.ts +5 -5
- package/dist/index.js +12 -14
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +3 -3
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +1 -19
- package/dist/rbac/index.js +7 -9
- package/dist/styles/index 2.js +12 -0
- package/dist/styles/index.js 5.map +1 -0
- package/dist/theming/runtime 5.js +19 -0
- package/dist/theming/runtime.js 5.map +1 -0
- package/dist/utils.js +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/Logger.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +2 -2
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +10 -10
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.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/ComplianceResult.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +24 -11
- 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/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.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/LoggerConfig.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/ParsedAddress.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.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/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +2 -2
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +2 -2
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +2 -2
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.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/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.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 +60 -38
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
- package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
- 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/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +194 -209
- package/docs/migration/database-changes-december-2025.md +2 -1
- package/docs/rbac/event-based-apps.md +124 -6
- package/package.json +1 -1
- package/scripts/check-pace-core-compliance.cjs +292 -57
- package/src/__tests__/rls-policies.test.ts +3 -1
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
- package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +75 -11
- package/src/components/DataTable/components/UnifiedTableBody.tsx +85 -14
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
- package/src/components/FileDisplay/FileDisplay.test.tsx +2 -1
- package/src/components/FileDisplay/FileDisplay.tsx +16 -4
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
- package/src/components/NavigationMenu/NavigationMenu.tsx +1 -10
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +25 -2
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +97 -68
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +0 -7
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
- package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +14 -7
- package/src/hooks/services/useAuthService.ts +21 -3
- package/src/hooks/services/useEventService.ts +21 -3
- package/src/hooks/services/useInactivityService.ts +21 -3
- package/src/hooks/services/useOrganisationService.ts +21 -3
- package/src/hooks/useFileDisplay.ts +10 -17
- package/src/hooks/useSecureDataAccess.test.ts +16 -9
- package/src/hooks/useSecureDataAccess.ts +3 -2
- package/src/providers/services/EventServiceProvider.tsx +0 -8
- package/src/providers/services/UnifiedAuthProvider.tsx +174 -24
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +10 -16
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
- package/src/rbac/adapters.tsx +3 -22
- package/src/rbac/api.test.ts +2 -2
- package/src/rbac/api.ts +7 -1
- package/src/rbac/components/EnhancedNavigationMenu.tsx +2 -15
- package/src/rbac/components/NavigationGuard.tsx +1 -10
- package/src/rbac/components/NavigationProvider.tsx +0 -1
- package/src/rbac/components/PermissionEnforcer.tsx +45 -12
- package/src/rbac/components/SecureDataProvider.tsx +0 -1
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
- package/src/rbac/engine.ts +14 -2
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/usePermissions.ts +51 -11
- package/src/rbac/hooks/useRBAC.ts +3 -13
- package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
- package/src/rbac/hooks/useResolvedScope.ts +58 -33
- package/src/rbac/hooks/useSecureSupabase.ts +4 -9
- package/src/rbac/secureClient.ts +31 -0
- package/src/services/EventService.ts +4 -57
- package/src/services/InactivityService.ts +127 -34
- package/src/services/OrganisationService.ts +68 -10
- package/dist/chunk-6LTQQAT6.js.map +0 -1
- package/dist/chunk-6TQDD426.js.map +0 -1
- package/dist/chunk-LOMZXPSN.js.map +0 -1
- package/dist/chunk-OETXORNB.js.map +0 -1
- package/dist/chunk-VKB2CO4Z.js.map +0 -1
- package/dist/chunk-VRGWKHDB.js.map +0 -1
- package/dist/chunk-XNYQOL3Z.js.map +0 -1
- package/dist/chunk-XYXSXPUK.js.map +0 -1
- package/scripts/check-pace-core-compliance.js +0 -512
- package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
- package/src/utils/context/superAdminOverride.ts +0 -58
- /package/dist/{DataTable-WKRZD47S.js.map → DataTable-5FU7IESH.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-FTSG5XH7.js.map → UnifiedAuthProvider-RGJTDE2C.js.map} +0 -0
- /package/dist/{api-IHKALJZD.js.map → api-N774RPUA.js.map} +0 -0
|
@@ -144,49 +144,74 @@ export function useResolvedScope({
|
|
|
144
144
|
let appConfig: AppConfig | null = null;
|
|
145
145
|
|
|
146
146
|
// Try to resolve app config from database (with caching)
|
|
147
|
+
// Only query if user is authenticated (RLS policies require authentication)
|
|
147
148
|
if (supabase && appName) {
|
|
148
149
|
try {
|
|
149
|
-
// Check
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
// Check if user is authenticated before querying (RLS requires auth)
|
|
151
|
+
// HTTP 406 errors are expected when not authenticated, so we skip the query
|
|
152
|
+
const { data: session } = await supabase.auth.getSession();
|
|
153
|
+
if (!session?.session) {
|
|
154
|
+
// User not authenticated - skip app resolution, will retry after login
|
|
155
|
+
// This is expected on login pages, so don't log as error
|
|
156
|
+
log.debug(`Skipping app resolution for "${appName}" - user not authenticated`);
|
|
155
157
|
} else {
|
|
156
|
-
//
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
.
|
|
161
|
-
.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// Check if app exists but is inactive
|
|
166
|
-
const { data: inactiveApp } = await supabase
|
|
158
|
+
// Check cache first
|
|
159
|
+
const cached = appConfigCache.get(appName);
|
|
160
|
+
const now = Date.now();
|
|
161
|
+
if (cached && (now - cached.timestamp) < CACHE_TTL) {
|
|
162
|
+
appId = cached.appId;
|
|
163
|
+
appConfig = cached.appConfig;
|
|
164
|
+
} else {
|
|
165
|
+
// Cache miss or expired - fetch from database
|
|
166
|
+
const { data: app, error } = await supabase
|
|
167
167
|
.from('rbac_apps')
|
|
168
|
-
.select('id, name, is_active')
|
|
168
|
+
.select('id, name, requires_event, is_active')
|
|
169
169
|
.eq('name', appName)
|
|
170
|
-
.
|
|
170
|
+
.eq('is_active', true)
|
|
171
|
+
.single() as { data: { id: string; name: string; requires_event: boolean; is_active: boolean } | null; error: any };
|
|
171
172
|
|
|
172
|
-
if (
|
|
173
|
-
|
|
174
|
-
// Don't
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
173
|
+
if (error) {
|
|
174
|
+
// HTTP 406 is expected when not authenticated (RLS blocks query)
|
|
175
|
+
// Don't log as error if it's a 406 - this is expected behavior
|
|
176
|
+
if (error.code === '406' || error.code === 'PGRST116' || error.message?.includes('406')) {
|
|
177
|
+
log.debug(`App resolution blocked by RLS for "${appName}" - user may not be authenticated`);
|
|
178
|
+
// Don't cache - will retry after authentication
|
|
179
|
+
appId = undefined;
|
|
180
|
+
} else {
|
|
181
|
+
// Check if app exists but is inactive
|
|
182
|
+
const { data: inactiveApp } = await supabase
|
|
183
|
+
.from('rbac_apps')
|
|
184
|
+
.select('id, name, is_active')
|
|
185
|
+
.eq('name', appName)
|
|
186
|
+
.single() as { data: { id: string; name: string; is_active: boolean } | null };
|
|
187
|
+
|
|
188
|
+
if (inactiveApp) {
|
|
189
|
+
log.error(`App "${appName}" exists but is inactive (is_active: ${inactiveApp.is_active})`);
|
|
190
|
+
// Don't cache inactive apps - set appId to undefined
|
|
191
|
+
appId = undefined;
|
|
192
|
+
} else {
|
|
193
|
+
log.error(`App "${appName}" not found in rbac_apps table`, { error });
|
|
194
|
+
// Don't cache missing apps - set appId to undefined
|
|
195
|
+
appId = undefined;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} else if (app) {
|
|
199
|
+
appId = app.id;
|
|
200
|
+
appConfig = { requires_event: app.requires_event ?? false };
|
|
201
|
+
// Only cache successful lookups of active apps
|
|
202
|
+
appConfigCache.set(appName, { appId, appConfig, timestamp: now });
|
|
180
203
|
}
|
|
181
|
-
} else if (app) {
|
|
182
|
-
appId = app.id;
|
|
183
|
-
appConfig = { requires_event: app.requires_event ?? false };
|
|
184
|
-
// Only cache successful lookups of active apps
|
|
185
|
-
appConfigCache.set(appName, { appId, appConfig, timestamp: now });
|
|
186
204
|
}
|
|
187
205
|
}
|
|
188
206
|
} catch (error) {
|
|
189
|
-
|
|
207
|
+
// Handle network errors or other unexpected errors gracefully
|
|
208
|
+
// Don't log 406 errors as they're expected when not authenticated
|
|
209
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
210
|
+
if (!errorMessage.includes('406') && !errorMessage.includes('PGRST116')) {
|
|
211
|
+
log.error('Unexpected error resolving app config:', error);
|
|
212
|
+
} else {
|
|
213
|
+
log.debug('App resolution skipped - authentication required');
|
|
214
|
+
}
|
|
190
215
|
}
|
|
191
216
|
}
|
|
192
217
|
|
|
@@ -120,7 +120,7 @@ import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
|
|
|
120
120
|
import { useOrganisations } from '../../hooks/useOrganisations';
|
|
121
121
|
import { useEvents } from '../../hooks/useEvents';
|
|
122
122
|
import { useResolvedScope } from './useResolvedScope';
|
|
123
|
-
import {
|
|
123
|
+
import { useOrganisationSecurity } from '../../hooks/useOrganisationSecurity';
|
|
124
124
|
import { createSecureClient, SecureSupabaseClient } from '../secureClient';
|
|
125
125
|
import type { Database } from '../../types/database';
|
|
126
126
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
@@ -215,14 +215,9 @@ export function useSecureSupabase(
|
|
|
215
215
|
const eventLoading = 'eventLoading' in eventsContext ? eventsContext.eventLoading : false;
|
|
216
216
|
|
|
217
217
|
// Check super admin status for conditional filtering
|
|
218
|
-
// Use
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const { isSuperAdmin: verifiedIsSuperAdmin, isLoading: isVerifyingSuperAdmin } = useSuperAdminBypass();
|
|
222
|
-
const metadataHint = Boolean(user?.app_metadata?.is_super_admin) || Boolean(user?.user_metadata?.is_super_admin);
|
|
223
|
-
// If verified as super admin, use that. If verification in progress, use metadata hint optimistically.
|
|
224
|
-
// Once verification completes and user is not super admin, verifiedIsSuperAdmin will be false.
|
|
225
|
-
const isSuperAdmin = verifiedIsSuperAdmin || (isVerifyingSuperAdmin && metadataHint);
|
|
218
|
+
// Use verified status from useOrganisationSecurity which checks the database
|
|
219
|
+
const { superAdminContext } = useOrganisationSecurity();
|
|
220
|
+
const isSuperAdmin = superAdminContext.isSuperAdmin;
|
|
226
221
|
|
|
227
222
|
// Resolve scope to get appId
|
|
228
223
|
const { resolvedScope } = useResolvedScope({
|
package/src/rbac/secureClient.ts
CHANGED
|
@@ -152,6 +152,19 @@ export class SecureSupabaseClient {
|
|
|
152
152
|
|
|
153
153
|
// Override insert to add organisation context
|
|
154
154
|
query.insert = (values: any) => {
|
|
155
|
+
// Tables that don't have organisation_id column
|
|
156
|
+
const tablesWithoutOrganisationId = [
|
|
157
|
+
'core_organisations', // Organisation table itself - uses 'id' as primary key
|
|
158
|
+
'rbac_apps', // App configuration table - no organisation scope
|
|
159
|
+
'rbac_app_pages', // Page configuration table - scoped by app_id, not organisation_id
|
|
160
|
+
'rbac_global_roles', // Global roles - no organisation scope
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
// Skip adding organisation_id for tables that don't have it
|
|
164
|
+
if (tablesWithoutOrganisationId.includes(tableName)) {
|
|
165
|
+
return originalInsert(values);
|
|
166
|
+
}
|
|
167
|
+
|
|
155
168
|
// For rbac_user_profiles, only add organisation_id if not super admin
|
|
156
169
|
// Super admins can create users in any org, non-super-admins are restricted
|
|
157
170
|
if (tableName === 'rbac_user_profiles') {
|
|
@@ -204,6 +217,24 @@ export class SecureSupabaseClient {
|
|
|
204
217
|
* - Always apply org filter unless super admin bypasses it
|
|
205
218
|
*/
|
|
206
219
|
private addOrganisationFilter(query: any, tableName: string) {
|
|
220
|
+
// Tables that don't have organisation_id column - RLS policies handle access control
|
|
221
|
+
const tablesWithoutOrganisationId = [
|
|
222
|
+
'core_organisations', // Organisation table itself - uses 'id' as primary key
|
|
223
|
+
'rbac_apps', // App configuration table - no organisation scope
|
|
224
|
+
'rbac_app_pages', // Page configuration table - scoped by app_id, not organisation_id
|
|
225
|
+
'rbac_global_roles', // Global roles - no organisation scope
|
|
226
|
+
];
|
|
227
|
+
|
|
228
|
+
// Skip organisation filter for tables that don't have organisation_id column
|
|
229
|
+
if (tablesWithoutOrganisationId.includes(tableName)) {
|
|
230
|
+
return query; // RLS policies handle access control for these tables
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// If organisation context is not set, don't add a filter (e.g., super admin without selected org)
|
|
234
|
+
if (!this.organisationId) {
|
|
235
|
+
return query;
|
|
236
|
+
}
|
|
237
|
+
|
|
207
238
|
// For rbac_user_profiles, use conditional filtering based on super admin status
|
|
208
239
|
if (tableName === 'rbac_user_profiles') {
|
|
209
240
|
// Super admins: No org filter (see all users via RLS)
|
|
@@ -116,10 +116,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
116
116
|
if (user?.id) {
|
|
117
117
|
try {
|
|
118
118
|
this.isSuperAdmin = await isSuperAdmin(user.id as UUID);
|
|
119
|
-
logger.debug('EventService', 'Updated super admin status', {
|
|
120
|
-
userId: user.id,
|
|
121
|
-
isSuperAdmin: this.isSuperAdmin
|
|
122
|
-
});
|
|
123
119
|
} catch (error) {
|
|
124
120
|
logger.warn('EventService', 'Failed to check super admin status', { error });
|
|
125
121
|
this.isSuperAdmin = false; // Default to false on error
|
|
@@ -151,9 +147,10 @@ export class EventService extends BaseService implements IEventService {
|
|
|
151
147
|
|
|
152
148
|
// Event state getters
|
|
153
149
|
getEvents(): Event[] {
|
|
154
|
-
// Return
|
|
155
|
-
// This
|
|
156
|
-
|
|
150
|
+
// Return stable array reference - only create new array when events actually change
|
|
151
|
+
// This prevents unnecessary re-renders when getEvents() is called on every render
|
|
152
|
+
// The service's notify() mechanism handles change detection
|
|
153
|
+
return this.events;
|
|
157
154
|
}
|
|
158
155
|
|
|
159
156
|
getSelectedEvent(): Event | null {
|
|
@@ -308,12 +305,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
308
305
|
async initialize(): Promise<void> {
|
|
309
306
|
// Only call super.initialize() which will call doInitialize() and fetchEvents()
|
|
310
307
|
// Don't call fetchEvents() again here to avoid double-fetching
|
|
311
|
-
logger.debug('EventService', 'initialize() called', {
|
|
312
|
-
isInitializedRef: this.isInitializedRef,
|
|
313
|
-
hasUser: !!this.user,
|
|
314
|
-
hasSession: !!this.session,
|
|
315
|
-
appName: this.appName
|
|
316
|
-
});
|
|
317
308
|
await super.initialize();
|
|
318
309
|
}
|
|
319
310
|
|
|
@@ -322,23 +313,13 @@ export class EventService extends BaseService implements IEventService {
|
|
|
322
313
|
}
|
|
323
314
|
|
|
324
315
|
protected async doInitialize(): Promise<void> {
|
|
325
|
-
logger.debug('EventService', 'doInitialize() called', {
|
|
326
|
-
isInitializedRef: this.isInitializedRef,
|
|
327
|
-
isFetchingRef: this.isFetchingRef,
|
|
328
|
-
hasUser: !!this.user,
|
|
329
|
-
hasSession: !!this.session,
|
|
330
|
-
appName: this.appName
|
|
331
|
-
});
|
|
332
|
-
|
|
333
316
|
// Skip if already initialized
|
|
334
317
|
if (this.isInitializedRef) {
|
|
335
|
-
logger.debug('EventService', 'Skipping initialization - already initialized');
|
|
336
318
|
return;
|
|
337
319
|
}
|
|
338
320
|
|
|
339
321
|
// Skip if already fetching
|
|
340
322
|
if (this.isFetchingRef) {
|
|
341
|
-
logger.debug('EventService', 'Skipping initialization - already fetching');
|
|
342
323
|
return;
|
|
343
324
|
}
|
|
344
325
|
|
|
@@ -355,16 +336,9 @@ export class EventService extends BaseService implements IEventService {
|
|
|
355
336
|
// For event-required apps, selectedOrganisation may be null (org derived from event)
|
|
356
337
|
// For org-required apps, selectedOrganisation is required
|
|
357
338
|
if (!this.user) {
|
|
358
|
-
logger.debug('EventService', 'Skipping initialization - missing user');
|
|
359
339
|
return;
|
|
360
340
|
}
|
|
361
341
|
|
|
362
|
-
logger.debug('EventService', 'Initializing - fetching events', {
|
|
363
|
-
userId: this.user.id,
|
|
364
|
-
organisationId: this.selectedOrganisation?.id || 'derived-from-event',
|
|
365
|
-
appName: this.appName
|
|
366
|
-
});
|
|
367
|
-
|
|
368
342
|
// Initial setup - fetch events on initialization
|
|
369
343
|
await this.fetchEvents(false);
|
|
370
344
|
|
|
@@ -380,12 +354,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
380
354
|
// For event-required apps, selectedOrganisation may be null (org derived from event)
|
|
381
355
|
// For org-required apps, selectedOrganisation is required
|
|
382
356
|
if (!this.user || !this.session || !this.supabaseClient || !this.appName) {
|
|
383
|
-
logger.debug('EventService', 'Skipping fetchEvents - missing dependencies', {
|
|
384
|
-
hasUser: !!this.user,
|
|
385
|
-
hasSession: !!this.session,
|
|
386
|
-
hasSupabaseClient: !!this.supabaseClient,
|
|
387
|
-
appName: this.appName
|
|
388
|
-
});
|
|
389
357
|
// Already false from initialization, just notify
|
|
390
358
|
this.notify();
|
|
391
359
|
return;
|
|
@@ -397,7 +365,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
397
365
|
|
|
398
366
|
// Prevent multiple simultaneous fetches
|
|
399
367
|
if (this.isFetchingRef) {
|
|
400
|
-
logger.debug('EventService', 'Skipping fetchEvents - already fetching');
|
|
401
368
|
return;
|
|
402
369
|
}
|
|
403
370
|
|
|
@@ -431,9 +398,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
431
398
|
if (userIsSuperAdmin) {
|
|
432
399
|
// Super admin: Pass null to see all events across all organisations
|
|
433
400
|
organisationIdForRpc = null;
|
|
434
|
-
logger.debug('EventService', 'Super admin detected - fetching all events', {
|
|
435
|
-
userId: this.user.id
|
|
436
|
-
});
|
|
437
401
|
} else {
|
|
438
402
|
// Not super admin: determine org from context based on app type
|
|
439
403
|
if (this.selectedEvent) {
|
|
@@ -443,10 +407,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
443
407
|
// Event-required app with no selected event yet: pass null to get all accessible events
|
|
444
408
|
// The RPC will filter by event app roles, returning all events the user has access to
|
|
445
409
|
organisationIdForRpc = null;
|
|
446
|
-
logger.debug('EventService', 'Event-required app: fetching all accessible events (no event selected yet)', {
|
|
447
|
-
userId: this.user.id,
|
|
448
|
-
appName: this.appName
|
|
449
|
-
});
|
|
450
410
|
} else if (this.selectedOrganisation) {
|
|
451
411
|
// Org-required app: use selected organisation
|
|
452
412
|
organisationIdForRpc = this.selectedOrganisation.id;
|
|
@@ -476,12 +436,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
476
436
|
}
|
|
477
437
|
}
|
|
478
438
|
|
|
479
|
-
logger.debug('EventService', 'Fetching events via RPC', {
|
|
480
|
-
userId: this.user.id,
|
|
481
|
-
organisationId: organisationIdForRpc,
|
|
482
|
-
appName: this.appName
|
|
483
|
-
});
|
|
484
|
-
|
|
485
439
|
// Call the RPC function following the established pattern
|
|
486
440
|
// For super admins, pass null for p_organisation_id to see all events
|
|
487
441
|
let { data, error: rpcError } = await this.supabaseClient.rpc('data_user_events_get', {
|
|
@@ -490,13 +444,6 @@ export class EventService extends BaseService implements IEventService {
|
|
|
490
444
|
p_app_name: this.appName
|
|
491
445
|
});
|
|
492
446
|
|
|
493
|
-
logger.debug('EventService', 'RPC response received', {
|
|
494
|
-
hasData: !!data,
|
|
495
|
-
dataLength: Array.isArray(data) ? data.length : 'not array',
|
|
496
|
-
hasError: !!rpcError,
|
|
497
|
-
error: rpcError
|
|
498
|
-
});
|
|
499
|
-
|
|
500
447
|
if (rpcError) {
|
|
501
448
|
logger.error('EventService', 'RPC error fetching events:', rpcError);
|
|
502
449
|
throw new Error(rpcError.message || 'Failed to fetch events');
|
|
@@ -145,12 +145,31 @@ export class InactivityService extends BaseService implements IInactivityService
|
|
|
145
145
|
if (this.inactivityTracker) {
|
|
146
146
|
this.inactivityTracker.resetActivity();
|
|
147
147
|
}
|
|
148
|
+
|
|
149
|
+
// Store previous values
|
|
150
|
+
const prevIsIdle = this._isIdle;
|
|
151
|
+
const prevShowWarning = this._showWarning;
|
|
152
|
+
const prevShowInactivityWarning = this._showInactivityWarning;
|
|
153
|
+
const prevInactivityTimeRemaining = this._inactivityTimeRemaining;
|
|
154
|
+
const prevTimeRemaining = this._timeRemaining;
|
|
155
|
+
|
|
156
|
+
// Update state
|
|
148
157
|
this._isIdle = false;
|
|
149
158
|
this._showWarning = false;
|
|
150
159
|
this._showInactivityWarning = false;
|
|
151
160
|
this._inactivityTimeRemaining = 0;
|
|
152
161
|
this._timeRemaining = this.idleTimeoutMs;
|
|
153
|
-
|
|
162
|
+
|
|
163
|
+
// Only notify if values actually changed
|
|
164
|
+
if (
|
|
165
|
+
prevIsIdle !== this._isIdle ||
|
|
166
|
+
prevShowWarning !== this._showWarning ||
|
|
167
|
+
prevShowInactivityWarning !== this._showInactivityWarning ||
|
|
168
|
+
prevInactivityTimeRemaining !== this._inactivityTimeRemaining ||
|
|
169
|
+
prevTimeRemaining !== this._timeRemaining
|
|
170
|
+
) {
|
|
171
|
+
this.notify();
|
|
172
|
+
}
|
|
154
173
|
}
|
|
155
174
|
|
|
156
175
|
startTracking(): void {
|
|
@@ -275,10 +294,63 @@ export class InactivityService extends BaseService implements IInactivityService
|
|
|
275
294
|
let idleTimer: NodeJS.Timeout | null = null;
|
|
276
295
|
let warningTimer: NodeJS.Timeout | null = null;
|
|
277
296
|
let lastActivity = Date.now();
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
297
|
+
let pollInterval: NodeJS.Timeout | null = null;
|
|
298
|
+
|
|
299
|
+
// Store previous state for comparison
|
|
300
|
+
let prevIsIdle = false;
|
|
301
|
+
let prevShowWarning = false;
|
|
302
|
+
let prevShowInactivityWarning = false;
|
|
303
|
+
let prevInactivityTimeRemaining = 0;
|
|
304
|
+
let prevTimeRemaining = this.idleTimeoutMs;
|
|
305
|
+
|
|
306
|
+
// Poll every 10 seconds to check for state changes
|
|
307
|
+
const pollInactivityState = () => {
|
|
308
|
+
const now = Date.now();
|
|
309
|
+
const timeSinceActivity = now - lastActivity;
|
|
310
|
+
const timeUntilIdle = this.idleTimeoutMs - timeSinceActivity;
|
|
311
|
+
const timeUntilWarning = (this.idleTimeoutMs - this.warnBeforeMs) - timeSinceActivity;
|
|
312
|
+
|
|
313
|
+
// Calculate new state values based on time since last activity
|
|
314
|
+
const newIsIdle = timeSinceActivity >= (this.idleTimeoutMs - this.warnBeforeMs);
|
|
315
|
+
const newShowWarning = newIsIdle && timeSinceActivity < this.idleTimeoutMs;
|
|
316
|
+
const newShowInactivityWarning = newShowWarning;
|
|
317
|
+
const newInactivityTimeRemaining = newShowWarning
|
|
318
|
+
? Math.ceil((this.idleTimeoutMs - timeSinceActivity) / 1000)
|
|
319
|
+
: 0;
|
|
320
|
+
const newTimeRemaining = Math.max(0, timeUntilIdle);
|
|
321
|
+
|
|
322
|
+
// Check if state actually changed
|
|
323
|
+
const stateChanged =
|
|
324
|
+
prevIsIdle !== newIsIdle ||
|
|
325
|
+
prevShowWarning !== newShowWarning ||
|
|
326
|
+
prevShowInactivityWarning !== newShowInactivityWarning ||
|
|
327
|
+
prevInactivityTimeRemaining !== newInactivityTimeRemaining ||
|
|
328
|
+
prevTimeRemaining !== newTimeRemaining;
|
|
329
|
+
|
|
330
|
+
// Only update and notify if state changed
|
|
331
|
+
if (stateChanged) {
|
|
332
|
+
this._isIdle = newIsIdle;
|
|
333
|
+
this._showWarning = newShowWarning;
|
|
334
|
+
this._showInactivityWarning = newShowInactivityWarning;
|
|
335
|
+
this._inactivityTimeRemaining = newInactivityTimeRemaining;
|
|
336
|
+
this._timeRemaining = newTimeRemaining;
|
|
337
|
+
|
|
338
|
+
// Update previous state
|
|
339
|
+
prevIsIdle = newIsIdle;
|
|
340
|
+
prevShowWarning = newShowWarning;
|
|
341
|
+
prevShowInactivityWarning = newShowInactivityWarning;
|
|
342
|
+
prevInactivityTimeRemaining = newInactivityTimeRemaining;
|
|
343
|
+
prevTimeRemaining = newTimeRemaining;
|
|
344
|
+
|
|
345
|
+
this.notify();
|
|
346
|
+
|
|
347
|
+
// Handle idle logout if needed
|
|
348
|
+
if (newIsIdle && timeSinceActivity >= this.idleTimeoutMs) {
|
|
349
|
+
this.handleIdleLogout();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
281
352
|
|
|
353
|
+
// Update timers based on current state
|
|
282
354
|
if (idleTimer) {
|
|
283
355
|
clearTimeout(idleTimer);
|
|
284
356
|
idleTimer = null;
|
|
@@ -288,47 +360,58 @@ export class InactivityService extends BaseService implements IInactivityService
|
|
|
288
360
|
clearTimeout(warningTimer);
|
|
289
361
|
warningTimer = null;
|
|
290
362
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
const startIdleTimer = () => {
|
|
300
|
-
if (idleTimer) {
|
|
301
|
-
clearTimeout(idleTimer);
|
|
363
|
+
|
|
364
|
+
// Set up timers for next state transitions
|
|
365
|
+
if (!newIsIdle && timeUntilIdle > 0) {
|
|
366
|
+
idleTimer = setTimeout(() => {
|
|
367
|
+
pollInactivityState();
|
|
368
|
+
}, Math.min(10000, timeUntilIdle));
|
|
302
369
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
this._isIdle = true;
|
|
306
|
-
this._showWarning = true;
|
|
307
|
-
this.notify();
|
|
308
|
-
|
|
309
|
-
// Start warning timer
|
|
370
|
+
|
|
371
|
+
if (newShowWarning && timeUntilIdle > 0) {
|
|
310
372
|
warningTimer = setTimeout(() => {
|
|
311
373
|
this.handleIdleLogout();
|
|
312
|
-
},
|
|
313
|
-
}
|
|
374
|
+
}, timeUntilIdle);
|
|
375
|
+
}
|
|
314
376
|
};
|
|
315
377
|
|
|
316
|
-
const
|
|
378
|
+
const resetTimers = () => {
|
|
379
|
+
// Only update lastActivity - don't notify yet
|
|
380
|
+
lastActivity = Date.now();
|
|
381
|
+
|
|
382
|
+
// Clear timers
|
|
383
|
+
if (idleTimer) {
|
|
384
|
+
clearTimeout(idleTimer);
|
|
385
|
+
idleTimer = null;
|
|
386
|
+
}
|
|
387
|
+
|
|
317
388
|
if (warningTimer) {
|
|
318
389
|
clearTimeout(warningTimer);
|
|
390
|
+
warningTimer = null;
|
|
319
391
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
392
|
+
|
|
393
|
+
// Reset state values (will be checked on next poll)
|
|
394
|
+
this._showInactivityWarning = false;
|
|
395
|
+
this._inactivityTimeRemaining = 0;
|
|
396
|
+
this._isIdle = false;
|
|
397
|
+
this._showWarning = false;
|
|
398
|
+
|
|
399
|
+
// Update previous state to match
|
|
400
|
+
prevIsIdle = false;
|
|
401
|
+
prevShowWarning = false;
|
|
402
|
+
prevShowInactivityWarning = false;
|
|
403
|
+
prevInactivityTimeRemaining = 0;
|
|
404
|
+
prevTimeRemaining = this.idleTimeoutMs;
|
|
405
|
+
|
|
406
|
+
// Poll will check and notify if needed
|
|
324
407
|
};
|
|
325
408
|
|
|
326
|
-
// Activity detection
|
|
409
|
+
// Activity detection - only updates lastActivity, doesn't notify
|
|
327
410
|
const activityEvents = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];
|
|
328
411
|
|
|
329
412
|
const handleActivity = () => {
|
|
330
413
|
resetTimers();
|
|
331
|
-
|
|
414
|
+
// Don't call notify - polling will handle state updates
|
|
332
415
|
};
|
|
333
416
|
|
|
334
417
|
// Add event listeners
|
|
@@ -336,8 +419,13 @@ export class InactivityService extends BaseService implements IInactivityService
|
|
|
336
419
|
document.addEventListener(event, handleActivity, true);
|
|
337
420
|
});
|
|
338
421
|
|
|
339
|
-
// Start
|
|
340
|
-
|
|
422
|
+
// Start polling every 10 seconds
|
|
423
|
+
pollInterval = setInterval(() => {
|
|
424
|
+
pollInactivityState();
|
|
425
|
+
}, 10000); // 10 seconds
|
|
426
|
+
|
|
427
|
+
// Initial poll
|
|
428
|
+
pollInactivityState();
|
|
341
429
|
|
|
342
430
|
// Store cleanup function
|
|
343
431
|
this.cleanupHandlers = () => {
|
|
@@ -350,6 +438,11 @@ export class InactivityService extends BaseService implements IInactivityService
|
|
|
350
438
|
clearTimeout(warningTimer);
|
|
351
439
|
warningTimer = null;
|
|
352
440
|
}
|
|
441
|
+
|
|
442
|
+
if (pollInterval) {
|
|
443
|
+
clearInterval(pollInterval);
|
|
444
|
+
pollInterval = null;
|
|
445
|
+
}
|
|
353
446
|
|
|
354
447
|
activityEvents.forEach(event => {
|
|
355
448
|
document.removeEventListener(event, handleActivity, true);
|
|
@@ -364,6 +457,6 @@ export class InactivityService extends BaseService implements IInactivityService
|
|
|
364
457
|
};
|
|
365
458
|
|
|
366
459
|
this._isTracking = true;
|
|
367
|
-
this.notify();
|
|
460
|
+
this.notify(); // Initial notification only
|
|
368
461
|
}
|
|
369
462
|
}
|