diff --git a/server/database.ts b/server/database.ts index 2aa6b7b..a88b63e 100644 --- a/server/database.ts +++ b/server/database.ts @@ -339,6 +339,10 @@ export const participantOps = { db.prepare('DELETE FROM participants WHERE id = ?').run(id); }, + update: (id: number, name: string, phone: string | null): void => { + db.prepare('UPDATE participants SET name = ?, phone = ? WHERE id = ?').run(name, phone, id); + }, + isTokenAvailable: (token: string): boolean => { const result = db.prepare('SELECT id FROM participants WHERE token = ?').get(token); return !result; diff --git a/server/routes/admin.ts b/server/routes/admin.ts index 3baa552..f326d7a 100644 --- a/server/routes/admin.ts +++ b/server/routes/admin.ts @@ -225,6 +225,30 @@ router.post('/questionnaires/:id/participants', (req: Request, res: Response) => res.json({ success: true, participant }); }); +// Update participant +router.put('/participants/:id', (req: Request, res: Response) => { + const { name, phone } = req.body; + const participant = participantOps.findById(parseInt(req.params.id)); + + if (!participant) { + res.status(404).json({ error: 'Deelnemer niet gevonden' }); + return; + } + + if (!name?.trim()) { + res.status(400).json({ error: 'Naam is verplicht' }); + return; + } + + // Clean phone number (remove spaces, keep + and digits) or set to null if empty + const cleanPhone = phone?.trim() ? phone.trim().replace(/[^\d+]/g, '') : null; + + participantOps.update(participant.id, name.trim(), cleanPhone); + const updated = participantOps.findById(participant.id); + + res.json({ success: true, participant: updated }); +}); + // Delete participant router.delete('/participants/:id', (req: Request, res: Response) => { participantOps.delete(parseInt(req.params.id)); diff --git a/src/pages/QuestionnaireDetail.tsx b/src/pages/QuestionnaireDetail.tsx index 8f2269f..e1ed010 100644 --- a/src/pages/QuestionnaireDetail.tsx +++ b/src/pages/QuestionnaireDetail.tsx @@ -48,6 +48,13 @@ export function QuestionnaireDetail() { const [editNameInput, setEditNameInput] = useState('') const [editDescriptionInput, setEditDescriptionInput] = useState('') const [editLoading, setEditLoading] = useState(false) + + // Edit participant modal state + const [showEditParticipantModal, setShowEditParticipantModal] = useState(false) + const [editingParticipant, setEditingParticipant] = useState(null) + const [editParticipantName, setEditParticipantName] = useState('') + const [editParticipantPhone, setEditParticipantPhone] = useState('') + const [editParticipantLoading, setEditParticipantLoading] = useState(false) useEffect(() => { fetchData() @@ -153,6 +160,44 @@ export function QuestionnaireDetail() { } } + function openEditParticipantModal(participant: Participant) { + setEditingParticipant(participant) + setEditParticipantName(participant.name) + setEditParticipantPhone(participant.phone || '') + setShowEditParticipantModal(true) + } + + async function handleEditParticipant(e: FormEvent) { + e.preventDefault() + if (!editingParticipant || !editParticipantName.trim()) return + + setEditParticipantLoading(true) + try { + const res = await fetch(`/api/admin/participants/${editingParticipant.id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + credentials: 'include', + body: JSON.stringify({ + name: editParticipantName.trim(), + phone: editParticipantPhone.trim() || null + }), + }) + const data = await res.json() + if (data.success && data.participant) { + setShowEditParticipantModal(false) + setEditingParticipant(null) + // Update the participant in the list + setParticipants(participants.map(p => + p.id === editingParticipant.id ? data.participant : p + )) + } + } catch (error) { + console.error('Failed to update participant:', error) + } finally { + setEditParticipantLoading(false) + } + } + async function handleDelete() { if (!confirm('Weet je zeker dat je deze vragenlijst wilt verwijderen? Dit verwijdert ook alle activiteiten en stemmen.')) { return @@ -332,6 +377,13 @@ export function QuestionnaireDetail() { )}
{participantUrl}
+ + + + {/* Form */} +
+
+ + setEditParticipantName(e.target.value)} + className="w-full px-4 py-2 bg-bg-input border border-border rounded-lg text-text placeholder-text-faint focus:outline-none focus:border-accent focus:ring-2 focus:ring-accent/20 transition-colors" + required + autoFocus + /> +
+ +
+ + setEditParticipantPhone(e.target.value)} + placeholder="+31612345678 (optioneel, leeg laten om te verwijderen)" + className="w-full px-4 py-2 bg-bg-input border border-border rounded-lg text-text placeholder-text-faint focus:outline-none focus:border-accent focus:ring-2 focus:ring-accent/20 transition-colors" + /> +

+ Laat leeg om het telefoonnummer te verwijderen +

+
+ +
+ + +
+
+ + + )} ) }