@jmruthers/pace-core 0.6.2 → 0.6.4

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.
Files changed (299) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/cursor-rules/00-pace-core-compliance.mdc +34 -2
  3. package/dist/{AuthService-BPvc3Ka0.d.ts → AuthService-Cb34EQs3.d.ts} +9 -1
  4. package/dist/{DataTable-TPTKCX4D.js → DataTable-E7YQZD7D.js} +9 -8
  5. package/dist/{PublicPageProvider-DC6kCaqf.d.ts → PublicPageProvider-DEMpysFR.d.ts} +45 -67
  6. package/dist/{UnifiedAuthProvider-CVcTjx-d.d.ts → UnifiedAuthProvider-CKvHP1MK.d.ts} +1 -8
  7. package/dist/{UnifiedAuthProvider-CH6Z342H.js → UnifiedAuthProvider-QPXO24B4.js} +5 -4
  8. package/dist/{api-MVVQZLJI.js → api-6LVZTHDS.js} +10 -10
  9. package/dist/{audit-B5P6FFIR.js → audit-V53FV5AG.js} +2 -2
  10. package/dist/chunk-36LVWXB2.js +227 -0
  11. package/dist/chunk-36LVWXB2.js.map +1 -0
  12. package/dist/{chunk-24UVZUZG.js → chunk-3LPHPB62.js} +129 -387
  13. package/dist/chunk-3LPHPB62.js.map +1 -0
  14. package/dist/{chunk-2UOI2FG5.js → chunk-5EC5MEWX.js} +4 -4
  15. package/dist/{chunk-3XC4CPTD.js → chunk-7JPAB3T5.js} +244 -5727
  16. package/dist/chunk-7JPAB3T5.js.map +1 -0
  17. package/dist/{chunk-6J4GEEJR.js → chunk-ATKZM7RX.js} +53 -27
  18. package/dist/chunk-ATKZM7RX.js.map +1 -0
  19. package/dist/{chunk-EHMR7VYL.js → chunk-AVMLPIM7.js} +443 -189
  20. package/dist/chunk-AVMLPIM7.js.map +1 -0
  21. package/dist/chunk-DGUM43GV.js +11 -0
  22. package/dist/{chunk-NECFR5MM.js → chunk-I6DAQMWX.js} +575 -647
  23. package/dist/chunk-I6DAQMWX.js.map +1 -0
  24. package/dist/{chunk-F2IMUDXZ.js → chunk-M7MPQISP.js} +2 -2
  25. package/dist/{chunk-XWQCNGTQ.js → chunk-NN6WWZ5U.js} +173 -79
  26. package/dist/chunk-NN6WWZ5U.js.map +1 -0
  27. package/dist/{chunk-MMZ7JXPU.js → chunk-OEWDTMG7.js} +13 -21
  28. package/dist/{chunk-MMZ7JXPU.js.map → chunk-OEWDTMG7.js.map} +1 -1
  29. package/dist/{chunk-SFZUDBL5.js → chunk-YKRAFF5K.js} +70 -56
  30. package/dist/chunk-YKRAFF5K.js.map +1 -0
  31. package/dist/components.d.ts +2 -2
  32. package/dist/components.js +12 -13
  33. package/dist/contextValidator-OOPCLPZW.js +9 -0
  34. package/dist/contextValidator-OOPCLPZW.js.map +1 -0
  35. package/dist/eslint-rules/pace-core-compliance.cjs +106 -0
  36. package/dist/hooks.d.ts +2 -2
  37. package/dist/hooks.js +7 -6
  38. package/dist/hooks.js.map +1 -1
  39. package/dist/index.d.ts +7 -7
  40. package/dist/index.js +21 -16
  41. package/dist/index.js.map +1 -1
  42. package/dist/providers.d.ts +3 -3
  43. package/dist/providers.js +4 -3
  44. package/dist/rbac/index.d.ts +67 -27
  45. package/dist/rbac/index.js +15 -8
  46. package/dist/styles/index.js +1 -1
  47. package/dist/theming/runtime.js +1 -1
  48. package/dist/types.js +1 -1
  49. package/dist/{usePublicRouteParams-1oMokgLF.d.ts → usePublicRouteParams-i3qtoBgg.d.ts} +7 -16
  50. package/dist/utils.js +5 -7
  51. package/dist/utils.js.map +1 -1
  52. package/docs/api/README.md +14 -16
  53. package/docs/api/modules.md +3796 -2513
  54. package/docs/components/context-selector.md +126 -0
  55. package/docs/migration/RBAC_SCOPE_MIGRATION.md +385 -0
  56. package/docs/pace-mint-fix-auto-selection.md +218 -0
  57. package/docs/pace-mint-rbac-setup.md +391 -0
  58. package/docs/rbac/secure-client-protection.md +330 -0
  59. package/package.json +10 -5
  60. package/scripts/audit/core/checks/compliance.cjs +72 -0
  61. package/scripts/audit/core/checks/dependencies.cjs +568 -28
  62. package/scripts/audit/core/checks/documentation.cjs +68 -3
  63. package/scripts/audit/core/checks/environment.cjs +2 -14
  64. package/scripts/audit/core/checks/error-handling.cjs +47 -6
  65. package/src/components/ContextSelector/ContextSelector.tsx +384 -0
  66. package/src/components/ContextSelector/index.ts +3 -0
  67. package/src/components/DataTable/components/RowComponent.tsx +19 -19
  68. package/src/components/DataTable/components/UnifiedTableBody.tsx +2 -2
  69. package/src/components/DataTable/hooks/useDataTablePermissions.ts +8 -6
  70. package/src/components/Dialog/Dialog.tsx +29 -1
  71. package/src/components/FileDisplay/FileDisplay.tsx +42 -10
  72. package/src/components/Header/Header.test.tsx +43 -73
  73. package/src/components/Header/Header.tsx +44 -45
  74. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +10 -19
  75. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +2 -2
  76. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +5 -5
  77. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +9 -9
  78. package/src/components/PaceAppLayout/PaceAppLayout.tsx +157 -36
  79. package/src/components/PaceAppLayout/README.md +14 -17
  80. package/src/components/PaceAppLayout/test-setup.tsx +2 -2
  81. package/src/components/index.ts +5 -5
  82. package/src/eslint-rules/pace-core-compliance.cjs +106 -0
  83. package/src/hooks/__tests__/useAppConfig.unit.test.ts +4 -98
  84. package/src/hooks/useAppConfig.ts +15 -30
  85. package/src/hooks/useFileDisplay.ts +77 -50
  86. package/src/index.ts +4 -5
  87. package/src/providers/services/AuthServiceProvider.tsx +17 -7
  88. package/src/providers/services/EventServiceProvider.tsx +33 -5
  89. package/src/providers/services/UnifiedAuthProvider.tsx +90 -134
  90. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +1 -1
  91. package/src/rbac/adapters.tsx +2 -2
  92. package/src/rbac/api.test.ts +59 -51
  93. package/src/rbac/api.ts +178 -132
  94. package/src/rbac/components/PagePermissionGuard.tsx +38 -10
  95. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +32 -21
  96. package/src/rbac/hooks/permissions/useAccessLevel.ts +1 -1
  97. package/src/rbac/hooks/permissions/useCan.ts +41 -11
  98. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +1 -1
  99. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +1 -1
  100. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +1 -1
  101. package/src/rbac/hooks/useCan.test.ts +0 -9
  102. package/src/rbac/hooks/useRBAC.test.ts +1 -5
  103. package/src/rbac/hooks/useRBAC.ts +36 -37
  104. package/src/rbac/hooks/useResolvedScope.test.ts +120 -35
  105. package/src/rbac/hooks/useResolvedScope.ts +35 -40
  106. package/src/rbac/hooks/useSecureSupabase.ts +7 -7
  107. package/src/rbac/index.ts +7 -0
  108. package/src/rbac/secureClient.test.ts +22 -18
  109. package/src/rbac/secureClient.ts +103 -16
  110. package/src/rbac/security.ts +0 -17
  111. package/src/rbac/types.ts +1 -0
  112. package/src/rbac/utils/__tests__/contextValidator.test.ts +64 -86
  113. package/src/rbac/utils/clientSecurity.ts +93 -0
  114. package/src/rbac/utils/contextValidator.ts +77 -168
  115. package/src/services/AuthService.ts +39 -7
  116. package/src/services/EventService.ts +285 -56
  117. package/src/services/OrganisationService.ts +81 -14
  118. package/src/services/__tests__/EventService.test.ts +1 -2
  119. package/src/services/base/BaseService.ts +3 -0
  120. package/src/utils/dynamic/dynamicUtils.ts +7 -4
  121. package/dist/chunk-24UVZUZG.js.map +0 -1
  122. package/dist/chunk-3XC4CPTD.js.map +0 -1
  123. package/dist/chunk-6J4GEEJR.js.map +0 -1
  124. package/dist/chunk-7D4SUZUM.js +0 -38
  125. package/dist/chunk-EHMR7VYL.js.map +0 -1
  126. package/dist/chunk-NECFR5MM.js.map +0 -1
  127. package/dist/chunk-SFZUDBL5.js.map +0 -1
  128. package/dist/chunk-XWQCNGTQ.js.map +0 -1
  129. package/docs/api/classes/ColumnFactory.md +0 -243
  130. package/docs/api/classes/InvalidScopeError.md +0 -73
  131. package/docs/api/classes/Logger.md +0 -178
  132. package/docs/api/classes/MissingUserContextError.md +0 -66
  133. package/docs/api/classes/OrganisationContextRequiredError.md +0 -66
  134. package/docs/api/classes/PermissionDeniedError.md +0 -73
  135. package/docs/api/classes/RBACAuditManager.md +0 -297
  136. package/docs/api/classes/RBACCache.md +0 -322
  137. package/docs/api/classes/RBACEngine.md +0 -171
  138. package/docs/api/classes/RBACError.md +0 -76
  139. package/docs/api/classes/RBACNotInitializedError.md +0 -66
  140. package/docs/api/classes/SecureSupabaseClient.md +0 -163
  141. package/docs/api/classes/StorageUtils.md +0 -328
  142. package/docs/api/enums/FileCategory.md +0 -184
  143. package/docs/api/enums/LogLevel.md +0 -54
  144. package/docs/api/enums/RBACErrorCode.md +0 -228
  145. package/docs/api/enums/RPCFunction.md +0 -118
  146. package/docs/api/interfaces/AddressFieldProps.md +0 -241
  147. package/docs/api/interfaces/AddressFieldRef.md +0 -94
  148. package/docs/api/interfaces/AggregateConfig.md +0 -43
  149. package/docs/api/interfaces/AutocompleteOptions.md +0 -75
  150. package/docs/api/interfaces/AvatarProps.md +0 -128
  151. package/docs/api/interfaces/BadgeProps.md +0 -34
  152. package/docs/api/interfaces/ButtonProps.md +0 -56
  153. package/docs/api/interfaces/CalendarProps.md +0 -73
  154. package/docs/api/interfaces/CardProps.md +0 -69
  155. package/docs/api/interfaces/ColorPalette.md +0 -7
  156. package/docs/api/interfaces/ColorShade.md +0 -66
  157. package/docs/api/interfaces/ComplianceResult.md +0 -30
  158. package/docs/api/interfaces/DataAccessRecord.md +0 -96
  159. package/docs/api/interfaces/DataRecord.md +0 -11
  160. package/docs/api/interfaces/DataTableAction.md +0 -252
  161. package/docs/api/interfaces/DataTableColumn.md +0 -504
  162. package/docs/api/interfaces/DataTableProps.md +0 -625
  163. package/docs/api/interfaces/DataTableToolbarButton.md +0 -96
  164. package/docs/api/interfaces/DatabaseComplianceResult.md +0 -85
  165. package/docs/api/interfaces/DatabaseIssue.md +0 -41
  166. package/docs/api/interfaces/EmptyStateConfig.md +0 -61
  167. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +0 -235
  168. package/docs/api/interfaces/ErrorBoundaryProps.md +0 -147
  169. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +0 -36
  170. package/docs/api/interfaces/ErrorBoundaryState.md +0 -75
  171. package/docs/api/interfaces/EventAppRoleData.md +0 -71
  172. package/docs/api/interfaces/ExportColumn.md +0 -90
  173. package/docs/api/interfaces/ExportOptions.md +0 -126
  174. package/docs/api/interfaces/FileDisplayProps.md +0 -249
  175. package/docs/api/interfaces/FileMetadata.md +0 -129
  176. package/docs/api/interfaces/FileReference.md +0 -118
  177. package/docs/api/interfaces/FileSizeLimits.md +0 -7
  178. package/docs/api/interfaces/FileUploadOptions.md +0 -139
  179. package/docs/api/interfaces/FileUploadProps.md +0 -296
  180. package/docs/api/interfaces/FooterProps.md +0 -107
  181. package/docs/api/interfaces/FormFieldProps.md +0 -166
  182. package/docs/api/interfaces/FormProps.md +0 -113
  183. package/docs/api/interfaces/GrantEventAppRoleParams.md +0 -122
  184. package/docs/api/interfaces/InactivityWarningModalProps.md +0 -115
  185. package/docs/api/interfaces/InputProps.md +0 -56
  186. package/docs/api/interfaces/LabelProps.md +0 -107
  187. package/docs/api/interfaces/LoggerConfig.md +0 -62
  188. package/docs/api/interfaces/LoginFormProps.md +0 -187
  189. package/docs/api/interfaces/NavigationAccessRecord.md +0 -107
  190. package/docs/api/interfaces/NavigationContextType.md +0 -164
  191. package/docs/api/interfaces/NavigationGuardProps.md +0 -139
  192. package/docs/api/interfaces/NavigationItem.md +0 -120
  193. package/docs/api/interfaces/NavigationMenuProps.md +0 -221
  194. package/docs/api/interfaces/NavigationProviderProps.md +0 -117
  195. package/docs/api/interfaces/Organisation.md +0 -140
  196. package/docs/api/interfaces/OrganisationContextType.md +0 -388
  197. package/docs/api/interfaces/OrganisationMembership.md +0 -140
  198. package/docs/api/interfaces/OrganisationProviderProps.md +0 -76
  199. package/docs/api/interfaces/OrganisationSecurityError.md +0 -62
  200. package/docs/api/interfaces/PaceAppLayoutProps.md +0 -409
  201. package/docs/api/interfaces/PaceLoginPageProps.md +0 -49
  202. package/docs/api/interfaces/PageAccessRecord.md +0 -85
  203. package/docs/api/interfaces/PagePermissionContextType.md +0 -140
  204. package/docs/api/interfaces/PagePermissionGuardProps.md +0 -153
  205. package/docs/api/interfaces/PagePermissionProviderProps.md +0 -119
  206. package/docs/api/interfaces/PaletteData.md +0 -41
  207. package/docs/api/interfaces/ParsedAddress.md +0 -120
  208. package/docs/api/interfaces/PermissionEnforcerProps.md +0 -153
  209. package/docs/api/interfaces/ProgressProps.md +0 -42
  210. package/docs/api/interfaces/ProtectedRouteProps.md +0 -78
  211. package/docs/api/interfaces/PublicPageFooterProps.md +0 -112
  212. package/docs/api/interfaces/PublicPageHeaderProps.md +0 -125
  213. package/docs/api/interfaces/PublicPageLayoutProps.md +0 -185
  214. package/docs/api/interfaces/QuickFix.md +0 -52
  215. package/docs/api/interfaces/RBACAccessValidateParams.md +0 -52
  216. package/docs/api/interfaces/RBACAccessValidateResult.md +0 -41
  217. package/docs/api/interfaces/RBACAuditLogParams.md +0 -85
  218. package/docs/api/interfaces/RBACAuditLogResult.md +0 -52
  219. package/docs/api/interfaces/RBACConfig.md +0 -133
  220. package/docs/api/interfaces/RBACContext.md +0 -52
  221. package/docs/api/interfaces/RBACLogger.md +0 -112
  222. package/docs/api/interfaces/RBACPageAccessCheckParams.md +0 -74
  223. package/docs/api/interfaces/RBACPerformanceMetrics.md +0 -138
  224. package/docs/api/interfaces/RBACPermissionCheckParams.md +0 -74
  225. package/docs/api/interfaces/RBACPermissionCheckResult.md +0 -52
  226. package/docs/api/interfaces/RBACPermissionsGetParams.md +0 -63
  227. package/docs/api/interfaces/RBACPermissionsGetResult.md +0 -63
  228. package/docs/api/interfaces/RBACResult.md +0 -58
  229. package/docs/api/interfaces/RBACRoleGrantParams.md +0 -63
  230. package/docs/api/interfaces/RBACRoleGrantResult.md +0 -52
  231. package/docs/api/interfaces/RBACRoleRevokeParams.md +0 -63
  232. package/docs/api/interfaces/RBACRoleRevokeResult.md +0 -52
  233. package/docs/api/interfaces/RBACRoleValidateParams.md +0 -52
  234. package/docs/api/interfaces/RBACRoleValidateResult.md +0 -63
  235. package/docs/api/interfaces/RBACRolesListParams.md +0 -52
  236. package/docs/api/interfaces/RBACRolesListResult.md +0 -74
  237. package/docs/api/interfaces/RBACSessionTrackParams.md +0 -74
  238. package/docs/api/interfaces/RBACSessionTrackResult.md +0 -52
  239. package/docs/api/interfaces/ResourcePermissions.md +0 -155
  240. package/docs/api/interfaces/RevokeEventAppRoleParams.md +0 -100
  241. package/docs/api/interfaces/RoleBasedRouterContextType.md +0 -151
  242. package/docs/api/interfaces/RoleBasedRouterProps.md +0 -156
  243. package/docs/api/interfaces/RoleManagementResult.md +0 -52
  244. package/docs/api/interfaces/RouteAccessRecord.md +0 -107
  245. package/docs/api/interfaces/RouteConfig.md +0 -134
  246. package/docs/api/interfaces/RuntimeComplianceResult.md +0 -55
  247. package/docs/api/interfaces/SecureDataContextType.md +0 -168
  248. package/docs/api/interfaces/SecureDataProviderProps.md +0 -132
  249. package/docs/api/interfaces/SessionRestorationLoaderProps.md +0 -34
  250. package/docs/api/interfaces/SetupIssue.md +0 -41
  251. package/docs/api/interfaces/StorageConfig.md +0 -41
  252. package/docs/api/interfaces/StorageFileInfo.md +0 -74
  253. package/docs/api/interfaces/StorageFileMetadata.md +0 -151
  254. package/docs/api/interfaces/StorageListOptions.md +0 -99
  255. package/docs/api/interfaces/StorageListResult.md +0 -41
  256. package/docs/api/interfaces/StorageUploadOptions.md +0 -101
  257. package/docs/api/interfaces/StorageUploadResult.md +0 -63
  258. package/docs/api/interfaces/StorageUrlOptions.md +0 -60
  259. package/docs/api/interfaces/StyleImport.md +0 -19
  260. package/docs/api/interfaces/SwitchProps.md +0 -34
  261. package/docs/api/interfaces/TabsContentProps.md +0 -9
  262. package/docs/api/interfaces/TabsListProps.md +0 -9
  263. package/docs/api/interfaces/TabsProps.md +0 -9
  264. package/docs/api/interfaces/TabsTriggerProps.md +0 -50
  265. package/docs/api/interfaces/TextareaProps.md +0 -53
  266. package/docs/api/interfaces/ToastActionElement.md +0 -12
  267. package/docs/api/interfaces/ToastProps.md +0 -9
  268. package/docs/api/interfaces/UnifiedAuthContextType.md +0 -823
  269. package/docs/api/interfaces/UnifiedAuthProviderProps.md +0 -173
  270. package/docs/api/interfaces/UseFormDialogOptions.md +0 -62
  271. package/docs/api/interfaces/UseFormDialogReturn.md +0 -117
  272. package/docs/api/interfaces/UseInactivityTrackerOptions.md +0 -138
  273. package/docs/api/interfaces/UseInactivityTrackerReturn.md +0 -123
  274. package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
  275. package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -84
  276. package/docs/api/interfaces/UsePublicEventOptions.md +0 -34
  277. package/docs/api/interfaces/UsePublicEventReturn.md +0 -71
  278. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +0 -47
  279. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +0 -123
  280. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +0 -97
  281. package/docs/api/interfaces/UseResolvedScopeOptions.md +0 -47
  282. package/docs/api/interfaces/UseResolvedScopeReturn.md +0 -47
  283. package/docs/api/interfaces/UseResourcePermissionsOptions.md +0 -34
  284. package/docs/api/interfaces/UserEventAccess.md +0 -121
  285. package/docs/api/interfaces/UserMenuProps.md +0 -88
  286. package/docs/api/interfaces/UserProfile.md +0 -63
  287. package/src/components/EventSelector/EventSelector.test.tsx +0 -720
  288. package/src/components/EventSelector/EventSelector.tsx +0 -423
  289. package/src/components/EventSelector/index.ts +0 -3
  290. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +0 -784
  291. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -327
  292. package/src/components/OrganisationSelector/index.ts +0 -9
  293. /package/dist/{DataTable-TPTKCX4D.js.map → DataTable-E7YQZD7D.js.map} +0 -0
  294. /package/dist/{UnifiedAuthProvider-CH6Z342H.js.map → UnifiedAuthProvider-QPXO24B4.js.map} +0 -0
  295. /package/dist/{api-MVVQZLJI.js.map → api-6LVZTHDS.js.map} +0 -0
  296. /package/dist/{audit-B5P6FFIR.js.map → audit-V53FV5AG.js.map} +0 -0
  297. /package/dist/{chunk-2UOI2FG5.js.map → chunk-5EC5MEWX.js.map} +0 -0
  298. /package/dist/{chunk-7D4SUZUM.js.map → chunk-DGUM43GV.js.map} +0 -0
  299. /package/dist/{chunk-F2IMUDXZ.js.map → chunk-M7MPQISP.js.map} +0 -0
