tractatus/src/controllers/crm.controller.js
TheFlow 42a7d95c0f fix: Resolve ESLint errors breaking CI
- audit.controller.js: Remove unused fs/path imports, add AuditLog import,
  fix indentation, use const for userCostFactors, use property shorthand
- crm.controller.js: Remove unused Contact, MediaInquiry, CaseSubmission imports
- cases.controller.js: Remove unused GovernanceLog, BoundaryEnforcer imports
- DiskMetrics.model.js: Use template literals instead of string concatenation
- framework-content-analysis.controller.js: Use template literals, prefix
  unused destructured vars with underscore
- feedback.controller.js: Use template literal for string concat
- DeliberationSession.model.js: Fix line length by moving comments to own lines

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:20:50 +13:00

284 lines
6.4 KiB
JavaScript

/**
* 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
};