/** * TASKMATE - Erweiterte Kontakte API * =================================== * REST API für Kontakte mit Institutionen */ const router = require('express').Router(); const { getDb } = require('../database'); const logger = require('../utils/logger'); // =================================== // INSTITUTIONEN // =================================== // Alle Institutionen abrufen router.get('/institutions', (req, res) => { try { const db = getDb(); const { project_id, search, type } = req.query; let query = 'SELECT * FROM institutions WHERE 1=1'; const params = []; if (project_id) { query += ' AND project_id = ?'; params.push(project_id); } if (search) { query += ' AND (name LIKE ? OR description LIKE ? OR tags LIKE ?)'; const searchPattern = `%${search}%`; params.push(searchPattern, searchPattern, searchPattern); } if (type) { query += ' AND type = ?'; params.push(type); } query += ' ORDER BY name ASC'; const institutions = db.prepare(query).all(...params); res.json({ success: true, data: institutions }); } catch (error) { logger.error('Fehler beim Abrufen der Institutionen:', error); res.status(500).json({ success: false, error: error.message }); } }); // Institution erstellen router.post('/institutions', (req, res) => { // Basis-Validierung if (!req.body.name || !req.body.project_id) { return res.status(400).json({ success: false, error: 'Name und Projekt-ID sind erforderlich' }); } try { const db = getDb(); const stmt = db.prepare(` INSERT INTO institutions ( name, type, industry, website, logo_url, description, main_address, main_postal_code, main_city, main_state, main_country, trade_register, notes, tags, created_by, project_id ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `); const result = stmt.run( req.body.name, req.body.type || null, req.body.industry || null, req.body.website || null, req.body.logo_url || null, req.body.description || null, req.body.main_address || null, req.body.main_postal_code || null, req.body.main_city || null, req.body.main_state || null, req.body.main_country || null, req.body.trade_register || null, req.body.notes || null, req.body.tags || null, req.userId, req.body.project_id ); const institution = db.prepare('SELECT * FROM institutions WHERE id = ?').get(result.lastInsertRowid); // Socket.io Event req.app.get('io').to(`project-${req.body.project_id}`).emit('institution:created', institution); res.status(201).json({ success: true, data: institution }); } catch (error) { logger.error('Fehler beim Erstellen der Institution:', error); res.status(500).json({ success: false, error: error.message }); } }); // Institution aktualisieren router.put('/institutions/:id', (req, res) => { try { const db = getDb(); const stmt = db.prepare(` UPDATE institutions SET name = ?, type = ?, industry = ?, website = ?, logo_url = ?, description = ?, main_address = ?, main_postal_code = ?, main_city = ?, main_state = ?, main_country = ?, trade_register = ?, notes = ?, tags = ? WHERE id = ? `); stmt.run( req.body.name, req.body.type, req.body.industry, req.body.website, req.body.logo_url, req.body.description, req.body.main_address, req.body.main_postal_code, req.body.main_city, req.body.main_state, req.body.main_country, req.body.trade_register, req.body.notes, req.body.tags, req.params.id ); const institution = db.prepare('SELECT * FROM institutions WHERE id = ?').get(req.params.id); // Socket.io Event req.app.get('io').to(`project-${institution.project_id}`).emit('institution:updated', institution); res.json({ success: true, data: institution }); } catch (error) { logger.error('Fehler beim Aktualisieren der Institution:', error); res.status(500).json({ success: false, error: error.message }); } }); // =================================== // KONTAKTE (PERSONEN) // =================================== // Alle Kontakte abrufen router.get('/contacts', (req, res) => { try { const db = getDb(); const { project_id, institution_id, search, is_active } = req.query; let query = ` SELECT c.*, i.name as institution_name FROM contacts_extended c LEFT JOIN institutions i ON c.institution_id = i.id WHERE 1=1 `; const params = []; if (project_id) { query += ' AND c.project_id = ?'; params.push(project_id); } if (institution_id) { query += ' AND c.institution_id = ?'; params.push(institution_id); } if (search) { query += ' AND (c.first_name LIKE ? OR c.last_name LIKE ? OR c.display_name LIKE ? OR c.tags LIKE ?)'; const searchPattern = `%${search}%`; params.push(searchPattern, searchPattern, searchPattern, searchPattern); } if (is_active !== undefined) { query += ' AND c.is_active = ?'; params.push(is_active === 'true' ? 1 : 0); } query += ' ORDER BY c.last_name, c.first_name ASC'; const contacts = db.prepare(query).all(...params); res.json({ success: true, data: contacts }); } catch (error) { logger.error('Fehler beim Abrufen der Kontakte:', error); res.status(500).json({ success: false, error: error.message }); } }); // Kontakt mit allen Details abrufen router.get('/contacts/:id/full', (req, res) => { try { const db = getDb(); // Basis-Kontaktdaten const contact = db.prepare(` SELECT c.*, i.name as institution_name FROM contacts_extended c LEFT JOIN institutions i ON c.institution_id = i.id WHERE c.id = ? `).get(req.params.id); if (!contact) { return res.status(404).json({ success: false, error: 'Kontakt nicht gefunden' }); } // Kontaktdetails contact.details = db.prepare(` SELECT * FROM contact_details WHERE contact_id = ? ORDER BY is_primary DESC, type ASC `).all(req.params.id); // Weitere Institutionen contact.institutions = db.prepare(` SELECT i.*, r.position, r.department, r.start_date, r.end_date, r.is_primary FROM person_institution_relations r JOIN institutions i ON r.institution_id = i.id WHERE r.contact_id = ? ORDER BY r.is_primary DESC, r.start_date DESC `).all(req.params.id); // Kategorien contact.categories = db.prepare(` SELECT c.* FROM contact_categories c JOIN contact_category_assignments a ON c.id = a.category_id WHERE a.contact_id = ? `).all(req.params.id); // Letzte Interaktionen contact.recent_interactions = db.prepare(` SELECT * FROM contact_interactions WHERE contact_id = ? ORDER BY date DESC LIMIT 10 `).all(req.params.id); res.json({ success: true, data: contact }); } catch (error) { logger.error('Fehler beim Abrufen des Kontakts:', error); res.status(500).json({ success: false, error: error.message }); } }); // Kontakt erstellen router.post('/contacts', (req, res) => { // Basis-Validierung if (!req.body.first_name || !req.body.last_name || !req.body.project_id) { return res.status(400).json({ success: false, error: 'Vor- und Nachname sowie Projekt-ID sind erforderlich' }); } const db = getDb(); const transaction = db.transaction((data) => { try { // Kontakt erstellen const stmt = db.prepare(` INSERT INTO contacts_extended ( salutation, title, first_name, last_name, display_name, position, department, institution_id, notes, tags, avatar_url, is_active, created_by, project_id ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `); const result = stmt.run( data.salutation || null, data.title || null, data.first_name, data.last_name, data.display_name || `${data.first_name} ${data.last_name}`, data.position || null, data.department || null, data.institution_id || null, data.notes || null, data.tags || null, data.avatar_url || null, data.is_active !== false ? 1 : 0, req.userId, data.project_id ); const contactId = result.lastInsertRowid; // Kontaktdetails hinzufügen if (data.details && Array.isArray(data.details)) { const detailStmt = db.prepare(` INSERT INTO contact_details ( contact_id, type, subtype, value, label, is_primary, is_public ) VALUES (?, ?, ?, ?, ?, ?, ?) `); for (const detail of data.details) { detailStmt.run( contactId, detail.type, detail.subtype || null, detail.value, detail.label || null, detail.is_primary ? 1 : 0, detail.is_public !== false ? 1 : 0 ); } } // Kategorien zuweisen if (data.categories && Array.isArray(data.categories)) { const categoryStmt = db.prepare(` INSERT INTO contact_category_assignments (contact_id, category_id) VALUES (?, ?) `); for (const categoryId of data.categories) { categoryStmt.run(contactId, categoryId); } } return contactId; } catch (error) { throw error; } }); try { const contactId = transaction(req.body); const contact = db.prepare('SELECT * FROM contacts_extended WHERE id = ?').get(contactId); // Socket.io Event req.app.get('io').to(`project-${req.body.project_id}`).emit('contact:created', contact); res.status(201).json({ success: true, data: contact }); } catch (error) { logger.error('Fehler beim Erstellen des Kontakts:', error); res.status(500).json({ success: false, error: error.message }); } }); // =================================== // KONTAKTDETAILS // =================================== // Kontaktdetail hinzufügen router.post('/contact-details', (req, res) => { // Basis-Validierung if (!req.body.type || !req.body.value) { return res.status(400).json({ success: false, error: 'Typ und Wert sind erforderlich' }); } try { const db = getDb(); const stmt = db.prepare(` INSERT INTO contact_details ( contact_id, institution_id, type, subtype, value, label, is_primary, is_public, notes ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) `); const result = stmt.run( req.body.contact_id || null, req.body.institution_id || null, req.body.type, req.body.subtype || null, req.body.value, req.body.label || null, req.body.is_primary ? 1 : 0, req.body.is_public !== false ? 1 : 0, req.body.notes || null ); const detail = db.prepare('SELECT * FROM contact_details WHERE id = ?').get(result.lastInsertRowid); // Socket.io Event const eventType = req.body.contact_id ? 'contact' : 'institution'; req.app.get('io').emit(`${eventType}:detail:added`, detail); res.status(201).json({ success: true, data: detail }); } catch (error) { logger.error('Fehler beim Hinzufügen des Kontaktdetails:', error); res.status(500).json({ success: false, error: error.message }); } }); // =================================== // PERSON-INSTITUTION BEZIEHUNGEN // =================================== // Beziehung erstellen router.post('/relations', (req, res) => { // Basis-Validierung if (!req.body.contact_id || !req.body.institution_id) { return res.status(400).json({ success: false, error: 'Kontakt-ID und Institutions-ID sind erforderlich' }); } try { const db = getDb(); const stmt = db.prepare(` INSERT INTO person_institution_relations ( contact_id, institution_id, position, department, start_date, end_date, is_primary, notes ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `); const result = stmt.run( req.body.contact_id, req.body.institution_id, req.body.position || null, req.body.department || null, req.body.start_date || null, req.body.end_date || null, req.body.is_primary ? 1 : 0, req.body.notes || null ); const relation = db.prepare('SELECT * FROM person_institution_relations WHERE id = ?') .get(result.lastInsertRowid); // Socket.io Event req.app.get('io').emit('relation:created', relation); res.status(201).json({ success: true, data: relation }); } catch (error) { logger.error('Fehler beim Erstellen der Beziehung:', error); res.status(500).json({ success: false, error: error.message }); } }); // =================================== // KATEGORIEN // =================================== // Alle Kategorien abrufen router.get('/categories', (req, res) => { try { const db = getDb(); const { project_id } = req.query; let query = 'SELECT * FROM contact_categories'; const params = []; if (project_id) { query += ' WHERE project_id = ?'; params.push(project_id); } query += ' ORDER BY name ASC'; const categories = db.prepare(query).all(...params); res.json({ success: true, data: categories }); } catch (error) { logger.error('Fehler beim Abrufen der Kategorien:', error); res.status(500).json({ success: false, error: error.message }); } }); // =================================== // INTERAKTIONEN // =================================== // Interaktion hinzufügen router.post('/interactions', (req, res) => { // Basis-Validierung if (!req.body.type) { return res.status(400).json({ success: false, error: 'Typ ist erforderlich' }); } try { const db = getDb(); const stmt = db.prepare(` INSERT INTO contact_interactions ( contact_id, institution_id, type, subject, content, date, duration_minutes, task_id, created_by ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) `); const result = stmt.run( req.body.contact_id || null, req.body.institution_id || null, req.body.type, req.body.subject || null, req.body.content || null, req.body.date || new Date().toISOString(), req.body.duration_minutes || null, req.body.task_id || null, req.userId ); const interaction = db.prepare('SELECT * FROM contact_interactions WHERE id = ?') .get(result.lastInsertRowid); // Socket.io Event req.app.get('io').emit('interaction:created', interaction); res.status(201).json({ success: true, data: interaction }); } catch (error) { logger.error('Fehler beim Hinzufügen der Interaktion:', error); res.status(500).json({ success: false, error: error.message }); } }); module.exports = router;