@jmruthers/pace-core 0.5.17 → 0.5.18

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 (115) hide show
  1. package/dist/{DataTable-RICY7YDA.js → DataTable-BTXP6MW4.js} +3 -3
  2. package/dist/{chunk-JZCNOXSG.js → chunk-7QEQCZDV.js} +2 -2
  3. package/dist/{chunk-F6IHN3DC.js → chunk-J3JWBQSC.js} +8 -8
  4. package/dist/chunk-J3JWBQSC.js.map +1 -0
  5. package/dist/{chunk-JPXJGMOO.js → chunk-NU3GLMIQ.js} +2 -2
  6. package/dist/{chunk-S7KTIIL3.js → chunk-UIBYODOF.js} +13 -6
  7. package/dist/chunk-UIBYODOF.js.map +1 -0
  8. package/dist/components.js +3 -3
  9. package/dist/index.js +4 -4
  10. package/dist/rbac/index.js +2 -2
  11. package/dist/utils.js +1 -1
  12. package/docs/api/classes/ErrorBoundary.md +1 -1
  13. package/docs/api/classes/InvalidScopeError.md +1 -1
  14. package/docs/api/classes/MissingUserContextError.md +1 -1
  15. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  16. package/docs/api/classes/PermissionDeniedError.md +1 -1
  17. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  18. package/docs/api/classes/RBACAuditManager.md +1 -1
  19. package/docs/api/classes/RBACCache.md +1 -1
  20. package/docs/api/classes/RBACEngine.md +1 -1
  21. package/docs/api/classes/RBACError.md +1 -1
  22. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  23. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  24. package/docs/api/interfaces/AggregateConfig.md +1 -1
  25. package/docs/api/interfaces/ButtonProps.md +1 -1
  26. package/docs/api/interfaces/CardProps.md +1 -1
  27. package/docs/api/interfaces/ColorPalette.md +1 -1
  28. package/docs/api/interfaces/ColorShade.md +1 -1
  29. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  30. package/docs/api/interfaces/DataTableAction.md +1 -1
  31. package/docs/api/interfaces/DataTableColumn.md +1 -1
  32. package/docs/api/interfaces/DataTableProps.md +1 -1
  33. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  34. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  35. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  36. package/docs/api/interfaces/EventContextType.md +1 -1
  37. package/docs/api/interfaces/EventLogoProps.md +1 -1
  38. package/docs/api/interfaces/EventProviderProps.md +1 -1
  39. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  40. package/docs/api/interfaces/FileUploadProps.md +1 -1
  41. package/docs/api/interfaces/FooterProps.md +1 -1
  42. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  43. package/docs/api/interfaces/InputProps.md +1 -1
  44. package/docs/api/interfaces/LabelProps.md +1 -1
  45. package/docs/api/interfaces/LoginFormProps.md +1 -1
  46. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  47. package/docs/api/interfaces/NavigationContextType.md +1 -1
  48. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  49. package/docs/api/interfaces/NavigationItem.md +1 -1
  50. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  51. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  52. package/docs/api/interfaces/Organisation.md +1 -1
  53. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  54. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  55. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  56. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  57. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  58. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  59. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  60. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  61. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  62. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  63. package/docs/api/interfaces/PaletteData.md +1 -1
  64. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  65. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  66. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  67. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  68. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  69. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  70. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  71. package/docs/api/interfaces/RBACConfig.md +1 -1
  72. package/docs/api/interfaces/RBACContextType.md +1 -1
  73. package/docs/api/interfaces/RBACLogger.md +1 -1
  74. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  75. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  76. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  77. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  78. package/docs/api/interfaces/RouteConfig.md +1 -1
  79. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  80. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  81. package/docs/api/interfaces/StorageConfig.md +1 -1
  82. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  83. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  84. package/docs/api/interfaces/StorageListOptions.md +1 -1
  85. package/docs/api/interfaces/StorageListResult.md +1 -1
  86. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  87. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  88. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  89. package/docs/api/interfaces/StyleImport.md +1 -1
  90. package/docs/api/interfaces/ToastActionElement.md +1 -1
  91. package/docs/api/interfaces/ToastProps.md +1 -1
  92. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  93. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  94. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  95. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  96. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  97. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  98. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  99. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  100. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  101. package/docs/api/interfaces/UserEventAccess.md +1 -1
  102. package/docs/api/interfaces/UserMenuProps.md +1 -1
  103. package/docs/api/interfaces/UserProfile.md +1 -1
  104. package/docs/api/modules.md +2 -2
  105. package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +187 -0
  106. package/docs/troubleshooting/page-permission-guard-diagnostic.js +129 -0
  107. package/docs/troubleshooting/page-permission-guard-ui-debugging.md +353 -0
  108. package/package.json +1 -1
  109. package/src/rbac/components/PagePermissionGuard.tsx +14 -6
  110. package/src/rbac/hooks/usePermissions.ts +7 -7
  111. package/dist/chunk-F6IHN3DC.js.map +0 -1
  112. package/dist/chunk-S7KTIIL3.js.map +0 -1
  113. /package/dist/{DataTable-RICY7YDA.js.map → DataTable-BTXP6MW4.js.map} +0 -0
  114. /package/dist/{chunk-JZCNOXSG.js.map → chunk-7QEQCZDV.js.map} +0 -0
  115. /package/dist/{chunk-JPXJGMOO.js.map → chunk-NU3GLMIQ.js.map} +0 -0
