const express = require('express'); const router = express.Router(); const path = require('path'); const fs = require('fs'); const multer = require('multer'); const { getDb, logAudit } = require('../db'); const UPLOAD_DIR = path.join(__dirname, '..', 'uploads'); if (!fs.existsSync(UPLOAD_DIR)) fs.mkdirSync(UPLOAD_DIR, { recursive: true }); const storage = multer.diskStorage({ destination: (req, file, cb) => cb(null, UPLOAD_DIR), filename: (req, file, cb) => cb(null, Date.now() + '-' + Math.round(Math.random() * 1e9) + path.extname(file.originalname)) }); const upload = multer({ storage, limits: { fileSize: 100 * 1024 * 1024 } }); router.get('/', (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'; const clients = db.prepare(sql).all(...params); res.render('client-list', { clients, filters: req.query }); }); router.get('/new', (req, res) => { res.render('client-form', { client: null, error: null }); }); router.post('/new', (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.render('client-form', { client: null, 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, `Cliënt ${name} aangemaakt`); res.redirect('/clients'); }); router.get('/:id', (req, res) => { const db = getDb(); const client = db.prepare('SELECT * FROM clients WHERE id = ?').get(req.params.id); if (!client) return res.redirect('/clients'); const engagements = db.prepare('SELECT * FROM engagements WHERE client_id = ? ORDER BY created_at DESC').all(req.params.id); const assessments = db.prepare('SELECT * FROM assessments WHERE client_id = ? ORDER BY created_at DESC').all(req.params.id); const advice = db.prepare('SELECT * FROM advice_log WHERE client_id = ? AND dismissed = 0 ORDER BY created_at DESC').all(req.params.id); const diagrams = db.prepare('SELECT * FROM data_diagrams WHERE client_id = ? ORDER BY updated_at DESC').all(req.params.id); const notes = db.prepare('SELECT * FROM client_notes WHERE client_id = ? ORDER BY created_at DESC').all(req.params.id); const files = db.prepare('SELECT * FROM client_files WHERE client_id = ? ORDER BY created_at DESC').all(req.params.id); res.render('client-detail', { client, engagements, assessments, advice, diagrams, notes, files }); }); router.get('/:id/edit', (req, res) => { const db = getDb(); const client = db.prepare('SELECT * FROM clients WHERE id = ?').get(req.params.id); if (!client) return res.redirect('/clients'); res.render('client-form', { client, error: null }); }); router.post('/:id/edit', (req, res) => { const db = getDb(); const { name, industry, website, contact_name, contact_email, contact_phone, status, source, notes } = req.body; if (!name || !name.trim()) { const client = db.prepare('SELECT * FROM clients WHERE id = ?').get(req.params.id); return res.render('client-form', { client, 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, `Cliënt ${name} bijgewerkt`); res.redirect('/clients/' + req.params.id); }); router.post('/:id/delete', (req, res) => { const db = getDb(); db.prepare('DELETE FROM clients WHERE id = ?').run(req.params.id); res.redirect('/clients'); }); router.post('/: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.redirect('/clients/' + req.params.id); }); router.post('/:id/notes', (req, res) => { const db = getDb(); const { content } = req.body; if (content && content.trim()) { db.prepare('INSERT INTO client_notes (client_id, content, created_by) VALUES (?, ?, ?)') .run(req.params.id, content.trim(), req.session.username || ''); } res.redirect('/clients/' + req.params.id + '#notes'); }); router.post('/:id/notes/:noteId/delete', (req, res) => { const db = getDb(); db.prepare('DELETE FROM client_notes WHERE id = ? AND client_id = ?').run(req.params.noteId, req.params.id); res.redirect('/clients/' + req.params.id + '#notes'); }); router.post('/:id/upload', upload.single('file'), (req, res) => { if (!req.file) return res.redirect('/clients/' + req.params.id + '#files'); const db = getDb(); db.prepare('INSERT INTO client_files (client_id, original_name, stored_name, mime_type, file_size) VALUES (?, ?, ?, ?, ?)') .run(req.params.id, req.file.originalname, req.file.filename, req.file.mimetype, req.file.size); res.redirect('/clients/' + req.params.id + '#files'); }); router.get('/:id/files/:fileId/view', (req, res) => { const db = getDb(); const file = db.prepare('SELECT * FROM client_files WHERE id = ? AND client_id = ?').get(req.params.fileId, req.params.id); if (!file) return res.status(404).send('Bestand niet gevonden'); const filePath = path.join(UPLOAD_DIR, file.stored_name); if (!fs.existsSync(filePath)) return res.status(404).send('Bestand niet gevonden op schijf'); if (file.mime_type === 'application/pdf') { res.render('pdf-viewer', { file, clientId: req.params.id }); } else { res.download(filePath, file.original_name); } }); router.get('/:id/files/:fileId/download', (req, res) => { const db = getDb(); const file = db.prepare('SELECT * FROM client_files WHERE id = ? AND client_id = ?').get(req.params.fileId, req.params.id); if (!file) return res.status(404).send('Bestand niet gevonden'); const filePath = path.join(UPLOAD_DIR, file.stored_name); if (!fs.existsSync(filePath)) return res.status(404).send('Bestand niet gevonden op schijf'); res.download(filePath, file.original_name); }); router.post('/:id/files/:fileId/delete', (req, res) => { const db = getDb(); const file = db.prepare('SELECT * FROM client_files WHERE id = ? AND client_id = ?').get(req.params.fileId, req.params.id); if (file) { const filePath = path.join(UPLOAD_DIR, file.stored_name); if (fs.existsSync(filePath)) fs.unlinkSync(filePath); db.prepare('DELETE FROM client_files WHERE id = ?').run(req.params.fileId); } res.redirect('/clients/' + req.params.id + '#files'); }); router.post('/:id/email/send', upload.array('attachments', 5), async (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 not found' }); const { to, cc, bcc, subject, text } = req.body; if (!to) return res.status(400).json({ error: 'Ontvanger is verplicht' }); const emailService = require('../email'); const account = db.prepare('SELECT * FROM email_accounts WHERE user_id = ? AND active = 1 LIMIT 1').get(req.session.userId); if (!account) return res.status(400).json({ error: 'Geen email account geconfigureerd. Ga naar /email/settings' }); try { await emailService.sendEmail(account.id, to, subject || `Mek-Tech — ${client.name}`, text || '', '', cc, bcc); logAudit(req.session.userId, req.session.username, 'email_sent_to_client', 'client', client.id, `Email naar ${to}: ${subject}`); res.redirect('/clients/' + req.params.id + '?email=sent'); } catch (e) { res.redirect('/clients/' + req.params.id + '?email=error&msg=' + encodeURIComponent(e.message)); } }); module.exports = router;