@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.
- package/dist/{DataTable-RICY7YDA.js → DataTable-BTXP6MW4.js} +3 -3
- package/dist/{chunk-JZCNOXSG.js → chunk-7QEQCZDV.js} +2 -2
- package/dist/{chunk-F6IHN3DC.js → chunk-J3JWBQSC.js} +8 -8
- package/dist/chunk-J3JWBQSC.js.map +1 -0
- package/dist/{chunk-JPXJGMOO.js → chunk-NU3GLMIQ.js} +2 -2
- package/dist/{chunk-S7KTIIL3.js → chunk-UIBYODOF.js} +13 -6
- package/dist/chunk-UIBYODOF.js.map +1 -0
- package/dist/components.js +3 -3
- package/dist/index.js +4 -4
- package/dist/rbac/index.js +2 -2
- package/dist/utils.js +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +2 -2
- package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +187 -0
- package/docs/troubleshooting/page-permission-guard-diagnostic.js +129 -0
- package/docs/troubleshooting/page-permission-guard-ui-debugging.md +353 -0
- package/package.json +1 -1
- package/src/rbac/components/PagePermissionGuard.tsx +14 -6
- package/src/rbac/hooks/usePermissions.ts +7 -7
- package/dist/chunk-F6IHN3DC.js.map +0 -1
- package/dist/chunk-S7KTIIL3.js.map +0 -1
- /package/dist/{DataTable-RICY7YDA.js.map → DataTable-BTXP6MW4.js.map} +0 -0
- /package/dist/{chunk-JZCNOXSG.js.map → chunk-7QEQCZDV.js.map} +0 -0
- /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.
|