freealberta/influence/RESPONSE_WALL_FIXES.md

7.6 KiB

Response Wall Bug Fixes

Issues Identified and Fixed

1. TypeError: responses.filter is not a function

Error Location: app/controllers/responses.js:292 in getResponseStats()

Root Cause: The getRepresentativeResponses() method in nocodb.js was returning the raw NocoDB API response object {list: [...], pageInfo: {...}} instead of an array. The controller code expected an array and tried to call .filter() on an object.

Fix Applied: Modified getRepresentativeResponses() to extract the list array from the response and normalize each item:

async getRepresentativeResponses(params = {}) {
  if (!this.tableIds.representativeResponses) {
    throw new Error('Representative responses table not configured');
  }
  const result = await this.getAll(this.tableIds.representativeResponses, params);
  // NocoDB returns {list: [...]} or {pageInfo: {...}, list: [...]}
  const list = result.list || [];
  return list.map(item => this.normalizeResponse(item));
}

2. TypeError: responses.map is not a function

Error Location: app/controllers/responses.js:51 in getCampaignResponses()

Root Cause: Same as issue #1 - the method was returning an object instead of an array.

Fix Applied: Same fix as above ensures an array is always returned.

3. Database Error: "A value is required for this field" (code 23502)

Error Location: NocoDB database constraint violation when creating a response

Root Cause: The campaign_id field was being set to null or undefined. Investigation revealed that:

  • The campaign object from NocoDB uses Id (capital I) as the primary key field
  • The controller was trying to access campaign.id (lowercase) which returned undefined
  • NocoDB's representative_responses table has campaign_id marked as required ("rqd": true)

Fix Applied:

  1. Updated submitResponse() controller to check multiple possible field names:
campaign_id: campaign.Id || campaign.id || campaign['Campaign ID'],
  1. Added validation in createRepresentativeResponse() to fail fast if campaign_id is missing:
if (!responseData.campaign_id) {
  throw new Error('Campaign ID is required for creating a response');
}
  1. Added debug logging to track the campaign_id value:
console.log('Submitting response with campaign_id:', newResponse.campaign_id, 'from campaign:', campaign);

4. Array Handling in Response Upvotes

Potential Issue: The getResponseUpvotes() method had the same array vs object issue

Fix Applied: Updated the method to return a normalized array:

async getResponseUpvotes(params = {}) {
  if (!this.tableIds.responseUpvotes) {
    throw new Error('Response upvotes table not configured');
  }
  const result = await this.getAll(this.tableIds.responseUpvotes, params);
  // NocoDB returns {list: [...]} or {pageInfo: {...}, list: [...]}
  const list = result.list || [];
  return list.map(item => this.normalizeUpvote(item));
}

Files Modified

  1. app/services/nocodb.js

    • Modified getRepresentativeResponses() - Extract and normalize list
    • Modified getResponseUpvotes() - Extract and normalize list
    • Modified createRepresentativeResponse() - Add campaign_id validation and logging
  2. app/controllers/responses.js

    • Modified submitResponse() - Handle multiple campaign ID field name variations (ID, Id, id)
    • Added debug logging for campaign_id
  3. app/public/js/admin.js

    • Modified renderAdminResponses() - Removed all inline onclick handlers, replaced with data-action attributes
    • Added setupResponseActionListeners() - Event delegation for response moderation buttons
    • Follows instruct.md guidelines: "No inline event handlers. Always use addEventListener in JS files."
  4. app/public/js/api-client.js

    • Added put() method for HTTP PUT requests
    • Added patch() method for HTTP PATCH requests
    • Added delete() method for HTTP DELETE requests
    • These methods were missing and causing errors in admin panel operations

Testing

After applying these fixes, test the following:

  1. Load Response Wall - Visit http://localhost:3333/response-wall.html?campaign=test-page

    • Stats should load without errors
    • Response list should load without errors
  2. Submit Response - Fill out and submit the response form

    • Should successfully create a response in pending status
    • Should return a success message
    • Check logs for "Submitting response with campaign_id: [number]"
  3. Upvote Response - Click the upvote button on an approved response

    • Should increment the upvote count
    • Should prevent duplicate upvotes
  4. Admin Moderation - Visit http://localhost:3333/admin.html → Response Moderation tab

    • Should see pending responses
    • Should be able to approve/reject responses

Deployment

The application container has been restarted with:

docker compose restart app

All fixes are now live and ready for testing.

Root Cause Analysis

The main issue was a misunderstanding of NocoDB's API response structure:

  • Expected: Array of records directly
  • Actual: Object with {list: [records], pageInfo: {...}}

This is a common pattern in REST APIs for pagination support. The fix ensures all service methods return properly normalized arrays for consistent usage throughout the application.

The secondary issue was field naming inconsistency:

  • NocoDB Primary Key: Uses ID (all caps) not Id or id
  • Application Code: Expected id (lowercase)

The fix handles all three variations to ensure compatibility: campaign.ID || campaign.Id || campaign.id

5. CSP Violation: Inline Event Handlers in Admin Panel

Error: "Refused to execute inline event handler because it violates the following Content Security Policy directive: 'script-src-attr 'none''"

Root Cause: The renderAdminResponses() method in admin.js was using inline onclick handlers like:

<button onclick="adminPanel.approveResponse(${response.id})">

This violates the development guidelines in instruct.md which explicitly state: "No inline event handlers. Always use addEventListener in JS files."

Fix Applied:

  • Replaced all inline onclick handlers with data-action attributes
  • Created setupResponseActionListeners() method using event delegation
  • All buttons now use pattern: data-action="approve-response" data-response-id="${response.id}"
  • Event delegation listens on container and routes actions based on data attributes

6. Missing HTTP Methods in API Client

Error: "TypeError: window.apiClient.patch is not a function"

Root Cause: The APIClient class in api-client.js only had get() and post() methods. Admin panel operations needed patch(), put(), and delete() methods for updating and deleting responses.

Fix Applied: Added three new methods to APIClient:

async put(endpoint, data) { ... }
async patch(endpoint, data) { ... }  
async delete(endpoint) { ... }

Prevention

To prevent similar issues in the future:

  1. Type Safety: Consider adding TypeScript or JSDoc type annotations
  2. Consistent Normalization: Always normalize data at the service layer
  3. Field Name Standards: Document NocoDB field naming conventions
  4. Validation: Add validation for required fields early in the request flow
  5. Logging: Continue adding debug logging for data transformations
  • See RESPONSE_WALL_USAGE.md for usage instructions
  • See RESPONSE_WALL_IMPLEMENTATION.md for feature implementation details
  • See scripts/build-nocodb.sh for database schema definitions