@jmruthers/pace-core 0.5.19 → 0.5.21

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 (132) hide show
  1. package/dist/{DataTable-ZEQDWNKA.js → DataTable-IX7N7XYG.js} +4 -4
  2. package/dist/{chunk-3ZVV6URZ.js → chunk-37WAIATW.js} +4 -4
  3. package/dist/{chunk-7UEIZCST.js → chunk-4TMM2IGR.js} +5 -4
  4. package/dist/chunk-4TMM2IGR.js.map +1 -0
  5. package/dist/{chunk-DKDTXS5Q.js → chunk-JE7K6Q3N.js} +10 -9
  6. package/dist/chunk-JE7K6Q3N.js.map +1 -0
  7. package/dist/{chunk-CLEIIMJO.js → chunk-R4QSIQDW.js} +2 -2
  8. package/dist/{chunk-J42NVDVM.js → chunk-ULP6OIQE.js} +42 -8
  9. package/dist/chunk-ULP6OIQE.js.map +1 -0
  10. package/dist/{chunk-CMXBNDPM.js → chunk-UYKKHRJN.js} +2 -2
  11. package/dist/{chunk-7EGP6KZT.js → chunk-YA77BOZM.js} +17 -17
  12. package/dist/chunk-YA77BOZM.js.map +1 -0
  13. package/dist/{chunk-DV2Z3RBQ.js → chunk-ZIYFAQJ5.js} +2 -2
  14. package/dist/components.js +6 -6
  15. package/dist/hooks.js +2 -2
  16. package/dist/index.js +8 -8
  17. package/dist/providers.js +2 -2
  18. package/dist/rbac/index.js +3 -3
  19. package/dist/utils.js +1 -1
  20. package/docs/api/classes/ErrorBoundary.md +1 -1
  21. package/docs/api/classes/InvalidScopeError.md +1 -1
  22. package/docs/api/classes/MissingUserContextError.md +1 -1
  23. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  24. package/docs/api/classes/PermissionDeniedError.md +1 -1
  25. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  26. package/docs/api/classes/RBACAuditManager.md +1 -1
  27. package/docs/api/classes/RBACCache.md +1 -1
  28. package/docs/api/classes/RBACEngine.md +1 -1
  29. package/docs/api/classes/RBACError.md +1 -1
  30. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  31. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  32. package/docs/api/interfaces/AggregateConfig.md +1 -1
  33. package/docs/api/interfaces/ButtonProps.md +1 -1
  34. package/docs/api/interfaces/CardProps.md +1 -1
  35. package/docs/api/interfaces/ColorPalette.md +1 -1
  36. package/docs/api/interfaces/ColorShade.md +1 -1
  37. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  38. package/docs/api/interfaces/DataTableAction.md +1 -1
  39. package/docs/api/interfaces/DataTableColumn.md +1 -1
  40. package/docs/api/interfaces/DataTableProps.md +1 -1
  41. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  42. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  43. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  44. package/docs/api/interfaces/EventContextType.md +7 -7
  45. package/docs/api/interfaces/EventLogoProps.md +1 -1
  46. package/docs/api/interfaces/EventProviderProps.md +2 -2
  47. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  48. package/docs/api/interfaces/FileUploadProps.md +1 -1
  49. package/docs/api/interfaces/FooterProps.md +1 -1
  50. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  51. package/docs/api/interfaces/InputProps.md +1 -1
  52. package/docs/api/interfaces/LabelProps.md +1 -1
  53. package/docs/api/interfaces/LoginFormProps.md +1 -1
  54. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  55. package/docs/api/interfaces/NavigationContextType.md +1 -1
  56. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  57. package/docs/api/interfaces/NavigationItem.md +1 -1
  58. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  59. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  60. package/docs/api/interfaces/Organisation.md +1 -1
  61. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  62. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  63. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  64. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  65. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  66. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  67. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  68. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  69. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  70. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  71. package/docs/api/interfaces/PaletteData.md +1 -1
  72. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  73. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  74. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  75. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  76. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  77. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  78. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  79. package/docs/api/interfaces/RBACConfig.md +1 -1
  80. package/docs/api/interfaces/RBACContextType.md +1 -1
  81. package/docs/api/interfaces/RBACLogger.md +1 -1
  82. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  83. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  84. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  85. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  86. package/docs/api/interfaces/RouteConfig.md +1 -1
  87. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  88. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  89. package/docs/api/interfaces/StorageConfig.md +1 -1
  90. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  91. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  92. package/docs/api/interfaces/StorageListOptions.md +1 -1
  93. package/docs/api/interfaces/StorageListResult.md +1 -1
  94. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  95. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  96. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  97. package/docs/api/interfaces/StyleImport.md +1 -1
  98. package/docs/api/interfaces/ToastActionElement.md +1 -1
  99. package/docs/api/interfaces/ToastProps.md +1 -1
  100. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  101. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  102. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  103. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  104. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  105. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  106. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  107. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  108. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  109. package/docs/api/interfaces/UserEventAccess.md +1 -1
  110. package/docs/api/interfaces/UserMenuProps.md +1 -1
  111. package/docs/api/interfaces/UserProfile.md +1 -1
  112. package/docs/api/modules.md +12 -12
  113. package/docs/troubleshooting/cake-infinite-rerender-debugging.md +284 -0
  114. package/docs/troubleshooting/cake-infinite-rerender-summary.md +117 -0
  115. package/docs/troubleshooting/cake-rerender-diagnostic.js +162 -0
  116. package/docs/troubleshooting/rbac-critical-fixes-summary.md +260 -0
  117. package/package.json +1 -1
  118. package/src/__tests__/hooks/usePermissions.test.ts +265 -0
  119. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +187 -0
  120. package/src/hooks/useAppConfig.ts +7 -6
  121. package/src/providers/EventProvider.tsx +4 -2
  122. package/src/rbac/components/PagePermissionGuard.tsx +56 -13
  123. package/src/rbac/hooks/usePermissions.ts +21 -14
  124. package/dist/chunk-7EGP6KZT.js.map +0 -1
  125. package/dist/chunk-7UEIZCST.js.map +0 -1
  126. package/dist/chunk-DKDTXS5Q.js.map +0 -1
  127. package/dist/chunk-J42NVDVM.js.map +0 -1
  128. /package/dist/{DataTable-ZEQDWNKA.js.map → DataTable-IX7N7XYG.js.map} +0 -0
  129. /package/dist/{chunk-3ZVV6URZ.js.map → chunk-37WAIATW.js.map} +0 -0
  130. /package/dist/{chunk-CLEIIMJO.js.map → chunk-R4QSIQDW.js.map} +0 -0
  131. /package/dist/{chunk-CMXBNDPM.js.map → chunk-UYKKHRJN.js.map} +0 -0
  132. /package/dist/{chunk-DV2Z3RBQ.js.map → chunk-ZIYFAQJ5.js.map} +0 -0
