6.2 KiB
Response Wall Admin Panel - Inline Handler Fix Summary
Issue
Content Security Policy (CSP) violation when clicking buttons in the Response Moderation tab of the admin panel.
Error Messages
Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src-attr 'none'"
TypeError: window.apiClient.patch is not a function
Root Causes
1. Inline Event Handlers (CSP Violation)
The admin panel's Response Moderation tab was using inline onclick handlers:
<button onclick="adminPanel.approveResponse(${response.id})">✓ Approve</button>
This violates:
- Browser Content Security Policy (CSP)
- Project development guidelines in
instruct.md: "No inline event handlers. Always use addEventListener in JS files."
2. Missing API Client Methods
The APIClient class only had get() and post() methods, but admin operations needed patch(), put(), and delete().
Solutions Implemented
Fix 1: Removed All Inline Handlers in admin.js
Before:
<button class="btn btn-success btn-sm" onclick="adminPanel.approveResponse(${response.id})">
✓ Approve
</button>
After:
<button class="btn btn-success btn-sm"
data-action="approve-response"
data-response-id="${response.id}">
✓ Approve
</button>
Fix 2: Added Event Delegation
Created new setupResponseActionListeners() method in AdminPanel class:
setupResponseActionListeners() {
const container = document.getElementById('admin-responses-container');
if (!container) return;
// Remove old listener if exists to avoid duplicates
const oldListener = container._responseActionListener;
if (oldListener) {
container.removeEventListener('click', oldListener);
}
// Create new listener with event delegation
const listener = (e) => {
const target = e.target;
const action = target.dataset.action;
const responseId = target.dataset.responseId;
if (!action || !responseId) return;
switch (action) {
case 'approve-response':
this.approveResponse(parseInt(responseId));
break;
case 'reject-response':
this.rejectResponse(parseInt(responseId));
break;
case 'verify-response':
const isVerified = target.dataset.verified === 'true';
this.toggleVerified(parseInt(responseId), isVerified);
break;
case 'delete-response':
this.deleteResponse(parseInt(responseId));
break;
}
};
// Store listener reference and add it
container._responseActionListener = listener;
container.addEventListener('click', listener);
}
This method is called at the end of renderAdminResponses() to set up listeners after the HTML is rendered.
Fix 3: Added Missing HTTP Methods to api-client.js
async put(endpoint, data) {
return this.makeRequest(endpoint, {
method: 'PUT',
body: JSON.stringify(data)
});
}
async patch(endpoint, data) {
return this.makeRequest(endpoint, {
method: 'PATCH',
body: JSON.stringify(data)
});
}
async delete(endpoint) {
return this.makeRequest(endpoint, {
method: 'DELETE'
});
}
Buttons Fixed
All response moderation buttons now use proper event delegation:
- Approve -
data-action="approve-response" - Reject -
data-action="reject-response" - Mark as Verified -
data-action="verify-response" data-verified="true" - Remove Verification -
data-action="verify-response" data-verified="false" - Unpublish -
data-action="reject-response"(reuses reject action) - Delete -
data-action="delete-response"
Files Modified
-
app/public/js/admin.js
- Modified
renderAdminResponses()- Replaced inline onclick with data attributes - Added
setupResponseActionListeners()- Event delegation implementation - Total changes: ~85 lines modified/added
- Modified
-
app/public/js/api-client.js
- Added
put()method - Added
patch()method - Added
delete()method - Total changes: ~18 lines added
- Added
Benefits of This Approach
1. Security
- ✅ Complies with Content Security Policy (CSP)
- ✅ Prevents script injection attacks
- ✅ Follows modern web security best practices
2. Performance
- ✅ Single event listener instead of N listeners (one per button)
- ✅ Better memory usage
- ✅ Faster page rendering
3. Maintainability
- ✅ Follows project coding standards (instruct.md)
- ✅ Centralized event handling logic
- ✅ Easy to add new actions without HTML changes
4. Reliability
- ✅ Prevents duplicate listeners
- ✅ Clean listener removal/re-attachment
- ✅ Works with dynamically rendered content
Testing Checklist
- Load admin panel → Response Moderation tab
- Click "Approve" button → Should approve response without CSP error
- Click "Reject" button → Should reject response
- Click "Mark as Verified" → Should add verification badge
- Click "Remove Verification" → Should remove badge
- Click "Delete" → Should delete response after confirmation
- Filter responses by status → Should reload list
- No console errors related to inline handlers
- No CSP violations
Lessons Learned
-
Always Follow Project Guidelines: The instruct.md file explicitly prohibits inline event handlers - following it from the start would have prevented this issue
-
Complete API Client: When building a REST client, implement all HTTP methods (GET, POST, PUT, PATCH, DELETE) from the beginning
-
Event Delegation for Dynamic Content: When rendering content dynamically with buttons/links, always use event delegation on a parent container
-
CSP is Your Friend: Content Security Policy errors point to real security issues - fix them rather than disabling CSP
Related Documentation
- See
instruct.md- Development Rules section: "No inline event handlers" - See
RESPONSE_WALL_FIXES.md- Full list of all Response Wall bug fixes - See
RESPONSE_WALL_USAGE.md- How to use the Response Wall feature