@jmruthers/pace-core 0.5.74 → 0.5.75

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 (278) hide show
  1. package/dist/{DataTable-2QR5TER5.js → DataTable-HWZQGASI.js} +8 -8
  2. package/dist/{PublicLoadingSpinner-DLpF5bbs.d.ts → PublicLoadingSpinner-BKNBT6b6.d.ts} +2 -2
  3. package/dist/RBACService-C4udt_Zp.d.ts +528 -0
  4. package/dist/{UnifiedAuthProvider-K4NRGXL4.js → UnifiedAuthProvider-3NKDOSOK.js} +6 -4
  5. package/dist/UnifiedAuthProvider-Bj6YCf7c.d.ts +113 -0
  6. package/dist/{chunk-UJMCGBLS.js → chunk-2CHATWBF.js} +5 -7
  7. package/dist/chunk-2CHATWBF.js.map +1 -0
  8. package/dist/{chunk-BKVGJVUR.js → chunk-2DFZ432F.js} +496 -30
  9. package/dist/chunk-2DFZ432F.js.map +1 -0
  10. package/dist/{chunk-LVQ26TCN.js → chunk-33PHABLB.js} +36 -3
  11. package/dist/chunk-33PHABLB.js.map +1 -0
  12. package/dist/chunk-5F3NDPJV.js +232 -0
  13. package/dist/chunk-5F3NDPJV.js.map +1 -0
  14. package/dist/chunk-A4FUBC7B.js +17 -0
  15. package/dist/chunk-A4FUBC7B.js.map +1 -0
  16. package/dist/{chunk-SMJZMKYN.js → chunk-A6HBIY5P.js} +2 -11
  17. package/dist/{chunk-SMJZMKYN.js.map → chunk-A6HBIY5P.js.map} +1 -1
  18. package/dist/{chunk-IHMMNKNA.js → chunk-CY3AHGO4.js} +6256 -1937
  19. package/dist/chunk-CY3AHGO4.js.map +1 -0
  20. package/dist/{chunk-H2TNUICK.js → chunk-DAXLNIDY.js} +47 -49
  21. package/dist/chunk-DAXLNIDY.js.map +1 -0
  22. package/dist/{chunk-VKOCWWVY.js → chunk-L3RV2ALE.js} +1 -6
  23. package/dist/{chunk-VKOCWWVY.js.map → chunk-L3RV2ALE.js.map} +1 -1
  24. package/dist/chunk-LW7MMEAQ.js +59 -0
  25. package/dist/chunk-LW7MMEAQ.js.map +1 -0
  26. package/dist/{chunk-DG5Z55HH.js → chunk-NTNILOBC.js} +7 -9
  27. package/dist/chunk-NTNILOBC.js.map +1 -0
  28. package/dist/chunk-PYUXFQJ3.js +11 -0
  29. package/dist/chunk-PYUXFQJ3.js.map +1 -0
  30. package/dist/chunk-URUTVZ7N.js +27 -0
  31. package/dist/chunk-URUTVZ7N.js.map +1 -0
  32. package/dist/chunk-WN6XJWOS.js +2468 -0
  33. package/dist/chunk-WN6XJWOS.js.map +1 -0
  34. package/dist/{chunk-3SP4P7NS.js → chunk-XLZ7U46Z.js} +59 -1
  35. package/dist/chunk-XLZ7U46Z.js.map +1 -0
  36. package/dist/{chunk-ORSMVXO2.js → chunk-ZTT2AXMX.js} +9 -14
  37. package/dist/chunk-ZTT2AXMX.js.map +1 -0
  38. package/dist/components.d.ts +4 -5
  39. package/dist/components.js +32 -39
  40. package/dist/components.js.map +1 -1
  41. package/dist/hooks.d.ts +3 -3
  42. package/dist/hooks.js +9 -8
  43. package/dist/hooks.js.map +1 -1
  44. package/dist/index.d.ts +156 -10
  45. package/dist/index.js +188 -93
  46. package/dist/index.js.map +1 -1
  47. package/dist/{organisation-t-vvQC3g.d.ts → organisation-BtshODVF.d.ts} +4 -3
  48. package/dist/providers.d.ts +27 -38
  49. package/dist/providers.js +33 -23
  50. package/dist/rbac/index.d.ts +61 -5
  51. package/dist/rbac/index.js +13 -14
  52. package/dist/styles/index.js +2 -2
  53. package/dist/theming/runtime.js +1 -3
  54. package/dist/types.d.ts +3 -3
  55. package/dist/types.js +1 -1
  56. package/dist/types.js.map +1 -1
  57. package/dist/{unified-CMPjE_fv.d.ts → unified-CM7T0aTK.d.ts} +1 -1
  58. package/dist/useInactivityTracker-MRUU55XI.js +10 -0
  59. package/dist/useInactivityTracker-MRUU55XI.js.map +1 -0
  60. package/dist/{usePublicRouteParams-Ua1Vz-HG.d.ts → usePublicRouteParams-B-CumWRc.d.ts} +3 -3
  61. package/dist/utils.js +7 -9
  62. package/dist/utils.js.map +1 -1
  63. package/dist/validation.d.ts +1 -1
  64. package/docs/api/classes/ColumnFactory.md +1 -1
  65. package/docs/api/classes/ErrorBoundary.md +1 -1
  66. package/docs/api/classes/InvalidScopeError.md +1 -1
  67. package/docs/api/classes/MissingUserContextError.md +1 -1
  68. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  69. package/docs/api/classes/PermissionDeniedError.md +1 -1
  70. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  71. package/docs/api/classes/RBACAuditManager.md +1 -1
  72. package/docs/api/classes/RBACCache.md +1 -1
  73. package/docs/api/classes/RBACEngine.md +1 -1
  74. package/docs/api/classes/RBACError.md +1 -1
  75. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  76. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  77. package/docs/api/classes/StorageUtils.md +1 -1
  78. package/docs/api/enums/FileCategory.md +1 -1
  79. package/docs/api/interfaces/AggregateConfig.md +1 -1
  80. package/docs/api/interfaces/ButtonProps.md +3 -3
  81. package/docs/api/interfaces/CardProps.md +2 -2
  82. package/docs/api/interfaces/ColorPalette.md +1 -1
  83. package/docs/api/interfaces/ColorShade.md +1 -1
  84. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  85. package/docs/api/interfaces/DataTableAction.md +1 -1
  86. package/docs/api/interfaces/DataTableColumn.md +1 -1
  87. package/docs/api/interfaces/DataTableProps.md +1 -1
  88. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  89. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  90. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  91. package/docs/api/interfaces/EventLogoProps.md +2 -2
  92. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  93. package/docs/api/interfaces/FileMetadata.md +1 -1
  94. package/docs/api/interfaces/FileReference.md +1 -1
  95. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  96. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  97. package/docs/api/interfaces/FileUploadProps.md +1 -1
  98. package/docs/api/interfaces/FooterProps.md +1 -1
  99. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  100. package/docs/api/interfaces/InputProps.md +2 -2
  101. package/docs/api/interfaces/LabelProps.md +1 -1
  102. package/docs/api/interfaces/LoginFormProps.md +1 -1
  103. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  104. package/docs/api/interfaces/NavigationContextType.md +1 -1
  105. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  106. package/docs/api/interfaces/NavigationItem.md +1 -1
  107. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  108. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  109. package/docs/api/interfaces/Organisation.md +1 -1
  110. package/docs/api/interfaces/OrganisationContextType.md +28 -17
  111. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  112. package/docs/api/interfaces/OrganisationProviderProps.md +2 -2
  113. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  114. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  115. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  116. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  117. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  118. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  119. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  120. package/docs/api/interfaces/PaletteData.md +1 -1
  121. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  122. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  123. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  124. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +2 -2
  125. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  126. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  127. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  128. package/docs/api/interfaces/RBACConfig.md +1 -1
  129. package/docs/api/interfaces/RBACContextType.md +5 -11
  130. package/docs/api/interfaces/RBACLogger.md +1 -1
  131. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  132. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  133. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  134. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  135. package/docs/api/interfaces/RouteConfig.md +1 -1
  136. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  137. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  138. package/docs/api/interfaces/StorageConfig.md +1 -1
  139. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  140. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  141. package/docs/api/interfaces/StorageListOptions.md +1 -1
  142. package/docs/api/interfaces/StorageListResult.md +1 -1
  143. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  144. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  145. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  146. package/docs/api/interfaces/StyleImport.md +1 -1
  147. package/docs/api/interfaces/SwitchProps.md +1 -1
  148. package/docs/api/interfaces/ToastActionElement.md +1 -1
  149. package/docs/api/interfaces/ToastProps.md +1 -1
  150. package/docs/api/interfaces/UnifiedAuthContextType.md +524 -440
  151. package/docs/api/interfaces/UnifiedAuthProviderProps.md +14 -14
  152. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  153. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  154. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  155. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  156. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  157. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  158. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  159. package/docs/api/interfaces/UserEventAccess.md +11 -11
  160. package/docs/api/interfaces/UserMenuProps.md +1 -1
  161. package/docs/api/interfaces/UserProfile.md +1 -1
  162. package/docs/api/modules.md +179 -52
  163. package/docs/architecture/services.md +30 -32
  164. package/docs/breaking-changes.md +2 -5
  165. package/docs/migration/service-architecture.md +121 -260
  166. package/docs/rbac/README-rbac-rls-integration.md +48 -38
  167. package/{src/rbac/examples → examples/RBAC}/CompleteRBACExample.tsx +3 -2
  168. package/{src/rbac/examples → examples/RBAC}/EventBasedApp.tsx +5 -4
  169. package/{src/components/examples → examples/RBAC}/PermissionExample.tsx +7 -6
  170. package/examples/RBAC/__tests__/PermissionExample.test.tsx +150 -0
  171. package/examples/RBAC/index.ts +13 -0
  172. package/examples/README.md +37 -0
  173. package/examples/index.ts +22 -0
  174. package/{src/examples → examples/public-pages}/CorrectPublicPageImplementation.tsx +1 -1
  175. package/{src/examples → examples/public-pages}/PublicEventPage.tsx +1 -1
  176. package/{src/examples → examples/public-pages}/PublicPageApp.tsx +1 -1
  177. package/{src/examples → examples/public-pages}/PublicPageUsageExample.tsx +1 -1
  178. package/examples/public-pages/__tests__/PublicPageUsageExample.test.tsx +159 -0
  179. package/examples/public-pages/index.ts +14 -0
  180. package/package.json +22 -18
  181. package/src/__tests__/TEST_GUIDE_CURSOR.md +650 -9
  182. package/src/__tests__/helpers/README.md +255 -0
  183. package/src/__tests__/helpers/index.ts +62 -0
  184. package/src/__tests__/helpers/supabaseMock.ts +27 -3
  185. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -8
  186. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +55 -0
  187. package/src/components/DataTable/core/ColumnManager.ts +10 -0
  188. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +254 -0
  189. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +193 -0
  190. package/src/components/DataTable/examples/__tests__/HierarchicalExample.test.tsx +45 -0
  191. package/src/components/DataTable/examples/__tests__/PerformanceExample.test.tsx +117 -0
  192. package/src/components/Dialog/examples/__tests__/HtmlDialogExample.test.tsx +71 -0
  193. package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +122 -0
  194. package/src/components/EventSelector/EventSelector.tsx +1 -1
  195. package/src/components/Header/Header.test.tsx +35 -1
  196. package/src/components/Header/Header.tsx +3 -1
  197. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -3
  198. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +24 -4
  199. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +3 -2
  200. package/src/hooks/__tests__/useFocusManagement.unit.test.ts +220 -0
  201. package/src/hooks/__tests__/useIsMobile.unit.test.ts +117 -0
  202. package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +295 -0
  203. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +29 -19
  204. package/src/hooks/__tests__/useRBAC.unit.test.ts +7 -3
  205. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +115 -19
  206. package/src/hooks/useEventTheme.test.ts +350 -0
  207. package/src/hooks/useEventTheme.ts +1 -1
  208. package/src/hooks/useEvents.ts +61 -0
  209. package/src/hooks/useOrganisationSecurity.test.ts +4 -4
  210. package/src/hooks/useOrganisationSecurity.ts +2 -2
  211. package/src/hooks/useOrganisations.ts +64 -0
  212. package/src/hooks/useSecureDataAccess.test.ts +9 -5
  213. package/src/hooks/useSecureDataAccess.ts +2 -2
  214. package/src/index.ts +18 -3
  215. package/src/providers/AuthProvider.tsx +8 -292
  216. package/src/providers/EventProvider.tsx +15 -425
  217. package/src/providers/InactivityProvider.tsx +8 -231
  218. package/src/providers/OrganisationProvider.test.simple.tsx +3 -2
  219. package/src/providers/OrganisationProvider.tsx +11 -890
  220. package/src/providers/UnifiedAuthProvider.tsx +8 -320
  221. package/src/providers/__tests__/AuthProvider.test.tsx +18 -17
  222. package/src/providers/__tests__/EventProvider.test.tsx +253 -2
  223. package/src/providers/__tests__/InactivityProvider.test-helper.tsx +65 -0
  224. package/src/providers/__tests__/InactivityProvider.test.tsx +46 -114
  225. package/src/providers/__tests__/OrganisationProvider.test.tsx +313 -3
  226. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +383 -2
  227. package/src/providers/index.ts +8 -7
  228. package/src/providers/services/EventServiceProvider.tsx +3 -0
  229. package/src/providers/services/UnifiedAuthProvider.tsx +3 -0
  230. package/src/rbac/hooks/usePermissions.test.ts +296 -0
  231. package/src/rbac/hooks/useRBAC.test.ts +9 -5
  232. package/src/rbac/hooks/useRBAC.ts +3 -3
  233. package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +688 -0
  234. package/src/rbac/providers/__tests__/RBACProvider.test.tsx +507 -0
  235. package/src/services/AuthService.ts +19 -4
  236. package/src/services/__tests__/AuthService.test.ts +288 -0
  237. package/src/styles/core.css +2 -0
  238. package/src/types/__tests__/guards.test.ts +246 -0
  239. package/src/types/guards.ts +1 -0
  240. package/src/types/organisation.ts +3 -2
  241. package/src/validation/__tests__/sanitization.unit.test.ts +250 -0
  242. package/src/validation/__tests__/schemaUtils.unit.test.ts +451 -0
  243. package/src/validation/__tests__/user.unit.test.ts +440 -0
  244. package/dist/RBACProvider-BO4ilsQB.d.ts +0 -63
  245. package/dist/UnifiedAuthProvider-D02AMXgO.d.ts +0 -103
  246. package/dist/chunk-3SP4P7NS.js.map +0 -1
  247. package/dist/chunk-B5LK25HV.js +0 -953
  248. package/dist/chunk-B5LK25HV.js.map +0 -1
  249. package/dist/chunk-BKVGJVUR.js.map +0 -1
  250. package/dist/chunk-C5Q5LRU5.js +0 -5691
  251. package/dist/chunk-C5Q5LRU5.js.map +0 -1
  252. package/dist/chunk-CDDYJCYU.js +0 -79
  253. package/dist/chunk-CDDYJCYU.js.map +0 -1
  254. package/dist/chunk-DG5Z55HH.js.map +0 -1
  255. package/dist/chunk-H2TNUICK.js.map +0 -1
  256. package/dist/chunk-IHMMNKNA.js.map +0 -1
  257. package/dist/chunk-LVQ26TCN.js.map +0 -1
  258. package/dist/chunk-ORSMVXO2.js.map +0 -1
  259. package/dist/chunk-UJMCGBLS.js.map +0 -1
  260. package/dist/chunk-V6BHACCH.js +0 -17
  261. package/dist/chunk-V6BHACCH.js.map +0 -1
  262. package/dist/rbac/cli/policy-manager.js +0 -278
  263. package/dist/rbac/cli/policy-manager.js.map +0 -1
  264. package/docs/api/interfaces/EventContextType.md +0 -96
  265. package/docs/api/interfaces/EventProviderProps.md +0 -19
  266. package/src/providers/OrganisationProvider.test.tsx +0 -164
  267. package/src/providers/UnifiedAuthProvider.test.tsx +0 -124
  268. package/src/providers/__tests__/AuthProvider.test.tsx.backup +0 -771
  269. package/src/providers/__tests__/EventProvider.test.tsx.backup +0 -824
  270. package/src/providers/__tests__/OrganisationProvider.test.tsx.backup +0 -820
  271. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx.backup +0 -911
  272. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx.backup2 +0 -166
  273. package/src/rbac/cli/__tests__/policy-manager.test.ts +0 -339
  274. package/src/rbac/cli/policy-manager.ts +0 -443
  275. package/dist/{DataTable-2QR5TER5.js.map → DataTable-HWZQGASI.js.map} +0 -0
  276. package/dist/{UnifiedAuthProvider-K4NRGXL4.js.map → UnifiedAuthProvider-3NKDOSOK.js.map} +0 -0
  277. package/dist/{validation-PM_iOaTI.d.ts → validation-D8VcbTzC.d.ts} +2 -2
  278. /package/src/utils/{appNameResolver.test.ts.backup → appNameResolver.test 2.ts} +0 -0
