const express = require('express'); const router = express.Router(); const { getDb, logAudit } = require('../../db'); // ============ CLIENTS ============ router.get('/clients', (req, res) => { const db = getDb(); const { status, search } = req.query; let sql = `SELECT c.*, (SELECT COUNT(*) FROM engagements e WHERE e.client_id = c.id) as engagement_count, (SELECT MAX(a.created_at) FROM assessments a WHERE a.client_id = c.id) as last_assessment FROM clients c WHERE 1=1`; const params = []; if (status) { sql += ' AND c.status = ?'; params.push(status); } if (search) { sql += ' AND (c.name LIKE ? OR c.contact_name LIKE ? OR c.industry LIKE ?)'; params.push(`%${search}%`, `%${search}%`, `%${search}%`); } sql += ' ORDER BY c.updated_at DESC'; res.json(db.prepare(sql).all(...params)); }); router.post('/clients', (req, res) => { const db = getDb(); const { name, industry, website, contact_name, contact_email, contact_phone, status, source, notes } = req.body; if (!name || !name.trim()) return res.status(400).json({ error: 'Bedrijfsnaam is verplicht' }); const result = db.prepare(`INSERT INTO clients (name, industry, website, contact_name, contact_email, contact_phone, status, source, notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(name, industry, website, contact_name, contact_email, contact_phone, status || 'lead', source, notes); logAudit(req.session.userId, req.session.username, 'client_created', 'client', result.lastInsertRowid, `Client ${name} aangemaakt via SPA`); res.status(201).json({ id: result.lastInsertRowid }); }); router.get('/clients/:id', (req, res) => { const db = getDb(); const client = db.prepare('SELECT * FROM clients WHERE id = ?').get(req.params.id); if (!client) return res.status(404).json({ error: 'Client niet gevonden' }); res.json({ client, engagements: db.prepare('SELECT * FROM engagements WHERE client_id = ? ORDER BY created_at DESC').all(req.params.id), assessments: db.prepare('SELECT id, client_id, engagement_id, created_at FROM assessments WHERE client_id = ? ORDER BY created_at DESC').all(req.params.id), advice: db.prepare('SELECT * FROM advice_log WHERE client_id = ? AND dismissed = 0 ORDER BY created_at DESC').all(req.params.id), diagrams: db.prepare('SELECT id, name, description, diagram_type, updated_at, created_at FROM data_diagrams WHERE client_id = ? ORDER BY updated_at DESC').all(req.params.id), notes: db.prepare('SELECT * FROM client_notes WHERE client_id = ? ORDER BY created_at DESC').all(req.params.id), files: db.prepare('SELECT id, client_id, original_name, mime_type, file_size, created_at FROM client_files WHERE client_id = ? ORDER BY created_at DESC').all(req.params.id), timeEntries: db.prepare('SELECT t.*, u.username FROM time_entries t LEFT JOIN users u ON u.id = t.user_id WHERE t.client_id = ? ORDER BY t.date DESC').all(req.params.id), invoices: db.prepare('SELECT * FROM invoices WHERE client_id = ? ORDER BY created_at DESC').all(req.params.id) }); }); router.put('/clients/:id', (req, res) => { const db = getDb(); const { name, industry, website, contact_name, contact_email, contact_phone, status, source, notes } = req.body; if (!name || !name.trim()) return res.status(400).json({ error: 'Bedrijfsnaam is verplicht' }); db.prepare(`UPDATE clients SET name=?, industry=?, website=?, contact_name=?, contact_email=?, contact_phone=?, status=?, source=?, notes=?, updated_at=datetime('now') WHERE id=?`) .run(name, industry, website, contact_name, contact_email, contact_phone, status, source, notes, req.params.id); logAudit(req.session.userId, req.session.username, 'client_updated', 'client', req.params.id, `Client ${name} bijgewerkt via SPA`); res.json({ ok: true }); }); router.delete('/clients/:id', (req, res) => { const db = getDb(); db.prepare('DELETE FROM clients WHERE id = ?').run(req.params.id); logAudit(req.session.userId, req.session.username, 'client_deleted', 'client', req.params.id, 'Client verwijderd via SPA'); res.json({ ok: true }); }); router.post('/clients/:id/advice/:adviceId/dismiss', (req, res) => { const db = getDb(); db.prepare('UPDATE advice_log SET dismissed = 1 WHERE id = ? AND client_id = ?').run(req.params.adviceId, req.params.id); res.json({ ok: true }); }); // ---- Client notes ---- router.post('/clients/:id/notes', (req, res) => { const db = getDb(); const { content } = req.body; if (!content || !content.trim()) return res.status(400).json({ error: 'Notitie is leeg' }); const result = db.prepare('INSERT INTO client_notes (client_id, content, created_by) VALUES (?, ?, ?)') .run(req.params.id, content.trim(), req.session.username || ''); res.status(201).json({ id: result.lastInsertRowid }); }); router.delete('/clients/:id/notes/:noteId', (req, res) => { const db = getDb(); db.prepare('DELETE FROM client_notes WHERE id = ? AND client_id = ?').run(req.params.noteId, req.params.id); res.json({ ok: true }); }); // ============ ENGAGEMENTS ============ router.get('/engagements', (req, res) => { const db = getDb(); const { type, status, client_id } = req.query; let sql = `SELECT e.*, c.name as client_name, (SELECT COUNT(*) FROM tasks t WHERE t.engagement_id = e.id) as task_count FROM engagements e JOIN clients c ON c.id = e.client_id WHERE 1=1`; const params = []; if (type) { sql += ' AND e.type = ?'; params.push(type); } if (status) { sql += ' AND e.status = ?'; params.push(status); } if (client_id) { sql += ' AND e.client_id = ?'; params.push(client_id); } sql += ' ORDER BY e.created_at DESC'; res.json(db.prepare(sql).all(...params)); }); router.post('/engagements', (req, res) => { const db = getDb(); const { client_id, type, title, description, start_date, end_date, status } = req.body; if (!client_id || !type || !title) return res.status(400).json({ error: 'Client, type en titel zijn verplicht' }); const result = db.prepare(`INSERT INTO engagements (client_id, type, title, description, start_date, end_date, status) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(client_id, type, title, description, start_date, end_date, status || 'planned'); logAudit(req.session.userId, req.session.username, 'engagement_created', 'engagement', result.lastInsertRowid, `Engagement ${title} aangemaakt via SPA`); res.status(201).json({ id: result.lastInsertRowid }); }); router.get('/engagements/:id', (req, res) => { const db = getDb(); const engagement = db.prepare('SELECT e.*, c.name as client_name FROM engagements e JOIN clients c ON c.id = e.client_id WHERE e.id = ?').get(req.params.id); if (!engagement) return res.status(404).json({ error: 'Engagement niet gevonden' }); res.json({ engagement, tasks: db.prepare('SELECT * FROM tasks WHERE engagement_id = ? ORDER BY created_at DESC').all(req.params.id), timeEntries: db.prepare('SELECT t.*, u.username FROM time_entries t LEFT JOIN users u ON u.id = t.user_id WHERE t.engagement_id = ? ORDER BY t.date DESC').all(req.params.id) }); }); router.put('/engagements/:id', (req, res) => { const db = getDb(); const { type, title, description, start_date, end_date, status, outcome } = req.body; db.prepare(`UPDATE engagements SET type=?, title=?, description=?, start_date=?, end_date=?, status=?, outcome=?, updated_at=datetime('now') WHERE id=?`) .run(type, title, description, start_date, end_date, status, outcome, req.params.id); res.json({ ok: true }); }); router.post('/engagements/:id/status', (req, res) => { const db = getDb(); db.prepare("UPDATE engagements SET status=?, updated_at=datetime('now') WHERE id=?").run(req.body.status, req.params.id); res.json({ ok: true }); }); router.delete('/engagements/:id', (req, res) => { const db = getDb(); db.prepare('DELETE FROM engagements WHERE id = ?').run(req.params.id); res.json({ ok: true }); }); // ============ TASKS ============ router.get('/tasks', (req, res) => { const db = getDb(); const tasks = db.prepare(` SELECT t.*, e.title as engagement_title, e.id as engagement_id, c.name as client_name, c.id as client_id FROM tasks t JOIN engagements e ON e.id = t.engagement_id JOIN clients c ON c.id = e.client_id ORDER BY CASE t.status WHEN 'in_progress' THEN 0 WHEN 'todo' THEN 1 WHEN 'done' THEN 2 ELSE 3 END, CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END, t.due_date IS NULL, t.due_date `).all(); res.json(tasks); }); router.post('/tasks', (req, res) => { const db = getDb(); const { engagement_id, title, description, priority, due_date, status } = req.body; if (!engagement_id || !title) return res.status(400).json({ error: 'Engagement en titel zijn verplicht' }); const result = db.prepare('INSERT INTO tasks (engagement_id, title, description, priority, due_date, status) VALUES (?,?,?,?,?,?)') .run(engagement_id, title, description || '', priority || 'medium', due_date || null, status || 'todo'); res.status(201).json({ id: result.lastInsertRowid }); }); router.post('/tasks/:id/status', (req, res) => { const db = getDb(); db.prepare("UPDATE tasks SET status=?, updated_at=datetime('now') WHERE id=?").run(req.body.status, req.params.id); res.json({ ok: true }); }); router.put('/tasks/:id', (req, res) => { const db = getDb(); const { title, description, priority, due_date, status } = req.body; db.prepare("UPDATE tasks SET title=?, description=?, priority=?, due_date=?, status=?, updated_at=datetime('now') WHERE id=?") .run(title, description, priority, due_date, status, req.params.id); res.json({ ok: true }); }); router.delete('/tasks/:id', (req, res) => { const db = getDb(); db.prepare('DELETE FROM tasks WHERE id = ?').run(req.params.id); res.json({ ok: true }); }); module.exports = router;