Add campaign detail panel: queue approval, health and variant stats

This commit is contained in:
Gustavo
2026-08-03 08:31:00 -03:00
parent 5fef71d157
commit 9aceaa140d
2 changed files with 127 additions and 1 deletions
+90 -1
View File
@@ -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 };
})();