const express = require('express'); const router = express.Router(); const { getDb, getSettings } = require('../../db'); const { getServicesStatus } = require('../../lib/services'); // ---- Auth is publiek (login), rest vereist sessie ---- router.use('/auth', require('./auth')); // Sessie-guard voor alle andere /api/spa routes: 401 JSON (nooit redirect) router.use((req, res, next) => { if (!req.session || !req.session.userId) { return res.status(401).json({ error: 'Niet ingelogd' }); } next(); }); router.use(require('./crm')); router.use(require('./finance')); for (const mod of ['./infra', './misc']) { try { router.use(require(mod)); } catch (e) { console.warn(`SPA module ${mod} niet geladen:`, e.message); } } // ============ DASHBOARD (geaggregeerde data voor SPA-home) ============ router.get('/dashboard', async (req, res) => { try { const db = getDb(); const clients = db.prepare(` SELECT c.*, (SELECT COUNT(*) FROM engagements e WHERE e.client_id = c.id) as engagement_count, (SELECT COUNT(*) FROM engagements e WHERE e.client_id = c.id AND e.status = 'in_progress') as active_engagements, (SELECT MAX(a.created_at) FROM assessments a WHERE a.client_id = c.id) as last_assessment FROM clients c ORDER BY c.updated_at DESC LIMIT 12 `).all(); const stats = { totalClients: db.prepare('SELECT COUNT(*) as cnt FROM clients').get().cnt, activeClients: db.prepare("SELECT COUNT(*) as cnt FROM clients WHERE status='active'").get().cnt, leads: db.prepare("SELECT COUNT(*) as cnt FROM clients WHERE status='lead'").get().cnt, activeEngagements: db.prepare("SELECT COUNT(*) as cnt FROM engagements WHERE status='in_progress'").get().cnt, totalTasks: db.prepare('SELECT COUNT(*) as cnt FROM tasks').get().cnt, completedTasks: db.prepare("SELECT COUNT(*) as cnt FROM tasks WHERE status='done'").get().cnt, overdueTasks: db.prepare("SELECT COUNT(*) as cnt FROM tasks WHERE status IN ('todo','in_progress') AND due_date < date('now')").get().cnt, totalRacks: db.prepare('SELECT COUNT(*) as cnt FROM racks').get().cnt, totalDiagrams: db.prepare('SELECT COUNT(*) as cnt FROM data_diagrams').get().cnt, totalNetworkDevices: db.prepare('SELECT COUNT(*) as cnt FROM network_devices').get().cnt, totalHours: db.prepare('SELECT COALESCE(SUM(hours),0) as h FROM time_entries').get().h, billableHours: db.prepare('SELECT COALESCE(SUM(hours),0) as h FROM time_entries WHERE billable=1').get().h, totalInvoiced: db.prepare("SELECT COALESCE(SUM(total),0) as t FROM invoices WHERE status != 'draft'").get().t, totalPaid: db.prepare("SELECT COALESCE(SUM(total),0) as t FROM invoices WHERE status='paid'").get().t, totalOutstanding: db.prepare("SELECT COALESCE(SUM(total),0) as t FROM invoices WHERE status IN ('sent','overdue')").get().t }; try { stats.datacenters = db.prepare('SELECT COUNT(*) as cnt FROM datacenters').get().cnt; } catch (e) { stats.datacenters = 0; } try { stats.marketCompanies = db.prepare('SELECT COUNT(*) as cnt FROM tech_companies').get().cnt; } catch (e) { stats.marketCompanies = 0; } const recentEngagements = db.prepare(` SELECT e.*, c.name as client_name FROM engagements e JOIN clients c ON c.id = e.client_id ORDER BY e.updated_at DESC LIMIT 8 `).all(); const upcomingTasks = db.prepare(` SELECT t.*, e.title as engagement_title, c.name as client_name FROM tasks t JOIN engagements e ON e.id = t.engagement_id JOIN clients c ON c.id = e.client_id WHERE t.status IN ('todo','in_progress') ORDER BY t.due_date IS NULL, t.due_date, CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END LIMIT 10 `).all(); const recentInvoices = db.prepare(` SELECT i.*, c.name as client_name FROM invoices i JOIN clients c ON c.id = i.client_id ORDER BY i.created_at DESC LIMIT 6 `).all(); const engagementByType = db.prepare('SELECT type, COUNT(*) as cnt FROM engagements GROUP BY type ORDER BY cnt DESC').all(); const engagementByStatus = db.prepare('SELECT status, COUNT(*) as cnt FROM engagements GROUP BY status ORDER BY cnt DESC').all(); const clientsByStatus = db.prepare('SELECT status, COUNT(*) as cnt FROM clients GROUP BY status').all(); const invoicesByStatus = db.prepare('SELECT status, COUNT(*) as cnt, COALESCE(SUM(total),0) as total FROM invoices GROUP BY status').all(); // Uren per maand (afgelopen 6 maanden) voor grafiek const hoursByMonth = db.prepare(` SELECT strftime('%Y-%m', date) as month, SUM(hours) as hours, SUM(CASE WHEN billable=1 THEN hours ELSE 0 END) as billable FROM time_entries WHERE date >= date('now', '-6 months', 'start of month') GROUP BY month ORDER BY month `).all(); // Omzet per maand (afgelopen 6 maanden) const revenueByMonth = db.prepare(` SELECT strftime('%Y-%m', date) as month, SUM(CASE WHEN status='paid' THEN total ELSE 0 END) as paid, SUM(CASE WHEN status IN ('sent','overdue') THEN total ELSE 0 END) as outstanding FROM invoices WHERE date >= date('now', '-6 months', 'start of month') GROUP BY month ORDER BY month `).all(); const activeAdvice = db.prepare(` SELECT a.*, c.name as client_name FROM advice_log a JOIN clients c ON c.id = a.client_id WHERE a.dismissed = 0 ORDER BY CASE a.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 ELSE 3 END, a.created_at DESC LIMIT 8 `).all(); const recentAudit = db.prepare('SELECT * FROM audit_log ORDER BY created_at DESC LIMIT 8').all(); let servicesStatus = {}; try { servicesStatus = await getServicesStatus(); } catch (e) { servicesStatus = {}; } res.json({ stats, clients, recentEngagements, upcomingTasks, recentInvoices, engagementByType, engagementByStatus, clientsByStatus, invoicesByStatus, hoursByMonth, revenueByMonth, activeAdvice, recentAudit, servicesStatus, settings: { brand_name: getSettings().brand_name, showcase_url: getSettings().showcase_url } }); } catch (e) { res.status(500).json({ error: e.message }); } }); module.exports = router;