Files
hope-events/backend/src/controllers/formController.js
T
joshuaandClaude Opus 5.5 d265047c84 POPIA toolkit part 2: privacy notice editor, operators, consent and access logs
- Admin -> Privacy (POPIA): edit the privacy notice (built-in text as the
  start, placeholders for org/IO/operators), drafts, preview, numbered
  versions; earlier versions readable on /legal/privacy?version=N
- "Significant change" versions are accepted once after login
  (NoticePrompt); sign-up records the version accepted
- Operator register (seeded with the services the app uses), listed in
  the notice via {operators}
- Consent log (news opt-ins, notice acceptances, parental consent) and
  access log (report exports, registers, certificates, group lists, form
  answers, consent/medical lists, backups, impersonation)
- Information Officer on the contact page
- Unit tests (privacyNotice.test.js), e2e (privacy-notice.spec.ts); docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012mt6TCv85T9VKRq3cV7rXq
2026-10-23 19:11:20 +02:00

64 lines
2.3 KiB
JavaScript

const prisma = require('../config/db');
// @desc List submitted form responses with optional filters
// @route GET /api/forms/responses
// @access Private/Staff (admin, supervisor, staff)
const listFormResponses = async (req, res) => {
try {
const { eventId, userId, registrationId, limit, cursor } = req.query;
// Build where clause
const where = {};
if (registrationId) {
where.registrationId = String(registrationId);
}
// For eventId/userId we filter through the related registration
const registrationFilter = {};
if (eventId) registrationFilter.eventId = String(eventId);
if (userId) registrationFilter.userId = String(userId);
if (Object.keys(registrationFilter).length > 0) {
// Prisma relation filter requires `is` wrapper
where.registration = { is: registrationFilter };
}
// Basic pagination (optional)
const take = Math.min(Math.max(parseInt(limit || '50', 10) || 50, 1), 200);
const cursorClause = cursor ? { id: String(cursor) } : undefined;
const responses = await prisma.formResponse.findMany({
where,
include: {
registration: {
include: {
user: { select: { id: true, name: true, email: true } },
event: { select: { id: true, title: true } },
}
},
answers: { include: { field: true } },
},
orderBy: { createdAt: 'desc' },
take,
...(cursorClause ? { skip: 1, cursor: cursorClause } : {}),
});
const nextCursor = responses.length === take ? responses[responses.length - 1].id : null;
// The first page of a list (not one registration's own form, nor the next pages of the
// same list) is logged as a bulk view of form answers.
if (!registrationId && !cursor && responses.length) {
require('../utils/accessLog').logDataAccess(req, {
action: 'form_responses',
what: `Attendee form answers${responses[0].registration?.event && eventId ? ` — ${responses[0].registration.event.title}` : ''}`,
eventId: eventId ? String(eventId) : null,
records: responses.length,
});
}
res.json({ items: responses, nextCursor });
} catch (error) {
res.status(400).json({ message: error.message });
}
};
module.exports = { listFormResponses };