@jmruthers/pace-core 0.5.57 → 0.5.59

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 (134) hide show
  1. package/dist/{DataTable-GABLO6H5.js → DataTable-BKUUSZC5.js} +5 -5
  2. package/dist/{chunk-77TYN5B4.js → chunk-5MLDIGHB.js} +3 -3
  3. package/dist/{chunk-YI25YXER.js → chunk-CGSYCF2W.js} +30 -14
  4. package/dist/chunk-CGSYCF2W.js.map +1 -0
  5. package/dist/{chunk-CACNKQ3B.js → chunk-E4FPK232.js} +3 -3
  6. package/dist/{chunk-CACNKQ3B.js.map → chunk-E4FPK232.js.map} +1 -1
  7. package/dist/{chunk-DH7VDOQQ.js → chunk-HSK6AJKC.js} +3 -3
  8. package/dist/{chunk-D3X2CGAG.js → chunk-ITPVFKDH.js} +2 -2
  9. package/dist/{chunk-S3MPG3BA.js → chunk-OJK7SV4M.js} +5 -5
  10. package/dist/{chunk-S3MPG3BA.js.map → chunk-OJK7SV4M.js.map} +1 -1
  11. package/dist/{chunk-E3ITLZBM.js → chunk-PHWA52IO.js} +3 -3
  12. package/dist/{chunk-VY3DOGPU.js → chunk-QWS45MLA.js} +4 -4
  13. package/dist/{chunk-JO3Y2BOW.js → chunk-STLY4XWV.js} +5 -5
  14. package/dist/{chunk-TCX7STCC.js → chunk-W7PPXKTZ.js} +2 -2
  15. package/dist/components.js +7 -7
  16. package/dist/hooks.js +4 -4
  17. package/dist/index.js +10 -10
  18. package/dist/providers.js +3 -3
  19. package/dist/rbac/index.js +5 -5
  20. package/dist/types.d.ts +7 -1
  21. package/dist/utils.js +1 -1
  22. package/docs/api/classes/ErrorBoundary.md +1 -1
  23. package/docs/api/classes/InvalidScopeError.md +1 -1
  24. package/docs/api/classes/MissingUserContextError.md +1 -1
  25. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  26. package/docs/api/classes/PermissionDeniedError.md +1 -1
  27. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  28. package/docs/api/classes/RBACAuditManager.md +1 -1
  29. package/docs/api/classes/RBACCache.md +1 -1
  30. package/docs/api/classes/RBACEngine.md +1 -1
  31. package/docs/api/classes/RBACError.md +1 -1
  32. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  33. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  34. package/docs/api/classes/StorageUtils.md +1 -1
  35. package/docs/api/interfaces/AggregateConfig.md +1 -1
  36. package/docs/api/interfaces/ButtonProps.md +1 -1
  37. package/docs/api/interfaces/CardProps.md +1 -1
  38. package/docs/api/interfaces/ColorPalette.md +1 -1
  39. package/docs/api/interfaces/ColorShade.md +1 -1
  40. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  41. package/docs/api/interfaces/DataTableAction.md +1 -1
  42. package/docs/api/interfaces/DataTableColumn.md +1 -1
  43. package/docs/api/interfaces/DataTableProps.md +1 -1
  44. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  45. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  46. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  47. package/docs/api/interfaces/EventContextType.md +1 -1
  48. package/docs/api/interfaces/EventLogoProps.md +1 -1
  49. package/docs/api/interfaces/EventProviderProps.md +1 -1
  50. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  51. package/docs/api/interfaces/FileUploadProps.md +1 -1
  52. package/docs/api/interfaces/FooterProps.md +1 -1
  53. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  54. package/docs/api/interfaces/InputProps.md +1 -1
  55. package/docs/api/interfaces/LabelProps.md +1 -1
  56. package/docs/api/interfaces/LoginFormProps.md +1 -1
  57. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  58. package/docs/api/interfaces/NavigationContextType.md +1 -1
  59. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  60. package/docs/api/interfaces/NavigationItem.md +1 -1
  61. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  62. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  63. package/docs/api/interfaces/Organisation.md +1 -1
  64. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  65. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  66. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  67. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  68. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  69. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  70. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  71. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  72. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  73. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  74. package/docs/api/interfaces/PaletteData.md +1 -1
  75. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  76. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  77. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  78. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  79. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  80. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  81. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  82. package/docs/api/interfaces/RBACConfig.md +1 -1
  83. package/docs/api/interfaces/RBACContextType.md +1 -1
  84. package/docs/api/interfaces/RBACLogger.md +1 -1
  85. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  86. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  87. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  88. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  89. package/docs/api/interfaces/RouteConfig.md +1 -1
  90. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  91. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  92. package/docs/api/interfaces/StorageConfig.md +1 -1
  93. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  94. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  95. package/docs/api/interfaces/StorageListOptions.md +1 -1
  96. package/docs/api/interfaces/StorageListResult.md +1 -1
  97. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  98. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  99. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  100. package/docs/api/interfaces/StyleImport.md +1 -1
  101. package/docs/api/interfaces/ToastActionElement.md +1 -1
  102. package/docs/api/interfaces/ToastProps.md +1 -1
  103. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  104. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  105. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  106. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  107. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  108. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  109. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  110. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  111. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  112. package/docs/api/interfaces/UserEventAccess.md +1 -1
  113. package/docs/api/interfaces/UserMenuProps.md +1 -1
  114. package/docs/api/interfaces/UserProfile.md +1 -1
  115. package/docs/api/modules.md +2 -2
  116. package/docs/getting-started/quick-start.md +21 -3
  117. package/docs/rbac/getting-started.md +43 -0
  118. package/docs/troubleshooting/common-issues.md +116 -1516
  119. package/package.json +1 -1
  120. package/src/__tests__/TESTING_GUIDELINES.md +1 -1
  121. package/src/hooks/__tests__/useRBAC.unit.test.ts +8 -8
  122. package/src/providers/EventProvider.tsx +32 -17
  123. package/src/providers/OrganisationProvider.tsx +2 -2
  124. package/src/rbac/hooks/useRBAC.ts +2 -2
  125. package/src/types/supabase.ts +17 -0
  126. package/dist/chunk-YI25YXER.js.map +0 -1
  127. /package/dist/{DataTable-GABLO6H5.js.map → DataTable-BKUUSZC5.js.map} +0 -0
  128. /package/dist/{chunk-77TYN5B4.js.map → chunk-5MLDIGHB.js.map} +0 -0
  129. /package/dist/{chunk-DH7VDOQQ.js.map → chunk-HSK6AJKC.js.map} +0 -0
  130. /package/dist/{chunk-D3X2CGAG.js.map → chunk-ITPVFKDH.js.map} +0 -0
  131. /package/dist/{chunk-E3ITLZBM.js.map → chunk-PHWA52IO.js.map} +0 -0
  132. /package/dist/{chunk-VY3DOGPU.js.map → chunk-QWS45MLA.js.map} +0 -0
  133. /package/dist/{chunk-JO3Y2BOW.js.map → chunk-STLY4XWV.js.map} +0 -0
  134. /package/dist/{chunk-TCX7STCC.js.map → chunk-W7PPXKTZ.js.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.5.57",
3
+ "version": "0.5.59",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -528,7 +528,7 @@ mockSupabase.rpc.mockImplementation((functionName: string, params) => {
528
528
  // useRBAC makes TWO RPC calls - must mock both
529
529
  const setupRBACMock = (permissions: any[] = []) => {
530
530
  mockSupabase.rpc.mockImplementation((functionName, params) => {
531
- if (functionName === 'rbac_app_resolve') {
531
+ if (functionName === 'util_app_resolve') {
532
532
  return Promise.resolve({
533
533
  data: [{ app_id: 'test-app-id', has_access: true }],
534
534
  error: null
@@ -24,10 +24,10 @@ vi.mock('../../providers/EventProvider', () => ({
24
24
  const mockSupabase = createMockSupabaseClient();
25
25
 
26
26
  // Helper to setup RPC mocks for useRBAC hook
27
- // useRBAC makes TWO RPC calls: rbac_app_resolve and rbac_permissions_get
27
+ // useRBAC makes TWO RPC calls: util_app_resolve and rbac_permissions_get
28
28
  const setupRBACMock = (permissions: any[] = []) => {
29
29
  mockSupabase.rpc.mockImplementation((functionName: string, params?: any) => {
30
- if (functionName === 'rbac_app_resolve') {
30
+ if (functionName === 'util_app_resolve') {
31
31
  return Promise.resolve({
32
32
  data: [{
33
33
  app_id: 'test-app-id',
@@ -279,7 +279,7 @@ describe('useRBAC', () => {
279
279
 
280
280
  // Fully mock RPC responses for this test
281
281
  mockSupabase.rpc.mockImplementation((functionName: string, params?: any) => {
282
- if (functionName === 'rbac_app_resolve') {
282
+ if (functionName === 'util_app_resolve') {
283
283
  return Promise.resolve({
284
284
  data: [{ app_id: 'test-app-id', app_name: 'test-app', has_access: true, is_active: true }],
285
285
  error: null
@@ -324,7 +324,7 @@ describe('useRBAC', () => {
324
324
 
325
325
  // Fully mock RPC responses for this test
326
326
  mockSupabase.rpc.mockImplementation((functionName: string, params?: any) => {
327
- if (functionName === 'rbac_app_resolve') {
327
+ if (functionName === 'util_app_resolve') {
328
328
  return Promise.resolve({
329
329
  data: [{ app_id: 'test-app-id', app_name: 'test-app', has_access: true, is_active: true }],
330
330
  error: null
@@ -363,7 +363,7 @@ describe('useRBAC', () => {
363
363
 
364
364
  // Fully mock RPC responses (reject on access check)
365
365
  mockSupabase.rpc.mockImplementation((functionName: string, params?: any) => {
366
- if (functionName === 'rbac_app_resolve') {
366
+ if (functionName === 'util_app_resolve') {
367
367
  return Promise.resolve({
368
368
  data: [{ app_id: 'test-app-id', app_name: 'test-app', has_access: true, is_active: true }],
369
369
  error: null
@@ -402,7 +402,7 @@ describe('useRBAC', () => {
402
402
 
403
403
  // Fully mock RPC responses
404
404
  mockSupabase.rpc.mockImplementation((functionName: string, params?: any) => {
405
- if (functionName === 'rbac_app_resolve') {
405
+ if (functionName === 'util_app_resolve') {
406
406
  return Promise.resolve({
407
407
  data: [{ app_id: 'test-app-id', app_name: 'test-app', has_access: true, is_active: true }],
408
408
  error: null
@@ -670,7 +670,7 @@ describe('useRBAC', () => {
670
670
 
671
671
  // Mock the app resolve to succeed, but permissions to fail
672
672
  mockSupabase.rpc.mockImplementation((functionName: string) => {
673
- if (functionName === 'rbac_app_resolve') {
673
+ if (functionName === 'util_app_resolve') {
674
674
  return Promise.resolve({
675
675
  data: [{
676
676
  app_id: 'test-app-id',
@@ -705,7 +705,7 @@ describe('useRBAC', () => {
705
705
 
706
706
  // Mock the app resolve to succeed, but permissions to fail
707
707
  mockSupabase.rpc.mockImplementation((functionName: string) => {
708
- if (functionName === 'rbac_app_resolve') {
708
+ if (functionName === 'util_app_resolve') {
709
709
  return Promise.resolve({
710
710
  data: [{
711
711
  app_id: 'test-app-id',
@@ -180,21 +180,18 @@ export function EventProvider({ children }: EventProviderProps) {
180
180
  await setOrganisationContext(supabase, selectedOrganisation.id);
181
181
  }
182
182
 
183
- // Resolve app name to app ID
184
- // Call the RPC function
185
- DebugLogger.log('EventProvider', 'Calling get_pace_user_events RPC with:', {
186
- user_uuid: user.id,
187
- app_name: appName,
188
- p_organisation_id: selectedOrganisation.id
189
- });
183
+ // Call the RPC function following the established pattern
184
+ DebugLogger.log('EventProvider', 'Calling data_user_events_get RPC with:', {
185
+ user_id: user.id,
186
+ organisation_id: selectedOrganisation.id,
187
+ app_name: appName
188
+ });
190
189
 
191
- const response = await supabase.rpc('get_pace_user_events', {
192
- user_uuid: user.id,
193
- app_name: appName,
194
- p_organisation_id: selectedOrganisation.id,
195
- });
196
-
197
- const { data, error: rpcError } = response || {};
190
+ const { data, error: rpcError } = await supabase.rpc('data_user_events_get', {
191
+ p_user_id: user.id,
192
+ p_organisation_id: selectedOrganisation.id,
193
+ p_app_name: appName
194
+ });
198
195
 
199
196
  DebugLogger.log('EventProvider', 'RPC response:', {
200
197
  data,
@@ -215,7 +212,25 @@ export function EventProvider({ children }: EventProviderProps) {
215
212
  organisationId: event.organisation_id,
216
213
  selectedOrganisationId: selectedOrganisation?.id
217
214
  })));
218
- setEvents(eventsData);
215
+
216
+ // Transform the data to match our Event interface
217
+ const transformedEvents: Event[] = eventsData.map((event: any) => ({
218
+ id: event.event_id, // Use event_id as the primary id
219
+ event_id: event.event_id,
220
+ event_name: event.event_name,
221
+ event_date: event.event_date,
222
+ event_venue: event.event_venue,
223
+ event_participants: event.event_participants,
224
+ event_colours: event.event_colours,
225
+ event_logo: '', // No logo field in event table
226
+ organisation_id: event.organisation_id,
227
+ is_visible: event.is_visible,
228
+ // Legacy compatibility
229
+ name: event.event_name,
230
+ start_date: event.event_date
231
+ }));
232
+
233
+ setEvents(transformedEvents);
219
234
  setError(null);
220
235
 
221
236
  // Note: Event colors are now managed by useEventTheme hook based on route and selected event
@@ -225,11 +240,11 @@ export function EventProvider({ children }: EventProviderProps) {
225
240
  hasAutoSelectedRef.current = false;
226
241
 
227
242
  // Try to restore persisted event first
228
- const persistedEventLoaded = await loadPersistedEvent(eventsData);
243
+ const persistedEventLoaded = await loadPersistedEvent(transformedEvents);
229
244
 
230
245
  // If no persisted event was loaded, auto-select the next event
231
246
  if (!persistedEventLoaded) {
232
- const nextEvent = getNextEventByDate(eventsData);
247
+ const nextEvent = getNextEventByDate(transformedEvents);
233
248
  if (nextEvent) {
234
249
  DebugLogger.log('EventProvider', 'Auto-selecting next event after no persisted event found:', nextEvent.event_name);
235
250
  hasAutoSelectedRef.current = true;
@@ -220,14 +220,14 @@ export function OrganisationProvider({ children }: OrganisationProviderProps) {
220
220
  // Only get actual members (org_admin, leader, member) - exclude supporters
221
221
  let memberships, membershipError;
222
222
  try {
223
- console.log("[OrganisationProvider] Making RPC call to rbac_user_organisation_roles_get...");
223
+ console.log("[OrganisationProvider] Making RPC call to data_user_organisation_roles_get...");
224
224
 
225
225
  // Add timeout to prevent hanging RPC calls
226
226
  const timeoutPromise = new Promise((_, reject) => {
227
227
  setTimeout(() => reject(new Error('RPC call timeout after 10 seconds')), 10000);
228
228
  });
229
229
 
230
- const rpcPromise = supabase.rpc('rbac_user_organisation_roles_get', {
230
+ const rpcPromise = supabase.rpc('data_user_organisation_roles_get', {
231
231
  p_user_id: user.id,
232
232
  p_organisation_id: null
233
233
  });
@@ -117,7 +117,7 @@ export function useRBAC(pageId?: string): UserRBACContext {
117
117
 
118
118
  try {
119
119
  // First resolve app name to app_id using secure RPC function
120
- const { data: appData, error: appError } = await supabase.rpc('rbac_app_resolve', {
120
+ const { data: appData, error: appError } = await supabase.rpc('util_app_resolve', {
121
121
  p_app_name: appName,
122
122
  p_user_id: user.id
123
123
  });
@@ -180,7 +180,7 @@ export function useRBAC(pageId?: string): UserRBACContext {
180
180
 
181
181
  try {
182
182
  // First resolve app name to app_id using secure RPC function
183
- const { data: appData, error: appError } = await supabase.rpc('rbac_app_resolve', {
183
+ const { data: appData, error: appError } = await supabase.rpc('util_app_resolve', {
184
184
  p_app_name: appName,
185
185
  p_user_id: user.id
186
186
  });
@@ -115,8 +115,15 @@ export type TableName =
115
115
 
116
116
  /**
117
117
  * Supabase RPC function names
118
+ *
119
+ * Categories:
120
+ * - Core RBAC (10): Permission checking, role management, session & audit
121
+ * - Data Access (5): Secure data access functions with RBAC checks
122
+ * - Utility (3): Helper functions supporting RBAC operations
123
+ * - Legacy (2): Deprecated functions to be removed
118
124
  */
119
125
  export type RPCFunction =
126
+ // Core RBAC Functions
120
127
  | 'rbac_permission_check'
121
128
  | 'rbac_permissions_get'
122
129
  | 'rbac_access_validate'
@@ -127,6 +134,16 @@ export type RPCFunction =
127
134
  | 'rbac_role_validate'
128
135
  | 'rbac_session_track'
129
136
  | 'rbac_audit_log'
137
+ // Data Access Functions
138
+ | 'data_user_events_get'
139
+ | 'data_user_organisation_roles_get'
140
+ | 'data_user_organisations_get'
141
+ | 'data_cake_meals_get'
142
+ // Utility Functions
143
+ | 'util_app_resolve'
144
+ | 'debug_auth_context'
145
+ | 'handle_new_user'
146
+ // Other functions
130
147
  | 'get_pace_user_events';
131
148
 
132
149
  /**
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/providers/EventProvider.tsx"],"sourcesContent":["import React, {\n createContext,\n useContext,\n useState,\n useEffect,\n useLayoutEffect,\n useCallback,\n useRef,\n useMemo,\n} from 'react';\nimport { useUnifiedAuth } from './UnifiedAuthProvider';\nimport { useOrganisations } from './OrganisationProvider';\nimport { setOrganisationContext } from '../utils/organisationContext';\nimport { DebugLogger } from '../utils/debugLogger';\n\nimport { Event } from '../types/unified';\n\nexport interface EventContextType {\n events: Event[];\n selectedEvent: Event | null;\n isLoading: boolean;\n error: Error | null;\n setSelectedEvent: (event: Event | null) => void;\n refreshEvents: () => Promise<void>;\n}\n\nconst EventContext = createContext<EventContextType | undefined>(undefined);\n\nexport const useEvents = () => {\n const context = useContext(EventContext);\n if (context === undefined) {\n throw new Error('useEvents must be used within an EventProvider');\n }\n return context;\n};\n\nexport interface EventProviderProps {\n children: React.ReactNode;\n}\n\n// Helper function to get the next event by date\nfunction getNextEventByDate(events: Event[]): Event | null {\n if (!events || events.length === 0) {\n return null;\n }\n\n const now = new Date();\n const futureEvents = events.filter(event => {\n if (!event.event_date) return false;\n const eventDate = new Date(event.event_date);\n return eventDate >= now;\n });\n\n if (futureEvents.length === 0) {\n return null;\n }\n\n // Sort by date (ascending) to get the next event\n const sortedFutureEvents = futureEvents.sort((a, b) => {\n const dateA = new Date(a.event_date!);\n const dateB = new Date(b.event_date!);\n return dateA.getTime() - dateB.getTime();\n });\n\n return sortedFutureEvents[0];\n}\n\nexport function EventProvider({ children }: EventProviderProps) {\n const [events, setEvents] = useState<Event[]>([]);\n const [selectedEvent, setSelectedEventState] = useState<Event | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const { user, session, supabase, appName, setSelectedEventId } = useUnifiedAuth();\n \n // Refs to prevent infinite loops\n const isInitializedRef = useRef(false);\n const isFetchingRef = useRef(false);\n const hasAutoSelectedRef = useRef(false);\n const userClearedEventRef = useRef(false);\n const setSelectedEventIdRef = useRef(setSelectedEventId);\n \n // Try to get organisation context, but don't fail if not available\n let selectedOrganisation = null;\n let ensureOrganisationContext = null;\n \n try {\n const orgContext = useOrganisations();\n selectedOrganisation = orgContext.selectedOrganisation;\n ensureOrganisationContext = orgContext.ensureOrganisationContext;\n } catch (error) {\n console.warn('[EventProvider] Organisation context not available:', error);\n }\n\n // Load persisted event selection with tab-scoped storage\n const loadPersistedEvent = useCallback(async (events: Event[]) => {\n try {\n // Try sessionStorage first (tab-specific)\n let persistedEventId = sessionStorage.getItem('pace-core-selected-event');\n \n // Fallback to localStorage if no sessionStorage value (for new tabs)\n if (!persistedEventId) {\n persistedEventId = localStorage.getItem('pace-core-selected-event');\n // If we found a value in localStorage, also store it in sessionStorage for this tab\n if (persistedEventId) {\n sessionStorage.setItem('pace-core-selected-event', persistedEventId);\n }\n }\n \n if (persistedEventId && events.length > 0) {\n const persistedEvent = events.find(event => event.event_id === persistedEventId);\n if (persistedEvent) {\n DebugLogger.log('EventProvider', 'Restoring persisted event:', persistedEvent.event_name);\n setSelectedEventState(persistedEvent);\n setSelectedEventId(persistedEventId);\n return true;\n } else {\n DebugLogger.log('EventProvider', 'Persisted event not found in current events, clearing storage');\n // Clear invalid persisted event\n sessionStorage.removeItem('pace-core-selected-event');\n localStorage.removeItem('pace-core-selected-event');\n }\n }\n } catch (error) {\n console.warn('[EventProvider] Failed to load persisted event:', error);\n }\n return false;\n }, [setSelectedEventId]);\n\n // Persist event selection with tab-scoped storage\n const persistEventSelection = useCallback((eventId: string) => {\n try {\n // Store in sessionStorage for tab-specific persistence\n sessionStorage.setItem('pace-core-selected-event', eventId);\n // Also store in localStorage as fallback for new tabs\n localStorage.setItem('pace-core-selected-event', eventId);\n } catch (error) {\n console.warn('[EventProvider] Failed to persist event selection:', error);\n }\n }, []);\n\n // Auto-select next event\n const autoSelectNextEvent = useCallback((events: Event[]) => {\n const nextEvent = getNextEventByDate(events);\n if (nextEvent) {\n DebugLogger.log('EventProvider', 'Auto-selecting next event:', nextEvent.event_name);\n setSelectedEventState(nextEvent);\n setSelectedEventId(nextEvent.event_id);\n persistEventSelection(nextEvent.event_id);\n }\n }, [setSelectedEventId, persistEventSelection]);\n\n // Main fetch function\n const fetchEvents = useCallback(async () => {\n if (!user || !session || !supabase || !appName || !selectedOrganisation) {\n DebugLogger.log('EventProvider', 'Missing required dependencies, skipping fetch');\n setIsLoading(false);\n return;\n }\n\n // Prevent multiple simultaneous fetches\n if (isFetchingRef.current) {\n DebugLogger.log('EventProvider', 'Already fetching events, skipping');\n return;\n }\n\n DebugLogger.log('EventProvider', 'User and organisation found, fetching events for:', {\n userId: user.id,\n appName: appName,\n organisationId: selectedOrganisation.id,\n organisationName: selectedOrganisation.display_name\n });\n\n isFetchingRef.current = true;\n let isMounted = true;\n\n try {\n // Ensure organisation context is set\n if (ensureOrganisationContext) {\n await ensureOrganisationContext();\n await setOrganisationContext(supabase, selectedOrganisation.id);\n }\n\n // Resolve app name to app ID\n // Call the RPC function\n DebugLogger.log('EventProvider', 'Calling get_pace_user_events RPC with:', {\n user_uuid: user.id,\n app_name: appName,\n p_organisation_id: selectedOrganisation.id\n });\n\n const response = await supabase.rpc('get_pace_user_events', {\n user_uuid: user.id,\n app_name: appName,\n p_organisation_id: selectedOrganisation.id,\n });\n \n const { data, error: rpcError } = response || {};\n\n DebugLogger.log('EventProvider', 'RPC response:', {\n data,\n error: rpcError,\n dataLength: data?.length || 0,\n organisationId: selectedOrganisation.id\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to fetch events');\n }\n\n if (isMounted) {\n const eventsData = data || [];\n console.log('[EventProvider] Loaded events:', eventsData.map((event: any) => ({\n eventId: event.event_id,\n eventName: event.event_name,\n organisationId: event.organisation_id,\n selectedOrganisationId: selectedOrganisation?.id\n })));\n setEvents(eventsData);\n setError(null);\n\n // Note: Event colors are now managed by useEventTheme hook based on route and selected event\n // This prevents unwanted color cycling on non-event pages\n\n // Reset auto-selection ref for new events\n hasAutoSelectedRef.current = false;\n\n // Try to restore persisted event first\n const persistedEventLoaded = await loadPersistedEvent(eventsData);\n \n // If no persisted event was loaded, auto-select the next event\n if (!persistedEventLoaded) {\n const nextEvent = getNextEventByDate(eventsData);\n if (nextEvent) {\n DebugLogger.log('EventProvider', 'Auto-selecting next event after no persisted event found:', nextEvent.event_name);\n hasAutoSelectedRef.current = true;\n setSelectedEventState(nextEvent);\n setSelectedEventIdRef.current(nextEvent.event_id);\n persistEventSelection(nextEvent.event_id);\n }\n }\n }\n } catch (err) {\n console.error('[EventProvider] Error fetching events:', err);\n const _error = err instanceof Error ? err : new Error('Unknown error occurred');\n \n if (isMounted) {\n setError(_error);\n setEvents([]);\n }\n } finally {\n if (isMounted) {\n setIsLoading(false);\n }\n isFetchingRef.current = false;\n }\n\n return () => {\n isMounted = false;\n };\n }, [user, session, supabase, appName, selectedOrganisation, ensureOrganisationContext, loadPersistedEvent, autoSelectNextEvent]);\n\n // Initialize events only once\n useEffect(() => {\n if (!isInitializedRef.current) {\n isInitializedRef.current = true;\n fetchEvents();\n }\n }, [fetchEvents]);\n\n // Update ref when setSelectedEventId changes\n useEffect(() => {\n setSelectedEventIdRef.current = setSelectedEventId;\n }, [setSelectedEventId]);\n\n // Auto-select next event when events are loaded and no event is selected\n useLayoutEffect(() => {\n if (events.length > 0 && !selectedEvent && !hasAutoSelectedRef.current && !userClearedEventRef.current) {\n const nextEvent = getNextEventByDate(events);\n if (nextEvent) {\n DebugLogger.log('EventProvider', 'Auto-selecting next event:', nextEvent.event_name);\n hasAutoSelectedRef.current = true;\n setSelectedEventState(nextEvent);\n setSelectedEventIdRef.current(nextEvent.event_id);\n persistEventSelection(nextEvent.event_id);\n }\n }\n }, [events, selectedEvent]);\n\n const setSelectedEvent = useCallback((event: Event | null) => {\n if (event) {\n // SECURITY: Validate event belongs to current organisation\n try {\n console.log('[EventProvider] Event selection validation:', {\n eventId: event.event_id,\n eventName: event.event_name,\n eventOrganisationId: event.organisation_id,\n selectedOrganisationId: selectedOrganisation?.id,\n selectedOrganisationName: selectedOrganisation?.display_name,\n match: event.organisation_id === selectedOrganisation?.id\n });\n \n if (selectedOrganisation && event.organisation_id !== selectedOrganisation.id) {\n console.error('[EventProvider] Event organisation_id does not match selected organisation', {\n eventOrganisationId: event.organisation_id,\n selectedOrganisationId: selectedOrganisation.id,\n eventName: event.event_name\n });\n return;\n }\n } catch (error) {\n console.error('[EventProvider] Error during event validation:', error);\n }\n\n setSelectedEventState(event);\n setSelectedEventId(event.event_id);\n persistEventSelection(event.event_id);\n // Reset the user cleared flag when selecting an event\n userClearedEventRef.current = false;\n } else {\n setSelectedEventState(null);\n setSelectedEventId(null);\n // Clear both sessionStorage and localStorage\n sessionStorage.removeItem('pace-core-selected-event');\n localStorage.removeItem('pace-core-selected-event');\n // Reset the auto-selection flag when clearing the event\n hasAutoSelectedRef.current = false;\n // Mark that user explicitly cleared the event to prevent auto-selection\n userClearedEventRef.current = true;\n }\n }, [selectedOrganisation, setSelectedEventId, persistEventSelection]);\n\n const refreshEvents = useCallback(async () => {\n isInitializedRef.current = false;\n isFetchingRef.current = false;\n // Reset the user cleared flag when refreshing events\n userClearedEventRef.current = false;\n await fetchEvents();\n }, [fetchEvents]);\n\n // Memoize the context value to prevent unnecessary re-renders\n const contextValue: EventContextType = useMemo(() => ({\n events,\n selectedEvent,\n isLoading,\n error,\n setSelectedEvent,\n refreshEvents,\n }), [events, selectedEvent, isLoading, error, setSelectedEvent, refreshEvents]);\n\n return (\n <EventContext.Provider value={contextValue}>\n {children}\n </EventContext.Provider>\n );\n}"],"mappings":"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","names":["error","events"]}