"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createLeadsRoutes = createLeadsRoutes; const express_1 = require("express"); let authenticate; let authorize; if (process.env.NODE_ENV === 'production') { authenticate = require('../../../dist/middleware/auth').authMiddleware; authorize = require('../../../dist/middleware/rbac').adminMiddleware; } else { authenticate = require('../../../backend/src/middleware/auth').authMiddleware; authorize = require('../../../backend/src/middleware/rbac').adminMiddleware; } const STATUS_VALUES = ['new', 'contacted', 'qualified', 'converted', 'lost']; const LEAD_TYPE_VALUES = ['cold', 'member']; function translateLead(lead) { if (!lead) return lead; return { ...lead, source: lead.source || 'manual', partner_id: lead.partner_id || null, benefit_id: lead.benefit_id || null, user_id: lead.user_id || null, lead_type: lead.lead_type || 'cold', }; } function createLeadsRoutes(ctx) { const router = (0, express_1.Router)(); const { db } = ctx; const protectedRoutes = [authenticate, authorize(['super_admin', 'admin'])]; // GET /api/leads/metrics — Global metrics for super admin router.get('/metrics', ...protectedRoutes, async (req, res) => { try { const [totalRow] = await db('leads').count('id as total'); const total = Number(totalRow.total); // Today const [todayRow] = await db('leads') .count('id as total') .whereRaw('DATE(created_at) = CURDATE()'); const today = Number(todayRow.total); // By status const byStatus = await db('leads') .select('status') .count('id as total') .groupBy('status'); // By lead type const byType = await db('leads') .select('lead_type') .count('id as total') .groupBy('lead_type'); // Converted count for conversion rate const converted = byStatus.find((r) => r.status === 'converted'); const conversionRate = total > 0 ? ((Number(converted?.total || 0) / total) * 100).toFixed(1) : '0.0'; // By city (via users join) const byCity = await db('leads') .join('users', 'leads.user_id', 'users.id') .select('users.city', 'users.state') .count('leads.id as total') .whereNotNull('users.city') .groupBy('users.city', 'users.state') .orderBy('total', 'desc') .limit(10); // By neighborhood (via users join) const byNeighborhood = await db('leads') .join('users', 'leads.user_id', 'users.id') .select('users.neighborhood', 'users.city', 'users.state') .count('leads.id as total') .whereNotNull('users.neighborhood') .groupBy('users.neighborhood', 'users.city', 'users.state') .orderBy('total', 'desc') .limit(10); // By partner const byPartner = await db('leads') .join('partners', 'leads.partner_id', 'partners.id') .select('partners.id as partner_id', 'partners.company_name') .count('leads.id as total') .groupBy('partners.id', 'partners.company_name') .orderBy('total', 'desc') .limit(10); // Recent leads const recent = await db('leads') .leftJoin('users', 'leads.user_id', 'users.id') .leftJoin('partners', 'leads.partner_id', 'partners.id') .select('leads.id', 'leads.name', 'leads.email', 'leads.status', 'leads.lead_type', 'leads.source', 'leads.created_at', 'partners.company_name as partner_name', 'users.city', 'users.neighborhood') .orderBy('leads.created_at', 'desc') .limit(15); res.json({ total, today, conversion_rate: conversionRate, by_status: byStatus, by_type: byType, by_city: byCity, by_neighborhood: byNeighborhood, by_partner: byPartner, recent, }); } catch (err) { console.error('Error fetching lead metrics:', err); res.status(500).json({ error: 'Erro ao buscar métricas' }); } }); router.get('/', authenticate, async (req, res) => { try { const { partner_id, status, type, page = '1', limit = '20' } = req.query; const offset = (parseInt(page) - 1) * parseInt(limit); const userRole = req.user?.role; const userPartner = req.user?.partner_id; let query = db('leads').orderBy('created_at', 'desc'); if (partner_id) query = query.where({ partner_id }); if (userRole === 'partner' && userPartner) { query = query.where({ partner_id: userPartner }); } if (type && LEAD_TYPE_VALUES.includes(type)) { query = query.where({ lead_type: type }); } if (status && STATUS_VALUES.includes(status)) { query = query.where({ status: status }); } else { query = query.where({ status: 'new' }); } const countResult = await query.clone().count('id as count'); const leads = await query.limit(parseInt(limit)).offset(offset); res.json({ leads: leads.map(translateLead), total: Number(countResult[0].count), page: parseInt(page) }); } catch (err) { res.status(500).json({ error: 'Erro ao listar leads' }); } }); router.get('/:id', authenticate, async (req, res) => { try { const id = Number(req.params.id); const lead = await db('leads').where({ id }).first(); if (!lead) return res.status(404).json({ error: 'Lead não encontrado' }); if (req.user?.role === 'partner' && req.user.partner_id && lead.partner_id !== req.user.partner_id) { return res.status(403).json({ error: 'Acesso negado' }); } const history = await db('lead_history').where({ lead_id: id }).orderBy('created_at', 'desc'); res.json({ lead: translateLead(lead), history }); } catch (err) { res.status(500).json({ error: 'Erro ao buscar lead' }); } }); router.post('/', ...protectedRoutes, async (req, res) => { try { const { name, email, phone, partner_id, source, notes, benefit_id, assigned_to, user_id } = req.body; const numericUserId = user_id ? Number(user_id) : null; const parsedUserId = Number.isInteger(numericUserId) ? numericUserId : null; const leadType = parsedUserId ? 'member' : 'cold'; const [id] = await db('leads').insert({ name, email, phone, partner_id: partner_id || null, benefit_id: benefit_id || null, user_id: parsedUserId, lead_type: leadType, source: source || 'manual', notes: notes || null, assigned_to: assigned_to || null, status: 'new', owner: 'clube67', }); await db('lead_history').insert({ lead_id: id, action: 'created', new_value: 'new', created_by: req.user?.id || null, }); await ctx.hooks.emit('lead:created', { id, email, partner_id }); const lead = await db('leads').where({ id }).first(); res.status(201).json(translateLead(lead)); } catch (err) { res.status(500).json({ error: 'Erro ao criar lead' }); } }); router.patch('/:id/status', ...protectedRoutes, async (req, res) => { try { const id = Number(req.params.id); const { status } = req.body; if (!STATUS_VALUES.includes(status)) return res.status(400).json({ error: 'Status inválido' }); const lead = await db('leads').where({ id }).first(); if (!lead) return res.status(404).json({ error: 'Lead não encontrado' }); await db('leads').where({ id }).update({ status, updated_at: db.fn.now() }); await db('lead_history').insert({ lead_id: id, action: 'status', old_value: lead.status, new_value: status, created_by: req.user?.id || null, }); if (status === 'converted') await ctx.hooks.emit('lead:converted', { id }); if (status === 'lost') await ctx.hooks.emit('lead:lost', { id }); const updated = await db('leads').where({ id }).first(); res.json(translateLead(updated)); } catch (err) { res.status(500).json({ error: 'Erro ao atualizar status' }); } }); router.post('/:id/notes', ...protectedRoutes, async (req, res) => { try { const id = Number(req.params.id); const { note } = req.body; if (!note) return res.status(400).json({ error: 'Nota obrigatória' }); const lead = await db('leads').where({ id }).first(); if (!lead) return res.status(404).json({ error: 'Lead não encontrado' }); await db('lead_history').insert({ lead_id: id, action: 'note', old_value: lead.notes, new_value: lead.notes ? `${lead.notes}\n${note}` : note, note, created_by: req.user?.id || null, }); await db('leads').where({ id }).update({ notes: db.raw('CONCAT(COALESCE(notes,""), "\n", ?)', [note]), updated_at: db.fn.now(), }); const updated = await db('leads').where({ id }).first(); res.json(translateLead(updated)); } catch (err) { res.status(500).json({ error: 'Erro ao salvar nota' }); } }); return router; } //# sourceMappingURL=routes.js.map