Campanhas UI: try/catch on Marcar button, inline error for segmentos, cap variants at 3

- Wrap the "Marcar" stage button's onclick body in try/catch (toast on
  error), matching the pattern already applied to the 5 campaign action
  buttons.
- carregarSegmentos' error handler (from the produto <select> change
  handler) can fire while #modal-nova-campanha is open, where a toast
  alone is hidden behind the open <dialog> (documented in api.js next to
  the toast() function) -- now also surfaces the error in the existing
  inline #campanha-erro paragraph.
- addVarianteRow now caps at 3 rows (A/B/C, matching the convention used
  elsewhere in the UI/backend) and disables "+ Variante" once the cap is
  reached.
This commit is contained in:
Gustavo
2026-08-03 13:07:13 -03:00
parent 6f6ed97206
commit 60d28c7465
+28 -9
View File
@@ -5,7 +5,10 @@ const Campanhas = (() => {
const STATUS_LABELS = { rascunho: 'Rascunho', rodando: 'Rodando', pausado: 'Pausado', concluido: 'Concluido' };
const MAX_VARIANTES = 3; // A/B/C — mesmo limite usado na convencao do restante da UI/backend.
function addVarianteRow(codigoSugerido) {
if (variantesCount >= MAX_VARIANTES) return;
const codigo = codigoSugerido || String.fromCharCode(65 + variantesCount);
variantesCount += 1;
el('campanha-variantes').append(
@@ -13,6 +16,11 @@ const Campanhas = (() => {
h('span', { class: 'chip' }, codigo),
h('textarea', { class: 'campanha-variante-mensagem', rows: 2, placeholder: 'Ola {empresa}, ...' }))
);
atualizarBotaoAddVariante();
}
function atualizarBotaoAddVariante() {
el('btn-add-variante').disabled = variantesCount >= MAX_VARIANTES;
}
async function carregarProdutos() {
@@ -83,7 +91,14 @@ const Campanhas = (() => {
el('btn-cancelar-campanha').addEventListener('click', () => el('modal-nova-campanha').close());
el('btn-add-variante').addEventListener('click', () => addVarianteRow());
el('campanha-produto').addEventListener('change', (event) => {
carregarSegmentos(event.target.value).catch((error) => toast(`Erro: ${error.message}`));
// O modal de nova campanha esta aberto (<dialog> via showModal()) quando
// isto dispara — toast sozinho fica escondido atras do dialog (ver
// comentario em api.js), entao o erro tambem precisa aparecer no
// paragrafo inline #campanha-erro que ja existe pro erro de submit.
carregarSegmentos(event.target.value).catch((error) => {
toast(`Erro: ${error.message}`);
el('campanha-erro').textContent = `Erro: ${error.message}`;
});
});
el('btn-salvar-campanha').addEventListener('click', async () => {
@@ -150,15 +165,19 @@ const Campanhas = (() => {
const marcarBtn = h('button', {
class: 'btn-ghost btn-sm', type: 'button', disabled: podeMarcarEstagio ? null : 'disabled',
onclick: async () => {
if (!stageSelect.value) return;
await API.post(`/api/campaigns/leads/${linha.lead_id}/estagio`, { stage: stageSelect.value, campaignId: campanhaId });
toast('Estagio atualizado');
if (stageSelect.value === 'demonstracao_agendada') {
const dataAgendamento = prompt('Data/hora do agendamento (AAAA-MM-DD HH:MM):');
if (dataAgendamento) {
await API.post(`/api/campaigns/leads/${linha.lead_id}/agendamentos`, { kind: 'demonstracao', scheduledAt: dataAgendamento });
toast('Agendamento registrado');
try {
if (!stageSelect.value) return;
await API.post(`/api/campaigns/leads/${linha.lead_id}/estagio`, { stage: stageSelect.value, campaignId: campanhaId });
toast('Estagio atualizado');
if (stageSelect.value === 'demonstracao_agendada') {
const dataAgendamento = prompt('Data/hora do agendamento (AAAA-MM-DD HH:MM):');
if (dataAgendamento) {
await API.post(`/api/campaigns/leads/${linha.lead_id}/agendamentos`, { kind: 'demonstracao', scheduledAt: dataAgendamento });
toast('Agendamento registrado');
}
}
} catch (error) {
toast(`Erro: ${error.message}`);
}
}
}, 'Marcar');