@@ -0,0 +1,284 @@
1
+ # Cake Infinite Re-render Debugging Guide
2
+
3
+ ## 🚨 Issue: Infinite Re-renders in Meals Component
4
+
5
+ The Cake team is experiencing infinite re-renders in their Meals component, causing the `PagePermissionGuard` to re-render continuously. The diagnostic logs show:
6
+
7
+ ```
8
+ [Meals] Diagnostic info:
9
+ Object { renderCount: 1, instanceCount: 0 }
10
+ [Meals] Diagnostic info:
11
+ Object { renderCount: 2, instanceCount: 0 }
12
+ [Meals] Diagnostic info:
13
+ Object { renderCount: 3, instanceCount: 0 }
14
+ ...
15
+ ```
16
+
17
+ ## 🔍 Root Cause Analysis
18
+
19
+ The issue is **NOT** in `PagePermissionGuard` - it's working correctly:
20
+ - ✅ App ID resolution: `Successfully resolved app ID: a0865252-b745-4639-8028-a53ee6128000`
21
+ - ✅ Permissions granted: `Final decision: { hasPermission: true }`
22
+ - ✅ No multiple instances: `instanceCount: 0`
23
+
24
+ The problem is in the **parent Meals component** that's re-rendering infinitely.
25
+
26
+ ## 🛠️ Debugging Steps
27
+
28
+ ### Step 1: Add Comprehensive Debugging to Meals Component
29
+
30
+ Add this debugging code to your Meals component:
31
+
32
+ ```typescript
33
+ import React, { useMemo, useCallback, useEffect, useRef } from 'react';
34
+
35
+ const Meals = React.memo(() => {
36
+ const renderCountRef = useRef(0);
37
+ renderCountRef.current += 1;
38
+
39
+ // Track what's causing re-renders
40
+ const [debugInfo, setDebugInfo] = useState({
41
+ renderCount: renderCountRef.current,
42
+ lastRenderTime: Date.now(),
43
+ reRenderReasons: []
44
+ });
45
+
46
+ // Get all your hooks
47
+ const eventsData = useEvents();
48
+ const authData = useUnifiedAuth();
49
+ const orgData = useOrganisations();
50
+ const configData = useAppConfig();
51
+
52
+ // Track changes in each hook
53
+ const prevEventsRef = useRef(eventsData);
54
+ const prevAuthRef = useRef(authData);
55
+ const prevOrgRef = useRef(orgData);
56
+ const prevConfigRef = useRef(configData);
57
+
58
+ useEffect(() => {
59
+ const reasons = [];
60
+
61
+ // Check if events data changed
62
+ if (prevEventsRef.current !== eventsData) {
63
+ reasons.push('eventsData changed');
64
+ prevEventsRef.current = eventsData;
65
+ }
66
+
67
+ // Check if auth data changed
68
+ if (prevAuthRef.current !== authData) {
69
+ reasons.push('authData changed');
70
+ prevAuthRef.current = authData;
71
+ }
72
+
73
+ // Check if org data changed
74
+ if (prevOrgRef.current !== orgData) {
75
+ reasons.push('orgData changed');
76
+ prevOrgRef.current = orgData;
77
+ }
78
+
79
+ // Check if config data changed
80
+ if (prevConfigRef.current !== configData) {
81
+ reasons.push('configData changed');
82
+ prevConfigRef.current = configData;
83
+ }
84
+
85
+ console.log('[Meals] Re-render reasons:', reasons);
86
+ console.log('[Meals] Current data:', {
87
+ eventsData: eventsData?.selectedEvent?.id,
88
+ authData: authData?.user?.id,
89
+ orgData: orgData?.selectedOrganisation?.id,
90
+ configData: configData?.requiresEvent
91
+ });
92
+ });
93
+
94
+ // Your existing component logic...
95
+
96
+ return (
97
+ <PagePermissionGuard
98
+ pageName="meals"
99
+ operation="read"
100
+ fallback={<div>Loading permissions...</div>}
101
+ >
102
+ {/* Your DataTable content */}
103
+ </PagePermissionGuard>
104
+ );
105
+ }, (prevProps, nextProps) => {
106
+ // This should never re-render since there are no props
107
+ console.log('[Meals] React.memo comparison called - this should not happen!');
108
+ return true;
109
+ });
110
+ ```
111
+
112
+ ### Step 2: Check Hook Dependencies
113
+
114
+ Look for these common causes of infinite re-renders:
115
+
116
+ #### A. Unstable Object References
117
+ ```typescript
118
+ // ❌ BAD - Creates new object on every render
119
+ const data = { events: eventsData, auth: authData };
120
+
121
+ // ✅ GOOD - Memoize the object
122
+ const data = useMemo(() => ({
123
+ events: eventsData,
124
+ auth: authData
125
+ }), [eventsData, authData]);
126
+ ```
127
+
128
+ #### B. Unstable Function References
129
+ ```typescript
130
+ // ❌ BAD - Creates new function on every render
131
+ const handleClick = () => { /* ... */ };
132
+
133
+ // ✅ GOOD - Memoize the function
134
+ const handleClick = useCallback(() => { /* ... */ }, []);
135
+ ```
136
+
137
+ #### C. useEffect Dependencies
138
+ ```typescript
139
+ // ❌ BAD - Missing dependencies or unstable dependencies
140
+ useEffect(() => {
141
+ // ...
142
+ }, []); // Missing dependencies
143
+
144
+ // ❌ BAD - Unstable dependency
145
+ useEffect(() => {
146
+ // ...
147
+ }, [someObject]); // someObject changes every render
148
+
149
+ // ✅ GOOD - Stable dependencies
150
+ useEffect(() => {
151
+ // ...
152
+ }, [userId, eventId]); // Primitive values
153
+ ```
154
+
155
+ ### Step 3: Check Parent Components
156
+
157
+ The infinite re-render might be coming from a parent component. Check:
158
+
159
+ 1. **Parent component re-renders**: Add debugging to parent components
160
+ 2. **Context providers**: Check if any context providers are re-rendering
161
+ 3. **State updates**: Look for state updates that trigger re-renders
162
+
163
+ ### Step 4: Use React DevTools Profiler
164
+
165
+ 1. Open React DevTools
166
+ 2. Go to Profiler tab
167
+ 3. Start recording
168
+ 4. Interact with the component
169
+ 5. Stop recording
170
+ 6. Look for components that re-render frequently
171
+
172
+ ### Step 5: Check for State Updates in Render
173
+
174
+ Look for state updates that happen during render:
175
+
176
+ ```typescript
177
+ // ❌ BAD - State update during render
178
+ const Meals = () => {
179
+ const [count, setCount] = useState(0);
180
+
181
+ // This will cause infinite re-renders!
182
+ setCount(count + 1);
183
+
184
+ return <div>{count}</div>;
185
+ };
186
+ ```
187
+
188
+ ## 🔧 Common Fixes
189
+
190
+ ### Fix 1: Memoize Expensive Calculations
191
+ ```typescript
192
+ const Meals = React.memo(() => {
193
+ const eventsData = useEvents();
194
+ const authData = useUnifiedAuth();
195
+
196
+ // Memoize expensive calculations
197
+ const processedData = useMemo(() => {
198
+ return processEventsData(eventsData);
199
+ }, [eventsData]);
200
+
201
+ // Memoize callbacks
202
+ const handleEventChange = useCallback((eventId) => {
203
+ // Handle event change
204
+ }, []);
205
+
206
+ return (
207
+ <PagePermissionGuard pageName="meals" operation="read">
208
+ {/* Your content */}
209
+ </PagePermissionGuard>
210
+ );
211
+ });
212
+ ```
213
+
214
+ ### Fix 2: Stabilize Hook Dependencies
215
+ ```typescript
216
+ const Meals = React.memo(() => {
217
+ const eventsData = useEvents();
218
+ const authData = useUnifiedAuth();
219
+
220
+ // Extract only the values you need
221
+ const selectedEventId = eventsData?.selectedEvent?.id;
222
+ const userId = authData?.user?.id;
223
+
224
+ // Use primitive values in dependencies
225
+ useEffect(() => {
226
+ // Your effect logic
227
+ }, [selectedEventId, userId]);
228
+
229
+ return (
230
+ <PagePermissionGuard pageName="meals" operation="read">
231
+ {/* Your content */}
232
+ </PagePermissionGuard>
233
+ );
234
+ });
235
+ ```
236
+
237
+ ### Fix 3: Check Context Providers
238
+ Make sure context providers are not re-rendering:
239
+
240
+ ```typescript
241
+ // ❌ BAD - Creates new object every render
242
+ const AppProvider = ({ children }) => {
243
+ const [state, setState] = useState({});
244
+
245
+ return (
246
+ <AppContext.Provider value={{ state, setState }}>
247
+ {children}
248
+ </AppContext.Provider>
249
+ );
250
+ };
251
+
252
+ // ✅ GOOD - Memoize the context value
253
+ const AppProvider = ({ children }) => {
254
+ const [state, setState] = useState({});
255
+
256
+ const contextValue = useMemo(() => ({
257
+ state,
258
+ setState
259
+ }), [state]);
260
+
261
+ return (
262
+ <AppContext.Provider value={contextValue}>
263
+ {children}
264
+ </AppContext.Provider>
265
+ );
266
+ };
267
+ ```
268
+
269
+ ## 🎯 Next Steps
270
+
271
+ 1. **Add the debugging code** to your Meals component
272
+ 2. **Run the app** and check the console logs
273
+ 3. **Identify the re-render reasons** from the logs
274
+ 4. **Apply the appropriate fix** based on what's causing the re-renders
275
+ 5. **Test the fix** to ensure the infinite re-renders stop
276
+
277
+ ## 📞 Need Help?
278
+
279
+ If you're still experiencing issues after following these steps, please share:
280
+ 1. The console logs from the debugging code
281
+ 2. The Meals component code
282
+ 3. Any parent component code that might be relevant
283
+
284
+ The issue is definitely in the Cake application code, not in pace-core, so we need to identify what's causing the parent component to re-render infinitely.
@@ -0,0 +1,117 @@
1
+ # Cake Infinite Re-render Issue - Summary & Action Plan
2
+
3
+ ## 🚨 Current Status
4
+
5
+ The Cake team is experiencing **infinite re-renders** in their Meals component, causing the `PagePermissionGuard` to re-render continuously. This is **NOT** a pace-core issue - it's a Cake application issue.
6
+
7
+ ## 📊 Evidence from Console Logs
8
+
9
+ ```
10
+ [Meals] Diagnostic info:
11
+ Object { renderCount: 1, instanceCount: 0 }
12
+ [Meals] Diagnostic info:
13
+ Object { renderCount: 2, instanceCount: 0 }
14
+ [Meals] Diagnostic info:
15
+ Object { renderCount: 3, instanceCount: 0 }
16
+ ...
17
+ [Meals] Diagnostic info:
18
+ Object { renderCount: 19, instanceCount: 0 }
19
+ ```
20
+
21
+ **Key Observations:**
22
+ - ✅ `PagePermissionGuard` is working correctly (permissions granted, app ID resolved)
23
+ - ✅ No multiple instances (`instanceCount: 0`)
24
+ - ❌ Parent Meals component is re-rendering infinitely (`renderCount: 1, 2, 3...`)
25
+
26
+ ## 🔍 Root Cause
27
+
28
+ The infinite re-renders are caused by the **parent Meals component**, not by `PagePermissionGuard`. The parent component is re-rendering continuously, which triggers `PagePermissionGuard` to re-render as well.
29
+
30
+ ## 🛠️ Immediate Action Plan
31
+
32
+ ### Step 1: Add Debugging Code
33
+ Use the diagnostic script provided in `cake-rerender-diagnostic.js` to identify what's causing the re-renders.
34
+
35
+ ### Step 2: Common Causes to Check
36
+
37
+ 1. **Unstable Object References**
38
+ ```typescript
39
+ // ❌ BAD - Creates new object every render
40
+ const data = { events: eventsData, auth: authData };
41
+
42
+ // ✅ GOOD - Memoize
43
+ const data = useMemo(() => ({ events: eventsData, auth: authData }), [eventsData, authData]);
44
+ ```
45
+
46
+ 2. **Unstable Function References**
47
+ ```typescript
48
+ // ❌ BAD - Creates new function every render
49
+ const handleClick = () => { /* ... */ };
50
+
51
+ // ✅ GOOD - Memoize
52
+ const handleClick = useCallback(() => { /* ... */ }, []);
53
+ ```
54
+
55
+ 3. **useEffect Dependencies**
56
+ ```typescript
57
+ // ❌ BAD - Missing or unstable dependencies
58
+ useEffect(() => { /* ... */ }, []);
59
+ useEffect(() => { /* ... */ }, [someObject]);
60
+
61
+ // ✅ GOOD - Stable dependencies
62
+ useEffect(() => { /* ... */ }, [userId, eventId]);
63
+ ```
64
+
65
+ 4. **State Updates During Render**
66
+ ```typescript
67
+ // ❌ BAD - Causes infinite re-renders
68
+ const [count, setCount] = useState(0);
69
+ setCount(count + 1); // Don't do this!
70
+ ```
71
+
72
+ 5. **Context Provider Re-renders**
73
+ ```typescript
74
+ // ❌ BAD - Creates new value every render
75
+ <Context.Provider value={{ state, setState }}>
76
+
77
+ // ✅ GOOD - Memoize the value
78
+ const value = useMemo(() => ({ state, setState }), [state]);
79
+ <Context.Provider value={value}>
80
+ ```
81
+
82
+ ### Step 3: Use React DevTools
83
+ 1. Open React DevTools Profiler
84
+ 2. Record a session
85
+ 3. Look for components that re-render frequently
86
+ 4. Identify the root cause
87
+
88
+ ## 📁 Files to Check
89
+
90
+ 1. **Meals component** - Add the debugging code
91
+ 2. **Parent components** - Check for re-render causes
92
+ 3. **Context providers** - Ensure they're not re-rendering
93
+ 4. **Custom hooks** - Check for unstable dependencies
94
+
95
+ ## 🎯 Expected Outcome
96
+
97
+ After applying the fixes:
98
+ - Render count should stabilize at 1-2 renders
99
+ - No more infinite re-renders
100
+ - `PagePermissionGuard` should work correctly
101
+ - DataTable should display properly
102
+
103
+ ## 📞 Next Steps
104
+
105
+ 1. **Immediate**: Add the debugging code to identify the cause
106
+ 2. **Short-term**: Apply the appropriate fixes based on the debugging output
107
+ 3. **Long-term**: Review the entire component tree for similar issues
108
+
109
+ ## 🔗 Resources
110
+
111
+ - **Debugging Guide**: `cake-infinite-rerender-debugging.md`
112
+ - **Diagnostic Script**: `cake-rerender-diagnostic.js`
113
+ - **React DevTools**: https://react.dev/learn/react-developer-tools
114
+
115
+ ---
116
+
117
+ **Remember**: This is a Cake application issue, not a pace-core issue. The `PagePermissionGuard` is working correctly - the problem is in the parent component that's re-rendering infinitely.
@@ -0,0 +1,162 @@
1
+ // Cake Infinite Re-render Diagnostic Script
2
+ // Add this to your Meals component to identify the cause of infinite re-renders
3
+
4
+ import React, { useMemo, useCallback, useEffect, useRef, useState } from 'react';
5
+
6
+ // Add this hook to track re-render causes
7
+ const useRenderTracker = (componentName) => {
8
+ const renderCountRef = useRef(0);
9
+ const prevPropsRef = useRef({});
10
+ const prevStateRef = useRef({});
11
+
12
+ renderCountRef.current += 1;
13
+
14
+ const [debugInfo, setDebugInfo] = useState({
15
+ renderCount: renderCountRef.current,
16
+ lastRenderTime: Date.now(),
17
+ reRenderReasons: []
18
+ });
19
+
20
+ const trackRender = (currentProps, currentState) => {
21
+ const reasons = [];
22
+
23
+ // Check if props changed
24
+ if (prevPropsRef.current !== currentProps) {
25
+ reasons.push('props changed');
26
+ prevPropsRef.current = currentProps;
27
+ }
28
+
29
+ // Check if state changed
30
+ if (prevStateRef.current !== currentState) {
31
+ reasons.push('state changed');
32
+ prevStateRef.current = currentState;
33
+ }
34
+
35
+ // If no obvious reason, it might be parent re-render
36
+ if (reasons.length === 0) {
37
+ reasons.push('parent re-render');
38
+ }
39
+
40
+ console.log(`[${componentName}] Re-render #${renderCountRef.current}:`, {
41
+ reasons,
42
+ timestamp: new Date().toISOString(),
43
+ props: currentProps,
44
+ state: currentState
45
+ });
46
+
47
+ setDebugInfo({
48
+ renderCount: renderCountRef.current,
49
+ lastRenderTime: Date.now(),
50
+ reRenderReasons: reasons
51
+ });
52
+ };
53
+
54
+ return { trackRender, debugInfo };
55
+ };
56
+
57
+ // Enhanced Meals component with debugging
58
+ const MealsWithDebugging = React.memo(() => {
59
+ const { trackRender, debugInfo } = useRenderTracker('Meals');
60
+
61
+ // Get all your hooks
62
+ const eventsData = useEvents();
63
+ const authData = useUnifiedAuth();
64
+ const orgData = useOrganisations();
65
+ const configData = useAppConfig();
66
+
67
+ // Track hook changes
68
+ const prevEventsRef = useRef(eventsData);
69
+ const prevAuthRef = useRef(authData);
70
+ const prevOrgRef = useRef(orgData);
71
+ const prevConfigRef = useRef(configData);
72
+
73
+ // Check what's changing
74
+ useEffect(() => {
75
+ const hookChanges = [];
76
+
77
+ if (prevEventsRef.current !== eventsData) {
78
+ hookChanges.push('eventsData changed');
79
+ console.log('[Meals] eventsData changed:', {
80
+ old: prevEventsRef.current,
81
+ new: eventsData
82
+ });
83
+ prevEventsRef.current = eventsData;
84
+ }
85
+
86
+ if (prevAuthRef.current !== authData) {
87
+ hookChanges.push('authData changed');
88
+ console.log('[Meals] authData changed:', {
89
+ old: prevAuthRef.current,
90
+ new: authData
91
+ });
92
+ prevAuthRef.current = authData;
93
+ }
94
+
95
+ if (prevOrgRef.current !== orgData) {
96
+ hookChanges.push('orgData changed');
97
+ console.log('[Meals] orgData changed:', {
98
+ old: prevOrgRef.current,
99
+ new: orgData
100
+ });
101
+ prevOrgRef.current = orgData;
102
+ }
103
+
104
+ if (prevConfigRef.current !== configData) {
105
+ hookChanges.push('configData changed');
106
+ console.log('[Meals] configData changed:', {
107
+ old: prevConfigRef.current,
108
+ new: configData
109
+ });
110
+ prevConfigRef.current = configData;
111
+ }
112
+
113
+ if (hookChanges.length > 0) {
114
+ console.log('[Meals] Hook changes detected:', hookChanges);
115
+ }
116
+ });
117
+
118
+ // Track render
119
+ trackRender({}, {});
120
+
121
+ // Your existing component logic...
122
+ const selectedEvent = useMemo(() => eventsData.selectedEvent, [eventsData.selectedEvent?.event_id, eventsData.selectedEvent?.id]);
123
+ const user = useMemo(() => authData.user, [authData.user?.id]);
124
+ const selectedOrganisation = useMemo(() => orgData.selectedOrganisation, [orgData.selectedOrganisation?.id]);
125
+ const requiresEvent = useMemo(() => configData.requiresEvent, [configData.requiresEvent]);
126
+
127
+ const eventId = useMemo(() => {
128
+ return selectedEvent?.event_id || selectedEvent?.id;
129
+ }, [selectedEvent?.event_id, selectedEvent?.id]);
130
+
131
+ // Log diagnostic info
132
+ console.log('[Meals] Diagnostic info:', {
133
+ renderCount: debugInfo.renderCount,
134
+ reRenderReasons: debugInfo.reRenderReasons,
135
+ hookValues: {
136
+ selectedEventId: selectedEvent?.id,
137
+ userId: user?.id,
138
+ organisationId: selectedOrganisation?.id,
139
+ requiresEvent
140
+ }
141
+ });
142
+
143
+ return (
144
+ <PagePermissionGuard
145
+ pageName="meals"
146
+ operation="read"
147
+ fallback={<div>Loading permissions...</div>}
148
+ >
149
+ <h1>Meals</h1>
150
+ <p>Manage the meals available for {selectedEvent?.event_name}</p>
151
+ {/* Your DataTable content */}
152
+ </PagePermissionGuard>
153
+ );
154
+ }, (prevProps, nextProps) => {
155
+ // This should never be called since there are no props
156
+ console.log('[Meals] React.memo comparison called - this indicates a problem!');
157
+ console.log('[Meals] prevProps:', prevProps);
158
+ console.log('[Meals] nextProps:', nextProps);
159
+ return true;
160
+ });
161
+
162
+ export default MealsWithDebugging;