diff --git a/public/index.html b/public/index.html index 69b1892..d867797 100644 --- a/public/index.html +++ b/public/index.html @@ -236,6 +236,43 @@
+ +

Nova campanha

diff --git a/public/js/campanhas.js b/public/js/campanhas.js index a660e3d..034e8fa 100644 --- a/public/js/campanhas.js +++ b/public/js/campanhas.js @@ -58,7 +58,7 @@ const Campanhas = (() => { } function renderCampanhaCard(campanha) { - return h('div', { class: 'item' }, + return h('div', { class: 'item', onclick: () => carregarDetalhe(campanha.id).catch((e) => toast(`Erro: ${e.message}`)) }, h('div', {}, h('strong', {}, campanha.nome), h('span', { class: 'sub' }, ` ${campanha.produto} — ${campanha.segmentos.join(', ')}`)), @@ -104,8 +104,97 @@ const Campanhas = (() => { } }); + ligarDetalheCampanha(); + carregarLista().catch((error) => toast(`Erro: ${error.message}`)); } + let campanhaAtualId = null; + + async function carregarDetalhe(campanhaId) { + campanhaAtualId = campanhaId; + const campanha = await API.get(`/api/campaigns/${campanhaId}`); + el('detalhe-nome').textContent = campanha.nome; + el('detalhe-status').textContent = STATUS_LABELS[campanha.status] || campanha.status; + el('campanha-detalhe').classList.remove('hidden'); + await Promise.all([carregarSaude(campanhaId), carregarVariantesStats(campanhaId), carregarFila(campanhaId)]); + } + + async function carregarSaude(campanhaId) { + const saude = await API.get(`/api/campaigns/${campanhaId}/saude`); + el('detalhe-saude').textContent = saude.pause + ? `Pausada automaticamente: ${saude.reason}` + : 'Saude: sem alertas.'; + } + + async function carregarVariantesStats(campanhaId) { + const { variantes } = await API.get(`/api/campaigns/${campanhaId}/variantes-stats`); + el('detalhe-variantes').replaceChildren(...variantes.map((v) => + h('span', { class: 'chip' }, + `${v.code}: ${v.enviados} enviados, ${(v.taxaResposta * 100).toFixed(1)}% resposta${v.elegivelParaComparacao ? ' (elegivel)' : ''}`) + )); + } + + async function carregarFila(campanhaId) { + const status = el('fila-filtro-status').value; + const { fila } = await API.get(`/api/campaigns/${campanhaId}/fila?status=${status}`); + el('fila-tabela').replaceChildren(...fila.map((linha) => + h('tr', {}, + h('td', {}, h('input', { type: 'checkbox', class: 'fila-check', value: linha.id })), + h('td', {}, String(linha.lead_id)), + h('td', {}, linha.numero), + h('td', {}, linha.rendered_message), + h('td', {}, linha.status)) + )); + } + + function filaSelecionada() { + return Array.from(document.querySelectorAll('.fila-check:checked')).map((i) => Number(i.value)); + } + + function ligarDetalheCampanha() { + el('btn-fechar-detalhe').addEventListener('click', () => el('campanha-detalhe').classList.add('hidden')); + el('fila-filtro-status').addEventListener('change', () => carregarFila(campanhaAtualId).catch((e) => toast(`Erro: ${e.message}`))); + + el('btn-gerar-fila').addEventListener('click', async () => { + try { + const resultado = await API.post(`/api/campaigns/${campanhaAtualId}/gerar-fila`, {}); + toast(`${resultado.adicionadosNaFila} leads adicionados a fila`); + await carregarFila(campanhaAtualId); + } catch (error) { toast(`Erro: ${error.message}`); } + }); + + el('btn-iniciar-campanha').addEventListener('click', async () => { + await API.post(`/api/campaigns/${campanhaAtualId}/iniciar`, {}); + await carregarDetalhe(campanhaAtualId); + await carregarLista(); + }); + el('btn-pausar-campanha').addEventListener('click', async () => { + await API.post(`/api/campaigns/${campanhaAtualId}/pausar`, { motivo: 'manual' }); + await carregarDetalhe(campanhaAtualId); + await carregarLista(); + }); + el('btn-retomar-campanha').addEventListener('click', async () => { + await API.post(`/api/campaigns/${campanhaAtualId}/retomar`, {}); + await carregarDetalhe(campanhaAtualId); + await carregarLista(); + }); + + el('btn-aprovar-selecionados').addEventListener('click', async () => { + const queueIds = filaSelecionada(); + if (!queueIds.length) return; + const resultado = await API.post(`/api/campaigns/${campanhaAtualId}/fila/aprovar`, { queueIds }); + toast(`${resultado.aprovados} aprovados`); + await carregarFila(campanhaAtualId); + }); + el('btn-cancelar-selecionados').addEventListener('click', async () => { + const queueIds = filaSelecionada(); + if (!queueIds.length) return; + const resultado = await API.post(`/api/campaigns/${campanhaAtualId}/fila/cancelar`, { queueIds, motivo: 'operador' }); + toast(`${resultado.cancelados} cancelados`); + await carregarFila(campanhaAtualId); + }); + } + return { ativar }; })();