@@ -0,0 +1,2468 @@
1
+ import {
2
+ AccessLevel,
3
+ init_unified
4
+ } from "./chunk-ULBI5JGB.js";
5
+ import {
6
+ DebugLogger,
7
+ init_debugLogger,
8
+ init_organisationContext,
9
+ setOrganisationContext
10
+ } from "./chunk-XLZ7U46Z.js";
11
+ import {
12
+ __esm
13
+ } from "./chunk-PLDDJCW6.js";
14
+
15
+ // src/services/base/BaseService.ts
16
+ var BaseService;
17
+ var init_BaseService = __esm({
18
+ "src/services/base/BaseService.ts"() {
19
+ "use strict";
20
+ BaseService = class {
21
+ constructor() {
22
+ this.subscribers = [];
23
+ this.isInitialized = false;
24
+ }
25
+ /**
26
+ * Subscribe to state changes
27
+ * @param callback Function to call when state changes
28
+ * @returns Unsubscribe function
29
+ */
30
+ subscribe(callback) {
31
+ this.subscribers.push(callback);
32
+ return () => {
33
+ const index = this.subscribers.indexOf(callback);
34
+ if (index > -1) {
35
+ this.subscribers.splice(index, 1);
36
+ }
37
+ };
38
+ }
39
+ /**
40
+ * Notify all subscribers of state changes
41
+ * This triggers React re-renders
42
+ */
43
+ notify() {
44
+ this.subscribers.forEach((callback) => {
45
+ try {
46
+ callback();
47
+ } catch (error) {
48
+ console.error("[BaseService] Error in subscriber callback:", error);
49
+ }
50
+ });
51
+ }
52
+ /**
53
+ * Initialize the service
54
+ * Override in subclasses to implement initialization logic
55
+ */
56
+ async initialize() {
57
+ if (this.isInitialized) {
58
+ return;
59
+ }
60
+ await this.doInitialize();
61
+ this.isInitialized = true;
62
+ }
63
+ /**
64
+ * Cleanup the service
65
+ * Override in subclasses to implement cleanup logic
66
+ */
67
+ cleanup() {
68
+ this.subscribers = [];
69
+ this.doCleanup();
70
+ this.isInitialized = false;
71
+ }
72
+ /**
73
+ * Check if service is initialized
74
+ */
75
+ getInitialized() {
76
+ return this.isInitialized;
77
+ }
78
+ };
79
+ }
80
+ });
81
+
82
+ // src/services/AuthService.ts
83
+ import { AuthError } from "@supabase/supabase-js";
84
+ var AuthService;
85
+ var init_AuthService = __esm({
86
+ "src/services/AuthService.ts"() {
87
+ "use strict";
88
+ init_BaseService();
89
+ init_debugLogger();
90
+ AuthService = class extends BaseService {
91
+ constructor(supabaseClient) {
92
+ super();
93
+ this.user = null;
94
+ this.session = null;
95
+ this.authLoading = true;
96
+ this.authError = null;
97
+ this.supabaseClient = null;
98
+ this.authStateSubscription = null;
99
+ this.supabaseClient = supabaseClient;
100
+ }
101
+ // Auth state getters
102
+ getUser() {
103
+ return this.user;
104
+ }
105
+ getSession() {
106
+ return this.session;
107
+ }
108
+ isAuthenticated() {
109
+ return !!(this.user && this.session);
110
+ }
111
+ isLoading() {
112
+ return this.authLoading;
113
+ }
114
+ getError() {
115
+ return this.authError;
116
+ }
117
+ getSupabaseClient() {
118
+ return this.supabaseClient;
119
+ }
120
+ // Auth methods
121
+ async signIn(email, password) {
122
+ if (!this.supabaseClient) {
123
+ const error = new AuthError("Supabase client not available");
124
+ this.authError = error;
125
+ this.notify();
126
+ return { user: null, session: null, error };
127
+ }
128
+ try {
129
+ const { data, error } = await this.supabaseClient.auth.signInWithPassword({
130
+ email,
131
+ password: password || ""
132
+ });
133
+ if (error) {
134
+ this.authError = error;
135
+ this.user = null;
136
+ this.session = null;
137
+ } else {
138
+ this.authError = null;
139
+ this.user = data.user;
140
+ this.session = data.session;
141
+ }
142
+ this.notify();
143
+ return { user: data.user, session: data.session, error };
144
+ } catch (error) {
145
+ const authError = error instanceof AuthError ? error : new AuthError(error instanceof Error ? error.message : "Authentication failed");
146
+ this.authError = authError;
147
+ this.user = null;
148
+ this.session = null;
149
+ this.notify();
150
+ return { user: null, session: null, error: authError };
151
+ }
152
+ }
153
+ async signUp(email, password) {
154
+ if (!this.supabaseClient) {
155
+ const error = new AuthError("Supabase client not available");
156
+ this.authError = error;
157
+ this.notify();
158
+ return { user: null, session: null, error };
159
+ }
160
+ try {
161
+ const { data, error } = await this.supabaseClient.auth.signUp({
162
+ email,
163
+ password
164
+ });
165
+ if (error) {
166
+ this.authError = error;
167
+ this.user = null;
168
+ this.session = null;
169
+ } else {
170
+ this.authError = null;
171
+ this.user = data.user;
172
+ this.session = data.session;
173
+ }
174
+ this.notify();
175
+ return { user: data.user, session: data.session, error };
176
+ } catch (error) {
177
+ const authError = error instanceof AuthError ? error : new AuthError(error instanceof Error ? error.message : "Authentication failed");
178
+ this.authError = authError;
179
+ this.user = null;
180
+ this.session = null;
181
+ this.notify();
182
+ return { user: null, session: null, error: authError };
183
+ }
184
+ }
185
+ async signOut() {
186
+ if (!this.supabaseClient) {
187
+ const error = new AuthError("Supabase client not available");
188
+ this.authError = error;
189
+ this.notify();
190
+ return { user: null, session: null, error };
191
+ }
192
+ try {
193
+ const { error } = await this.supabaseClient.auth.signOut();
194
+ if (error) {
195
+ this.authError = error;
196
+ } else {
197
+ this.authError = null;
198
+ this.user = null;
199
+ this.session = null;
200
+ }
201
+ this.notify();
202
+ return { user: null, session: null, error };
203
+ } catch (error) {
204
+ const authError = error instanceof AuthError ? error : new AuthError(error instanceof Error ? error.message : "Authentication failed");
205
+ this.authError = authError;
206
+ this.user = null;
207
+ this.session = null;
208
+ this.notify();
209
+ return { user: null, session: null, error: authError };
210
+ }
211
+ }
212
+ async resetPassword(email) {
213
+ if (!this.supabaseClient) {
214
+ const error = new AuthError("Supabase client not available");
215
+ this.authError = error;
216
+ this.notify();
217
+ return { user: null, session: null, error };
218
+ }
219
+ try {
220
+ const { error } = await this.supabaseClient.auth.resetPasswordForEmail(email);
221
+ if (error) {
222
+ this.authError = error;
223
+ } else {
224
+ this.authError = null;
225
+ }
226
+ this.notify();
227
+ return { user: null, session: null, error };
228
+ } catch (error) {
229
+ const authError = error instanceof AuthError ? error : new AuthError(error instanceof Error ? error.message : "Authentication failed");
230
+ this.authError = authError;
231
+ this.notify();
232
+ return { user: null, session: null, error: authError };
233
+ }
234
+ }
235
+ async updatePassword(password) {
236
+ if (!this.supabaseClient) {
237
+ const error = new AuthError("Supabase client not available");
238
+ this.authError = error;
239
+ this.notify();
240
+ return { user: null, session: null, error };
241
+ }
242
+ try {
243
+ const { error } = await this.supabaseClient.auth.updateUser({
244
+ password
245
+ });
246
+ if (error) {
247
+ this.authError = error;
248
+ } else {
249
+ this.authError = null;
250
+ }
251
+ this.notify();
252
+ return { user: null, session: null, error };
253
+ } catch (error) {
254
+ const authError = error;
255
+ this.authError = authError;
256
+ this.notify();
257
+ return { user: null, session: null, error: authError };
258
+ }
259
+ }
260
+ async refreshSession() {
261
+ if (!this.supabaseClient) {
262
+ const error = new AuthError("Supabase client not available");
263
+ this.authError = error;
264
+ this.notify();
265
+ return { user: null, session: null, error };
266
+ }
267
+ try {
268
+ const { data, error } = await this.supabaseClient.auth.refreshSession();
269
+ if (error) {
270
+ this.authError = error;
271
+ this.user = null;
272
+ this.session = null;
273
+ } else {
274
+ this.authError = null;
275
+ if (data?.user && data?.session) {
276
+ this.user = data.user;
277
+ this.session = data.session;
278
+ } else {
279
+ this.user = null;
280
+ this.session = null;
281
+ }
282
+ }
283
+ this.notify();
284
+ return {
285
+ user: data?.user && data?.session ? data.user : null,
286
+ session: data?.session ?? null,
287
+ error
288
+ };
289
+ } catch (error) {
290
+ const authError = error instanceof AuthError ? error : new AuthError(error instanceof Error ? error.message : "Authentication failed");
291
+ this.authError = authError;
292
+ this.user = null;
293
+ this.session = null;
294
+ this.notify();
295
+ return { user: null, session: null, error: authError };
296
+ }
297
+ }
298
+ // Lifecycle methods
299
+ async initialize() {
300
+ await super.initialize();
301
+ await this.setupAuthStateListener();
302
+ await this.restoreSession();
303
+ }
304
+ cleanup() {
305
+ if (this.authStateSubscription && typeof this.authStateSubscription.unsubscribe === "function") {
306
+ this.authStateSubscription.unsubscribe();
307
+ this.authStateSubscription = null;
308
+ }
309
+ super.cleanup();
310
+ }
311
+ async doInitialize() {
312
+ this.setupErrorHandlers();
313
+ }
314
+ doCleanup() {
315
+ this.removeErrorHandlers();
316
+ }
317
+ async setupAuthStateListener() {
318
+ if (!this.supabaseClient) {
319
+ this.authLoading = false;
320
+ this.notify();
321
+ return;
322
+ }
323
+ try {
324
+ this.authStateSubscription = this.supabaseClient.auth.onAuthStateChange(
325
+ (event, session) => {
326
+ try {
327
+ DebugLogger.log("AuthService", "Auth state changed:", event, session?.user?.email);
328
+ if (event === "SIGNED_OUT") {
329
+ DebugLogger.log("AuthService", "User signed out, clearing all state");
330
+ this.session = null;
331
+ this.user = null;
332
+ this.authError = null;
333
+ } else if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED") {
334
+ DebugLogger.log("AuthService", "User signed in or token refreshed");
335
+ this.session = session;
336
+ this.user = session?.user ?? null;
337
+ if (session) {
338
+ this.authError = null;
339
+ }
340
+ } else if (event === "INITIAL_SESSION") {
341
+ DebugLogger.log("AuthService", "Initial session event");
342
+ if (session) {
343
+ this.session = session;
344
+ this.user = session.user ?? null;
345
+ this.authError = null;
346
+ }
347
+ }
348
+ this.authLoading = false;
349
+ this.notify();
350
+ } catch (error) {
351
+ console.warn("[AuthService] Error in auth state change handler:", error);
352
+ this.authLoading = false;
353
+ this.notify();
354
+ }
355
+ }
356
+ );
357
+ } catch (error) {
358
+ console.error("[AuthService] Failed to setup auth state listener:", error);
359
+ throw error;
360
+ }
361
+ }
362
+ async restoreSession() {
363
+ if (!this.supabaseClient) {
364
+ this.authLoading = false;
365
+ this.notify();
366
+ return;
367
+ }
368
+ try {
369
+ DebugLogger.log("AuthService", "Initializing authentication...");
370
+ const { data: { session: currentSession }, error: sessionError } = await this.supabaseClient.auth.getSession();
371
+ if (sessionError) {
372
+ console.warn("[AuthService] Error getting current session:", sessionError);
373
+ }
374
+ if (currentSession) {
375
+ DebugLogger.log("AuthService", "Session restored from storage:", currentSession.user?.email);
376
+ this.session = currentSession;
377
+ this.user = currentSession.user;
378
+ this.authError = null;
379
+ } else {
380
+ DebugLogger.log("AuthService", "No session found in storage");
381
+ const { data: { user: currentUser }, error: userError } = await this.supabaseClient.auth.getUser();
382
+ if (userError) {
383
+ DebugLogger.log("AuthService", "No user found:", userError.message);
384
+ } else if (currentUser) {
385
+ DebugLogger.log("AuthService", "User found without session:", currentUser.email);
386
+ this.user = currentUser;
387
+ }
388
+ }
389
+ this.authLoading = false;
390
+ this.notify();
391
+ } catch (error) {
392
+ console.error("[AuthService] Error during auth initialization:", error);
393
+ this.authLoading = false;
394
+ this.notify();
395
+ }
396
+ }
397
+ setupErrorHandlers() {
398
+ if (typeof window === "undefined") return;
399
+ const handleError = (event) => {
400
+ if (event.error?.message?.includes("AuthSessionMissingError") || event.error?.message?.includes("Auth session missing")) {
401
+ console.warn("[AuthService] Suppressing AuthSessionMissingError during logout");
402
+ event.preventDefault();
403
+ return false;
404
+ }
405
+ };
406
+ const handleUnhandledRejection = (event) => {
407
+ if (event.reason?.message?.includes("AuthSessionMissingError") || event.reason?.message?.includes("Auth session missing")) {
408
+ console.warn("[AuthService] Suppressing unhandled AuthSessionMissingError");
409
+ event.preventDefault();
410
+ return false;
411
+ }
412
+ };
413
+ window.addEventListener("error", handleError);
414
+ window.addEventListener("unhandledrejection", handleUnhandledRejection);
415
+ }
416
+ removeErrorHandlers() {
417
+ if (typeof window === "undefined") return;
418
+ }
419
+ };
420
+ }
421
+ });
422
+
423
+ // src/providers/services/AuthServiceProvider.tsx
424
+ import { createContext, useContext, useMemo, useEffect } from "react";
425
+ import { jsx } from "react/jsx-runtime";
426
+ function AuthServiceProvider({ children, supabaseClient }) {
427
+ const authService = useMemo(
428
+ () => new AuthService(supabaseClient),
429
+ [supabaseClient]
430
+ );
431
+ useEffect(() => {
432
+ authService.initialize().catch((error) => {
433
+ console.error("[AuthServiceProvider] Failed to initialize auth service:", error);
434
+ });
435
+ return () => {
436
+ authService.cleanup();
437
+ };
438
+ }, [authService]);
439
+ const contextValue = useMemo(() => ({
440
+ authService
441
+ }), [authService]);
442
+ return /* @__PURE__ */ jsx(AuthServiceContext.Provider, { value: contextValue, children });
443
+ }
444
+ var AuthServiceContext, useAuthService;
445
+ var init_AuthServiceProvider = __esm({
446
+ "src/providers/services/AuthServiceProvider.tsx"() {
447
+ "use strict";
448
+ init_AuthService();
449
+ AuthServiceContext = createContext(null);
450
+ useAuthService = () => {
451
+ const context = useContext(AuthServiceContext);
452
+ if (!context) {
453
+ throw new Error("useAuthService must be used within AuthServiceProvider");
454
+ }
455
+ return context.authService;
456
+ };
457
+ }
458
+ });
459
+
460
+ // src/services/RBACService.ts
461
+ var RBACService;
462
+ var init_RBACService = __esm({
463
+ "src/services/RBACService.ts"() {
464
+ "use strict";
465
+ init_BaseService();
466
+ init_unified();
467
+ init_debugLogger();
468
+ RBACService = class extends BaseService {
469
+ constructor(supabaseClient, user, session, appName) {
470
+ super();
471
+ this.permissions = {};
472
+ this.roles = [];
473
+ this.accessLevel = "viewer" /* VIEWER */;
474
+ this.rbacLoading = false;
475
+ this.rbacError = null;
476
+ this.selectedEventId = null;
477
+ this.appConfig = null;
478
+ this.userEventAccess = [];
479
+ this.eventAccessLoading = false;
480
+ this.selectedOrganisationId = null;
481
+ // Dependencies
482
+ this.supabaseClient = null;
483
+ this.user = null;
484
+ this.session = null;
485
+ this.appName = "";
486
+ this.supabaseClient = supabaseClient;
487
+ this.user = user;
488
+ this.session = session;
489
+ this.appName = appName;
490
+ }
491
+ // Update dependencies
492
+ updateDependencies(user, session, appName) {
493
+ this.user = user;
494
+ this.session = session;
495
+ this.appName = appName;
496
+ this.notify();
497
+ }
498
+ // RBAC state getters
499
+ getPermissions() {
500
+ return this.permissions;
501
+ }
502
+ getRoles() {
503
+ return this.roles;
504
+ }
505
+ getAccessLevel() {
506
+ return this.accessLevel;
507
+ }
508
+ isLoading() {
509
+ return this.rbacLoading;
510
+ }
511
+ getError() {
512
+ return this.rbacError;
513
+ }
514
+ getSelectedEventId() {
515
+ return this.selectedEventId;
516
+ }
517
+ getAppConfig() {
518
+ return this.appConfig;
519
+ }
520
+ getUserEventAccess() {
521
+ return this.userEventAccess;
522
+ }
523
+ isEventAccessLoading() {
524
+ return this.eventAccessLoading;
525
+ }
526
+ getSelectedOrganisationId() {
527
+ return this.selectedOrganisationId;
528
+ }
529
+ // RBAC methods
530
+ hasPermission(permission, orgId) {
531
+ return !!this.permissions[permission];
532
+ }
533
+ hasAnyPermission(permissions, orgId) {
534
+ return permissions.some((p) => !!this.permissions[p]);
535
+ }
536
+ hasAllPermissions(permissions, orgId) {
537
+ return permissions.every((p) => !!this.permissions[p]);
538
+ }
539
+ hasRole(role) {
540
+ const isSuperAdmin = this.user?.user_metadata?.globalRole === "super_admin";
541
+ if (role.toLowerCase() === "super_admin" || role.toLowerCase() === "super") {
542
+ return isSuperAdmin;
543
+ }
544
+ return this.roles.includes(role);
545
+ }
546
+ hasAccessLevel(level) {
547
+ const levels = Object.values(AccessLevel);
548
+ return levels.indexOf(this.accessLevel) >= levels.indexOf(level);
549
+ }
550
+ canAccess(resource, action, orgId) {
551
+ const permission = `${resource}:${action}`;
552
+ return this.hasPermission(permission, orgId);
553
+ }
554
+ async validatePermission(permission, orgId) {
555
+ return this.hasPermission(permission, orgId);
556
+ }
557
+ async validateAccess(resource, action, orgId) {
558
+ return this.canAccess(resource, action, orgId);
559
+ }
560
+ async refreshPermissions(eventId, orgId) {
561
+ if (!this.supabaseClient || !this.user || !this.appConfig || !this.session) {
562
+ DebugLogger.log("RBACService", "refreshPermissions: Missing required dependencies, clearing permissions");
563
+ this.permissions = {};
564
+ this.roles = [];
565
+ this.accessLevel = "viewer" /* VIEWER */;
566
+ this.notify();
567
+ return;
568
+ }
569
+ const isSuperAdmin = this.user?.user_metadata?.globalRole === "super_admin";
570
+ if (isSuperAdmin) {
571
+ this.permissions = {
572
+ "admin:create": true,
573
+ "admin:read": true,
574
+ "admin:update": true,
575
+ "admin:delete": true,
576
+ "users:create": true,
577
+ "users:read": true,
578
+ "users:update": true,
579
+ "users:delete": true,
580
+ "events:create": true,
581
+ "events:read": true,
582
+ "events:update": true,
583
+ "events:delete": true
584
+ };
585
+ this.roles = ["super_admin"];
586
+ this.accessLevel = "admin" /* ADMIN */;
587
+ this.notify();
588
+ return;
589
+ }
590
+ const shouldLoadDirectPermissions = !eventId && !this.appConfig.requires_event;
591
+ const shouldLoadEventPermissions = eventId;
592
+ const shouldClearPermissions = !eventId && this.appConfig.requires_event;
593
+ if (shouldClearPermissions) {
594
+ this.permissions = {};
595
+ this.roles = [];
596
+ this.accessLevel = "viewer" /* VIEWER */;
597
+ this.notify();
598
+ return;
599
+ }
600
+ if (!shouldLoadDirectPermissions && !shouldLoadEventPermissions) {
601
+ return;
602
+ }
603
+ this.rbacLoading = true;
604
+ this.rbacError = null;
605
+ this.notify();
606
+ try {
607
+ const { getCurrentAppName } = await import("./appNameResolver-UURKN7NF.js");
608
+ const resolvedAppName = getCurrentAppName() || this.appName;
609
+ const { data: appData, error: appError } = await this.supabaseClient.from("rbac_apps").select("id").eq("name", resolvedAppName).eq("is_active", true).single();
610
+ if (appError || !appData) {
611
+ console.warn("App not found or inactive:", resolvedAppName);
612
+ this.rbacLoading = false;
613
+ this.notify();
614
+ return;
615
+ }
616
+ const { data, error } = await this.supabaseClient.rpc("rbac_permissions_get", {
617
+ p_user_id: this.user.id,
618
+ p_app_id: appData.id,
619
+ p_event_id: eventId || null,
620
+ p_organisation_id: orgId || this.selectedOrganisationId || null
621
+ });
622
+ if (error) {
623
+ throw error;
624
+ }
625
+ const { permissions, roles, access_level } = this.transformRBACPermissions(data, this.appName);
626
+ this.permissions = permissions;
627
+ this.roles = roles;
628
+ this.accessLevel = access_level;
629
+ } catch (err) {
630
+ this.rbacError = err;
631
+ } finally {
632
+ this.rbacLoading = false;
633
+ this.notify();
634
+ }
635
+ }
636
+ setSelectedEventId(eventId) {
637
+ this.selectedEventId = eventId;
638
+ this.notify();
639
+ }
640
+ async loadUserEventAccess(orgId) {
641
+ if (!this.supabaseClient || !this.user || !this.session) {
642
+ DebugLogger.log("RBACService", "loadUserEventAccess: Missing required dependencies, clearing event access");
643
+ this.userEventAccess = [];
644
+ this.notify();
645
+ return;
646
+ }
647
+ this.eventAccessLoading = true;
648
+ this.notify();
649
+ try {
650
+ const { getCurrentAppName } = await import("./appNameResolver-UURKN7NF.js");
651
+ const resolvedAppName = getCurrentAppName() || this.appName;
652
+ const { data: appData, error: appError } = await this.supabaseClient.from("rbac_apps").select("id").eq("name", resolvedAppName).eq("is_active", true).single();
653
+ if (appError || !appData) {
654
+ console.warn("App not found or inactive:", resolvedAppName);
655
+ this.eventAccessLoading = false;
656
+ this.notify();
657
+ return;
658
+ }
659
+ const { data, error } = await this.supabaseClient.from("rbac_event_app_roles").select(`
660
+ event_id,
661
+ role,
662
+ granted_at
663
+ `).eq("user_id", this.user.id).eq("app_id", appData.id);
664
+ if (error) {
665
+ console.error("Failed to load user event access:", error);
666
+ this.userEventAccess = [];
667
+ this.notify();
668
+ return;
669
+ }
670
+ const eventAccess = data?.map((item) => ({
671
+ event_id: item.event_id,
672
+ event_name: item.event_name || "Unknown Event",
673
+ // Event details not available in this query
674
+ event_description: item.event_description || null,
675
+ // Not available in this schema
676
+ start_date: item.start_date || "",
677
+ // Event date not available in this query
678
+ end_date: item.end_date || "",
679
+ // Event date not available in this query
680
+ event_status: item.event_status || "unknown",
681
+ // Not available in this schema
682
+ app_id: item.app_id || appData.id,
683
+ access_level: item.access_level || item.role,
684
+ // Map role to access_level
685
+ granted_at: item.granted_at,
686
+ organisation_id: item.organisation_id || ""
687
+ // Will be populated from event's organisation_id if needed
688
+ })) || [];
689
+ this.userEventAccess = eventAccess;
690
+ } catch (error) {
691
+ console.error("Error loading user event access:", error);
692
+ this.userEventAccess = [];
693
+ } finally {
694
+ this.eventAccessLoading = false;
695
+ this.notify();
696
+ }
697
+ }
698
+ getUserEventAccessById(eventId) {
699
+ return this.userEventAccess.find((access) => access.event_id === eventId);
700
+ }
701
+ requireOrganisationContext() {
702
+ if (!this.selectedOrganisationId) {
703
+ throw new Error("Organisation context is required but not available");
704
+ }
705
+ return this.selectedOrganisationId;
706
+ }
707
+ // Lifecycle methods
708
+ async initialize() {
709
+ await super.initialize();
710
+ await this.loadAppConfig();
711
+ const isSuperAdmin = this.user?.user_metadata?.globalRole === "super_admin";
712
+ if (!isSuperAdmin && this.appConfig) {
713
+ await this.refreshPermissions(this.selectedEventId || void 0);
714
+ }
715
+ await this.loadPersistedState();
716
+ }
717
+ cleanup() {
718
+ super.cleanup();
719
+ }
720
+ async doInitialize() {
721
+ const isSuperAdmin = this.user?.user_metadata?.globalRole === "super_admin";
722
+ if (isSuperAdmin) {
723
+ this.roles = ["super_admin"];
724
+ this.accessLevel = "admin" /* ADMIN */;
725
+ this.permissions = {
726
+ "admin:create": true,
727
+ "admin:read": true,
728
+ "admin:update": true,
729
+ "admin:delete": true,
730
+ "users:create": true,
731
+ "users:read": true,
732
+ "users:update": true,
733
+ "users:delete": true,
734
+ "events:create": true,
735
+ "events:read": true,
736
+ "events:update": true,
737
+ "events:delete": true
738
+ };
739
+ } else {
740
+ this.roles = [];
741
+ this.accessLevel = "viewer" /* VIEWER */;
742
+ this.permissions = {};
743
+ }
744
+ }
745
+ doCleanup() {
746
+ }
747
+ async loadAppConfig() {
748
+ if (!this.supabaseClient) return;
749
+ try {
750
+ const { getCurrentAppName } = await import("./appNameResolver-UURKN7NF.js");
751
+ const resolvedAppName = getCurrentAppName() || this.appName;
752
+ const { data: appData, error: appError } = await this.supabaseClient.from("rbac_apps").select("id").eq("name", resolvedAppName).eq("is_active", true).single();
753
+ if (appError || !appData) {
754
+ console.warn("App not found or inactive:", resolvedAppName);
755
+ this.appConfig = {
756
+ supports_direct_access: false,
757
+ requires_event: true
758
+ };
759
+ return;
760
+ }
761
+ const response = await this.supabaseClient.rpc("get_app_config", {
762
+ app_id: appData.id
763
+ });
764
+ const { data } = response || {};
765
+ if (data && data.length > 0) {
766
+ this.appConfig = {
767
+ supports_direct_access: false,
768
+ requires_event: data[0].requires_event
769
+ };
770
+ } else {
771
+ this.appConfig = {
772
+ supports_direct_access: false,
773
+ requires_event: true
774
+ };
775
+ }
776
+ } catch (error) {
777
+ console.warn("Failed to load app configuration:", error);
778
+ this.appConfig = {
779
+ supports_direct_access: false,
780
+ requires_event: true
781
+ };
782
+ }
783
+ }
784
+ async loadPersistedState() {
785
+ try {
786
+ const persistedEvent = localStorage.getItem("pace-core-selected-event");
787
+ if (persistedEvent) {
788
+ this.selectedEventId = JSON.parse(persistedEvent);
789
+ }
790
+ } catch (error) {
791
+ console.warn("Clearing corrupted localStorage data");
792
+ localStorage.removeItem("pace-core-selected-event");
793
+ }
794
+ }
795
+ transformRBACPermissions(rbacData, appName) {
796
+ const permissions = {};
797
+ let roles = [];
798
+ let access_level = "viewer" /* VIEWER */;
799
+ if (!rbacData || !Array.isArray(rbacData)) {
800
+ return { permissions: {}, roles: ["viewer"], access_level: "viewer" /* VIEWER */ };
801
+ }
802
+ const superAdminPerm = rbacData.find((p) => p.permission_type === "all_permissions");
803
+ if (superAdminPerm) {
804
+ return {
805
+ permissions: { "all:all": true },
806
+ roles: ["super"],
807
+ access_level: "super" /* SUPER */
808
+ };
809
+ }
810
+ const eventAppPerms = rbacData.filter((p) => p.permission_type === "event_app_access");
811
+ if (eventAppPerms.length > 0) {
812
+ const role = eventAppPerms[0].role_name;
813
+ switch (role) {
814
+ case "event_admin":
815
+ access_level = "admin" /* ADMIN */;
816
+ roles = ["admin"];
817
+ break;
818
+ case "planner":
819
+ access_level = "planner" /* PLANNER */;
820
+ roles = ["planner"];
821
+ break;
822
+ case "participant":
823
+ access_level = "participant" /* PARTICIPANT */;
824
+ roles = ["participant"];
825
+ break;
826
+ case "editor":
827
+ access_level = "editor" /* EDITOR */;
828
+ roles = ["editor"];
829
+ break;
830
+ case "viewer":
831
+ default:
832
+ access_level = "viewer" /* VIEWER */;
833
+ roles = ["viewer"];
834
+ break;
835
+ }
836
+ eventAppPerms.forEach((perm) => {
837
+ if (perm.permission) {
838
+ permissions[perm.permission] = true;
839
+ }
840
+ });
841
+ const basePermissions = ["read"];
842
+ if (["event_admin", "planner"].includes(role)) {
843
+ basePermissions.push("create", "update");
844
+ }
845
+ if (role === "event_admin") {
846
+ basePermissions.push("delete");
847
+ }
848
+ basePermissions.forEach((operation) => {
849
+ permissions[`default:${operation}`] = true;
850
+ });
851
+ }
852
+ const orgPerms = rbacData.filter((p) => p.permission_type === "organisation_access");
853
+ if (orgPerms.length > 0) {
854
+ const role = orgPerms[0].role_name;
855
+ if (role === "org_admin") {
856
+ access_level = "admin" /* ADMIN */;
857
+ roles = ["admin"];
858
+ ["create", "read", "update", "delete"].forEach((operation) => {
859
+ permissions[`default:${operation}`] = true;
860
+ });
861
+ }
862
+ }
863
+ return { permissions, roles, access_level };
864
+ }
865
+ };
866
+ }
867
+ });
868
+
869
+ // src/providers/services/RBACServiceProvider.tsx
870
+ import { createContext as createContext2, useContext as useContext2, useMemo as useMemo2, useEffect as useEffect2 } from "react";
871
+ import { jsx as jsx2 } from "react/jsx-runtime";
872
+ function RBACServiceProvider({
873
+ children,
874
+ supabaseClient,
875
+ user,
876
+ session,
877
+ appName
878
+ }) {
879
+ const rbacService = useMemo2(
880
+ () => new RBACService(supabaseClient, user, session, appName),
881
+ [supabaseClient, user, session, appName]
882
+ );
883
+ useEffect2(() => {
884
+ rbacService.updateDependencies(user, session, appName);
885
+ }, [rbacService, user, session, appName]);
886
+ useEffect2(() => {
887
+ rbacService.initialize().catch((error) => {
888
+ console.error("[RBACServiceProvider] Failed to initialize RBAC service:", error);
889
+ });
890
+ return () => {
891
+ rbacService.cleanup();
892
+ };
893
+ }, [rbacService]);
894
+ const contextValue = useMemo2(() => ({
895
+ rbacService
896
+ }), [rbacService]);
897
+ return /* @__PURE__ */ jsx2(RBACServiceContext.Provider, { value: contextValue, children });
898
+ }
899
+ var RBACServiceContext, useRBACService;
900
+ var init_RBACServiceProvider = __esm({
901
+ "src/providers/services/RBACServiceProvider.tsx"() {
902
+ "use strict";
903
+ init_RBACService();
904
+ RBACServiceContext = createContext2(null);
905
+ useRBACService = () => {
906
+ const context = useContext2(RBACServiceContext);
907
+ if (!context) {
908
+ throw new Error("useRBACService must be used within RBACServiceProvider");
909
+ }
910
+ return context.rbacService;
911
+ };
912
+ }
913
+ });
914
+
915
+ // src/services/OrganisationService.ts
916
+ var OrganisationService;
917
+ var init_OrganisationService = __esm({
918
+ "src/services/OrganisationService.ts"() {
919
+ "use strict";
920
+ init_BaseService();
921
+ init_organisationContext();
922
+ init_debugLogger();
923
+ OrganisationService = class extends BaseService {
924
+ constructor(supabaseClient, user, session) {
925
+ super();
926
+ this._selectedOrganisation = null;
927
+ this._organisations = [];
928
+ this._userMemberships = [];
929
+ this._roleMapState = /* @__PURE__ */ new Map();
930
+ this._isLoading = true;
931
+ this._error = null;
932
+ this._isContextReady = false;
933
+ this.retryCount = 0;
934
+ // Dependencies
935
+ this.supabaseClient = null;
936
+ this.user = null;
937
+ this.session = null;
938
+ // Internal state management
939
+ this.isLoadingRef = false;
940
+ this.lastLoadTimeRef = 0;
941
+ this.hasFailedRef = false;
942
+ this.abortControllerRef = null;
943
+ this.supabaseClient = supabaseClient;
944
+ this.user = user;
945
+ this.session = session;
946
+ }
947
+ // Interface implementation
948
+ getSelectedOrganisation() {
949
+ return this._selectedOrganisation;
950
+ }
951
+ getOrganisations() {
952
+ return this._organisations;
953
+ }
954
+ getUserMemberships() {
955
+ return this._userMemberships;
956
+ }
957
+ isLoading() {
958
+ return this._isLoading;
959
+ }
960
+ getError() {
961
+ return this._error;
962
+ }
963
+ hasValidOrganisationContext() {
964
+ return !!(this._selectedOrganisation && !this._isLoading && !this._error && this._isContextReady);
965
+ }
966
+ isContextReady() {
967
+ return this._isContextReady;
968
+ }
969
+ // Additional methods for testing
970
+ setSelectedOrganisation(organisation) {
971
+ this._selectedOrganisation = organisation;
972
+ if (organisation) {
973
+ localStorage.setItem("pace-core-selected-organisation", JSON.stringify(organisation));
974
+ this.setDatabaseOrganisationContext(organisation);
975
+ } else {
976
+ localStorage.removeItem("pace-core-selected-organisation");
977
+ this._isContextReady = false;
978
+ }
979
+ this.notify();
980
+ }
981
+ // For testing: expose dependencies
982
+ getDependencies() {
983
+ return {
984
+ user: this.user,
985
+ session: this.session,
986
+ supabaseClient: this.supabaseClient
987
+ };
988
+ }
989
+ // For testing: manually set state
990
+ setTestState(organisations, memberships, roleMap, selectedOrg = null) {
991
+ this._organisations = organisations;
992
+ this._userMemberships = memberships;
993
+ this._roleMapState = roleMap;
994
+ if (selectedOrg) {
995
+ this._selectedOrganisation = selectedOrg;
996
+ } else if (organisations.length > 0) {
997
+ this._selectedOrganisation = organisations[0];
998
+ }
999
+ this._isLoading = false;
1000
+ this._error = null;
1001
+ this.notify();
1002
+ }
1003
+ // Update dependencies
1004
+ updateDependencies(user, session) {
1005
+ this.user = user;
1006
+ this.session = session;
1007
+ this.notify();
1008
+ }
1009
+ // Organisation methods
1010
+ async switchOrganisation(orgId) {
1011
+ DebugLogger.log("OrganisationService", "Switching to organisation:", orgId);
1012
+ if (!this.validateOrganisationAccess(orgId)) {
1013
+ throw new Error(`User does not have access to organisation ${orgId}`);
1014
+ }
1015
+ const targetOrg = this._organisations.find((org) => org.id === orgId);
1016
+ if (!targetOrg) {
1017
+ throw new Error(`Organisation ${orgId} not found in user's organisations`);
1018
+ }
1019
+ this._selectedOrganisation = targetOrg;
1020
+ localStorage.setItem("pace-core-selected-organisation", JSON.stringify(targetOrg));
1021
+ await this.setDatabaseOrganisationContext(targetOrg);
1022
+ DebugLogger.log("OrganisationService", "Switched to organisation:", targetOrg.display_name);
1023
+ this.notify();
1024
+ }
1025
+ getUserRole(orgId) {
1026
+ const targetOrgId = orgId || this._selectedOrganisation?.id;
1027
+ if (!targetOrgId) return "no_access";
1028
+ return this._roleMapState.get(targetOrgId) || "no_access";
1029
+ }
1030
+ validateOrganisationAccess(orgId) {
1031
+ return this._userMemberships.some(
1032
+ (m) => m.organisation_id === orgId && m.status === "active" && m.revoked_at === null
1033
+ );
1034
+ }
1035
+ async refreshOrganisations() {
1036
+ if (!this.user || !this.session || !this.supabaseClient) return;
1037
+ this._isLoading = true;
1038
+ this.notify();
1039
+ await this.loadUserOrganisations();
1040
+ }
1041
+ ensureOrganisationContext() {
1042
+ if (!this._selectedOrganisation) {
1043
+ throw new Error("Organisation context is required but not available");
1044
+ }
1045
+ return this._selectedOrganisation;
1046
+ }
1047
+ isOrganisationSecure() {
1048
+ return !!(this._selectedOrganisation && this.user);
1049
+ }
1050
+ getPrimaryOrganisation() {
1051
+ const rolePriority = ["org_admin", "leader", "member"];
1052
+ for (const role of rolePriority) {
1053
+ const membership = this._userMemberships.find((m) => m.role === role);
1054
+ if (membership) {
1055
+ return this._organisations.find((org) => org.id === membership.organisation_id) || null;
1056
+ }
1057
+ }
1058
+ return null;
1059
+ }
1060
+ buildOrganisationHierarchy(orgs) {
1061
+ const orgMap = /* @__PURE__ */ new Map();
1062
+ orgs.forEach((org) => orgMap.set(org.id, org));
1063
+ const roots = [];
1064
+ orgs.forEach((org) => {
1065
+ if (!org.parent_id) {
1066
+ roots.push({
1067
+ organisation: org,
1068
+ children: [],
1069
+ depth: 0
1070
+ });
1071
+ }
1072
+ });
1073
+ return roots;
1074
+ }
1075
+ // Lifecycle methods
1076
+ async initialize() {
1077
+ await super.initialize();
1078
+ await this.loadUserOrganisations();
1079
+ }
1080
+ cleanup() {
1081
+ this.isLoadingRef = false;
1082
+ this.hasFailedRef = false;
1083
+ this.lastLoadTimeRef = 0;
1084
+ if (this.abortControllerRef) {
1085
+ this.abortControllerRef.abort();
1086
+ this.abortControllerRef = null;
1087
+ }
1088
+ this._selectedOrganisation = null;
1089
+ this._organisations = [];
1090
+ this._userMemberships = [];
1091
+ this._roleMapState = /* @__PURE__ */ new Map();
1092
+ this._isLoading = false;
1093
+ this._error = null;
1094
+ this._isContextReady = false;
1095
+ super.cleanup();
1096
+ }
1097
+ async doInitialize() {
1098
+ }
1099
+ doCleanup() {
1100
+ }
1101
+ async setDatabaseOrganisationContext(organisation) {
1102
+ if (!this.supabaseClient || !this.session) {
1103
+ console.warn("[OrganisationService] No Supabase client or session available for setting organisation context");
1104
+ this._isContextReady = false;
1105
+ this.notify();
1106
+ return;
1107
+ }
1108
+ try {
1109
+ await setOrganisationContext(this.supabaseClient, organisation.id);
1110
+ DebugLogger.log("OrganisationService", "Database organisation context set to:", organisation.display_name);
1111
+ this._isContextReady = true;
1112
+ this.notify();
1113
+ } catch (error) {
1114
+ console.error("[OrganisationService] Failed to set database organisation context:", error);
1115
+ this._isContextReady = false;
1116
+ this.notify();
1117
+ }
1118
+ }
1119
+ async loadUserOrganisations() {
1120
+ const callId = Math.random().toString(36).substr(2, 9);
1121
+ console.log(`[OrganisationService] Starting loadUserOrganisations call ${callId}`);
1122
+ if (!this.user || !this.session || !this.supabaseClient) {
1123
+ DebugLogger.log("OrganisationService", "Clearing organisation state - no user, session, or supabase client");
1124
+ this._selectedOrganisation = null;
1125
+ this._organisations = [];
1126
+ this._userMemberships = [];
1127
+ this._isLoading = false;
1128
+ this._error = null;
1129
+ this.notify();
1130
+ return;
1131
+ }
1132
+ if (this.isLoadingRef) {
1133
+ console.log("OrganisationService", "Already loading, skipping duplicate load");
1134
+ return;
1135
+ }
1136
+ const now = Date.now();
1137
+ if (now - this.lastLoadTimeRef < 2e3) {
1138
+ console.log("OrganisationService", "Too soon since last load, skipping");
1139
+ return;
1140
+ }
1141
+ if (this.abortControllerRef) {
1142
+ this.abortControllerRef.abort();
1143
+ }
1144
+ this.abortControllerRef = new AbortController();
1145
+ const abortSignal = this.abortControllerRef.signal;
1146
+ this.lastLoadTimeRef = now;
1147
+ this.isLoadingRef = true;
1148
+ this._isLoading = true;
1149
+ this._error = null;
1150
+ this.notify();
1151
+ try {
1152
+ DebugLogger.log("OrganisationService", "Loading organisations for user:", this.user.id);
1153
+ console.log("[OrganisationService] Supabase client ready:", {
1154
+ isConnected: !!this.supabaseClient,
1155
+ hasAuth: !!this.supabaseClient.auth,
1156
+ hasRpc: !!this.supabaseClient.rpc
1157
+ });
1158
+ let memberships, membershipError;
1159
+ try {
1160
+ console.log("[OrganisationService] Making RPC call to data_user_organisation_roles_get...");
1161
+ const timeoutPromise = new Promise((_, reject) => {
1162
+ const timeoutId = setTimeout(() => reject(new Error("RPC call timeout after 10 seconds")), 1e4);
1163
+ abortSignal.addEventListener("abort", () => {
1164
+ clearTimeout(timeoutId);
1165
+ reject(new Error("Request aborted"));
1166
+ });
1167
+ });
1168
+ const rpcPromise = this.supabaseClient.rpc("data_user_organisation_roles_get", {
1169
+ p_user_id: this.user.id,
1170
+ p_organisation_id: null
1171
+ });
1172
+ if (abortSignal.aborted) {
1173
+ throw new Error("Request aborted");
1174
+ }
1175
+ const result = await Promise.race([rpcPromise, timeoutPromise]);
1176
+ console.log("[OrganisationService] RPC call completed:", {
1177
+ hasData: !!result.data,
1178
+ hasError: !!result.error,
1179
+ dataLength: result.data?.length || 0,
1180
+ errorMessage: result.error?.message || "No error"
1181
+ });
1182
+ memberships = result.data?.filter(
1183
+ (role) => ["org_admin", "leader", "member"].includes(role.role)
1184
+ ) || [];
1185
+ membershipError = result.error;
1186
+ } catch (queryError) {
1187
+ membershipError = queryError;
1188
+ }
1189
+ if (membershipError) {
1190
+ console.error("[OrganisationService] Error loading memberships:", membershipError);
1191
+ if (membershipError.message?.includes("timeout")) {
1192
+ console.log("[OrganisationService] RPC timed out, trying direct database query as fallback...");
1193
+ try {
1194
+ if (abortSignal.aborted) {
1195
+ throw new Error("Request aborted");
1196
+ }
1197
+ const { data: fallbackData, error: fallbackError } = await this.supabaseClient.from("rbac_organisation_roles").select(`
1198
+ id,
1199
+ user_id,
1200
+ organisation_id,
1201
+ role,
1202
+ status,
1203
+ granted_at,
1204
+ granted_by,
1205
+ revoked_at,
1206
+ revoked_by,
1207
+ notes,
1208
+ created_at,
1209
+ updated_at,
1210
+ organisations!inner(
1211
+ id,
1212
+ name,
1213
+ display_name,
1214
+ subscription_tier,
1215
+ settings,
1216
+ is_active,
1217
+ parent_id,
1218
+ created_at,
1219
+ updated_at
1220
+ )
1221
+ `).eq("user_id", this.user.id).eq("status", "active").is("revoked_at", null).in("role", ["org_admin", "leader", "member"]);
1222
+ if (fallbackError) {
1223
+ console.error("[OrganisationService] Fallback query also failed:", fallbackError);
1224
+ throw membershipError;
1225
+ }
1226
+ console.log("[OrganisationService] Fallback query successful, got", fallbackData?.length || 0, "memberships");
1227
+ memberships = fallbackData || [];
1228
+ membershipError = null;
1229
+ } catch (fallbackErr) {
1230
+ console.error("[OrganisationService] Fallback query failed:", fallbackErr);
1231
+ throw membershipError;
1232
+ }
1233
+ } else {
1234
+ throw membershipError;
1235
+ }
1236
+ }
1237
+ DebugLogger.log("OrganisationService", "Raw memberships data:", memberships);
1238
+ if (!memberships || memberships.length === 0) {
1239
+ throw new Error("User has no active organisation memberships");
1240
+ }
1241
+ const organisationIds = memberships.map((m) => m.organisation_id).filter((id) => {
1242
+ if (!id || typeof id !== "string") {
1243
+ console.warn("[OrganisationService] Invalid organisation ID (not string):", id);
1244
+ return false;
1245
+ }
1246
+ const trimmedId = id.trim();
1247
+ if (trimmedId === "") {
1248
+ console.warn("[OrganisationService] Empty organisation ID found");
1249
+ return false;
1250
+ }
1251
+ const isValidUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(trimmedId);
1252
+ if (!isValidUuid) {
1253
+ console.warn("[OrganisationService] Invalid UUID format:", trimmedId);
1254
+ }
1255
+ return isValidUuid;
1256
+ });
1257
+ if (organisationIds.length === 0) {
1258
+ console.warn("[OrganisationService] No valid organisation IDs found in memberships:", memberships);
1259
+ throw new Error("No valid organisation IDs found in memberships");
1260
+ }
1261
+ DebugLogger.log("OrganisationService", "Valid organisation IDs:", organisationIds);
1262
+ if (abortSignal.aborted) {
1263
+ throw new Error("Request aborted");
1264
+ }
1265
+ const { data: allOrganisations, error: orgError } = await this.supabaseClient.from("organisations").select("id, name, display_name, subscription_tier, settings, is_active, parent_id, created_at, updated_at");
1266
+ if (orgError) {
1267
+ console.error("[OrganisationService] Error loading organisations:", orgError);
1268
+ throw orgError;
1269
+ }
1270
+ const organisations = allOrganisations?.filter(
1271
+ (org) => organisationIds.includes(org.id)
1272
+ ) || [];
1273
+ const roleMap = /* @__PURE__ */ new Map();
1274
+ memberships?.forEach((membership) => {
1275
+ roleMap.set(membership.organisation_id, membership.role);
1276
+ });
1277
+ const orgs = organisations;
1278
+ const activeOrgs = orgs.filter((org) => org.is_active);
1279
+ if (activeOrgs.length === 0) {
1280
+ throw new Error("User has no access to active organisations");
1281
+ }
1282
+ DebugLogger.log("OrganisationService", "Active organisations:", activeOrgs);
1283
+ this._organisations = activeOrgs;
1284
+ this._userMemberships = memberships;
1285
+ this._roleMapState = roleMap;
1286
+ let initialOrg = null;
1287
+ try {
1288
+ const persistedOrgString = localStorage.getItem("pace-core-selected-organisation");
1289
+ if (persistedOrgString) {
1290
+ const persistedOrg = JSON.parse(persistedOrgString);
1291
+ if (persistedOrg.id && typeof persistedOrg.id === "string" && persistedOrg.id.trim() !== "") {
1292
+ const validPersistedOrg = activeOrgs.find((org) => org.id === persistedOrg.id);
1293
+ if (validPersistedOrg) {
1294
+ initialOrg = validPersistedOrg;
1295
+ DebugLogger.log("OrganisationService", "Restored persisted organisation:", initialOrg.display_name);
1296
+ } else {
1297
+ console.warn("[OrganisationService] Persisted organisation not found in active orgs, clearing cache");
1298
+ localStorage.removeItem("pace-core-selected-organisation");
1299
+ }
1300
+ } else {
1301
+ console.warn("[OrganisationService] Invalid persisted organisation ID, clearing cache");
1302
+ localStorage.removeItem("pace-core-selected-organisation");
1303
+ }
1304
+ }
1305
+ } catch (storageError) {
1306
+ console.warn("[OrganisationService] Failed to restore persisted organisation:", storageError);
1307
+ localStorage.removeItem("pace-core-selected-organisation");
1308
+ }
1309
+ if (!initialOrg) {
1310
+ const adminMembership = memberships.find((m) => m.role === "org_admin");
1311
+ if (adminMembership) {
1312
+ const foundOrg = organisations.find((org) => org.id === adminMembership.organisation_id);
1313
+ if (foundOrg) {
1314
+ initialOrg = foundOrg;
1315
+ DebugLogger.log("OrganisationService", "Selected org_admin organisation:", initialOrg.display_name);
1316
+ }
1317
+ }
1318
+ }
1319
+ if (!initialOrg) {
1320
+ initialOrg = activeOrgs[0];
1321
+ DebugLogger.log("OrganisationService", "Selected first organisation:", initialOrg.display_name);
1322
+ }
1323
+ if (!initialOrg) {
1324
+ throw new Error("No valid organisation found for user");
1325
+ }
1326
+ this._selectedOrganisation = initialOrg;
1327
+ localStorage.setItem("pace-core-selected-organisation", JSON.stringify(initialOrg));
1328
+ DebugLogger.log("OrganisationService", "Organisation context established:", {
1329
+ selectedOrganisation: initialOrg.display_name,
1330
+ totalOrganisations: activeOrgs.length,
1331
+ userRole: roleMap.get(initialOrg.id)
1332
+ });
1333
+ this.retryCount = 0;
1334
+ this.hasFailedRef = false;
1335
+ } catch (err) {
1336
+ console.error("[OrganisationService] Failed to load organisations:", err);
1337
+ this._error = err;
1338
+ this.retryCount = this.retryCount + 1;
1339
+ this.hasFailedRef = true;
1340
+ this.clearAllCachedData();
1341
+ } finally {
1342
+ this.isLoadingRef = false;
1343
+ this._isLoading = false;
1344
+ this.abortControllerRef = null;
1345
+ this.notify();
1346
+ }
1347
+ }
1348
+ clearAllCachedData() {
1349
+ localStorage.removeItem("pace-core-selected-organisation");
1350
+ localStorage.removeItem("pace-core-organisation-context");
1351
+ this._selectedOrganisation = null;
1352
+ this._organisations = [];
1353
+ this._userMemberships = [];
1354
+ this._roleMapState = /* @__PURE__ */ new Map();
1355
+ this.retryCount = 0;
1356
+ this._isContextReady = false;
1357
+ }
1358
+ };
1359
+ }
1360
+ });
1361
+
1362
+ // src/providers/services/OrganisationServiceProvider.tsx
1363
+ import { createContext as createContext3, useContext as useContext3, useMemo as useMemo3, useEffect as useEffect3 } from "react";
1364
+ import { jsx as jsx3 } from "react/jsx-runtime";
1365
+ function OrganisationServiceProvider({
1366
+ children,
1367
+ supabaseClient,
1368
+ user,
1369
+ session
1370
+ }) {
1371
+ const organisationService = useMemo3(
1372
+ () => new OrganisationService(supabaseClient, user, session),
1373
+ [supabaseClient, user, session]
1374
+ );
1375
+ useEffect3(() => {
1376
+ organisationService.updateDependencies(user, session);
1377
+ }, [organisationService, user, session]);
1378
+ useEffect3(() => {
1379
+ organisationService.initialize().catch((error) => {
1380
+ console.error("[OrganisationServiceProvider] Failed to initialize organisation service:", error);
1381
+ });
1382
+ return () => {
1383
+ organisationService.cleanup();
1384
+ };
1385
+ }, [organisationService]);
1386
+ const contextValue = useMemo3(() => ({
1387
+ organisationService
1388
+ }), [organisationService]);
1389
+ return /* @__PURE__ */ jsx3(OrganisationServiceContext.Provider, { value: contextValue, children });
1390
+ }
1391
+ var OrganisationServiceContext, useOrganisationService;
1392
+ var init_OrganisationServiceProvider = __esm({
1393
+ "src/providers/services/OrganisationServiceProvider.tsx"() {
1394
+ "use strict";
1395
+ init_OrganisationService();
1396
+ OrganisationServiceContext = createContext3(null);
1397
+ useOrganisationService = () => {
1398
+ const context = useContext3(OrganisationServiceContext);
1399
+ if (!context) {
1400
+ throw new Error("useOrganisationService must be used within OrganisationServiceProvider");
1401
+ }
1402
+ return context.organisationService;
1403
+ };
1404
+ }
1405
+ });
1406
+
1407
+ // src/services/EventService.ts
1408
+ var EventService;
1409
+ var init_EventService = __esm({
1410
+ "src/services/EventService.ts"() {
1411
+ "use strict";
1412
+ init_BaseService();
1413
+ init_debugLogger();
1414
+ EventService = class extends BaseService {
1415
+ constructor(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId) {
1416
+ super();
1417
+ this.events = [];
1418
+ this.selectedEvent = null;
1419
+ this._isLoading = true;
1420
+ this.error = null;
1421
+ // Dependencies
1422
+ this.supabaseClient = null;
1423
+ this.user = null;
1424
+ this.session = null;
1425
+ this.appName = "";
1426
+ this.selectedOrganisation = null;
1427
+ this.setSelectedEventId = null;
1428
+ // Internal state management
1429
+ this.isInitializedRef = false;
1430
+ this.isFetchingRef = false;
1431
+ this.hasAutoSelectedRef = false;
1432
+ this.userClearedEventRef = false;
1433
+ this.supabaseClient = supabaseClient;
1434
+ this.user = user;
1435
+ this.session = session;
1436
+ this.appName = appName;
1437
+ this.selectedOrganisation = selectedOrganisation;
1438
+ this.setSelectedEventId = setSelectedEventId;
1439
+ }
1440
+ // Update dependencies
1441
+ updateDependencies(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId) {
1442
+ this.supabaseClient = supabaseClient;
1443
+ this.user = user;
1444
+ this.session = session;
1445
+ this.appName = appName;
1446
+ this.selectedOrganisation = selectedOrganisation;
1447
+ this.setSelectedEventId = setSelectedEventId;
1448
+ this.notify();
1449
+ }
1450
+ // Event state getters
1451
+ getEvents() {
1452
+ return this.events;
1453
+ }
1454
+ getSelectedEvent() {
1455
+ return this.selectedEvent;
1456
+ }
1457
+ isLoading() {
1458
+ return this._isLoading;
1459
+ }
1460
+ getError() {
1461
+ return this.error;
1462
+ }
1463
+ // Event methods
1464
+ setSelectedEvent(event) {
1465
+ if (event) {
1466
+ try {
1467
+ console.log("[EventService] Event selection validation:", {
1468
+ eventId: event.event_id,
1469
+ eventName: event.event_name,
1470
+ eventOrganisationId: event.organisation_id,
1471
+ selectedOrganisationId: this.selectedOrganisation?.id,
1472
+ selectedOrganisationName: this.selectedOrganisation?.display_name,
1473
+ match: event.organisation_id === this.selectedOrganisation?.id
1474
+ });
1475
+ if (this.selectedOrganisation && event.organisation_id !== this.selectedOrganisation.id) {
1476
+ console.error("[EventService] Event organisation_id does not match selected organisation", {
1477
+ eventOrganisationId: event.organisation_id,
1478
+ selectedOrganisationId: this.selectedOrganisation.id,
1479
+ eventName: event.event_name
1480
+ });
1481
+ return;
1482
+ }
1483
+ } catch (error) {
1484
+ console.error("[EventService] Error during event validation:", error);
1485
+ }
1486
+ this.selectedEvent = event;
1487
+ this.setSelectedEventId?.(event.event_id);
1488
+ this.persistEventSelection(event.event_id);
1489
+ this.userClearedEventRef = false;
1490
+ } else {
1491
+ this.selectedEvent = null;
1492
+ this.setSelectedEventId?.(null);
1493
+ sessionStorage.removeItem("pace-core-selected-event");
1494
+ localStorage.removeItem("pace-core-selected-event");
1495
+ this.hasAutoSelectedRef = false;
1496
+ this.userClearedEventRef = true;
1497
+ }
1498
+ this.notify();
1499
+ }
1500
+ async refreshEvents() {
1501
+ this.isInitializedRef = false;
1502
+ this.isFetchingRef = false;
1503
+ await this.fetchEvents();
1504
+ }
1505
+ async loadPersistedEvent(events) {
1506
+ try {
1507
+ let persistedEventId = sessionStorage.getItem("pace-core-selected-event");
1508
+ if (!persistedEventId) {
1509
+ persistedEventId = localStorage.getItem("pace-core-selected-event");
1510
+ if (persistedEventId) {
1511
+ sessionStorage.setItem("pace-core-selected-event", persistedEventId);
1512
+ }
1513
+ }
1514
+ if (persistedEventId && events.length > 0) {
1515
+ const persistedEvent = events.find((event) => event.event_id === persistedEventId);
1516
+ if (persistedEvent) {
1517
+ DebugLogger.log("EventService", "Restoring persisted event:", persistedEvent.event_name);
1518
+ this.selectedEvent = persistedEvent;
1519
+ this.setSelectedEventId?.(persistedEventId);
1520
+ return true;
1521
+ } else {
1522
+ DebugLogger.log("EventService", "Persisted event not found in current events, clearing storage");
1523
+ sessionStorage.removeItem("pace-core-selected-event");
1524
+ localStorage.removeItem("pace-core-selected-event");
1525
+ }
1526
+ }
1527
+ } catch (error) {
1528
+ console.warn("[EventService] Failed to load persisted event:", error);
1529
+ }
1530
+ return false;
1531
+ }
1532
+ persistEventSelection(eventId) {
1533
+ try {
1534
+ sessionStorage.setItem("pace-core-selected-event", eventId);
1535
+ localStorage.setItem("pace-core-selected-event", eventId);
1536
+ } catch (error) {
1537
+ console.warn("[EventService] Failed to persist event selection:", error);
1538
+ }
1539
+ }
1540
+ clearEventSelection() {
1541
+ try {
1542
+ sessionStorage.removeItem("pace-core-selected-event");
1543
+ localStorage.removeItem("pace-core-selected-event");
1544
+ this.selectedEvent = null;
1545
+ this.setSelectedEventId?.(null);
1546
+ } catch (error) {
1547
+ console.warn("[EventService] Failed to clear event selection:", error);
1548
+ }
1549
+ }
1550
+ autoSelectNextEvent(events) {
1551
+ const nextEvent = this.getNextEventByDate(events);
1552
+ if (nextEvent) {
1553
+ DebugLogger.log("EventService", "Auto-selecting next event:", nextEvent.event_name);
1554
+ this.selectedEvent = nextEvent;
1555
+ this.setSelectedEventId?.(nextEvent.event_id);
1556
+ this.persistEventSelection(nextEvent.event_id);
1557
+ }
1558
+ }
1559
+ // Lifecycle methods
1560
+ async initialize() {
1561
+ await super.initialize();
1562
+ await this.fetchEvents();
1563
+ }
1564
+ cleanup() {
1565
+ super.cleanup();
1566
+ }
1567
+ async doInitialize() {
1568
+ await this.fetchEvents();
1569
+ }
1570
+ doCleanup() {
1571
+ }
1572
+ async fetchEvents() {
1573
+ if (!this.user || !this.session || !this.supabaseClient || !this.appName || !this.selectedOrganisation) {
1574
+ DebugLogger.log("EventService", "Missing required dependencies, skipping fetch");
1575
+ this._isLoading = false;
1576
+ this.notify();
1577
+ return;
1578
+ }
1579
+ if (this.isFetchingRef) {
1580
+ DebugLogger.log("EventService", "Already fetching events, skipping");
1581
+ return;
1582
+ }
1583
+ DebugLogger.log("EventService", "User and organisation found, fetching events for:", {
1584
+ userId: this.user.id,
1585
+ appName: this.appName,
1586
+ organisationId: this.selectedOrganisation.id,
1587
+ organisationName: this.selectedOrganisation.display_name
1588
+ });
1589
+ this.isFetchingRef = true;
1590
+ let isMounted = true;
1591
+ try {
1592
+ DebugLogger.log("EventService", "Calling data_user_events_get RPC with:", {
1593
+ user_id: this.user.id,
1594
+ organisation_id: this.selectedOrganisation.id,
1595
+ app_name: this.appName
1596
+ });
1597
+ const { data, error: rpcError } = await this.supabaseClient.rpc("data_user_events_get", {
1598
+ p_user_id: this.user.id,
1599
+ p_organisation_id: this.selectedOrganisation.id,
1600
+ p_app_name: this.appName
1601
+ });
1602
+ DebugLogger.log("EventService", "RPC response:", {
1603
+ data,
1604
+ error: rpcError,
1605
+ dataLength: data?.length || 0,
1606
+ organisationId: this.selectedOrganisation.id
1607
+ });
1608
+ if (rpcError) {
1609
+ throw new Error(rpcError.message || "Failed to fetch events");
1610
+ }
1611
+ if (isMounted) {
1612
+ const eventsData = data || [];
1613
+ console.log("[EventService] Loaded events:", eventsData.map((event) => ({
1614
+ eventId: event.event_id,
1615
+ eventName: event.event_name,
1616
+ organisationId: event.organisation_id,
1617
+ selectedOrganisationId: this.selectedOrganisation?.id
1618
+ })));
1619
+ const transformedEvents = eventsData.map((event) => ({
1620
+ id: event.event_id,
1621
+ // Use event_id as the primary id
1622
+ event_id: event.event_id,
1623
+ event_name: event.event_name,
1624
+ event_date: event.event_date,
1625
+ event_venue: event.event_venue,
1626
+ event_participants: event.event_participants,
1627
+ event_colours: event.event_colours,
1628
+ event_logo: "",
1629
+ // No logo field in event table
1630
+ organisation_id: event.organisation_id,
1631
+ is_visible: event.is_visible,
1632
+ // Legacy compatibility
1633
+ name: event.event_name,
1634
+ start_date: event.event_date
1635
+ }));
1636
+ this.events = transformedEvents;
1637
+ this.error = null;
1638
+ this.hasAutoSelectedRef = false;
1639
+ const persistedEventLoaded = await this.loadPersistedEvent(transformedEvents);
1640
+ if (!persistedEventLoaded && !this.userClearedEventRef) {
1641
+ const nextEvent = this.getNextEventByDate(transformedEvents);
1642
+ if (nextEvent) {
1643
+ DebugLogger.log("EventService", "Auto-selecting next event after no persisted event found:", nextEvent.event_name);
1644
+ this.hasAutoSelectedRef = true;
1645
+ this.selectedEvent = nextEvent;
1646
+ this.setSelectedEventId?.(nextEvent.event_id);
1647
+ this.persistEventSelection(nextEvent.event_id);
1648
+ }
1649
+ }
1650
+ }
1651
+ } catch (err) {
1652
+ console.error("[EventService] Error fetching events:", err);
1653
+ const _error = err instanceof Error ? err : new Error("Unknown error occurred");
1654
+ if (isMounted) {
1655
+ this.error = _error;
1656
+ this.events = [];
1657
+ }
1658
+ } finally {
1659
+ if (isMounted) {
1660
+ this._isLoading = false;
1661
+ }
1662
+ this.isFetchingRef = false;
1663
+ this.notify();
1664
+ }
1665
+ }
1666
+ getNextEventByDate(events) {
1667
+ const eventsToUse = events || this.events;
1668
+ if (!eventsToUse || eventsToUse.length === 0) {
1669
+ return null;
1670
+ }
1671
+ const now = /* @__PURE__ */ new Date();
1672
+ const futureEvents = eventsToUse.filter((event) => {
1673
+ if (!event.event_date) return false;
1674
+ const eventDate = new Date(event.event_date);
1675
+ return eventDate >= now;
1676
+ });
1677
+ if (futureEvents.length === 0) {
1678
+ return null;
1679
+ }
1680
+ const sortedFutureEvents = futureEvents.sort((a, b) => {
1681
+ const dateA = new Date(a.event_date);
1682
+ const dateB = new Date(b.event_date);
1683
+ return dateA.getTime() - dateB.getTime();
1684
+ });
1685
+ return sortedFutureEvents[0];
1686
+ }
1687
+ };
1688
+ }
1689
+ });
1690
+
1691
+ // src/providers/services/EventServiceProvider.tsx
1692
+ import { createContext as createContext4, useContext as useContext4, useMemo as useMemo4, useEffect as useEffect4 } from "react";
1693
+ import { jsx as jsx4 } from "react/jsx-runtime";
1694
+ function EventServiceProvider({
1695
+ children,
1696
+ supabaseClient,
1697
+ user,
1698
+ session,
1699
+ appName,
1700
+ selectedOrganisation,
1701
+ setSelectedEventId
1702
+ }) {
1703
+ const eventService = useMemo4(
1704
+ () => new EventService(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId),
1705
+ [supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId]
1706
+ );
1707
+ useEffect4(() => {
1708
+ eventService.updateDependencies(supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId);
1709
+ }, [eventService, supabaseClient, user, session, appName, selectedOrganisation, setSelectedEventId]);
1710
+ useEffect4(() => {
1711
+ eventService.initialize().catch((error) => {
1712
+ console.error("[EventServiceProvider] Failed to initialize event service:", error);
1713
+ });
1714
+ return () => {
1715
+ eventService.cleanup();
1716
+ };
1717
+ }, [eventService]);
1718
+ const contextValue = useMemo4(() => ({
1719
+ eventService
1720
+ }), [eventService]);
1721
+ return /* @__PURE__ */ jsx4(EventServiceContext.Provider, { value: contextValue, children });
1722
+ }
1723
+ var EventServiceContext, useEventService;
1724
+ var init_EventServiceProvider = __esm({
1725
+ "src/providers/services/EventServiceProvider.tsx"() {
1726
+ "use strict";
1727
+ init_EventService();
1728
+ EventServiceContext = createContext4(null);
1729
+ useEventService = () => {
1730
+ const context = useContext4(EventServiceContext);
1731
+ if (!context) {
1732
+ throw new Error("useEventService must be used within EventServiceProvider");
1733
+ }
1734
+ return context.eventService;
1735
+ };
1736
+ }
1737
+ });
1738
+
1739
+ // src/services/InactivityService.ts
1740
+ var InactivityService;
1741
+ var init_InactivityService = __esm({
1742
+ "src/services/InactivityService.ts"() {
1743
+ "use strict";
1744
+ init_BaseService();
1745
+ InactivityService = class extends BaseService {
1746
+ constructor(supabaseClient, user, session, idleTimeoutMs = 30 * 60 * 1e3, warnBeforeMs = 60 * 1e3, onIdleLogout) {
1747
+ super();
1748
+ this._showInactivityWarning = false;
1749
+ this._inactivityTimeRemaining = 0;
1750
+ this._isIdle = false;
1751
+ this._timeRemaining = 0;
1752
+ this._showWarning = false;
1753
+ this._isTracking = false;
1754
+ // Dependencies
1755
+ this.supabaseClient = null;
1756
+ this.user = null;
1757
+ this.session = null;
1758
+ this.idleTimeoutMs = 30 * 60 * 1e3;
1759
+ // 30 minutes
1760
+ this.warnBeforeMs = 60 * 1e3;
1761
+ // 60 seconds
1762
+ this.onIdleLogout = null;
1763
+ // Internal state management
1764
+ this.inactivityTracker = null;
1765
+ this.isInactivityEnabled = true;
1766
+ this.supabaseClient = supabaseClient;
1767
+ this.user = user;
1768
+ this.session = session;
1769
+ this.idleTimeoutMs = idleTimeoutMs;
1770
+ this.warnBeforeMs = warnBeforeMs;
1771
+ this.onIdleLogout = onIdleLogout;
1772
+ this._timeRemaining = idleTimeoutMs;
1773
+ }
1774
+ // Interface implementation
1775
+ isIdle() {
1776
+ return this._isIdle;
1777
+ }
1778
+ getTimeRemaining() {
1779
+ return this._timeRemaining;
1780
+ }
1781
+ isWarningShown() {
1782
+ return this._showWarning;
1783
+ }
1784
+ isTracking() {
1785
+ return this._isTracking;
1786
+ }
1787
+ getShowInactivityWarning() {
1788
+ return this._showInactivityWarning;
1789
+ }
1790
+ getInactivityTimeRemaining() {
1791
+ return this._inactivityTimeRemaining;
1792
+ }
1793
+ // Additional getter methods that tests expect
1794
+ getIsIdle() {
1795
+ return this._isIdle;
1796
+ }
1797
+ getIsTracking() {
1798
+ return this._isTracking;
1799
+ }
1800
+ getShowWarning() {
1801
+ return this._showWarning;
1802
+ }
1803
+ // Additional methods for testing
1804
+ setShowInactivityWarning(value) {
1805
+ this._showInactivityWarning = value;
1806
+ this.notify();
1807
+ }
1808
+ setInactivityTimeRemaining(value) {
1809
+ this._inactivityTimeRemaining = value;
1810
+ this.notify();
1811
+ }
1812
+ setIsIdle(value) {
1813
+ this._isIdle = value;
1814
+ this.notify();
1815
+ }
1816
+ setTimeRemaining(value) {
1817
+ this._timeRemaining = value;
1818
+ this.notify();
1819
+ }
1820
+ setShowWarning(value) {
1821
+ this._showWarning = value;
1822
+ this.notify();
1823
+ }
1824
+ setIsTracking(value) {
1825
+ this._isTracking = value;
1826
+ this.notify();
1827
+ }
1828
+ triggerWarning(timeRemaining) {
1829
+ this._showInactivityWarning = true;
1830
+ this._inactivityTimeRemaining = Math.ceil(timeRemaining / 1e3);
1831
+ this._showWarning = true;
1832
+ this.notify();
1833
+ }
1834
+ triggerIdle() {
1835
+ this._isIdle = true;
1836
+ this.handleIdleLogout();
1837
+ this.notify();
1838
+ }
1839
+ // Update dependencies
1840
+ updateDependencies(supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout) {
1841
+ this.supabaseClient = supabaseClient;
1842
+ this.user = user;
1843
+ this.session = session;
1844
+ if (idleTimeoutMs !== void 0) this.idleTimeoutMs = idleTimeoutMs;
1845
+ if (warnBeforeMs !== void 0) this.warnBeforeMs = warnBeforeMs;
1846
+ if (onIdleLogout !== void 0) this.onIdleLogout = onIdleLogout;
1847
+ this.notify();
1848
+ }
1849
+ // Inactivity methods
1850
+ resetActivity() {
1851
+ if (this.inactivityTracker) {
1852
+ this.inactivityTracker.resetActivity();
1853
+ }
1854
+ this._isIdle = false;
1855
+ this._showWarning = false;
1856
+ this._showInactivityWarning = false;
1857
+ this._inactivityTimeRemaining = 0;
1858
+ this._timeRemaining = this.idleTimeoutMs;
1859
+ this.notify();
1860
+ }
1861
+ startTracking() {
1862
+ if (this.inactivityTracker) {
1863
+ this.inactivityTracker.startTracking();
1864
+ }
1865
+ this._isTracking = true;
1866
+ this.notify();
1867
+ }
1868
+ stopTracking() {
1869
+ if (this.inactivityTracker) {
1870
+ this.inactivityTracker.stopTracking();
1871
+ }
1872
+ this._isTracking = false;
1873
+ this.notify();
1874
+ }
1875
+ async handleIdleLogout() {
1876
+ this._showInactivityWarning = false;
1877
+ this._inactivityTimeRemaining = 0;
1878
+ this.stopTracking();
1879
+ try {
1880
+ if (this.supabaseClient) {
1881
+ await this.supabaseClient.auth.signOut();
1882
+ }
1883
+ } catch (error) {
1884
+ console.error("[InactivityService] Error during idle logout:", error);
1885
+ }
1886
+ this.onIdleLogout?.("inactivity");
1887
+ this.notify();
1888
+ }
1889
+ handleStaySignedIn() {
1890
+ this._showInactivityWarning = false;
1891
+ this._inactivityTimeRemaining = 0;
1892
+ this.resetActivity();
1893
+ this.notify();
1894
+ }
1895
+ async handleSignOutNow() {
1896
+ this._showInactivityWarning = false;
1897
+ this._inactivityTimeRemaining = 0;
1898
+ this.stopTracking();
1899
+ try {
1900
+ if (this.supabaseClient) {
1901
+ await this.supabaseClient.auth.signOut();
1902
+ }
1903
+ } catch (error) {
1904
+ console.error("[InactivityService] Error during manual sign out:", error);
1905
+ }
1906
+ this.onIdleLogout?.("inactivity");
1907
+ this.notify();
1908
+ }
1909
+ // Lifecycle methods
1910
+ async initialize() {
1911
+ await super.initialize();
1912
+ await this.setupInactivityTracker();
1913
+ }
1914
+ cleanup() {
1915
+ if (this.inactivityTracker) {
1916
+ this.inactivityTracker.cleanup?.();
1917
+ this.inactivityTracker = null;
1918
+ }
1919
+ this._isTracking = false;
1920
+ this._isIdle = false;
1921
+ this._showWarning = false;
1922
+ this._showInactivityWarning = false;
1923
+ this._timeRemaining = 0;
1924
+ this._inactivityTimeRemaining = 0;
1925
+ super.cleanup();
1926
+ }
1927
+ async doInitialize() {
1928
+ if (typeof window !== "undefined") {
1929
+ const isProduction = import.meta.env.MODE === "production";
1930
+ if (isProduction) {
1931
+ console.warn("[InactivityService] Inactivity feature enabled in production");
1932
+ }
1933
+ }
1934
+ }
1935
+ doCleanup() {
1936
+ }
1937
+ async setupInactivityTracker() {
1938
+ if (typeof window === "undefined") return;
1939
+ this.isInactivityEnabled = this.user && this.session;
1940
+ if (!this.isInactivityEnabled) {
1941
+ return;
1942
+ }
1943
+ try {
1944
+ const { useInactivityTracker } = await import("./useInactivityTracker-MRUU55XI.js");
1945
+ this.inactivityTracker = {
1946
+ isIdle: false,
1947
+ timeRemaining: 0,
1948
+ showWarning: false,
1949
+ isTracking: false,
1950
+ resetActivity: () => {
1951
+ this._isIdle = false;
1952
+ this._timeRemaining = 0;
1953
+ this._showWarning = false;
1954
+ this.notify();
1955
+ },
1956
+ startTracking: () => {
1957
+ this._isTracking = true;
1958
+ this.notify();
1959
+ },
1960
+ stopTracking: () => {
1961
+ this._isTracking = false;
1962
+ this.notify();
1963
+ },
1964
+ cleanup: () => {
1965
+ this._isTracking = false;
1966
+ this._isIdle = false;
1967
+ this._showWarning = false;
1968
+ this._timeRemaining = 0;
1969
+ }
1970
+ };
1971
+ this.setupEventHandlers();
1972
+ } catch (error) {
1973
+ console.error("[InactivityService] Failed to setup inactivity tracker:", error);
1974
+ }
1975
+ }
1976
+ setupEventHandlers() {
1977
+ if (typeof window === "undefined") return;
1978
+ let idleTimer = null;
1979
+ let warningTimer = null;
1980
+ let lastActivity = Date.now();
1981
+ const resetTimers = () => {
1982
+ lastActivity = Date.now();
1983
+ if (idleTimer) {
1984
+ clearTimeout(idleTimer);
1985
+ idleTimer = null;
1986
+ }
1987
+ if (warningTimer) {
1988
+ clearTimeout(warningTimer);
1989
+ warningTimer = null;
1990
+ }
1991
+ this._showInactivityWarning = false;
1992
+ this._inactivityTimeRemaining = 0;
1993
+ this._isIdle = false;
1994
+ this._showWarning = false;
1995
+ this.notify();
1996
+ };
1997
+ const startIdleTimer = () => {
1998
+ if (idleTimer) {
1999
+ clearTimeout(idleTimer);
2000
+ }
2001
+ idleTimer = setTimeout(() => {
2002
+ this._isIdle = true;
2003
+ this._showWarning = true;
2004
+ this.notify();
2005
+ warningTimer = setTimeout(() => {
2006
+ this.handleIdleLogout();
2007
+ }, this.warnBeforeMs);
2008
+ }, this.idleTimeoutMs - this.warnBeforeMs);
2009
+ };
2010
+ const startWarningTimer = () => {
2011
+ if (warningTimer) {
2012
+ clearTimeout(warningTimer);
2013
+ }
2014
+ warningTimer = setTimeout(() => {
2015
+ this.handleIdleLogout();
2016
+ }, this.warnBeforeMs);
2017
+ };
2018
+ const activityEvents = ["mousedown", "mousemove", "keypress", "scroll", "touchstart", "click"];
2019
+ const handleActivity = () => {
2020
+ resetTimers();
2021
+ startIdleTimer();
2022
+ };
2023
+ activityEvents.forEach((event) => {
2024
+ document.addEventListener(event, handleActivity, true);
2025
+ });
2026
+ startIdleTimer();
2027
+ this.inactivityTracker.cleanup = () => {
2028
+ if (idleTimer) {
2029
+ clearTimeout(idleTimer);
2030
+ idleTimer = null;
2031
+ }
2032
+ if (warningTimer) {
2033
+ clearTimeout(warningTimer);
2034
+ warningTimer = null;
2035
+ }
2036
+ activityEvents.forEach((event) => {
2037
+ document.removeEventListener(event, handleActivity, true);
2038
+ });
2039
+ this._isTracking = false;
2040
+ this._isIdle = false;
2041
+ this._showWarning = false;
2042
+ this._timeRemaining = 0;
2043
+ this._showInactivityWarning = false;
2044
+ this._inactivityTimeRemaining = 0;
2045
+ };
2046
+ this._isTracking = true;
2047
+ this.notify();
2048
+ }
2049
+ };
2050
+ }
2051
+ });
2052
+
2053
+ // src/providers/services/InactivityServiceProvider.tsx
2054
+ import { createContext as createContext5, useContext as useContext5, useMemo as useMemo5, useEffect as useEffect5 } from "react";
2055
+ import { jsx as jsx5 } from "react/jsx-runtime";
2056
+ function InactivityServiceProvider({
2057
+ children,
2058
+ supabaseClient,
2059
+ user,
2060
+ session,
2061
+ idleTimeoutMs = 30 * 60 * 1e3,
2062
+ warnBeforeMs = 60 * 1e3,
2063
+ onIdleLogout
2064
+ }) {
2065
+ const inactivityService = useMemo5(
2066
+ () => new InactivityService(supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout),
2067
+ [supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout]
2068
+ );
2069
+ useEffect5(() => {
2070
+ inactivityService.updateDependencies(supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout);
2071
+ }, [inactivityService, supabaseClient, user, session, idleTimeoutMs, warnBeforeMs, onIdleLogout]);
2072
+ useEffect5(() => {
2073
+ inactivityService.initialize().catch((error) => {
2074
+ console.error("[InactivityServiceProvider] Failed to initialize inactivity service:", error);
2075
+ });
2076
+ return () => {
2077
+ inactivityService.cleanup();
2078
+ };
2079
+ }, [inactivityService]);
2080
+ const contextValue = useMemo5(() => ({
2081
+ inactivityService
2082
+ }), [inactivityService]);
2083
+ return /* @__PURE__ */ jsx5(InactivityServiceContext.Provider, { value: contextValue, children });
2084
+ }
2085
+ var InactivityServiceContext, useInactivityService;
2086
+ var init_InactivityServiceProvider = __esm({
2087
+ "src/providers/services/InactivityServiceProvider.tsx"() {
2088
+ "use strict";
2089
+ init_InactivityService();
2090
+ InactivityServiceContext = createContext5(null);
2091
+ useInactivityService = () => {
2092
+ const context = useContext5(InactivityServiceContext);
2093
+ if (!context) {
2094
+ throw new Error("useInactivityService must be used within InactivityServiceProvider");
2095
+ }
2096
+ return context.inactivityService;
2097
+ };
2098
+ }
2099
+ });
2100
+
2101
+ // src/hooks/services/useAuthService.ts
2102
+ import { useContext as useContext6, useReducer, useEffect as useEffect6 } from "react";
2103
+ function useAuthService2() {
2104
+ const context = useContext6(AuthServiceContext);
2105
+ if (!context) {
2106
+ throw new Error("useAuthService must be used within AuthServiceProvider");
2107
+ }
2108
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
2109
+ useEffect6(() => {
2110
+ return context.authService.subscribe(() => forceUpdate());
2111
+ }, [context.authService]);
2112
+ return context.authService;
2113
+ }
2114
+ var init_useAuthService = __esm({
2115
+ "src/hooks/services/useAuthService.ts"() {
2116
+ "use strict";
2117
+ init_AuthServiceProvider();
2118
+ }
2119
+ });
2120
+
2121
+ // src/hooks/services/useRBACService.ts
2122
+ import { useContext as useContext7, useReducer as useReducer2, useEffect as useEffect7 } from "react";
2123
+ function useRBACService2() {
2124
+ const context = useContext7(RBACServiceContext);
2125
+ if (!context) {
2126
+ throw new Error("useRBACService must be used within RBACServiceProvider");
2127
+ }
2128
+ const [, forceUpdate] = useReducer2((x) => x + 1, 0);
2129
+ useEffect7(() => {
2130
+ return context.rbacService.subscribe(() => forceUpdate());
2131
+ }, [context.rbacService]);
2132
+ return context.rbacService;
2133
+ }
2134
+ var init_useRBACService = __esm({
2135
+ "src/hooks/services/useRBACService.ts"() {
2136
+ "use strict";
2137
+ init_RBACServiceProvider();
2138
+ }
2139
+ });
2140
+
2141
+ // src/hooks/services/useOrganisationService.ts
2142
+ import { useContext as useContext8, useReducer as useReducer3, useEffect as useEffect8 } from "react";
2143
+ function useOrganisationService2() {
2144
+ const context = useContext8(OrganisationServiceContext);
2145
+ if (!context) {
2146
+ throw new Error("useOrganisationService must be used within OrganisationServiceProvider");
2147
+ }
2148
+ const [, forceUpdate] = useReducer3((x) => x + 1, 0);
2149
+ useEffect8(() => {
2150
+ return context.organisationService.subscribe(() => forceUpdate());
2151
+ }, [context.organisationService]);
2152
+ return context.organisationService;
2153
+ }
2154
+ var init_useOrganisationService = __esm({
2155
+ "src/hooks/services/useOrganisationService.ts"() {
2156
+ "use strict";
2157
+ init_OrganisationServiceProvider();
2158
+ }
2159
+ });
2160
+
2161
+ // src/hooks/services/useEventService.ts
2162
+ import { useContext as useContext9, useReducer as useReducer4, useEffect as useEffect9 } from "react";
2163
+ function useEventService2() {
2164
+ const context = useContext9(EventServiceContext);
2165
+ if (!context) {
2166
+ throw new Error("useEventService must be used within EventServiceProvider");
2167
+ }
2168
+ const [, forceUpdate] = useReducer4((x) => x + 1, 0);
2169
+ useEffect9(() => {
2170
+ return context.eventService.subscribe(() => forceUpdate());
2171
+ }, [context.eventService]);
2172
+ return context.eventService;
2173
+ }
2174
+ var init_useEventService = __esm({
2175
+ "src/hooks/services/useEventService.ts"() {
2176
+ "use strict";
2177
+ init_EventServiceProvider();
2178
+ }
2179
+ });
2180
+
2181
+ // src/hooks/services/useInactivityService.ts
2182
+ import { useContext as useContext10, useReducer as useReducer5, useEffect as useEffect10 } from "react";
2183
+ function useInactivityService2() {
2184
+ const context = useContext10(InactivityServiceContext);
2185
+ if (!context) {
2186
+ throw new Error("useInactivityService must be used within InactivityServiceProvider");
2187
+ }
2188
+ const [, forceUpdate] = useReducer5((x) => x + 1, 0);
2189
+ useEffect10(() => {
2190
+ return context.inactivityService.subscribe(() => forceUpdate());
2191
+ }, [context.inactivityService]);
2192
+ return context.inactivityService;
2193
+ }
2194
+ var init_useInactivityService = __esm({
2195
+ "src/hooks/services/useInactivityService.ts"() {
2196
+ "use strict";
2197
+ init_InactivityServiceProvider();
2198
+ }
2199
+ });
2200
+
2201
+ // src/providers/services/UnifiedAuthProvider.tsx
2202
+ import { createContext as createContext6, useContext as useContext11, useMemo as useMemo6 } from "react";
2203
+ import { jsx as jsx6 } from "react/jsx-runtime";
2204
+ function UnifiedAuthContextProvider({
2205
+ children,
2206
+ appName,
2207
+ ...props
2208
+ }) {
2209
+ const authService = useAuthService2();
2210
+ const rbacService = useRBACService2();
2211
+ const organisationService = useOrganisationService2();
2212
+ const eventService = useEventService2();
2213
+ const inactivityService = useInactivityService2();
2214
+ const contextValue = useMemo6(() => ({
2215
+ // Auth state
2216
+ user: authService.getUser(),
2217
+ session: authService.getSession(),
2218
+ isAuthenticated: authService.isAuthenticated(),
2219
+ authLoading: authService.isLoading(),
2220
+ authError: authService.getError(),
2221
+ error: authService.getError(),
2222
+ // Alias for backward compatibility
2223
+ supabase: authService.getSupabaseClient(),
2224
+ // Auth methods
2225
+ signIn: authService.signIn.bind(authService),
2226
+ signUp: authService.signUp.bind(authService),
2227
+ signOut: authService.signOut.bind(authService),
2228
+ resetPassword: authService.resetPassword.bind(authService),
2229
+ updatePassword: authService.updatePassword.bind(authService),
2230
+ refreshSession: authService.refreshSession.bind(authService),
2231
+ // RBAC state
2232
+ permissions: rbacService.getPermissions(),
2233
+ roles: rbacService.getRoles(),
2234
+ accessLevel: rbacService.getAccessLevel(),
2235
+ rbacLoading: rbacService.isLoading(),
2236
+ rbacError: rbacService.getError(),
2237
+ selectedEventId: rbacService.getSelectedEventId(),
2238
+ appConfig: rbacService.getAppConfig(),
2239
+ userEventAccess: rbacService.getUserEventAccess(),
2240
+ eventAccessLoading: rbacService.isEventAccessLoading(),
2241
+ selectedOrganisationId: rbacService.getSelectedOrganisationId(),
2242
+ // RBAC methods
2243
+ hasPermission: rbacService.hasPermission.bind(rbacService),
2244
+ hasAnyPermission: rbacService.hasAnyPermission.bind(rbacService),
2245
+ hasAllPermissions: rbacService.hasAllPermissions.bind(rbacService),
2246
+ hasRole: rbacService.hasRole.bind(rbacService),
2247
+ hasAccessLevel: rbacService.hasAccessLevel.bind(rbacService),
2248
+ canAccess: rbacService.canAccess.bind(rbacService),
2249
+ validatePermission: rbacService.validatePermission.bind(rbacService),
2250
+ validateAccess: rbacService.validateAccess.bind(rbacService),
2251
+ refreshPermissions: rbacService.refreshPermissions.bind(rbacService),
2252
+ setSelectedEventId: rbacService.setSelectedEventId.bind(rbacService),
2253
+ loadUserEventAccess: rbacService.loadUserEventAccess.bind(rbacService),
2254
+ getUserEventAccess: rbacService.getUserEventAccess.bind(rbacService),
2255
+ requireOrganisationContext: rbacService.requireOrganisationContext.bind(rbacService),
2256
+ // Organisation state
2257
+ selectedOrganisation: organisationService.getSelectedOrganisation(),
2258
+ organisations: organisationService.getOrganisations(),
2259
+ userMemberships: organisationService.getUserMemberships(),
2260
+ organisationLoading: organisationService.isLoading(),
2261
+ organisationError: organisationService.getError(),
2262
+ hasValidOrganisationContext: organisationService.hasValidOrganisationContext(),
2263
+ isContextReady: organisationService.isContextReady(),
2264
+ // Organisation methods
2265
+ switchOrganisation: organisationService.switchOrganisation.bind(organisationService),
2266
+ getUserRole: organisationService.getUserRole.bind(organisationService),
2267
+ validateOrganisationAccess: organisationService.validateOrganisationAccess.bind(organisationService),
2268
+ refreshOrganisations: organisationService.refreshOrganisations.bind(organisationService),
2269
+ ensureOrganisationContext: organisationService.ensureOrganisationContext.bind(organisationService),
2270
+ isOrganisationSecure: organisationService.isOrganisationSecure.bind(organisationService),
2271
+ getPrimaryOrganisation: organisationService.getPrimaryOrganisation.bind(organisationService),
2272
+ // Event state
2273
+ events: eventService.getEvents(),
2274
+ selectedEvent: eventService.getSelectedEvent(),
2275
+ eventLoading: eventService.isLoading(),
2276
+ eventError: eventService.getError(),
2277
+ // Event methods
2278
+ setSelectedEvent: eventService.setSelectedEvent.bind(eventService),
2279
+ refreshEvents: eventService.refreshEvents.bind(eventService),
2280
+ // Inactivity state
2281
+ showInactivityWarning: inactivityService.getShowInactivityWarning(),
2282
+ inactivityTimeRemaining: inactivityService.getInactivityTimeRemaining(),
2283
+ isIdle: inactivityService.isIdle(),
2284
+ timeRemaining: inactivityService.getTimeRemaining(),
2285
+ showWarning: inactivityService.isWarningShown(),
2286
+ isTracking: inactivityService.isTracking(),
2287
+ // Inactivity methods
2288
+ resetActivity: inactivityService.resetActivity.bind(inactivityService),
2289
+ startTracking: inactivityService.startTracking.bind(inactivityService),
2290
+ stopTracking: inactivityService.stopTracking.bind(inactivityService),
2291
+ handleIdleLogout: inactivityService.handleIdleLogout.bind(inactivityService),
2292
+ handleStaySignedIn: inactivityService.handleStaySignedIn.bind(inactivityService),
2293
+ handleSignOutNow: inactivityService.handleSignOutNow.bind(inactivityService),
2294
+ // Additional unified properties
2295
+ appName,
2296
+ isLoading: authService.isLoading() || rbacService.isLoading() || organisationService.isLoading() || eventService.isLoading(),
2297
+ hasErrors: !!(authService.getError() || rbacService.getError() || organisationService.getError() || eventService.getError())
2298
+ }), [authService, rbacService, organisationService, eventService, inactivityService, appName]);
2299
+ return /* @__PURE__ */ jsx6(UnifiedAuthContext.Provider, { value: contextValue, children });
2300
+ }
2301
+ function ServiceAwareProviders({
2302
+ children,
2303
+ supabaseClient,
2304
+ appName,
2305
+ persistState,
2306
+ enablePersistence,
2307
+ requireOrganisationContext,
2308
+ enableRBAC,
2309
+ idleTimeoutMs,
2310
+ warnBeforeMs,
2311
+ onIdleLogout,
2312
+ renderInactivityWarning,
2313
+ dangerouslyDisableInactivity
2314
+ }) {
2315
+ const authService = useAuthService2();
2316
+ return /* @__PURE__ */ jsx6(
2317
+ RBACServiceProvider,
2318
+ {
2319
+ supabaseClient,
2320
+ user: authService.getUser(),
2321
+ session: authService.getSession(),
2322
+ appName,
2323
+ children: /* @__PURE__ */ jsx6(
2324
+ OrganisationServiceProvider,
2325
+ {
2326
+ supabaseClient,
2327
+ user: authService.getUser(),
2328
+ session: authService.getSession(),
2329
+ children: /* @__PURE__ */ jsx6(
2330
+ EventServiceProvider,
2331
+ {
2332
+ supabaseClient,
2333
+ user: authService.getUser(),
2334
+ session: authService.getSession(),
2335
+ appName,
2336
+ selectedOrganisation: null,
2337
+ setSelectedEventId: (eventId) => {
2338
+ },
2339
+ children: /* @__PURE__ */ jsx6(
2340
+ InactivityServiceProvider,
2341
+ {
2342
+ supabaseClient,
2343
+ user: authService.getUser(),
2344
+ session: authService.getSession(),
2345
+ idleTimeoutMs,
2346
+ warnBeforeMs,
2347
+ onIdleLogout,
2348
+ children: /* @__PURE__ */ jsx6(
2349
+ UnifiedAuthContextProvider,
2350
+ {
2351
+ appName,
2352
+ supabaseClient,
2353
+ persistState,
2354
+ enablePersistence,
2355
+ requireOrganisationContext,
2356
+ enableRBAC,
2357
+ idleTimeoutMs,
2358
+ warnBeforeMs,
2359
+ onIdleLogout,
2360
+ renderInactivityWarning,
2361
+ dangerouslyDisableInactivity,
2362
+ children
2363
+ }
2364
+ )
2365
+ }
2366
+ )
2367
+ }
2368
+ )
2369
+ }
2370
+ )
2371
+ }
2372
+ );
2373
+ }
2374
+ function UnifiedAuthProvider({
2375
+ children,
2376
+ supabaseClient,
2377
+ appName,
2378
+ persistState = true,
2379
+ enablePersistence,
2380
+ requireOrganisationContext = true,
2381
+ enableRBAC = false,
2382
+ idleTimeoutMs = 30 * 60 * 1e3,
2383
+ // 30 minutes
2384
+ warnBeforeMs = 60 * 1e3,
2385
+ // 60 seconds
2386
+ onIdleLogout,
2387
+ renderInactivityWarning,
2388
+ dangerouslyDisableInactivity = false
2389
+ }) {
2390
+ return /* @__PURE__ */ jsx6(AuthServiceProvider, { supabaseClient, children: /* @__PURE__ */ jsx6(
2391
+ ServiceAwareProviders,
2392
+ {
2393
+ supabaseClient,
2394
+ appName,
2395
+ persistState,
2396
+ enablePersistence,
2397
+ requireOrganisationContext,
2398
+ enableRBAC,
2399
+ idleTimeoutMs,
2400
+ warnBeforeMs,
2401
+ onIdleLogout,
2402
+ renderInactivityWarning,
2403
+ dangerouslyDisableInactivity,
2404
+ children
2405
+ }
2406
+ ) });
2407
+ }
2408
+ var UnifiedAuthContext, useUnifiedAuth;
2409
+ var init_UnifiedAuthProvider = __esm({
2410
+ "src/providers/services/UnifiedAuthProvider.tsx"() {
2411
+ "use strict";
2412
+ init_AuthServiceProvider();
2413
+ init_RBACServiceProvider();
2414
+ init_OrganisationServiceProvider();
2415
+ init_EventServiceProvider();
2416
+ init_InactivityServiceProvider();
2417
+ init_useAuthService();
2418
+ init_useRBACService();
2419
+ init_useOrganisationService();
2420
+ init_useEventService();
2421
+ init_useInactivityService();
2422
+ UnifiedAuthContext = createContext6(void 0);
2423
+ useUnifiedAuth = () => {
2424
+ const context = useContext11(UnifiedAuthContext);
2425
+ if (!context) {
2426
+ throw new Error("useUnifiedAuth must be used within a UnifiedAuthProvider");
2427
+ }
2428
+ return context;
2429
+ };
2430
+ }
2431
+ });
2432
+
2433
+ export {
2434
+ AuthServiceContext,
2435
+ AuthServiceProvider,
2436
+ useAuthService,
2437
+ init_AuthServiceProvider,
2438
+ RBACServiceContext,
2439
+ RBACServiceProvider,
2440
+ useRBACService,
2441
+ init_RBACServiceProvider,
2442
+ OrganisationServiceContext,
2443
+ OrganisationServiceProvider,
2444
+ useOrganisationService,
2445
+ init_OrganisationServiceProvider,
2446
+ EventServiceContext,
2447
+ EventServiceProvider,
2448
+ useEventService,
2449
+ init_EventServiceProvider,
2450
+ InactivityServiceContext,
2451
+ InactivityServiceProvider,
2452
+ useInactivityService,
2453
+ init_InactivityServiceProvider,
2454
+ useAuthService2,
2455
+ init_useAuthService,
2456
+ useRBACService2,
2457
+ init_useRBACService,
2458
+ useOrganisationService2,
2459
+ init_useOrganisationService,
2460
+ useEventService2,
2461
+ init_useEventService,
2462
+ useInactivityService2,
2463
+ init_useInactivityService,
2464
+ useUnifiedAuth,
2465
+ UnifiedAuthProvider,
2466
+ init_UnifiedAuthProvider
2467
+ };
2468
+ //# sourceMappingURL=chunk-WN6XJWOS.js.map