Files
mek-tech-consulting/routes/clients.js
T

169 lines
8.4 KiB
JavaScript

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;