chore(ops): migrate clube67 to newwhats.clube67.com directory
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)
This commit is contained in:
@@ -1,235 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createLeadsRoutes = createLeadsRoutes;
|
||||
const express_1 = require("express");
|
||||
const auth_1 = require("../../../backend/src/middleware/auth");
|
||||
const rbac_1 = require("../../../backend/src/middleware/rbac");
|
||||
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 = [auth_1.authenticate, (0, rbac_1.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('/', auth_1.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', auth_1.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
|
||||
File diff suppressed because one or more lines are too long
@@ -1,247 +0,0 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { PluginContext } from '../../../backend/src/core/types';
|
||||
import { authenticate } from '../../../backend/src/middleware/auth';
|
||||
import { authorize } from '../../../backend/src/middleware/rbac';
|
||||
|
||||
const STATUS_VALUES = ['new', 'contacted', 'qualified', 'converted', 'lost'];
|
||||
const LEAD_TYPE_VALUES = ['cold', 'member'];
|
||||
|
||||
function translateLead(lead: any) {
|
||||
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',
|
||||
};
|
||||
}
|
||||
|
||||
export function createLeadsRoutes(ctx: PluginContext): Router {
|
||||
const router = 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: Request, res: Response) => {
|
||||
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: any) => 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: any) {
|
||||
console.error('Error fetching lead metrics:', err);
|
||||
res.status(500).json({ error: 'Erro ao buscar métricas' });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { partner_id, status, type, page = '1', limit = '20' } = req.query;
|
||||
const offset = (parseInt(page as string) - 1) * parseInt(limit as string);
|
||||
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 as string)) {
|
||||
query = query.where({ lead_type: type as string });
|
||||
}
|
||||
if (status && STATUS_VALUES.includes(status as string)) {
|
||||
query = query.where({ status: status as string });
|
||||
} else {
|
||||
query = query.where({ status: 'new' });
|
||||
}
|
||||
|
||||
const countResult = await query.clone().count('id as count');
|
||||
const leads = await query.limit(parseInt(limit as string)).offset(offset);
|
||||
res.json({ leads: leads.map(translateLead), total: Number(countResult[0].count), page: parseInt(page as string) });
|
||||
} catch (err: any) {
|
||||
res.status(500).json({ error: 'Erro ao listar leads' });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/:id', authenticate, async (req: Request, res: Response) => {
|
||||
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: any) {
|
||||
res.status(500).json({ error: 'Erro ao buscar lead' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/', ...protectedRoutes, async (req: Request, res: Response) => {
|
||||
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 as any).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: any) {
|
||||
res.status(500).json({ error: 'Erro ao criar lead' });
|
||||
}
|
||||
});
|
||||
|
||||
router.patch('/:id/status', ...protectedRoutes, async (req: Request, res: Response) => {
|
||||
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 as any).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: any) {
|
||||
res.status(500).json({ error: 'Erro ao atualizar status' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/:id/notes', ...protectedRoutes, async (req: Request, res: Response) => {
|
||||
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 as any).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: any) {
|
||||
res.status(500).json({ error: 'Erro ao salvar nota' });
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const routes_1 = require("./backend/routes");
|
||||
const manifest_json_1 = __importDefault(require("./manifest.json"));
|
||||
const plugin = {
|
||||
manifest: manifest_json_1.default,
|
||||
async activate(ctx) {
|
||||
const router = (0, routes_1.createLeadsRoutes)(ctx);
|
||||
ctx.app.use('/api/leads', router);
|
||||
ctx.hooks.register('user:register', async (data) => {
|
||||
await ctx.db('leads').insert({
|
||||
user_id: data.userId,
|
||||
source: 'registration',
|
||||
status: 'new',
|
||||
lead_type: 'member',
|
||||
}).catch(() => { });
|
||||
});
|
||||
ctx.logger.info('Leads routes registered');
|
||||
},
|
||||
async deactivate(ctx) { ctx.logger.info('Leads deactivated'); },
|
||||
};
|
||||
exports.default = plugin;
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../plugins/leads/index.ts"],"names":[],"mappings":";;;;;AACA,6CAAqD;AACrD,oEAAuC;AAEvC,MAAM,MAAM,GAAmB;IAC3B,QAAQ,EAAE,uBAAe;IACzB,KAAK,CAAC,QAAQ,CAAC,GAAkB;QAC7B,MAAM,MAAM,GAAG,IAAA,0BAAiB,EAAC,GAAG,CAAC,CAAC;QACtC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAClC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,eAAe,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;YACpD,MAAM,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;gBACzB,OAAO,EAAE,IAAI,CAAC,MAAM;gBACpB,MAAM,EAAE,cAAc;gBACtB,MAAM,EAAE,KAAK;gBACb,SAAS,EAAE,QAAQ;aACtB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,GAAkB,IAAmB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;CAChG,CAAC;AACF,kBAAe,MAAM,CAAC"}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { PluginInstance, PluginContext } from '../../backend/src/core/types';
|
||||
import { createLeadsRoutes } from './backend/routes';
|
||||
import manifest from './manifest.json';
|
||||
|
||||
const plugin: PluginInstance = {
|
||||
manifest: manifest as any,
|
||||
async activate(ctx: PluginContext): Promise<void> {
|
||||
const router = createLeadsRoutes(ctx);
|
||||
ctx.app.use('/api/leads', router);
|
||||
ctx.hooks.register('user:register', async (data: any) => {
|
||||
await ctx.db('leads').insert({
|
||||
user_id: data.userId,
|
||||
source: 'registration',
|
||||
status: 'new',
|
||||
lead_type: 'member',
|
||||
}).catch(() => { });
|
||||
});
|
||||
ctx.logger.info('Leads routes registered');
|
||||
},
|
||||
async deactivate(ctx: PluginContext): Promise<void> { ctx.logger.info('Leads deactivated'); },
|
||||
};
|
||||
export default plugin;
|
||||
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"name": "leads",
|
||||
"displayName": "Leads & CRM",
|
||||
"version": "1.0.0",
|
||||
"description": "Captura, qualificação e acompanhamento de leads.",
|
||||
"author": "Clube67",
|
||||
"category": "business",
|
||||
"enabled": true,
|
||||
"canDisable": true,
|
||||
"dependencies": [
|
||||
"core-auth",
|
||||
"partners"
|
||||
],
|
||||
"backend": {
|
||||
"routePrefix": "/api/leads",
|
||||
"hasMigrations": true
|
||||
},
|
||||
"frontend": {
|
||||
"menuItems": [
|
||||
{
|
||||
"id": "leads-list",
|
||||
"label": "Leads",
|
||||
"icon": "UserPlus",
|
||||
"href": "/admin/leads",
|
||||
"roles": [
|
||||
"super_admin"
|
||||
],
|
||||
"order": 35
|
||||
},
|
||||
{
|
||||
"id": "leads-partner",
|
||||
"label": "Meus Leads",
|
||||
"icon": "UserPlus",
|
||||
"href": "/partner/leads",
|
||||
"roles": [
|
||||
"partner_admin"
|
||||
],
|
||||
"order": 35
|
||||
}
|
||||
]
|
||||
},
|
||||
"hooks": {
|
||||
"subscribes": [
|
||||
"user:register"
|
||||
],
|
||||
"emits": [
|
||||
"lead:created",
|
||||
"lead:converted",
|
||||
"lead:lost"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user