185 lines
7.9 KiB
JavaScript
185 lines
7.9 KiB
JavaScript
const pool = require('./db.js');
|
|
const crypto = require('crypto');
|
|
|
|
const PRODUCTS = [
|
|
"Pneu Aro 13 175/70R13",
|
|
"Pneu Aro 14 175/70R14",
|
|
"Pneu Aro 15 195/60R15",
|
|
"Pneu Aro 16 205/55R16",
|
|
"Pneu Aro 17 225/45R17",
|
|
"Alinhamento e Balanceamento",
|
|
"Revisão do Sistema de Arrefecimento",
|
|
"Manutenção de Freios",
|
|
"Troca de Óleo e Filtros",
|
|
"Limpeza de Bico"
|
|
];
|
|
|
|
const DEFAULT_ORIGINS = ['WhatsApp', 'Instagram', 'Website', 'LinkedIn', 'Indicação'];
|
|
const DEFAULT_FUNNELS = ['Sem atendimento', 'Identificação', 'Negociação', 'Ganhos', 'Perdidos'];
|
|
|
|
const FIRST_NAMES = ["Ana", "Bruno", "Carlos", "Daniela", "Eduardo", "Fernanda", "Gabriel", "Helena", "Igor", "Julia", "Lucas", "Mariana", "Nicolas", "Olivia", "Pedro", "Quintino", "Rafael", "Sofia", "Thiago", "Ursula", "Victor", "Wagner", "Xuxa", "Yuri", "Zeca", "Amanda", "Beto", "Camila", "Diogo", "Elisa", "Fabio", "Gisele", "Henrique", "Isabela", "Joao", "Karla"];
|
|
const LAST_NAMES = ["Silva", "Santos", "Oliveira", "Souza", "Rodrigues", "Ferreira", "Alves", "Pereira", "Lima", "Gomes", "Costa", "Ribeiro", "Martins", "Carvalho", "Almeida", "Lopes", "Soares", "Fernandes", "Vieira", "Barbosa"];
|
|
|
|
function getRandomItem(arr) {
|
|
return arr[Math.floor(Math.random() * arr.length)];
|
|
}
|
|
|
|
function getRandomInt(min, max) {
|
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
}
|
|
|
|
function generateUniqueNames(count) {
|
|
const names = new Set();
|
|
while (names.size < count) {
|
|
names.add(`${getRandomItem(FIRST_NAMES)} ${getRandomItem(LAST_NAMES)}`);
|
|
}
|
|
return Array.from(names);
|
|
}
|
|
|
|
async function run() {
|
|
try {
|
|
console.log('🔄 Iniciando geração de dados...');
|
|
|
|
// 1. Encontrar o tenant "teste"
|
|
const [tenants] = await pool.query(`SELECT id FROM tenants WHERE slug = 'teste' OR name LIKE '%teste%' LIMIT 1`);
|
|
if (tenants.length === 0) {
|
|
console.log('❌ Tenant "teste" não encontrado.');
|
|
process.exit(1);
|
|
}
|
|
const tenantId = tenants[0].id;
|
|
console.log(`✅ Tenant "teste" encontrado: ${tenantId}`);
|
|
|
|
// Pegar origens e funis dinâmicos se existirem
|
|
let origins = [...DEFAULT_ORIGINS];
|
|
let funnels = [...DEFAULT_FUNNELS];
|
|
let originGroupId = null;
|
|
let funnelId = null;
|
|
|
|
try {
|
|
const [originGroups] = await pool.query(`SELECT id FROM origin_groups WHERE tenant_id = ? LIMIT 1`, [tenantId]);
|
|
if (originGroups.length > 0) {
|
|
originGroupId = originGroups[0].id;
|
|
const [originItems] = await pool.query(`SELECT name FROM origin_items WHERE origin_group_id = ?`, [originGroupId]);
|
|
if (originItems.length > 0) origins = originItems.map(o => o.name);
|
|
}
|
|
const [funnelGroups] = await pool.query(`SELECT id FROM funnels WHERE tenant_id = ? LIMIT 1`, [tenantId]);
|
|
if (funnelGroups.length > 0) {
|
|
funnelId = funnelGroups[0].id;
|
|
const [funnelStages] = await pool.query(`SELECT name FROM funnel_stages WHERE funnel_id = ?`, [funnelId]);
|
|
if (funnelStages.length > 0) funnels = funnelStages.map(f => f.name);
|
|
}
|
|
} catch (e) {
|
|
console.log('Aviso: Usando origens/funis padrão devido a erro na busca de dinâmicos.');
|
|
}
|
|
|
|
// 2. Limpar dados existentes do tenant (exceto admin e tenant em si)
|
|
console.log('🧹 Limpando attendances antigas...');
|
|
await pool.query(`DELETE FROM attendances WHERE tenant_id = ?`, [tenantId]);
|
|
|
|
console.log('🧹 Limpando usuários antigos (exceto admin)...');
|
|
await pool.query(`DELETE FROM users WHERE tenant_id = ? AND role != 'admin'`, [tenantId]);
|
|
|
|
console.log('🧹 Limpando times antigos...');
|
|
await pool.query(`DELETE FROM teams WHERE tenant_id = ?`, [tenantId]);
|
|
|
|
// 3. Criar 5 times
|
|
const teams = [];
|
|
for (let i = 1; i <= 5; i++) {
|
|
const teamId = crypto.randomUUID();
|
|
await pool.query(`INSERT INTO teams (id, tenant_id, name, description, origin_group_id, funnel_id) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
[teamId, tenantId, `Equipe Vendas ${i}`, `Equipe responsável pela região ${i}`, originGroupId, funnelId]);
|
|
teams.push(teamId);
|
|
}
|
|
console.log('✅ 5 times criados.');
|
|
|
|
// 4. Criar 36 usuários (1 manager por time = 5 managers, 31 agents)
|
|
const users = [];
|
|
const roles = ['manager', 'manager', 'manager', 'manager', 'manager', ...Array(31).fill('agent')];
|
|
const uniqueNames = generateUniqueNames(36);
|
|
|
|
// Distribuir usuários entre os times
|
|
for (let i = 0; i < 36; i++) {
|
|
const userId = crypto.randomUUID();
|
|
const role = roles[i];
|
|
const teamId = teams[i % 5];
|
|
const name = uniqueNames[i];
|
|
const email = `${name.split(' ')[0].toLowerCase()}.${name.split(' ')[1].toLowerCase()}.${i}@teste.com`;
|
|
|
|
await pool.query(
|
|
`INSERT INTO users (id, tenant_id, team_id, name, email, password_hash, role, status) VALUES (?, ?, ?, ?, ?, ?, ?, 'active')`,
|
|
[userId, tenantId, teamId, name, email, 'dummy_hash_not_for_login', role]
|
|
);
|
|
users.push(userId);
|
|
}
|
|
console.log('✅ 36 usuários criados (5 managers, 31 agents).');
|
|
|
|
// 5. Gerar attendances (01/01/2026 até 22/04/2026)
|
|
// 112 dias totais
|
|
const startDate = new Date('2026-01-01T08:00:00Z');
|
|
const endDate = new Date('2026-04-22T18:00:00Z');
|
|
|
|
let currentDay = new Date(startDate);
|
|
const attendancesToInsert = [];
|
|
|
|
console.log('⏳ Gerando dados de attendances em memória...');
|
|
while (currentDay <= endDate) {
|
|
// Pular domingos para dar mais realismo, ou deixar todos os dias? Vamos deixar todos os dias.
|
|
for (const userId of users) {
|
|
const numAttendances = getRandomInt(3, 5); // 3 a 5 por dia por usuário
|
|
for (let a = 0; a < numAttendances; a++) {
|
|
const createdAt = new Date(currentDay);
|
|
createdAt.setHours(getRandomInt(8, 17), getRandomInt(0, 59), getRandomInt(0, 59));
|
|
|
|
const isConverted = Math.random() > 0.7; // 30% conversão
|
|
const reqProduct = getRandomItem(PRODUCTS);
|
|
const soldProduct = isConverted ? reqProduct : null;
|
|
|
|
let score = getRandomInt(40, 100);
|
|
if (isConverted) score = getRandomInt(85, 100);
|
|
|
|
attendancesToInsert.push([
|
|
crypto.randomUUID(),
|
|
tenantId,
|
|
userId,
|
|
isConverted ? "Venda efetuada com sucesso" : "Cliente não finalizou a compra",
|
|
isConverted ? "Cliente comprou com sucesso. Excelente atendimento." : "Cliente achou o valor alto e desistiu.",
|
|
score,
|
|
getRandomInt(1, 45), // first_response_time_min
|
|
getRandomInt(10, 120), // handling_time_min
|
|
isConverted ? 'Ganhos' : (Math.random() > 0.5 ? 'Perdidos' : getRandomItem(funnels)), // funnel_stage
|
|
getRandomItem(origins), // origin
|
|
reqProduct,
|
|
soldProduct,
|
|
isConverted ? 1 : 0,
|
|
JSON.stringify(isConverted ? [] : ["Faltou oferecer desconto", "Demora no primeiro contato"]),
|
|
JSON.stringify(["Melhorar rapport inicial"]),
|
|
createdAt
|
|
]);
|
|
}
|
|
}
|
|
currentDay.setDate(currentDay.getDate() + 1);
|
|
}
|
|
|
|
// 6. Inserir em lotes
|
|
console.log(`⏳ Inserindo ${attendancesToInsert.length} attendances no banco em lotes...`);
|
|
const batchSize = 1000;
|
|
for (let i = 0; i < attendancesToInsert.length; i += batchSize) {
|
|
const batch = attendancesToInsert.slice(i, i + batchSize);
|
|
await pool.query(
|
|
`INSERT INTO attendances
|
|
(id, tenant_id, user_id, title, full_summary, score, first_response_time_min, handling_time_min, funnel_stage, origin, product_requested, product_sold, converted, attention_points, improvement_points, created_at)
|
|
VALUES ?`,
|
|
[batch]
|
|
);
|
|
process.stdout.write(`\r✅ Inseridos ${Math.min(i + batchSize, attendancesToInsert.length)} / ${attendancesToInsert.length}`);
|
|
}
|
|
console.log('\n🎉 Todos os dados foram gerados com sucesso!');
|
|
process.exit(0);
|
|
} catch (error) {
|
|
console.error('❌ Erro:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
run();
|