@jmruthers/pace-core 0.6.3 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/{DataTable-THFPBKTP.js → DataTable-E7YQZD7D.js} +7 -7
  2. package/dist/{UnifiedAuthProvider-KAGUYQ4J.js → UnifiedAuthProvider-QPXO24B4.js} +4 -4
  3. package/dist/{api-IAGWF3ZG.js → api-6LVZTHDS.js} +3 -3
  4. package/dist/{chunk-QRPVRXYT.js → chunk-36LVWXB2.js} +2 -1
  5. package/dist/{chunk-RWEBCB47.js → chunk-3LPHPB62.js} +2 -2
  6. package/dist/{chunk-HFZBI76P.js → chunk-5EC5MEWX.js} +4 -4
  7. package/dist/{chunk-PQBSKX33.js → chunk-7JPAB3T5.js} +4 -4
  8. package/dist/{chunk-CNCQDFLN.js → chunk-ATKZM7RX.js} +14 -14
  9. package/dist/{chunk-DWUBLJJM.js → chunk-AVMLPIM7.js} +88 -8
  10. package/dist/chunk-AVMLPIM7.js.map +1 -0
  11. package/dist/{chunk-ZNIWI3UC.js → chunk-I6DAQMWX.js} +25 -15
  12. package/dist/{chunk-ZNIWI3UC.js.map → chunk-I6DAQMWX.js.map} +1 -1
  13. package/dist/{chunk-YDQHOZNA.js → chunk-NN6WWZ5U.js} +8 -8
  14. package/dist/{chunk-6Z7LTB3D.js → chunk-OEWDTMG7.js} +4 -4
  15. package/dist/{chunk-2T2IG7T7.js → chunk-YKRAFF5K.js} +3 -3
  16. package/dist/components.js +8 -8
  17. package/dist/{contextValidator-3JNZKUTX.js → contextValidator-OOPCLPZW.js} +2 -2
  18. package/dist/hooks.js +6 -6
  19. package/dist/index.js +10 -10
  20. package/dist/providers.js +3 -3
  21. package/dist/rbac/index.js +6 -6
  22. package/dist/utils.js +1 -1
  23. package/docs/api/modules.md +2 -2
  24. package/package.json +8 -3
  25. package/scripts/audit/core/checks/dependencies.cjs +9 -0
  26. package/src/components/PaceAppLayout/PaceAppLayout.tsx +22 -3
  27. package/src/services/EventService.ts +99 -2
  28. package/dist/chunk-DWUBLJJM.js.map +0 -1
  29. /package/dist/{DataTable-THFPBKTP.js.map → DataTable-E7YQZD7D.js.map} +0 -0
  30. /package/dist/{UnifiedAuthProvider-KAGUYQ4J.js.map → UnifiedAuthProvider-QPXO24B4.js.map} +0 -0
  31. /package/dist/{api-IAGWF3ZG.js.map → api-6LVZTHDS.js.map} +0 -0
  32. /package/dist/{chunk-QRPVRXYT.js.map → chunk-36LVWXB2.js.map} +0 -0
  33. /package/dist/{chunk-RWEBCB47.js.map → chunk-3LPHPB62.js.map} +0 -0
  34. /package/dist/{chunk-HFZBI76P.js.map → chunk-5EC5MEWX.js.map} +0 -0
  35. /package/dist/{chunk-PQBSKX33.js.map → chunk-7JPAB3T5.js.map} +0 -0
  36. /package/dist/{chunk-CNCQDFLN.js.map → chunk-ATKZM7RX.js.map} +0 -0
  37. /package/dist/{chunk-YDQHOZNA.js.map → chunk-NN6WWZ5U.js.map} +0 -0
  38. /package/dist/{chunk-6Z7LTB3D.js.map → chunk-OEWDTMG7.js.map} +0 -0
  39. /package/dist/{chunk-2T2IG7T7.js.map → chunk-YKRAFF5K.js.map} +0 -0
  40. /package/dist/{contextValidator-3JNZKUTX.js.map → contextValidator-OOPCLPZW.js.map} +0 -0