@@ -0,0 +1,187 @@
1
+ # Cake PagePermissionGuard Issue - Summary & Action Plan
2
+
3
+ ## 🚨 Issue Status: CAKE APPLICATION BUG (Not pace-core)
4
+
5
+ **Date**: January 16, 2025
6
+ **Priority**: Critical
7
+ **Status**: Requires Cake team action
8
+
9
+ ## Problem Summary
10
+
11
+ The `PagePermissionGuard` component from pace-core is working correctly, but Cake's implementation has a UI layering issue causing the Access Denied modal to persist despite correct permissions.
12
+
13
+ ### Evidence from Console Logs
14
+
15
+ ✅ **pace-core is working correctly**:
16
+ - `useCan` returns `can: true`
17
+ - `willShowAccessDenied: false`
18
+ - `willShowContent: true`
19
+ - `RENDERING CONTENT STATE` is logged
20
+
21
+ ❌ **Cake has implementation issues**:
22
+ - Access Denied modal remains visible
23
+ - Infinite re-render loop detected (same instance ID `14bp0efg9`)
24
+ - UI state not updating despite correct component state
25
+
26
+ ## Root Cause Analysis
27
+
28
+ Based on the console logs, this is **definitely a Cake application issue**, not a pace-core bug. The most likely causes are:
29
+
30
+ 1. **Multiple PagePermissionGuard instances** rendering simultaneously
31
+ 2. **Infinite re-render loop** in the Meals component
32
+ 3. **CSS/Modal layering conflicts** keeping the modal visible
33
+ 4. **React state management issues** preventing UI updates
34
+
35
+ ## Immediate Action Required
36
+
37
+ ### Step 1: Add Diagnostic Code
38
+
39
+ Add this to your Meals component to identify the exact issue:
40
+
41
+ ```tsx
42
+ import { useEffect, useRef } from 'react';
43
+
44
+ export function Meals() {
45
+ const renderCount = useRef(0);
46
+ renderCount.current += 1;
47
+
48
+ useEffect(() => {
49
+ // Check for infinite re-renders
50
+ if (renderCount.current > 10) {
51
+ console.error('🚨 INFINITE RE-RENDER DETECTED!', {
52
+ renderCount: renderCount.current,
53
+ component: 'Meals'
54
+ });
55
+ }
56
+
57
+ // Check for multiple PagePermissionGuard instances
58
+ const guards = document.querySelectorAll('[data-testid*="page-permission-guard"]');
59
+ console.log('[Meals] PagePermissionGuard instances:', guards.length);
60
+
61
+ // Check for multiple modals
62
+ const modals = document.querySelectorAll('[role="dialog"], .modal, [data-testid*="modal"]');
63
+ console.log('[Meals] Modal elements:', modals.length);
64
+
65
+ }, []);
66
+
67
+ console.log('[Meals] Component render count:', renderCount.current);
68
+
69
+ return (
70
+ <div>
71
+ <PagePermissionGuard pageName="meals" operation="read">
72
+ <DataTable ... />
73
+ </PagePermissionGuard>
74
+ </div>
75
+ );
76
+ }
77
+ ```
78
+
79
+ ### Step 2: Check for Common Issues
80
+
81
+ 1. **Multiple PagePermissionGuard wrappers**:
82
+ ```tsx
83
+ // ❌ WRONG - Don't do this
84
+ <PagePermissionGuard>
85
+ <PagePermissionGuard>
86
+ <DataTable />
87
+ </PagePermissionGuard>
88
+ </PagePermissionGuard>
89
+ ```
90
+
91
+ 2. **Conditional rendering**:
92
+ ```tsx
93
+ // ❌ WRONG - Don't conditionally render the guard
94
+ {someCondition && (
95
+ <PagePermissionGuard>
96
+ <DataTable />
97
+ </PagePermissionGuard>
98
+ )}
99
+ ```
100
+
101
+ 3. **State management causing re-renders**:
102
+ ```tsx
103
+ // ❌ WRONG - This can cause infinite loops
104
+ const [loading, setLoading] = useState(true);
105
+ useEffect(() => {
106
+ setLoading(false);
107
+ }, [loading]); // This dependency causes infinite loop
108
+ ```
109
+
110
+ ### Step 3: Simplify the Implementation
111
+
112
+ Use this minimal implementation to test:
113
+
114
+ ```tsx
115
+ import React from 'react';
116
+ import { PagePermissionGuard, DataTable } from '@jmruthers/pace-core';
117
+
118
+ export function Meals() {
119
+ console.log('[Meals] Rendering Meals component');
120
+
121
+ return (
122
+ <div>
123
+ <h1>Meals</h1>
124
+ <p>Manage the meals available for AJ2025...</p>
125
+
126
+ <PagePermissionGuard
127
+ pageName="meals"
128
+ operation="read"
129
+ fallback={
130
+ <div className="min-h-screen flex items-center justify-center">
131
+ <div className="text-center">
132
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto mb-4"></div>
133
+ <p>Loading permissions...</p>
134
+ </div>
135
+ </div>
136
+ }
137
+ >
138
+ <DataTable
139
+ data={processedMeals || []}
140
+ columns={columns}
141
+ rbac={{
142
+ resource: 'meals',
143
+ pageId: 'meals'
144
+ }}
145
+ // ... other props
146
+ />
147
+ </PagePermissionGuard>
148
+ </div>
149
+ );
150
+ }
151
+ ```
152
+
153
+ ## Expected Results After Fix
154
+
155
+ After implementing the fix, you should see:
156
+
157
+ 1. ✅ Only one `PagePermissionGuard` instance in console
158
+ 2. ✅ No infinite re-render warnings
159
+ 3. ✅ `RENDERING CONTENT STATE` appears once
160
+ 4. ✅ Access Denied modal disappears
161
+ 5. ✅ DataTable content becomes visible
162
+
163
+ ## Files to Check
164
+
165
+ 1. **Meals component** - Look for multiple PagePermissionGuard wrappers
166
+ 2. **Parent components** - Check for conditional rendering
167
+ 3. **State management** - Look for infinite re-render causes
168
+ 4. **CSS files** - Check for modal z-index conflicts
169
+
170
+ ## Debugging Tools
171
+
172
+ 1. **React DevTools** - Check component tree for multiple instances
173
+ 2. **Browser DevTools** - Check for multiple modal elements
174
+ 3. **Console logs** - Look for infinite re-render warnings
175
+
176
+ ## Contact Information
177
+
178
+ If you need further assistance after implementing these fixes, please provide:
179
+
180
+ 1. The complete Meals component code
181
+ 2. Console logs after adding the diagnostic code
182
+ 3. Screenshots of React DevTools component tree
183
+ 4. Any parent component that renders the Meals component
184
+
185
+ ---
186
+
187
+ **Note**: This is confirmed to be a Cake application issue, not a pace-core bug. The pace-core component is working correctly as evidenced by the console logs showing proper permission resolution and state management.
@@ -0,0 +1,129 @@
1
+ /**
2
+ * PagePermissionGuard Diagnostic Script
3
+ *
4
+ * Add this to your Cake application's Meals component to diagnose the UI issue.
5
+ * Copy and paste this code into your Meals component temporarily.
6
+ */
7
+
8
+ // Add this to your Meals component
9
+ export function usePagePermissionGuardDiagnostic() {
10
+ const renderCount = useRef(0);
11
+ const instanceIds = useRef(new Set());
12
+
13
+ renderCount.current += 1;
14
+
15
+ useEffect(() => {
16
+ // Check for infinite re-renders
17
+ if (renderCount.current > 10) {
18
+ console.error('🚨 INFINITE RE-RENDER DETECTED!', {
19
+ renderCount: renderCount.current,
20
+ timestamp: new Date().toISOString(),
21
+ component: 'Meals'
22
+ });
23
+ }
24
+
25
+ // Check for multiple PagePermissionGuard instances
26
+ const guards = document.querySelectorAll('[data-testid*="page-permission-guard"]');
27
+ if (guards.length > 1) {
28
+ console.error('🚨 MULTIPLE PagePermissionGuard INSTANCES!', {
29
+ count: guards.length,
30
+ elements: Array.from(guards).map(g => ({
31
+ element: g,
32
+ visible: g.offsetParent !== null,
33
+ zIndex: window.getComputedStyle(g).zIndex
34
+ }))
35
+ });
36
+ }
37
+
38
+ // Check for multiple modals
39
+ const modals = document.querySelectorAll('[role="dialog"], .modal, [data-testid*="modal"], [data-testid*="access-denied"]');
40
+ if (modals.length > 1) {
41
+ console.error('🚨 MULTIPLE MODALS DETECTED!', {
42
+ count: modals.length,
43
+ modals: Array.from(modals).map(m => ({
44
+ element: m,
45
+ visible: m.offsetParent !== null,
46
+ zIndex: window.getComputedStyle(m).zIndex,
47
+ display: window.getComputedStyle(m).display
48
+ }))
49
+ });
50
+ }
51
+
52
+ // Check for CSS conflicts
53
+ const elements = document.querySelectorAll('*');
54
+ const zIndexes = Array.from(elements)
55
+ .map(el => ({
56
+ element: el,
57
+ zIndex: window.getComputedStyle(el).zIndex,
58
+ position: window.getComputedStyle(el).position
59
+ }))
60
+ .filter(item => item.zIndex !== 'auto' && item.zIndex !== '0' && item.position === 'fixed')
61
+ .sort((a, b) => parseInt(b.zIndex) - parseInt(a.zIndex));
62
+
63
+ if (zIndexes.length > 0) {
64
+ console.log('🔍 Fixed positioned elements with z-index:', zIndexes.slice(0, 5));
65
+ }
66
+
67
+ // Check for React Strict Mode
68
+ const isStrictMode = process.env.NODE_ENV === 'development' &&
69
+ document.querySelector('[data-reactroot]')?.__reactInternalInstance;
70
+
71
+ if (isStrictMode) {
72
+ console.log('⚠️ React Strict Mode detected - this can cause double rendering in development');
73
+ }
74
+
75
+ }, []);
76
+
77
+ // Track PagePermissionGuard instance IDs
78
+ useEffect(() => {
79
+ const interval = setInterval(() => {
80
+ const logs = Array.from(document.querySelectorAll('*'))
81
+ .map(el => el.textContent)
82
+ .join(' ')
83
+ .match(/\[PagePermissionGuard\].*?\(instance: ([a-z0-9]+)\)/g);
84
+
85
+ if (logs) {
86
+ logs.forEach(log => {
87
+ const match = log.match(/instance: ([a-z0-9]+)/);
88
+ if (match) {
89
+ instanceIds.current.add(match[1]);
90
+ }
91
+ });
92
+
93
+ if (instanceIds.current.size > 1) {
94
+ console.error('🚨 MULTIPLE PagePermissionGuard INSTANCE IDS!', {
95
+ instanceIds: Array.from(instanceIds.current),
96
+ count: instanceIds.current.size
97
+ });
98
+ }
99
+ }
100
+ }, 1000);
101
+
102
+ return () => clearInterval(interval);
103
+ }, []);
104
+
105
+ return {
106
+ renderCount: renderCount.current,
107
+ instanceCount: instanceIds.current.size
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Usage in your Meals component:
113
+ *
114
+ * import { usePagePermissionGuardDiagnostic } from './path/to/this/file';
115
+ *
116
+ * export function Meals() {
117
+ * const diagnostic = usePagePermissionGuardDiagnostic();
118
+ *
119
+ * console.log('[Meals] Diagnostic info:', diagnostic);
120
+ *
121
+ * return (
122
+ * <div>
123
+ * <PagePermissionGuard pageName="meals" operation="read">
124
+ * <DataTable ... />
125
+ * </PagePermissionGuard>
126
+ * </div>
127
+ * );
128
+ * }
129
+ */
@@ -0,0 +1,353 @@
1
+ # PagePermissionGuard UI Debugging Guide
2
+
3
+ ## Issue: Access Denied Modal Persists Despite Correct Permissions
4
+
5
+ ### Problem Description
6
+ The `PagePermissionGuard` component is correctly determining permissions (`can: true`, `willShowAccessDenied: false`, `willShowContent: true`) and logging `RENDERING CONTENT STATE`, but the Access Denied modal remains visible, obscuring the content.
7
+
8
+ ### Root Cause Analysis
9
+ Based on the console logs, this is **NOT** a pace-core bug. The issue is in the Cake application's implementation. The logs show:
10
+
11
+ 1. ✅ **Permission Check Works**: `useCan` returns `can: true`
12
+ 2. ✅ **State Resolution Works**: `willShowAccessDenied: false`, `willShowContent: true`
13
+ 3. ✅ **Component Logic Works**: `RENDERING CONTENT STATE` is logged
14
+ 4. ❌ **UI Issue**: Access Denied modal remains visible
15
+ 5. ❌ **Infinite Loop**: Same instance ID (`14bp0efg9`) renders repeatedly
16
+
17
+ ## Debugging Steps for Cake Team
18
+
19
+ ### Step 1: Check for Multiple PagePermissionGuard Instances
20
+
21
+ **Problem**: Multiple `PagePermissionGuard` components may be rendering simultaneously.
22
+
23
+ **Debug Code**:
24
+ ```tsx
25
+ // Add this to your Meals component
26
+ console.log('[Meals] PagePermissionGuard instances:', document.querySelectorAll('[data-testid*="page-permission-guard"]').length);
27
+ console.log('[Meals] Access Denied modals:', document.querySelectorAll('[data-testid*="access-denied"]').length);
28
+ ```
29
+
30
+ **Fix**: Ensure only one `PagePermissionGuard` wraps your content.
31
+
32
+ ### Step 2: Check for CSS/Modal Layering Issues
33
+
34
+ **Problem**: The Access Denied modal might be rendered by a different component or have CSS that keeps it visible.
35
+
36
+ **Debug Code**:
37
+ ```tsx
38
+ // Add this to your Meals component
39
+ useEffect(() => {
40
+ const modals = document.querySelectorAll('[role="dialog"], .modal, [data-testid*="modal"]');
41
+ console.log('[Meals] Found modals:', modals.length);
42
+ modals.forEach((modal, index) => {
43
+ console.log(`[Meals] Modal ${index}:`, {
44
+ element: modal,
45
+ visible: modal.offsetParent !== null,
46
+ zIndex: window.getComputedStyle(modal).zIndex,
47
+ display: window.getComputedStyle(modal).display
48
+ });
49
+ });
50
+ }, []);
51
+ ```
52
+
53
+ **Fix**: Check if there are multiple modals or CSS conflicts.
54
+
55
+ ### Step 3: Check for State Management Issues
56
+
57
+ **Problem**: React state might not be updating correctly, causing stale UI.
58
+
59
+ **Debug Code**:
60
+ ```tsx
61
+ // Add this to your Meals component
62
+ const [debugState, setDebugState] = useState({});
63
+ useEffect(() => {
64
+ setDebugState({
65
+ timestamp: new Date().toISOString(),
66
+ renderCount: (debugState.renderCount || 0) + 1
67
+ });
68
+ }, []);
69
+
70
+ console.log('[Meals] Component render state:', debugState);
71
+ ```
72
+
73
+ **Fix**: Look for infinite re-renders or state update issues.
74
+
75
+ ### Step 4: Check for Conditional Rendering Issues
76
+
77
+ **Problem**: The `PagePermissionGuard` might be conditionally rendered, causing React to unmount/remount.
78
+
79
+ **Debug Code**:
80
+ ```tsx
81
+ // Add this to your Meals component
82
+ const [showGuard, setShowGuard] = useState(true);
83
+ console.log('[Meals] PagePermissionGuard should render:', showGuard);
84
+
85
+ // If you have conditional rendering, log it
86
+ if (someCondition) {
87
+ console.log('[Meals] Conditional rendering condition:', someCondition);
88
+ }
89
+ ```
90
+
91
+ **Fix**: Ensure `PagePermissionGuard` is not conditionally rendered.
92
+
93
+ ### Step 5: Check for Parent Component Re-renders
94
+
95
+ **Problem**: The parent component might be re-rendering and causing issues.
96
+
97
+ **Debug Code**:
98
+ ```tsx
99
+ // Add this to your Meals component
100
+ const renderCount = useRef(0);
101
+ renderCount.current += 1;
102
+ console.log('[Meals] Component render count:', renderCount.current);
103
+
104
+ // Check if props are changing
105
+ useEffect(() => {
106
+ console.log('[Meals] Props changed:', { selectedEvent, otherProps });
107
+ }, [selectedEvent, otherProps]);
108
+ ```
109
+
110
+ **Fix**: Memoize expensive operations and prevent unnecessary re-renders.
111
+
112
+ ### Step 6: Check for React Strict Mode Issues
113
+
114
+ **Problem**: React Strict Mode can cause double rendering in development.
115
+
116
+ **Debug Code**:
117
+ ```tsx
118
+ // Check if you're in strict mode
119
+ console.log('[Meals] React Strict Mode:', process.env.NODE_ENV === 'development' && React.StrictMode);
120
+ ```
121
+
122
+ **Fix**: Test in production build to see if issue persists.
123
+
124
+ ### Step 7: Check for CSS-in-JS or Styled Components Issues
125
+
126
+ **Problem**: CSS-in-JS libraries might be causing style conflicts.
127
+
128
+ **Debug Code**:
129
+ ```tsx
130
+ // Check for style conflicts
131
+ useEffect(() => {
132
+ const styles = document.querySelectorAll('style');
133
+ console.log('[Meals] Style elements:', styles.length);
134
+
135
+ // Check for conflicting z-index values
136
+ const elements = document.querySelectorAll('*');
137
+ const zIndexes = Array.from(elements)
138
+ .map(el => ({
139
+ element: el,
140
+ zIndex: window.getComputedStyle(el).zIndex
141
+ }))
142
+ .filter(item => item.zIndex !== 'auto' && item.zIndex !== '0')
143
+ .sort((a, b) => parseInt(b.zIndex) - parseInt(a.zIndex));
144
+
145
+ console.log('[Meals] Z-index conflicts:', zIndexes.slice(0, 10));
146
+ }, []);
147
+ ```
148
+
149
+ **Fix**: Check for CSS conflicts and z-index issues.
150
+
151
+ ## Common Cake Implementation Issues
152
+
153
+ ### Issue 1: Multiple PagePermissionGuard Wrappers
154
+
155
+ ```tsx
156
+ // ❌ WRONG: Multiple guards
157
+ <PagePermissionGuard pageName="meals" operation="read">
158
+ <PagePermissionGuard pageName="meals" operation="read">
159
+ <DataTable ... />
160
+ </PagePermissionGuard>
161
+ </PagePermissionGuard>
162
+
163
+ // ✅ CORRECT: Single guard
164
+ <PagePermissionGuard pageName="meals" operation="read">
165
+ <DataTable ... />
166
+ </PagePermissionGuard>
167
+ ```
168
+
169
+ ### Issue 2: Conditional Rendering
170
+
171
+ ```tsx
172
+ // ❌ WRONG: Conditional guard
173
+ {someCondition && (
174
+ <PagePermissionGuard pageName="meals" operation="read">
175
+ <DataTable ... />
176
+ </PagePermissionGuard>
177
+ )}
178
+
179
+ // ✅ CORRECT: Always render guard
180
+ <PagePermissionGuard pageName="meals" operation="read">
181
+ {someCondition && <DataTable ... />}
182
+ </PagePermissionGuard>
183
+ ```
184
+
185
+ ### Issue 3: State Management in Parent
186
+
187
+ ```tsx
188
+ // ❌ WRONG: State changes causing re-renders
189
+ const [loading, setLoading] = useState(true);
190
+ useEffect(() => {
191
+ // This might cause infinite re-renders
192
+ setLoading(false);
193
+ }, [loading]);
194
+
195
+ // ✅ CORRECT: Stable state management
196
+ const [loading, setLoading] = useState(true);
197
+ useEffect(() => {
198
+ setLoading(false);
199
+ }, []); // Empty dependency array
200
+ ```
201
+
202
+ ### Issue 4: CSS Conflicts
203
+
204
+ ```tsx
205
+ // ❌ WRONG: CSS that might conflict
206
+ .modal {
207
+ z-index: 9999 !important;
208
+ position: fixed !important;
209
+ }
210
+
211
+ // ✅ CORRECT: Use pace-core's built-in styling
212
+ // Let pace-core handle the modal styling
213
+ ```
214
+
215
+ ## Recommended Fix for Cake
216
+
217
+ ### 1. Simplify the Meals Component
218
+
219
+ ```tsx
220
+ // Meals.tsx - Simplified version
221
+ import React from 'react';
222
+ import { PagePermissionGuard } from '@jmruthers/pace-core';
223
+ import { DataTable } from '@jmruthers/pace-core';
224
+
225
+ export function Meals() {
226
+ console.log('[Meals] Rendering Meals component');
227
+
228
+ return (
229
+ <div>
230
+ <h1>Meals</h1>
231
+ <p>Manage the meals available for AJ2025...</p>
232
+
233
+ <PagePermissionGuard
234
+ pageName="meals"
235
+ operation="read"
236
+ fallback={
237
+ <div className="min-h-screen flex items-center justify-center">
238
+ <div className="text-center">
239
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto mb-4"></div>
240
+ <p>Loading permissions...</p>
241
+ </div>
242
+ </div>
243
+ }
244
+ >
245
+ <DataTable
246
+ data={processedMeals || []}
247
+ columns={columns}
248
+ rbac={{
249
+ resource: 'meals',
250
+ pageId: 'meals'
251
+ }}
252
+ // ... other props
253
+ />
254
+ </PagePermissionGuard>
255
+ </div>
256
+ );
257
+ }
258
+ ```
259
+
260
+ ### 2. Check for Infinite Re-renders
261
+
262
+ Add this to your Meals component to detect infinite re-renders:
263
+
264
+ ```tsx
265
+ import { useEffect, useRef } from 'react';
266
+
267
+ export function Meals() {
268
+ const renderCount = useRef(0);
269
+ renderCount.current += 1;
270
+
271
+ useEffect(() => {
272
+ if (renderCount.current > 10) {
273
+ console.error('[Meals] INFINITE RE-RENDER DETECTED!', {
274
+ renderCount: renderCount.current,
275
+ timestamp: new Date().toISOString()
276
+ });
277
+ }
278
+ });
279
+
280
+ // ... rest of component
281
+ }
282
+ ```
283
+
284
+ ### 3. Check for Multiple Modals
285
+
286
+ Add this to detect multiple modals:
287
+
288
+ ```tsx
289
+ useEffect(() => {
290
+ const checkModals = () => {
291
+ const modals = document.querySelectorAll('[role="dialog"], .modal, [data-testid*="modal"], [data-testid*="access-denied"]');
292
+ if (modals.length > 1) {
293
+ console.error('[Meals] MULTIPLE MODALS DETECTED!', {
294
+ count: modals.length,
295
+ modals: Array.from(modals).map(m => ({
296
+ element: m,
297
+ visible: m.offsetParent !== null,
298
+ zIndex: window.getComputedStyle(m).zIndex
299
+ }))
300
+ });
301
+ }
302
+ };
303
+
304
+ checkModals();
305
+ const interval = setInterval(checkModals, 1000);
306
+ return () => clearInterval(interval);
307
+ }, []);
308
+ ```
309
+
310
+ ## Testing the Fix
311
+
312
+ ### 1. Test in Production Build
313
+
314
+ ```bash
315
+ npm run build
316
+ npm run preview
317
+ ```
318
+
319
+ ### 2. Test with React DevTools
320
+
321
+ 1. Install React DevTools browser extension
322
+ 2. Open the Components tab
323
+ 3. Look for multiple `PagePermissionGuard` instances
324
+ 4. Check for infinite re-renders
325
+
326
+ ### 3. Test with Network Tab
327
+
328
+ 1. Open DevTools Network tab
329
+ 2. Look for repeated API calls
330
+ 3. Check if permission checks are being called repeatedly
331
+
332
+ ## Expected Behavior
333
+
334
+ After fixing the issues, you should see:
335
+
336
+ 1. ✅ Only one `PagePermissionGuard` instance
337
+ 2. ✅ No infinite re-renders
338
+ 3. ✅ `RENDERING CONTENT STATE` log appears once
339
+ 4. ✅ Access Denied modal disappears
340
+ 5. ✅ DataTable content is visible
341
+
342
+ ## Contact Information
343
+
344
+ If you need further assistance, please provide:
345
+
346
+ 1. The complete Meals component code
347
+ 2. Any parent component that renders Meals
348
+ 3. The console logs after implementing the debugging code
349
+ 4. Screenshots of React DevTools showing component tree
350
+
351
+ ---
352
+
353
+ **Note**: This issue is in the Cake application's implementation, not in pace-core. The `PagePermissionGuard` component is working correctly as evidenced by the console logs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.5.17",
3
+ "version": "0.5.18",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {