/** * CRM Controller * Multi-project CRM system for contacts, organizations, activities, and SLAs */ const UnifiedContact = require('../models/UnifiedContact.model'); const Organization = require('../models/Organization.model'); const ActivityTimeline = require('../models/ActivityTimeline.model'); const ResponseTemplate = require('../models/ResponseTemplate.model'); const SLATracking = require('../models/SLATracking.model'); /** * Get CRM dashboard statistics */ async function getDashboardStats(req, res) { try { const [contactStats, orgStats, slaStats, templateStats] = await Promise.all([ UnifiedContact.getStats(), Organization.getStats(), SLATracking.getStats({ project: 'tractatus' }), ResponseTemplate.getStats() ]); res.json({ success: true, stats: { contacts: contactStats, organizations: orgStats, sla: slaStats, templates: templateStats } }); } catch (error) { console.error('CRM dashboard stats error:', error); res.status(500).json({ success: false, error: 'Failed to load dashboard statistics' }); } } /** * List unified contacts */ async function listContacts(req, res) { try { const { project, status, tag, organization_id, limit = 50, skip = 0 } = req.query; const filters = {}; if (project) filters.project = project; if (status) filters.status = status; if (tag) filters.tag = tag; if (organization_id) filters.organization_id = organization_id; const contacts = await UnifiedContact.list(filters, { limit: parseInt(limit), skip: parseInt(skip) }); res.json({ success: true, total: contacts.length, contacts }); } catch (error) { console.error('List contacts error:', error); res.status(500).json({ success: false, error: 'Failed to list contacts' }); } } /** * Get single contact with full details */ async function getContact(req, res) { try { const { id } = req.params; const contact = await UnifiedContact.findById(id); if (!contact) { return res.status(404).json({ success: false, error: 'Contact not found' }); } // Get organization if linked let organization = null; if (contact.organization_id) { organization = await Organization.findById(contact.organization_id); } // Get activity timeline const activities = await ActivityTimeline.getByContact(id, { limit: 50 }); res.json({ success: true, contact, organization, activities }); } catch (error) { console.error('Get contact error:', error); res.status(500).json({ success: false, error: 'Failed to load contact' }); } } /** * List organizations */ async function listOrganizations(req, res) { try { const { type, country, project, status, tier, limit = 50, skip = 0 } = req.query; const filters = {}; if (type) filters.type = type; if (country) filters.country = country; if (project) filters.project = project; if (status) filters.status = status; if (tier) filters.tier = tier; const organizations = await Organization.list(filters, { limit: parseInt(limit), skip: parseInt(skip) }); res.json({ success: true, total: organizations.length, organizations }); } catch (error) { console.error('List organizations error:', error); res.status(500).json({ success: false, error: 'Failed to list organizations' }); } } /** * Get single organization with contacts and activity */ async function getOrganization(req, res) { try { const { id } = req.params; const organization = await Organization.findById(id); if (!organization) { return res.status(404).json({ success: false, error: 'Organization not found' }); } // Get contacts at this organization const contacts = await UnifiedContact.findByOrganization(id, { limit: 100 }); // Get activity timeline const activities = await ActivityTimeline.getByOrganization(id, { limit: 50 }); res.json({ success: true, organization, contacts, activities }); } catch (error) { console.error('Get organization error:', error); res.status(500).json({ success: false, error: 'Failed to load organization' }); } } /** * Get SLA dashboard */ async function getSLADashboard(req, res) { try { const { project = 'tractatus' } = req.query; const [stats, pending, approaching, breached] = await Promise.all([ SLATracking.getStats({ project }), SLATracking.getPending({ project, limit: 20 }), SLATracking.getApproachingBreach({ project }), SLATracking.getBreached({ project, limit: 20 }) ]); res.json({ success: true, stats, pending, approaching_breach: approaching, breached }); } catch (error) { console.error('SLA dashboard error:', error); res.status(500).json({ success: false, error: 'Failed to load SLA dashboard' }); } } /** * List response templates */ async function listTemplates(req, res) { try { const { category, language, project, tag, limit = 50, skip = 0 } = req.query; const filters = {}; if (category) filters.category = category; if (language) filters.language = language; if (project) filters.project = project; if (tag) filters.tag = tag; const templates = await ResponseTemplate.list(filters, { limit: parseInt(limit), skip: parseInt(skip) }); res.json({ success: true, total: templates.length, templates }); } catch (error) { console.error('List templates error:', error); res.status(500).json({ success: false, error: 'Failed to list templates' }); } } /** * Render template with variables */ async function renderTemplate(req, res) { try { const { id } = req.params; const { variables } = req.body; const rendered = await ResponseTemplate.render(id, variables || {}); res.json({ success: true, rendered }); } catch (error) { console.error('Render template error:', error); res.status(500).json({ success: false, error: 'Failed to render template' }); } } module.exports = { getDashboardStats, listContacts, getContact, listOrganizations, getOrganization, getSLADashboard, listTemplates, renderTemplate };