@@ -2,11 +2,11 @@ import {
2
2
  useAppConfig,
3
3
  useEvents,
4
4
  useOrganisationSecurity
5
- } from "./chunk-6Z7LTB3D.js";
5
+ } from "./chunk-OEWDTMG7.js";
6
6
  import {
7
7
  useOrganisations,
8
8
  useUnifiedAuth
9
- } from "./chunk-DWUBLJJM.js";
9
+ } from "./chunk-AVMLPIM7.js";
10
10
  import {
11
11
  getAccessLevel,
12
12
  getPageScopeType,
@@ -16,11 +16,11 @@ import {
16
16
  isPermitted,
17
17
  isPermittedCached,
18
18
  resolveAppContext
19
- } from "./chunk-RWEBCB47.js";
19
+ } from "./chunk-3LPHPB62.js";
20
20
  import {
21
21
  ContextValidator,
22
22
  OrganisationContextRequiredError
23
- } from "./chunk-QRPVRXYT.js";
23
+ } from "./chunk-36LVWXB2.js";
24
24
  import {
25
25
  getCurrentAppName
26
26
  } from "./chunk-M7MPQISP.js";
@@ -542,7 +542,7 @@ function useResolvedScope({
542
542
  }
543
543
  return;
544
544
  }
545
- const { ContextValidator: ContextValidator2 } = await import("./contextValidator-3JNZKUTX.js");
545
+ const { ContextValidator: ContextValidator2 } = await import("./contextValidator-OOPCLPZW.js");
546
546
  const validation = await ContextValidator2.resolveScopeForPage(
547
547
  initialScope,
548
548
  "organisation",
@@ -822,7 +822,7 @@ function useAccessLevel(userId, scope) {
822
822
  try {
823
823
  setIsLoading(true);
824
824
  setError(null);
825
- const { isSuperAdmin: checkSuperAdmin } = await import("./api-IAGWF3ZG.js");
825
+ const { isSuperAdmin: checkSuperAdmin } = await import("./api-6LVZTHDS.js");
826
826
  const isSuperAdminUser = await checkSuperAdmin(userId);
827
827
  if (isSuperAdminUser) {
828
828
  setAccessLevel("super");
@@ -941,7 +941,7 @@ function useCan(userId, scope, permission, pageId, useCache = true, precomputedS
941
941
  const checkSuperAdmin = async () => {
942
942
  const startTime = Date.now();
943
943
  try {
944
- const { isSuperAdmin: checkSuperAdmin2 } = await import("./api-IAGWF3ZG.js");
944
+ const { isSuperAdmin: checkSuperAdmin2 } = await import("./api-6LVZTHDS.js");
945
945
  const timeoutWarning = setTimeout(() => {
946
946
  if (!cancelled) {
947
947
  console.warn("[useCan] Super admin check taking longer than 5 seconds", {
@@ -1942,4 +1942,4 @@ export {
1942
1942
  useRoleManagement,
1943
1943
  useSecureSupabase
1944
1944
  };
1945
- //# sourceMappingURL=chunk-YDQHOZNA.js.map
1945
+ //# sourceMappingURL=chunk-NN6WWZ5U.js.map
@@ -2,7 +2,7 @@ import {
2
2
  useEventService,
3
3
  useOrganisations,
4
4
  useUnifiedAuth
5
- } from "./chunk-DWUBLJJM.js";
5
+ } from "./chunk-AVMLPIM7.js";
6
6
  import {
7
7
  performanceBudgetMonitor
8
8
  } from "./chunk-FMUCXFII.js";
@@ -403,7 +403,7 @@ var useOrganisationSecurity = () => {
403
403
  const targetOrgId = orgId || selectedOrganisation?.id;
404
404
  if (!targetOrgId || !user) return false;
405
405
  try {
406
- const { isPermittedCached } = await import("./api-IAGWF3ZG.js");
406
+ const { isPermittedCached } = await import("./api-6LVZTHDS.js");
407
407
  const scope = {
408
408
  organisationId: targetOrgId,
409
409
  eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,
@@ -426,7 +426,7 @@ var useOrganisationSecurity = () => {
426
426
  const targetOrgId = orgId || selectedOrganisation?.id;
427
427
  if (!targetOrgId || !user) return [];
428
428
  try {
429
- const { getPermissionMap } = await import("./api-IAGWF3ZG.js");
429
+ const { getPermissionMap } = await import("./api-6LVZTHDS.js");
430
430
  const scope = {
431
431
  organisationId: targetOrgId,
432
432
  eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,
@@ -515,4 +515,4 @@ export {
515
515
  useAppConfig,
516
516
  useOrganisationSecurity
517
517
  };
518
- //# sourceMappingURL=chunk-6Z7LTB3D.js.map
518
+ //# sourceMappingURL=chunk-OEWDTMG7.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  useEvents
3
- } from "./chunk-6Z7LTB3D.js";
3
+ } from "./chunk-OEWDTMG7.js";
4
4
  import {
5
5
  isSuperAdmin
6
- } from "./chunk-RWEBCB47.js";
6
+ } from "./chunk-3LPHPB62.js";
7
7
  import {
8
8
  assertAppId
9
9
  } from "./chunk-QXHPKYJV.js";
@@ -2234,4 +2234,4 @@ export {
2234
2234
  useEventTheme,
2235
2235
  usePreventTabReload
2236
2236
  };
2237
- //# sourceMappingURL=chunk-2T2IG7T7.js.map
2237
+ //# sourceMappingURL=chunk-YKRAFF5K.js.map
@@ -47,7 +47,7 @@ import {
47
47
  useFileReferenceById,
48
48
  useFileReferenceForRecord,
49
49
  useFilesByCategory
50
- } from "./chunk-ZNIWI3UC.js";
50
+ } from "./chunk-I6DAQMWX.js";
51
51
  import {
52
52
  Alert,
53
53
  AlertDescription,
@@ -88,9 +88,9 @@ import {
88
88
  TooltipProvider,
89
89
  TooltipRoot,
90
90
  TooltipTrigger
91
- } from "./chunk-PQBSKX33.js";
92
- import "./chunk-YDQHOZNA.js";
93
- import "./chunk-2T2IG7T7.js";
91
+ } from "./chunk-7JPAB3T5.js";
92
+ import "./chunk-NN6WWZ5U.js";
93
+ import "./chunk-YKRAFF5K.js";
94
94
  import {
95
95
  useToast
96
96
  } from "./chunk-6SOIHG6Z.js";
@@ -100,15 +100,15 @@ import {
100
100
  PublicPageProvider,
101
101
  useIsPublicPage,
102
102
  usePublicPageContext
103
- } from "./chunk-6Z7LTB3D.js";
103
+ } from "./chunk-OEWDTMG7.js";
104
104
  import "./chunk-KQCRWDSA.js";
105
105
  import {
106
106
  UnifiedAuthProvider,
107
107
  useUnifiedAuth
108
- } from "./chunk-DWUBLJJM.js";
109
- import "./chunk-RWEBCB47.js";
108
+ } from "./chunk-AVMLPIM7.js";
109
+ import "./chunk-3LPHPB62.js";
110
110
  import "./chunk-63FOKYGO.js";
111
- import "./chunk-QRPVRXYT.js";
111
+ import "./chunk-36LVWXB2.js";
112
112
  import {
113
113
  FileCategory
114
114
  } from "./chunk-ZSAAAMVR.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  ContextValidator
3
- } from "./chunk-QRPVRXYT.js";
3
+ } from "./chunk-36LVWXB2.js";
4
4
  import "./chunk-PWLANIRT.js";
5
5
  import "./chunk-DGUM43GV.js";
6
6
  export {
7
7
  ContextValidator
8
8
  };
9
- //# sourceMappingURL=contextValidator-3JNZKUTX.js.map
9
+ //# sourceMappingURL=contextValidator-OOPCLPZW.js.map
package/dist/hooks.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  usePublicEventLogo,
13
13
  usePublicRouteParams,
14
14
  useZodForm
15
- } from "./chunk-HFZBI76P.js";
15
+ } from "./chunk-5EC5MEWX.js";
16
16
  import {
17
17
  archiveFile,
18
18
  cleanupQueryCache,
@@ -34,7 +34,7 @@ import {
34
34
  usePreventTabReload,
35
35
  usePublicFileDisplay,
36
36
  useQueryCache
37
- } from "./chunk-2T2IG7T7.js";
37
+ } from "./chunk-YKRAFF5K.js";
38
38
  import {
39
39
  useDataTablePerformance,
40
40
  useToast
@@ -42,12 +42,12 @@ import {
42
42
  import {
43
43
  useAppConfig,
44
44
  useOrganisationSecurity
45
- } from "./chunk-6Z7LTB3D.js";
45
+ } from "./chunk-OEWDTMG7.js";
46
46
  import "./chunk-KQCRWDSA.js";
47
- import "./chunk-DWUBLJJM.js";
48
- import "./chunk-RWEBCB47.js";
47
+ import "./chunk-AVMLPIM7.js";
48
+ import "./chunk-3LPHPB62.js";
49
49
  import "./chunk-63FOKYGO.js";
50
- import "./chunk-QRPVRXYT.js";
50
+ import "./chunk-36LVWXB2.js";
51
51
  import "./chunk-ZSAAAMVR.js";
52
52
  import "./chunk-QXHPKYJV.js";
53
53
  import {
package/dist/index.js CHANGED
@@ -39,7 +39,7 @@ import {
39
39
  withAccessLevelGuard,
40
40
  withPermissionGuard,
41
41
  withRoleGuard
42
- } from "./chunk-CNCQDFLN.js";
42
+ } from "./chunk-ATKZM7RX.js";
43
43
  import {
44
44
  AddressField,
45
45
  Avatar,
@@ -90,7 +90,7 @@ import {
90
90
  useFileReferenceById,
91
91
  useFileReferenceForRecord,
92
92
  useFilesByCategory
93
- } from "./chunk-ZNIWI3UC.js";
93
+ } from "./chunk-I6DAQMWX.js";
94
94
  import {
95
95
  Alert,
96
96
  AlertDescription,
@@ -140,7 +140,7 @@ import {
140
140
  max,
141
141
  min,
142
142
  sum
143
- } from "./chunk-PQBSKX33.js";
143
+ } from "./chunk-7JPAB3T5.js";
144
144
  import {
145
145
  SECURE_CLIENT_SYMBOL,
146
146
  SecureSupabaseClient,
@@ -160,7 +160,7 @@ import {
160
160
  useRoleManagement,
161
161
  useSecureSupabase,
162
162
  warnIfInsecureClient
163
- } from "./chunk-YDQHOZNA.js";
163
+ } from "./chunk-NN6WWZ5U.js";
164
164
  import {
165
165
  StorageUtils,
166
166
  clearPublicEventCache,
@@ -176,7 +176,7 @@ import {
176
176
  usePublicEventLogo,
177
177
  usePublicRouteParams,
178
178
  useZodForm
179
- } from "./chunk-HFZBI76P.js";
179
+ } from "./chunk-5EC5MEWX.js";
180
180
  import {
181
181
  APP_PATH_MAPPING,
182
182
  DEFAULT_FILE_SIZE_LIMIT,
@@ -200,7 +200,7 @@ import {
200
200
  useEventTheme,
201
201
  usePublicFileDisplay,
202
202
  validateFileSize
203
- } from "./chunk-2T2IG7T7.js";
203
+ } from "./chunk-YKRAFF5K.js";
204
204
  import {
205
205
  useToast
206
206
  } from "./chunk-6SOIHG6Z.js";
@@ -213,7 +213,7 @@ import {
213
213
  useIsPublicPage,
214
214
  useOrganisationSecurity,
215
215
  usePublicPageContext
216
- } from "./chunk-6Z7LTB3D.js";
216
+ } from "./chunk-OEWDTMG7.js";
217
217
  import "./chunk-KQCRWDSA.js";
218
218
  import {
219
219
  EventServiceProvider,
@@ -227,7 +227,7 @@ import {
227
227
  useOrganisations,
228
228
  useSessionRestoration,
229
229
  useUnifiedAuth
230
- } from "./chunk-DWUBLJJM.js";
230
+ } from "./chunk-AVMLPIM7.js";
231
231
  import {
232
232
  CACHE_PATTERNS,
233
233
  RBACCache,
@@ -259,7 +259,7 @@ import {
259
259
  resetPerformanceMetrics,
260
260
  resolveAppContext,
261
261
  setupRBAC
262
- } from "./chunk-RWEBCB47.js";
262
+ } from "./chunk-3LPHPB62.js";
263
263
  import {
264
264
  RBACAuditManager,
265
265
  createAuditManager,
@@ -267,7 +267,7 @@ import {
267
267
  getGlobalAuditManager,
268
268
  setGlobalAuditManager
269
269
  } from "./chunk-63FOKYGO.js";
270
- import "./chunk-QRPVRXYT.js";
270
+ import "./chunk-36LVWXB2.js";
271
271
  import {
272
272
  FileCategory
273
273
  } from "./chunk-ZSAAAMVR.js";
package/dist/providers.js CHANGED
@@ -11,10 +11,10 @@ import {
11
11
  UnifiedAuthContext,
12
12
  UnifiedAuthProvider,
13
13
  useUnifiedAuth
14
- } from "./chunk-DWUBLJJM.js";
15
- import "./chunk-RWEBCB47.js";
14
+ } from "./chunk-AVMLPIM7.js";
15
+ import "./chunk-3LPHPB62.js";
16
16
  import "./chunk-63FOKYGO.js";
17
- import "./chunk-QRPVRXYT.js";
17
+ import "./chunk-36LVWXB2.js";
18
18
  import "./chunk-QXHPKYJV.js";
19
19
  import "./chunk-VBXEHIUJ.js";
20
20
  import "./chunk-PWLANIRT.js";
@@ -39,7 +39,7 @@ import {
39
39
  withAccessLevelGuard,
40
40
  withPermissionGuard,
41
41
  withRoleGuard
42
- } from "../chunk-CNCQDFLN.js";
42
+ } from "../chunk-ATKZM7RX.js";
43
43
  import {
44
44
  SECURE_CLIENT_SYMBOL,
45
45
  SecureSupabaseClient,
@@ -59,10 +59,10 @@ import {
59
59
  useRoleManagement,
60
60
  useSecureSupabase,
61
61
  warnIfInsecureClient
62
- } from "../chunk-YDQHOZNA.js";
63
- import "../chunk-6Z7LTB3D.js";
62
+ } from "../chunk-NN6WWZ5U.js";
63
+ import "../chunk-OEWDTMG7.js";
64
64
  import "../chunk-KQCRWDSA.js";
65
- import "../chunk-DWUBLJJM.js";
65
+ import "../chunk-AVMLPIM7.js";
66
66
  import {
67
67
  CACHE_PATTERNS,
68
68
  RBACCache,
@@ -94,7 +94,7 @@ import {
94
94
  resetPerformanceMetrics,
95
95
  resolveAppContext,
96
96
  setupRBAC
97
- } from "../chunk-RWEBCB47.js";
97
+ } from "../chunk-3LPHPB62.js";
98
98
  import {
99
99
  RBACAuditManager,
100
100
  createAuditManager,
@@ -102,7 +102,7 @@ import {
102
102
  getGlobalAuditManager,
103
103
  setGlobalAuditManager
104
104
  } from "../chunk-63FOKYGO.js";
105
- import "../chunk-QRPVRXYT.js";
105
+ import "../chunk-36LVWXB2.js";
106
106
  import "../chunk-QXHPKYJV.js";
107
107
  import "../chunk-M7MPQISP.js";
108
108
  import "../chunk-FMUCXFII.js";
package/dist/utils.js CHANGED
@@ -951,7 +951,7 @@ function createLazyComponent(importFn, componentName, options = {}) {
951
951
  return WrappedComponent;
952
952
  }
953
953
  var LazyDataTable = createLazyComponent(
954
- () => import("./DataTable-THFPBKTP.js").then((module) => ({ default: module.DataTable })),
954
+ () => import("./DataTable-E7YQZD7D.js").then((module) => ({ default: module.DataTable })),
955
955
  "DataTable"
956
956
  );
957
957
 
@@ -1161,7 +1161,7 @@ Configures the application layout including navigation, header, and footer.
1161
1161
 
1162
1162
  #### Defined in
1163
1163
 
1164
- [packages/core/src/components/PaceAppLayout/PaceAppLayout.tsx:128](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PaceAppLayout/PaceAppLayout.tsx#L128)
1164
+ [packages/core/src/components/PaceAppLayout/PaceAppLayout.tsx:129](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PaceAppLayout/PaceAppLayout.tsx#L129)
1165
1165
 
1166
1166
  ___
1167
1167
 
@@ -4778,7 +4778,7 @@ function AdminApp() {
4778
4778
 
4779
4779
  #### Defined in
4780
4780
 
4781
- [packages/core/src/components/PaceAppLayout/PaceAppLayout.tsx:351](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PaceAppLayout/PaceAppLayout.tsx#L351)
4781
+ [packages/core/src/components/PaceAppLayout/PaceAppLayout.tsx:352](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PaceAppLayout/PaceAppLayout.tsx#L352)
4782
4782
 
4783
4783
  ___
4784
4784
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -222,7 +222,6 @@
222
222
  "zod": "^3.20.0"
223
223
  },
224
224
  "devDependencies": {
225
- "@tanstack/react-virtual": "^3.10.8",
226
225
  "@testing-library/jest-dom": "^6.6.3",
227
226
  "@testing-library/react": "^16.1.0",
228
227
  "@testing-library/user-event": "^14.5.2",
@@ -247,9 +246,15 @@
247
246
  "vite": "^6.0.3"
248
247
  },
249
248
  "dependencies": {
249
+ "@hookform/resolvers": "^3.9.0",
250
250
  "@supabase/supabase-js": "^2.49.10",
251
251
  "@tanstack/react-query": "^5.56.2",
252
+ "@tanstack/react-virtual": "^3.13.12",
252
253
  "date-fns": "^3.0.0",
253
- "date-fns-tz": "^3.0.0"
254
+ "date-fns-tz": "^3.0.0",
255
+ "lodash.debounce": "^4.0.8",
256
+ "lodash.throttle": "^4.1.1",
257
+ "papaparse": "^5.5.3",
258
+ "react-is": "^19.2.0"
254
259
  }
255
260
  }
@@ -647,6 +647,15 @@ const dependenciesCheck = {
647
647
  if (dep === '@jmruthers/pace-core') {
648
648
  return;
649
649
  }
650
+
651
+ // Skip if it's a dependency of pace-core (required by pace-core's bundled code)
652
+ if (!isPaceCoreRepository) {
653
+ const paceCoreDeps = getPaceCoreDeps(projectRoot);
654
+ if (paceCoreDeps[dep]) {
655
+ // This dependency is required by pace-core, so it's not unused
656
+ return;
657
+ }
658
+ }
650
659
 
651
660
  // Check if package is imported in source files (runtime)
652
661
  if (importedPackages.has(dep)) {
@@ -109,6 +109,7 @@ import { getCurrentAppName } from '../../utils/app/appNameResolver';
109
109
  import { isSuperAdmin as checkSuperAdminApi } from '../../rbac/api';
110
110
  import { logger } from '../../utils/core/logger';
111
111
  import type { Permission, Scope } from '../../rbac/types';
112
+ import { EventContextRequiredError, OrganisationContextRequiredError } from '../../rbac/types';
112
113
 
113
114
  // Stable empty objects to prevent infinite loops
114
115
  const EMPTY_PAGE_ID_MAPPING = {};
@@ -548,6 +549,21 @@ export function PaceAppLayout({
548
549
  // Use combined super admin check (RBAC + direct check)
549
550
  const can = isSuperAdmin ? true : canFromHook;
550
551
  const hasPermission = enforcePermissions ? can : true;
552
+
553
+ // Check if the permission error is a context error (missing event/org context)
554
+ // Context errors should allow the page to render so it can show helpful messaging
555
+ // Real permission denials should still show "Access Denied"
556
+ const isContextError = useMemo(() => {
557
+ if (!permissionError) {
558
+ return false;
559
+ }
560
+ return (
561
+ permissionError instanceof EventContextRequiredError ||
562
+ permissionError instanceof OrganisationContextRequiredError ||
563
+ permissionError.name === 'EventContextRequiredError' ||
564
+ permissionError.name === 'OrganisationContextRequiredError'
565
+ );
566
+ }, [permissionError]);
551
567
 
552
568
  // Handle permission check results with audit logging and callbacks
553
569
  useEffect(() => {
@@ -1006,9 +1022,11 @@ export function PaceAppLayout({
1006
1022
  );
1007
1023
  }
1008
1024
 
1009
- // Show permission error (only after BOTH checks are complete)
1025
+ // Show permission error (only after check is complete)
1010
1026
  // Super admins bypass all permission checks, so don't show errors for them
1011
- if (enforcePermissions && permissionError && !isSuperAdmin) {
1027
+ // Context errors (missing event/org) should allow the page to render so it can show helpful messaging
1028
+ // Real permission errors should still show "Access Denied"
1029
+ if (enforcePermissions && permissionError && !isSuperAdmin && !isContextError) {
1012
1030
  return (
1013
1031
  <div className="flex items-center justify-center min-h-screen">
1014
1032
  <div className="text-center">
@@ -1022,7 +1040,8 @@ export function PaceAppLayout({
1022
1040
 
1023
1041
  // Show permission fallback if user lacks permission
1024
1042
  // Only show this if super admin check is complete and user is not a super admin
1025
- if (enforcePermissions && hasPermission === false && !isCheckingSuperAdminDirect && !isSuperAdmin) {
1043
+ // Skip if it's a context error (missing event/org) - allow page to render and show helpful messaging
1044
+ if (enforcePermissions && hasPermission === false && !isCheckingSuperAdminDirect && !isSuperAdmin && !isContextError) {
1026
1045
  // NEW: Phase 1 - Use page permission fallback if available
1027
1046
  if (enforcePagePermissions && pagePermissionFallback) {
1028
1047
  return <>{pagePermissionFallback}</>;
@@ -108,6 +108,9 @@ export class EventService extends BaseService implements IEventService {
108
108
  this.resetInitialization();
109
109
  this.isInitializedRef = false;
110
110
  this.isFetchingRef = false;
111
+ // Reset user cleared flag when new user logs in - allows auto-selection for new user
112
+ this.userClearedEventRef = false;
113
+ this.hasAutoSelectedRef = false;
111
114
 
112
115
  logger.debug('EventService', `User changed [ID:${this.instanceId}]`, {
113
116
  previousUserId,
@@ -177,15 +180,40 @@ export class EventService extends BaseService implements IEventService {
177
180
  // Do not clear events for super admins when organisation context is removed
178
181
  const shouldClearEvents = !this.isSuperAdmin;
179
182
 
183
+ // Determine if this is the first time an org is being set (from null/undefined to a value)
184
+ const isFirstOrgSet = (previousOrgId === null || previousOrgId === undefined) && newOrgId !== null && newOrgId !== undefined;
185
+
180
186
  // Clear events ONLY when switching between different organisations (not when org first becomes available)
181
- if (previousOrgId !== null && newOrgId !== null && previousOrgId !== newOrgId) {
187
+ // IMPORTANT: Check isFirstOrgSet FIRST to prevent clearing when org is first set
188
+ if (isFirstOrgSet) {
189
+ // Organisation first becomes available - DO NOT clear the event, preserve it
190
+ // The event will be validated when events are re-fetched with org context
191
+ // If it's no longer valid, it will be cleared without setting userClearedEventRef = true
192
+ const hadAutoSelectedEvent = this.hasAutoSelectedRef && !!this.selectedEvent;
193
+ this.userClearedEventRef = false;
194
+ // Don't reset hasAutoSelectedRef if we had an auto-selected event - preserve it
195
+ // This ensures the event remains selected when org is first set
196
+ if (!hadAutoSelectedEvent) {
197
+ this.hasAutoSelectedRef = false;
198
+ }
199
+ logger.debug('EventService', 'Organisation first set - preserving event and resetting auto-selection flags', {
200
+ organisationId: newOrgId,
201
+ hasSelectedEvent: !!this.selectedEvent,
202
+ selectedEventId: this.selectedEvent?.event_id,
203
+ hadAutoSelectedEvent,
204
+ preservingEvent: hadAutoSelectedEvent,
205
+ previousOrgId,
206
+ newOrgId
207
+ });
208
+ } else if (previousOrgId !== null && previousOrgId !== undefined && newOrgId !== null && newOrgId !== undefined && previousOrgId !== newOrgId) {
209
+ // Switching between different organisations - clear events
182
210
  if (shouldClearEvents) {
183
211
  this.events = [];
184
212
  // Use setSelectedEvent(null) to preserve userClearedEventRef flag if user explicitly cleared
185
213
  // This prevents auto-selection from re-selecting the event after org switch
186
214
  this.setSelectedEvent(null);
187
215
  }
188
- } else if (previousOrgId !== null && newOrgId === null) {
216
+ } else if (previousOrgId !== null && previousOrgId !== undefined && newOrgId === null) {
189
217
  // Organisation was removed - clear events if not super admin
190
218
  if (shouldClearEvents) {
191
219
  this.events = [];
@@ -232,7 +260,13 @@ export class EventService extends BaseService implements IEventService {
232
260
  });
233
261
  // Reset the user cleared flag when selecting an event
234
262
  this.userClearedEventRef = false;
263
+ logger.debug('EventService', 'Event selected', {
264
+ eventId: event.event_id,
265
+ eventName: event.event_name,
266
+ userClearedEventRef: this.userClearedEventRef
267
+ });
235
268
  } else {
269
+ const previousEventId = this.selectedEvent?.event_id;
236
270
  this.selectedEvent = null;
237
271
  this.setSelectedEventId?.(null);
238
272
  // Clear from secure storage (don't await to avoid blocking)
@@ -243,6 +277,11 @@ export class EventService extends BaseService implements IEventService {
243
277
  this.hasAutoSelectedRef = false;
244
278
  // Mark that user explicitly cleared the event to prevent auto-selection
245
279
  this.userClearedEventRef = true;
280
+ logger.debug('EventService', 'Event cleared via setSelectedEvent(null)', {
281
+ previousEventId,
282
+ userClearedEventRef: this.userClearedEventRef,
283
+ stackTrace: new Error().stack
284
+ });
246
285
  }
247
286
  this.notify();
248
287
  }
@@ -617,6 +656,28 @@ export class EventService extends BaseService implements IEventService {
617
656
  this.events = sortedEvents;
618
657
  this.error = null;
619
658
 
659
+ // Validate selected event - if it's no longer in the events list, clear it
660
+ // This can happen when org context changes or events are refreshed
661
+ // Don't set userClearedEventRef to true in this case - it's an automatic clear, not user-initiated
662
+ if (this.selectedEvent) {
663
+ const selectedEventId = this.selectedEvent.event_id;
664
+ const eventStillExists = transformedEvents.some(
665
+ e => e.event_id === selectedEventId
666
+ );
667
+ if (!eventStillExists) {
668
+ // Event no longer available - clear it but don't mark as user-cleared
669
+ const previousUserClearedRef = this.userClearedEventRef;
670
+ this.selectedEvent = null;
671
+ this.setSelectedEventId?.(null);
672
+ // Restore the previous userClearedEventRef value - this was an automatic clear, not user-initiated
673
+ this.userClearedEventRef = previousUserClearedRef;
674
+ logger.debug('EventService', 'Cleared selected event - no longer in events list', {
675
+ previousEventId: selectedEventId,
676
+ eventsCount: transformedEvents.length
677
+ });
678
+ }
679
+ }
680
+
620
681
  // Reset auto-selection ref for new events
621
682
  this.hasAutoSelectedRef = false;
622
683
 
@@ -624,26 +685,62 @@ export class EventService extends BaseService implements IEventService {
624
685
  if (!skipLoadPersisted) {
625
686
  const persistedEventLoaded = await this.loadPersistedEvent(transformedEvents);
626
687
 
688
+ logger.debug('EventService', 'Event selection check', {
689
+ persistedEventLoaded,
690
+ userClearedEventRef: this.userClearedEventRef,
691
+ eventsCount: transformedEvents.length,
692
+ hasSelectedEvent: !!this.selectedEvent
693
+ });
694
+
627
695
  // If no persisted event was loaded and user hasn't explicitly cleared an event, auto-select the next event
628
696
  if (!persistedEventLoaded && !this.userClearedEventRef) {
629
697
  const nextEvent = this.getNextEventByDate(transformedEvents);
698
+ logger.debug('EventService', 'Auto-selection attempt', {
699
+ nextEventFound: !!nextEvent,
700
+ nextEventId: nextEvent?.event_id,
701
+ nextEventDate: nextEvent?.event_date
702
+ });
630
703
  if (nextEvent) {
631
704
  this.hasAutoSelectedRef = true;
632
705
  // Use setSelectedEvent() to ensure consistent behavior
633
706
  // Theme will be applied by useEventTheme() hook
634
707
  this.setSelectedEvent(nextEvent);
708
+ logger.debug('EventService', 'Auto-selected next event', {
709
+ eventId: nextEvent.event_id,
710
+ eventName: nextEvent.event_name,
711
+ eventDate: nextEvent.event_date
712
+ });
713
+ } else {
714
+ logger.debug('EventService', 'No next event found for auto-selection', {
715
+ eventsCount: transformedEvents.length,
716
+ eventsWithDates: transformedEvents.filter(e => e.event_date).length
717
+ });
635
718
  }
719
+ } else if (persistedEventLoaded) {
720
+ logger.debug('EventService', 'Skipped auto-selection - persisted event loaded');
721
+ } else if (this.userClearedEventRef) {
722
+ logger.debug('EventService', 'Skipped auto-selection - user explicitly cleared event');
636
723
  }
637
724
  } else {
638
725
  // If skipping persisted event load, still do auto-selection for new users
639
726
  if (!this.userClearedEventRef) {
640
727
  const nextEvent = this.getNextEventByDate(transformedEvents);
728
+ logger.debug('EventService', 'Auto-selection attempt (skip persisted)', {
729
+ nextEventFound: !!nextEvent,
730
+ nextEventId: nextEvent?.event_id
731
+ });
641
732
  if (nextEvent) {
642
733
  this.hasAutoSelectedRef = true;
643
734
  // Use setSelectedEvent() to ensure consistent behavior
644
735
  // Theme will be applied by useEventTheme() hook
645
736
  this.setSelectedEvent(nextEvent);
737
+ logger.debug('EventService', 'Auto-selected next event (skip persisted)', {
738
+ eventId: nextEvent.event_id,
739
+ eventName: nextEvent.event_name
740
+ });
646
741
  }
742
+ } else {
743
+ logger.debug('EventService', 'Skipped auto-selection (skip persisted) - user explicitly cleared event');
647
744
  }
648
745
  }
649
746
  }