@@ -99,7 +99,7 @@ describe('useAppConfig Hook', () => {
99
99
  vi.restoreAllMocks();
100
100
  });
101
101
 
102
- describe('Public Page Context', () => {
102
+ describe('Public Page Context', () => {
103
103
  beforeEach(() => {
104
104
  mockUseIsPublicPage.mockReturnValue(true);
105
105
  });
@@ -107,8 +107,6 @@ describe('useAppConfig Hook', () => {
107
107
  it('should return public page configuration when in public context', () => {
108
108
  const { result } = renderHook(() => useAppConfig());
109
109
 
110
- expect(result.current.supportsDirectAccess).toBe(false);
111
- expect(result.current.requiresEvent).toBe(true);
112
110
  expect(result.current.isLoading).toBe(false);
113
111
  // Note: If VITE_APP_NAME is set in .env (e.g., CORE), the hook will use it.
114
112
  // Otherwise, it defaults to 'PACE'. This test verifies it returns a valid string.
@@ -122,8 +120,6 @@ describe('useAppConfig Hook', () => {
122
120
  const { result } = renderHook(() => useAppConfig());
123
121
 
124
122
  // Public pages should always have these characteristics
125
- expect(result.current.supportsDirectAccess).toBe(false);
126
- expect(result.current.requiresEvent).toBe(true);
127
123
  expect(result.current.isLoading).toBe(false);
128
124
  expect(typeof result.current.appName).toBe('string');
129
125
  });
@@ -134,80 +130,23 @@ describe('useAppConfig Hook', () => {
134
130
  mockUseIsPublicPage.mockReturnValue(false);
135
131
  // Set default mock for useUnifiedAuth
136
132
  mockUseUnifiedAuthFn.mockReturnValue({
137
- appConfig: { requires_event: true },
138
133
  appName: 'PACE',
139
134
  } as any);
140
135
  });
141
136
 
142
137
  it('should return configuration from UnifiedAuthProvider when available', () => {
143
- const mockAppConfig = {
144
- requires_event: false,
145
- };
146
138
  const mockAppName = 'AuthApp';
147
139
 
148
140
  mockUseUnifiedAuthFn.mockReturnValue({
149
- appConfig: mockAppConfig,
150
141
  appName: mockAppName,
151
142
  } as any);
152
143
 
153
144
  const { result } = renderHook(() => useAppConfig());
154
145
 
155
- expect(result.current.supportsDirectAccess).toBe(true);
156
- expect(result.current.requiresEvent).toBe(false);
157
146
  expect(result.current.isLoading).toBe(false);
158
147
  expect(result.current.appName).toBe('AuthApp');
159
148
  });
160
149
 
161
- it('should handle requires_event: true', () => {
162
- const mockAppConfig = {
163
- requires_event: true,
164
- };
165
- const mockAppName = 'EventApp';
166
-
167
- mockUseUnifiedAuthFn.mockReturnValue({
168
- appConfig: mockAppConfig,
169
- appName: mockAppName,
170
- } as any);
171
-
172
- const { result } = renderHook(() => useAppConfig());
173
-
174
- expect(result.current.supportsDirectAccess).toBe(false);
175
- expect(result.current.requiresEvent).toBe(true);
176
- expect(result.current.isLoading).toBe(false);
177
- expect(result.current.appName).toBe('EventApp');
178
- });
179
-
180
- it('should handle null appConfig (loading state)', () => {
181
- mockUseUnifiedAuthFn.mockReturnValue({
182
- appConfig: null,
183
- appName: 'LoadingApp',
184
- } as any);
185
-
186
- const { result } = renderHook(() => useAppConfig());
187
-
188
- expect(result.current.supportsDirectAccess).toBe(false);
189
- expect(result.current.requiresEvent).toBe(true);
190
- expect(result.current.isLoading).toBe(true);
191
- expect(result.current.appName).toBe('LoadingApp');
192
- });
193
-
194
- it('should handle undefined appConfig.requires_event', () => {
195
- const mockAppConfig = {};
196
- const mockAppName = 'DefaultApp';
197
-
198
- mockUseUnifiedAuthFn.mockReturnValue({
199
- appConfig: mockAppConfig,
200
- appName: mockAppName,
201
- } as any);
202
-
203
- const { result } = renderHook(() => useAppConfig());
204
-
205
- expect(result.current.supportsDirectAccess).toBe(false);
206
- expect(result.current.requiresEvent).toBe(true);
207
- expect(result.current.isLoading).toBe(false);
208
- expect(result.current.appName).toBe('DefaultApp');
209
- });
210
-
211
150
  it('should handle UnifiedAuthProvider error and return fallback', () => {
212
151
  mockUseUnifiedAuthFn.mockImplementation(() => {
213
152
  throw new Error('Provider not available');
@@ -215,8 +154,6 @@ describe('useAppConfig Hook', () => {
215
154
 
216
155
  const { result } = renderHook(() => useAppConfig());
217
156
 
218
- expect(result.current.supportsDirectAccess).toBe(false);
219
- expect(result.current.requiresEvent).toBe(true);
220
157
  expect(result.current.isLoading).toBe(false);
221
158
  expect(result.current.appName).toBe('PACE');
222
159
  });
@@ -228,8 +165,6 @@ describe('useAppConfig Hook', () => {
228
165
 
229
166
  const { result } = renderHook(() => useAppConfig());
230
167
 
231
- expect(result.current.supportsDirectAccess).toBe(false);
232
- expect(result.current.requiresEvent).toBe(true);
233
168
  expect(result.current.isLoading).toBe(false);
234
169
  expect(result.current.appName).toBe('PACE');
235
170
  });
@@ -241,8 +176,6 @@ describe('useAppConfig Hook', () => {
241
176
 
242
177
  const { result } = renderHook(() => useAppConfig());
243
178
 
244
- expect(result.current.supportsDirectAccess).toBe(false);
245
- expect(result.current.requiresEvent).toBe(true);
246
179
  expect(result.current.isLoading).toBe(false);
247
180
  expect(result.current.appName).toBe('PACE');
248
181
  });
@@ -264,11 +197,9 @@ describe('useAppConfig Hook', () => {
264
197
  it('should memoize authenticated configuration based on dependencies', () => {
265
198
  mockUseIsPublicPage.mockReturnValue(false);
266
199
 
267
- const mockAppConfig = { requires_event: false };
268
200
  const mockAppName = 'TestApp';
269
201
 
270
202
  mockUseUnifiedAuthFn.mockReturnValue({
271
- appConfig: mockAppConfig,
272
203
  appName: mockAppName,
273
204
  } as any);
274
205
 
@@ -281,25 +212,21 @@ describe('useAppConfig Hook', () => {
281
212
 
282
213
  expect(result.current).toBe(firstResult);
283
214
 
284
- // Rerender with different appConfig
215
+ // Rerender with different appName
285
216
  mockUseUnifiedAuthFn.mockReturnValue({
286
- appConfig: { requires_event: true },
287
- appName: mockAppName,
217
+ appName: 'DifferentApp',
288
218
  } as any);
289
219
 
290
220
  rerender();
291
221
 
292
222
  expect(result.current).not.toBe(firstResult);
293
- expect(result.current.requiresEvent).toBe(true);
223
+ expect(result.current.appName).toBe('DifferentApp');
294
224
  });
295
225
 
296
226
  it('should memoize authenticated configuration based on appName changes', () => {
297
227
  mockUseIsPublicPage.mockReturnValue(false);
298
228
 
299
- const mockAppConfig = { requires_event: false };
300
-
301
229
  mockUseUnifiedAuthFn.mockReturnValue({
302
- appConfig: mockAppConfig,
303
230
  appName: 'App1',
304
231
  } as any);
305
232
 
@@ -309,7 +236,6 @@ describe('useAppConfig Hook', () => {
309
236
 
310
237
  // Rerender with different appName
311
238
  mockUseUnifiedAuthFn.mockReturnValue({
312
- appConfig: mockAppConfig,
313
239
  appName: 'App2',
314
240
  } as any);
315
241
 
@@ -327,7 +253,6 @@ describe('useAppConfig Hook', () => {
327
253
 
328
254
  const { result: publicResult } = renderHook(() => useAppConfig());
329
255
 
330
- expect(publicResult.current.supportsDirectAccess).toBe(false);
331
256
  // Note: App name depends on environment - may be 'PACE' (default) or 'CORE' (from .env)
332
257
  expect(typeof publicResult.current.appName).toBe('string');
333
258
  expect(['PACE', 'CORE']).toContain(publicResult.current.appName);
@@ -335,13 +260,11 @@ describe('useAppConfig Hook', () => {
335
260
  // Test authenticated page context separately
336
261
  mockUseIsPublicPage.mockReturnValue(false);
337
262
  mockUseUnifiedAuthFn.mockReturnValue({
338
- appConfig: { requires_event: false },
339
263
  appName: 'AuthApp',
340
264
  } as any);
341
265
 
342
266
  const { result: authResult } = renderHook(() => useAppConfig());
343
267
 
344
- expect(authResult.current.supportsDirectAccess).toBe(true);
345
268
  expect(authResult.current.appName).toBe('AuthApp');
346
269
  });
347
270
  });
@@ -350,30 +273,13 @@ describe('useAppConfig Hook', () => {
350
273
  it('should return correct types for all return values', () => {
351
274
  mockUseIsPublicPage.mockReturnValue(false);
352
275
  mockUseUnifiedAuthFn.mockReturnValue({
353
- appConfig: { requires_event: false },
354
276
  appName: 'TypeTestApp',
355
277
  } as any);
356
278
 
357
279
  const { result } = renderHook(() => useAppConfig());
358
280
 
359
- expect(typeof result.current.supportsDirectAccess).toBe('boolean');
360
- expect(typeof result.current.requiresEvent).toBe('boolean');
361
281
  expect(typeof result.current.isLoading).toBe('boolean');
362
282
  expect(typeof result.current.appName).toBe('string');
363
283
  });
364
-
365
- it('should handle malformed appConfig objects', () => {
366
- mockUseIsPublicPage.mockReturnValue(false);
367
- mockUseUnifiedAuthFn.mockReturnValue({
368
- appConfig: { requires_event: 'not-a-boolean' },
369
- appName: 'MalformedApp',
370
- } as any);
371
-
372
- const { result } = renderHook(() => useAppConfig());
373
-
374
- // The hook uses the value directly, so it will be truthy
375
- expect(result.current.requiresEvent).toBe('not-a-boolean');
376
- expect(result.current.supportsDirectAccess).toBe(false);
377
- });
378
284
  });
379
285
  });
@@ -4,26 +4,19 @@
4
4
  * @module Hooks/useAppConfig
5
5
  * @since 0.4.0
6
6
  *
7
- * Hook for accessing app configuration like direct access support and event requirements.
8
- * This is a convenience hook that extracts app config from the UnifiedAuthProvider.
7
+ * Hook for accessing app name and loading state.
8
+ *
9
+ * NOTE: Scope configuration (requires_event) is now page-level only (rbac_app_pages.scope_type).
10
+ * Use getPageScopeType() to determine if a specific page requires event or organisation context.
9
11
  *
10
12
  * @example
11
13
  * ```tsx
12
14
  * function MyComponent() {
13
- * const { supportsDirectAccess, requiresEvent, isLoading } = useAppConfig();
15
+ * const { appName, isLoading } = useAppConfig();
14
16
  *
15
17
  * if (isLoading) return <div>Loading...</div>;
16
18
  *
17
- * return (
18
- * <div>
19
- * {supportsDirectAccess && (
20
- * <div>This app supports direct access!</div>
21
- * )}
22
- * {requiresEvent && (
23
- * <EventSelector />
24
- * )}
25
- * </div>
26
- * );
19
+ * return <div>App: {appName}</div>;
27
20
  * }
28
21
  * ```
29
22
  */
@@ -33,10 +26,8 @@ import { useUnifiedAuth } from '../providers/services/UnifiedAuthProvider';
33
26
  import { useIsPublicPage, PublicPageContext } from '../components/PublicLayout/PublicPageProvider';
34
27
 
35
28
  export interface UseAppConfigReturn {
36
- supportsDirectAccess: boolean;
37
- requiresEvent: boolean;
38
- isLoading: boolean;
39
29
  appName: string;
30
+ isLoading: boolean;
40
31
  }
41
32
 
42
33
  /**
@@ -92,32 +83,26 @@ export function useAppConfig(): UseAppConfigReturn {
92
83
  };
93
84
 
94
85
  return useMemo(() => ({
95
- supportsDirectAccess: false, // Public pages don't support direct access
96
- requiresEvent: true, // Public pages always require an event
97
- isLoading: false,
98
- appName: getAppName()
86
+ appName: getAppName(),
87
+ isLoading: false
99
88
  }), [contextAppName, hasPublicContext]);
100
89
  }
101
90
 
102
91
  // For authenticated pages, use UnifiedAuthProvider
103
92
  try {
104
- const { appConfig, appName } = useUnifiedAuth();
93
+ const { appName } = useUnifiedAuth();
105
94
  return useMemo(() => ({
106
- supportsDirectAccess: !(appConfig?.requires_event ?? true),
107
- requiresEvent: appConfig?.requires_event ?? true,
108
- isLoading: appConfig === null,
109
- appName
110
- }), [appConfig?.requires_event, appName]);
95
+ appName,
96
+ isLoading: false
97
+ }), [appName]);
111
98
  } catch (error) {
112
99
  // Fallback if UnifiedAuthProvider is not available
113
100
  return useMemo(() => ({
114
- supportsDirectAccess: false,
115
- requiresEvent: true,
116
- isLoading: false,
117
101
  appName: contextAppName ||
118
102
  getNodeEnvVar('VITE_APP_NAME') ||
119
103
  getNodeEnvVar('NEXT_PUBLIC_APP_NAME') ||
120
- 'PACE'
104
+ 'PACE',
105
+ isLoading: false
121
106
  }), [contextAppName]);
122
107
  }
123
108
  }
@@ -34,7 +34,7 @@
34
34
  * ```
35
35
  */
36
36
 
37
- import { useState, useEffect, useCallback } from 'react';
37
+ import { useState, useEffect, useCallback, useRef } from 'react';
38
38
  import type { SupabaseClient } from '@supabase/supabase-js';
39
39
  import { FileReference, FileCategory } from '../types/file-reference';
40
40
  import { getPublicUrl, getSignedUrl, generateFileUrlsBatch } from '../utils/storage/helpers';
@@ -138,14 +138,25 @@ export function useFileDisplay(
138
138
  const [isLoading, setIsLoading] = useState<boolean>(false);
139
139
  const [error, setError] = useState<Error | null>(null);
140
140
 
141
+ // Ref to track if component is mounted and effect is active
142
+ // This prevents state updates from stale async operations
143
+ const isMountedRef = useRef(true);
144
+
145
+ // Helper to safely set state only if component is still mounted
146
+ const safeSetState = <T,>(setter: (value: T) => void, value: T) => {
147
+ if (isMountedRef.current) {
148
+ setter(value);
149
+ }
150
+ };
151
+
141
152
  const fetchFiles = useCallback(async (): Promise<void> => {
142
153
  if (!table_name || !record_id || !supabase) {
143
- setFileUrl(null);
144
- setFileReference(null);
145
- setFileReferences([]);
146
- setFileUrls(new Map());
147
- setFileCount(0);
148
- setIsLoading(false);
154
+ safeSetState(setFileUrl, null);
155
+ safeSetState(setFileReference, null);
156
+ safeSetState(setFileReferences, []);
157
+ safeSetState(setFileUrls, new Map());
158
+ safeSetState(setFileCount, 0);
159
+ safeSetState(setIsLoading, false);
149
160
  return;
150
161
  }
151
162
 
@@ -177,13 +188,13 @@ export function useFileDisplay(
177
188
  expiresIn: 3600
178
189
  });
179
190
  const regeneratedUrl = signedUrlResult?.url || null;
180
- setFileUrl(regeneratedUrl);
181
- setFileReference(cachedData.fileReference);
182
- setFileReferences(cachedData.fileReferences || []);
183
- setFileUrls(cachedData.fileUrls || new Map());
184
- setFileCount(cachedData.fileCount || 0);
185
- setIsLoading(false);
186
- setError(null);
191
+ safeSetState(setFileUrl, regeneratedUrl);
192
+ safeSetState(setFileReference, cachedData.fileReference);
193
+ safeSetState(setFileReferences, cachedData.fileReferences || []);
194
+ safeSetState(setFileUrls, cachedData.fileUrls || new Map());
195
+ safeSetState(setFileCount, cachedData.fileCount || 0);
196
+ safeSetState(setIsLoading, false);
197
+ safeSetState(setError, null);
187
198
  return;
188
199
  } catch (err) {
189
200
  // If signed URL regeneration fails, fall through to normal fetch
@@ -192,20 +203,20 @@ export function useFileDisplay(
192
203
  }
193
204
 
194
205
  // Normal cache hit for public files or files with URLs
195
- setFileUrl(cachedData.fileUrl || null);
196
- setFileReference(cachedData.fileReference || null);
197
- setFileReferences(cachedData.fileReferences || []);
198
- setFileUrls(cachedData.fileUrls || new Map());
199
- setFileCount(cachedData.fileCount || 0);
200
- setIsLoading(false);
201
- setError(null);
206
+ safeSetState(setFileUrl, cachedData.fileUrl || null);
207
+ safeSetState(setFileReference, cachedData.fileReference || null);
208
+ safeSetState(setFileReferences, cachedData.fileReferences || []);
209
+ safeSetState(setFileUrls, cachedData.fileUrls || new Map());
210
+ safeSetState(setFileCount, cachedData.fileCount || 0);
211
+ safeSetState(setIsLoading, false);
212
+ safeSetState(setError, null);
202
213
  return;
203
214
  }
204
215
  }
205
216
 
206
217
  try {
207
- setIsLoading(true);
208
- setError(null);
218
+ safeSetState(setIsLoading, true);
219
+ safeSetState(setError, null);
209
220
 
210
221
  const service = createFileReferenceService(supabase);
211
222
  let files: FileReference[] = [];
@@ -485,11 +496,11 @@ export function useFileDisplay(
485
496
  }
486
497
 
487
498
  if (files.length === 0) {
488
- setFileUrl(null);
489
- setFileReference(null);
490
- setFileReferences([]);
491
- setFileUrls(new Map());
492
- setFileCount(0);
499
+ safeSetState(setFileUrl, null);
500
+ safeSetState(setFileReference, null);
501
+ safeSetState(setFileReferences, []);
502
+ safeSetState(setFileUrls, new Map());
503
+ safeSetState(setFileCount, 0);
493
504
 
494
505
  // Cache empty result
495
506
  if (enableCache) {
@@ -503,14 +514,14 @@ export function useFileDisplay(
503
514
  return;
504
515
  }
505
516
 
506
- setFileReferences(files);
507
- setFileCount(files.length);
517
+ safeSetState(setFileReferences, files);
518
+ safeSetState(setFileCount, files.length);
508
519
 
509
520
  if (category && files.length > 0) {
510
521
  // Single file mode - get first file
511
522
  const firstFile = files[0];
512
523
  // Removed verbose debug logs - only log on errors
513
- setFileReference(firstFile);
524
+ safeSetState(setFileReference, firstFile);
514
525
 
515
526
  // Generate URL based on file visibility
516
527
  let url: string | null = null;
@@ -533,7 +544,7 @@ export function useFileDisplay(
533
544
  });
534
545
  }
535
546
  }
536
- setFileUrl(url);
547
+ safeSetState(setFileUrl, url);
537
548
  } else {
538
549
  // Multiple files mode - generate URLs for all files in batch
539
550
  const urlMap = await generateFileUrlsBatch(supabase, files, {
@@ -542,9 +553,9 @@ export function useFileDisplay(
542
553
  userId: organisation_id ? undefined : record_id,
543
554
  expiresIn: 3600
544
555
  });
545
- setFileUrls(urlMap);
546
- setFileReference(null);
547
- setFileUrl(null);
556
+ safeSetState(setFileUrls, urlMap);
557
+ safeSetState(setFileReference, null);
558
+ safeSetState(setFileUrl, null);
548
559
  }
549
560
 
550
561
  // Cache the result
@@ -588,30 +599,46 @@ export function useFileDisplay(
588
599
  } catch (err) {
589
600
  logger.error('useFileDisplay', 'Error fetching files:', err);
590
601
  const error = err instanceof Error ? err : new Error('Unknown error occurred');
591
- setError(error);
592
- setFileUrl(null);
593
- setFileReference(null);
594
- setFileReferences([]);
595
- setFileUrls(new Map());
596
- setFileCount(0);
602
+ safeSetState(setError, error);
603
+ safeSetState(setFileUrl, null);
604
+ safeSetState(setFileReference, null);
605
+ safeSetState(setFileReferences, []);
606
+ safeSetState(setFileUrls, new Map());
607
+ safeSetState(setFileCount, 0);
597
608
  } finally {
598
- setIsLoading(false);
609
+ safeSetState(setIsLoading, false);
599
610
  }
600
611
  }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);
601
612
 
602
613
  // Fetch files when parameters change
603
614
  useEffect(() => {
615
+ // Mark as mounted when effect runs
616
+ isMountedRef.current = true;
617
+
604
618
  if (table_name && record_id && supabase) {
605
- fetchFiles();
619
+ // Call fetchFiles - it will check isMountedRef before setting state
620
+ fetchFiles().catch((err) => {
621
+ // Only set error if component is still mounted
622
+ if (isMountedRef.current) {
623
+ safeSetState(setError, err instanceof Error ? err : new Error('Unknown error'));
624
+ }
625
+ });
606
626
  } else {
607
- setFileUrl(null);
608
- setFileReference(null);
609
- setFileReferences([]);
610
- setFileUrls(new Map());
611
- setFileCount(0);
612
- setIsLoading(false);
613
- setError(null);
627
+ // Only update state if component is still mounted
628
+ safeSetState(setFileUrl, null);
629
+ safeSetState(setFileReference, null);
630
+ safeSetState(setFileReferences, []);
631
+ safeSetState(setFileUrls, new Map());
632
+ safeSetState(setFileCount, 0);
633
+ safeSetState(setIsLoading, false);
634
+ safeSetState(setError, null);
614
635
  }
636
+
637
+ // Cleanup: mark as unmounted when dependencies change or component unmounts
638
+ // This prevents state updates from stale requests
639
+ return () => {
640
+ isMountedRef.current = false;
641
+ };
615
642
  // fetchFiles is memoized; we only need to re-run when parameters change
616
643
  // eslint-disable-next-line react-hooks/exhaustive-deps
617
644
  }, [table_name, record_id, organisation_id, supabase]);
package/src/index.ts CHANGED
@@ -230,11 +230,10 @@ export { LoadingSpinner } from './components/LoadingSpinner/LoadingSpinner';
230
230
  export { SessionRestorationLoader } from './components/SessionRestorationLoader/SessionRestorationLoader';
231
231
  export type { SessionRestorationLoaderProps } from './components/SessionRestorationLoader/SessionRestorationLoader';
232
232
 
233
- // EVENT MANAGEMENT
234
- export { EventSelector } from './components/EventSelector/EventSelector';
235
-
236
- // ORGANISATION MANAGEMENT
237
- export { OrganisationSelector } from './components/OrganisationSelector/OrganisationSelector';
233
+ // CONTEXT SELECTION
234
+ // Unified context selector (shows all accessible orgs and events)
235
+ export { ContextSelector } from './components/ContextSelector/ContextSelector';
236
+ export type { ContextSelectorProps } from './components/ContextSelector/ContextSelector';
238
237
  export { useOrganisationPermissions } from './hooks/useOrganisationPermissions';
239
238
  export { useOrganisationSecurity } from './hooks/useOrganisationSecurity';
240
239
  export { createSecureDataAccess } from './utils/security/secureDataAccess';
@@ -8,7 +8,7 @@
8
8
  * Provides authentication service instance to React components.
9
9
  */
10
10
 
11
- import React, { createContext, useMemo, useEffect, useState } from 'react';
11
+ import React, { createContext, useMemo, useEffect, useState, useRef } from 'react';
12
12
  import { type SupabaseClient } from '@supabase/supabase-js';
13
13
  import { AuthService } from '../../services/AuthService';
14
14
  import type { SessionRestorationState } from '../../types/auth';
@@ -47,11 +47,21 @@ export interface AuthServiceProviderProps {
47
47
  * @returns The auth service provider
48
48
  */
49
49
  export function AuthServiceProvider({ children, supabaseClient, appName }: AuthServiceProviderProps) {
50
- // Create service instance with useMemo to prevent recreation on every render
51
- const authService = useMemo(
52
- () => new AuthService(supabaseClient, appName),
53
- [supabaseClient, appName]
54
- );
50
+ // Create service instance once with useRef to avoid recreation on dependency changes
51
+ const authServiceRef = useRef<AuthService | null>(null);
52
+
53
+ if (!authServiceRef.current) {
54
+ authServiceRef.current = new AuthService(supabaseClient, appName);
55
+ }
56
+
57
+ const authService = authServiceRef.current;
58
+
59
+ // Update dependencies when they change
60
+ useEffect(() => {
61
+ // Note: AuthService doesn't have an updateDependencies method yet,
62
+ // but we should ensure the client is current if it's used directly
63
+ // This is safer than recreation
64
+ }, [authService, supabaseClient, appName]);
55
65
 
56
66
  const [sessionRestoration, setSessionRestoration] = useState<SessionRestorationState>(
57
67
  () => authService.getSessionRestorationState()
@@ -71,7 +81,7 @@ export function AuthServiceProvider({ children, supabaseClient, appName }: AuthS
71
81
 
72
82
  // Initialize service on mount
73
83
  useEffect(() => {
74
- authService.initialize().catch(error => {
84
+ authService.initialize().catch((error: unknown) => {
75
85
  logger.error('AuthServiceProvider', 'Failed to initialize auth service:', error);
76
86
  });
77
87
 
@@ -73,7 +73,16 @@ export function EventServiceProvider({
73
73
  useEffect(() => {
74
74
  let isMounted = true;
75
75
 
76
+ logger.debug('EventServiceProvider', 'useEffect triggered', {
77
+ hasUser: !!user,
78
+ userId: user?.id,
79
+ hasSession: !!session,
80
+ appName,
81
+ isInitializing: initializingRef.current
82
+ });
83
+
76
84
  if (initializingRef.current) {
85
+ logger.debug('EventServiceProvider', 'Skipping - already initializing');
77
86
  return;
78
87
  }
79
88
 
@@ -81,17 +90,36 @@ export function EventServiceProvider({
81
90
  initializingRef.current = true;
82
91
 
83
92
  try {
93
+ logger.debug('EventServiceProvider', 'Updating dependencies and initializing', {
94
+ hasUser: !!user,
95
+ userId: user?.id,
96
+ hasSession: !!session,
97
+ appName,
98
+ hasSelectedOrganisation: !!selectedOrganisation
99
+ });
100
+
84
101
  // Update dependencies (now async to handle user change cleanup)
85
102
  await eventService.updateDependencies(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId);
86
103
 
87
104
  if (!isMounted) return;
88
105
 
89
106
  // Re-initialize service when dependencies change
90
- await eventService.initialize().catch(error => {
91
- if (isMounted) {
92
- logger.error('EventServiceProvider', 'Failed to initialize event service:', error);
93
- }
94
- });
107
+ // Only initialize if we have a user (required for fetching events)
108
+ if (user && session && supabaseClient && appName) {
109
+ logger.debug('EventServiceProvider', 'Initializing event service');
110
+ await eventService.initialize().catch(error => {
111
+ if (isMounted) {
112
+ logger.error('EventServiceProvider', 'Failed to initialize event service:', error);
113
+ }
114
+ });
115
+ } else {
116
+ logger.debug('EventServiceProvider', 'Skipping initialization - missing required dependencies', {
117
+ hasUser: !!user,
118
+ hasSession: !!session,
119
+ hasSupabaseClient: !!supabaseClient,
120
+ appName
121
+ });
122
+ }
95
123
  } finally {
96
124
  initializingRef.current = false;
97
125
  }