Valor: ${formatarMoeda(seg.valor)}
Participação: ${pct}%
`;
tooltipGastos.style.borderColor = CATEGORIAS_CORES[seg.cat];
const offset = 14;
tooltipGastos.style.left = (e.clientX + offset) + 'px';
tooltipGastos.style.top = (e.clientY + offset) + 'px';
tooltipGastos.style.display = 'block';
});
canvas.addEventListener('mouseleave', ()=> tooltipGastos.style.display='none');
}
// ------- HELPERS DE LEITURA -------
function parseValorCampo(inputEl) {
if (!inputEl || !inputEl.value) return 0;
const val = inputEl.value.replace(/[R$\s.]/g, '').replace(',', '.');
const num = parseFloat(val);
return isNaN(num) ? 0 : num;
}
function somarListaIdsBasica(ids) {
// Soma campos que NÃO são dinâmicos (sem sufixo -N)
let soma = 0;
ids.forEach(id => {
const isDinamico = IDS_DINAMICOS_PREFIX.some(pref => id === `form-field-${pref}`);
if (isDinamico) return; // dinâmicos serão somados à parte
const el = document.getElementById(id);
soma += parseValorCampo(el);
});
return soma;
}
function somarIdsDinamicosSeExistirem(ids) {
let soma = 0;
ids.forEach(id => {
const match = id.match(/^form-field-(\d+)/);
if (!match) return;
const numero = match[1];
if (!IDS_DINAMICOS_PREFIX.includes(numero)) return;
// pega todos que começam com esse prefixo (com e sem -N)
document.querySelectorAll(`input[id^="form-field-${numero}"]`).forEach(el => {
soma += parseValorCampo(el);
});
});
return soma;
}
// ------- TOTALIZAR POR CATEGORIA -------
function totalizarCategoriasGastos() {
// mantém os IDs como você já tinha
const base = {
essenciais: ['form-field-276','form-field-277_1','form-field-277_2','form-field-277_3','form-field-279', 'form-field-396'],
recorrentes: ['form-field-280','form-field-281','form-field-282','form-field-283','form-field-284','form-field-285','form-field-391','form-field-287'],
variaveis: ['form-field-288','form-field-289','form-field-290','form-field-291','form-field-292','form-field-293','form-field-294','form-field-296'],
futuro: ['form-field-297','form-field-298','form-field-299','form-field-300','form-field-302']
};
const IDS_DINAMICOS_PREFIX = ['279','287','296','302'];
function parseValorCampo(inputEl) {
if (!inputEl || !inputEl.value) return 0;
const val = inputEl.value.replace(/[R$\s.]/g, '').replace(',', '.');
const num = parseFloat(val);
return isNaN(num) ? 0 : num;
}
function somaGrupo(ids) {
let soma = 0;
ids.forEach(id => {
const isDinamico = IDS_DINAMICOS_PREFIX.some(pref => id === `form-field-${pref}`);
if (isDinamico) return;
const el = document.getElementById(id);
soma += parseValorCampo(el);
});
// somar clones dinâmicos (ex.: 279-1, 279-2…)
ids.forEach(id => {
const m = id.match(/^form-field-(\d+)/);
if (!m) return;
const n = m[1];
if (!IDS_DINAMICOS_PREFIX.includes(n)) return;
document.querySelectorAll(`input[id^="form-field-${n}"]`).forEach(el => {
soma += parseValorCampo(el);
});
});
return soma;
}
const totaisRaw = {
essenciais: somaGrupo(base.essenciais),
recorrentes: somaGrupo(base.recorrentes),
variaveis: somaGrupo(base.variaveis),
futuro: somaGrupo(base.futuro)
};
const totais = {
fixo: (totaisRaw.essenciais || 0) + (totaisRaw.recorrentes || 0),
variaveis: totaisRaw.variaveis || 0,
futuro: totaisRaw.futuro || 0
};
const totalGeral = Object.values(totais).reduce((a, v) => a + v, 0);
return { totais, totalGeral };
}
function anexarTooltipDonut(canvasId){
const canvas = document.getElementById(canvasId);
if (!canvas) return;
// evita listeners duplicados
if (canvas.dataset.tooltipReady === '1') return;
canvas.dataset.tooltipReady = '1';
canvas.addEventListener('mousemove', (e) => {
const segmentos = segmentosPorCanvas[canvasId] || [];
if (!segmentos.length) { tooltipGastos.style.display = 'none'; return; }
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const x = (e.clientX - rect.left) * scaleX;
const y = (e.clientY - rect.top) * scaleY;
const w = canvas.width, h = canvas.height;
const cx = w/2, cy = h/2;
const rOuter = Math.min(w, h) * 0.45;
const rInner = rOuter * 0.60;
const dx = x - cx, dy = y - cy;
const dist = Math.hypot(dx, dy);
if (dist rOuter){ tooltipGastos.style.display='none'; return; }
let ang = Math.atan2(dy, dx);
if (ang ang >= s.start && ang
Categoria: ${NOME_CATEGORIA[seg.cat]}
Valor: ${formatarMoeda(seg.valor)}
Participação: ${pct}%
`;
tooltipGastos.style.borderColor = CATEGORIAS_CORES[seg.cat];
const offset = 14;
tooltipGastos.style.left = (e.clientX + offset) + 'px';
tooltipGastos.style.top = (e.clientY + offset) + 'px';
tooltipGastos.style.display = 'block';
});
canvas.addEventListener('mouseleave', ()=> tooltipGastos.style.display='none');
}
// ------- DESENHAR DONUT (CANVAS PURO) -------
function desenharDonutGenerico({ canvasId, legendaId, totais, totalGeral }){
const canvas = document.getElementById(canvasId);
const legenda = document.getElementById(legendaId);
if (!canvas || !canvas.getContext) return;
const ctx = canvas.getContext('2d');
const w = canvas.width, h = canvas.height;
const cx = w/2, cy = h/2;
const rOuter = Math.min(w, h) * 0.45;
const rInner = rOuter * 0.60;
ctx.clearRect(0,0,w,h);
segmentosPorCanvas[canvasId] = [];
if (!totalGeral || totalGeral Preencha seus gastos para ver o gráfico.';
return;
}
let anguloAtual = -Math.PI/2;
const ordem = ['fixo','variaveis','futuro'];
ordem.forEach(cat=>{
const valor = totais[cat] || 0;
if (valor {
const valor = totais[cat] || 0;
if (valor {
const input = document.getElementById(id);
if (!input || !input.value) return;
const valor = input.value.replace(/[R$\s.]/g, '').replace(',', '.');
const numero = parseFloat(valor);
if (!isNaN(numero)) {
if (idsBrutosAnuais.includes(id)) {
total += numero / 12;
} else {
total += numero;
}
}
});
receitaBrutaAnualGlobal = total * 12;
const valorFinal = `
${formatarMoeda(total)}`;
campoReceitaBruta.innerHTML = `Receita bruta mensal: ${valorFinal}`;
// Atualiza receita bruta anual
const campoReceitaBrutaAnual = document.querySelector('[data-id="71b80110"] .elementor-heading-title');
if (campoReceitaBrutaAnual) {
campoReceitaBrutaAnual.innerHTML = `Receita bruta anual:
${formatarMoeda(receitaBrutaAnualGlobal)}`;
}
atualizarValorTresPorCento();
}
function atualizarValorTresPorCento() {
const campoTresPorCento = document.querySelector('[data-id="71b80111"] .elementor-heading-title');
const campoParcelado = document.querySelector('[data-id="71b80112"] .elementor-heading-title');
const campoValorParcela = document.querySelector('[data-id="71b80113"] .elementor-heading-title');
if (!campoTresPorCento || !campoParcelado || !campoValorParcela) return;
const inputPrecoEditado = document.getElementById('form-field-precoEditado');
let base = receitaBrutaAnualGlobal * 0.033;
if (base {
const sels = Array.from(document.querySelectorAll(
'select#form-field-meioPagamentoParcelado, select[name="form_fields[meioPagamentoParcelado]"]'
));
return sels.find(s => s.offsetParent !== null) || sels[0] || null;
})();
const valorSelecionado = seletorParcelado?.value || '';
if (valorSelecionado) {
numeroParcelas = parseInt(valorSelecionado.replace('x', ''), 10);
if (!isNaN(numeroParcelas) && numeroParcelas >= 2 && numeroParcelas 0) {
const valorParcela = valorFinalParcelado / numeroParcelas;
campoValorParcela.innerHTML = `Valor da Parcela:
${formatarMoeda(valorParcela)}`;
} else {
campoValorParcela.innerHTML = `Valor da Parcela: -`;
}
// ✅ recalc leve (1 por frame) do multi
if (typeof window.__avelRecalcMulti === 'function') {
window.__avelRecalcMulti(0);
}
}
// ====== MULTI PAGAMENTO (ÚNICO): permite editar "Valor" + BRL + validação soma vs 71b80117 ======
(function () {
const PERCENTUAIS_PARCELADO = {
2: 0.0515, 3: 0.0565, 4: 0.0635, 5: 0.0696, 6: 0.0757,
7: 0.0856, 8: 0.0918, 9: 0.0981, 10: 0.1044, 11: 0.1107, 12: 0.1171
};
const brl = new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL', minimumFractionDigits: 2 });
function formatBRLFromCents(cents) {
return brl.format((Number(cents || 0)) / 100);
}
function parseBRLToCents(str) {
if (!str) return 0;
const digits = String(str).replace(/\D/g, '');
return digits ? parseInt(digits, 10) : 0;
}
function getInBoxVisible(box, selector) {
if (!box) return null;
const list = Array.from(box.querySelectorAll(selector));
return list.find(el => el.offsetParent !== null) || list[0] || null;
}
function getSelectVisivel(selector) {
const sels = Array.from(document.querySelectorAll(selector));
return sels.find(s => s.offsetParent !== null) || sels[0] || null;
}
function getMetodosAtivos() {
const selQtd = getSelectVisivel('select#form-field-MaismeioPagamento, select[name="form_fields[MaismeioPagamento]"]');
const val = selQtd?.value || '';
const m = String(val).match(/\d/);
const qtd = m ? parseInt(m[0], 10) : 0;
if (qtd === 2) return [2, 3];
if (qtd === 3) return [2, 3, 4];
if (qtd >= 4) return [2, 3, 4, 5];
return [];
}
function feeCentsForMethod(baseParteCents, metodo, parcelasValue, parcelasVisivel) {
const met = String(metodo || '').toUpperCase();
if (met === 'PIX') return 149;
if (met === 'BOLETO') return 189;
if (met === 'CREDIT_CARD') {
if (parcelasVisivel) {
const n = parseInt(String(parcelasValue || '').replace(/\D/g, ''), 10);
if (!isNaN(n) && n >= 2 && n 0 falta | 0 ? '-' : '+';
el.style.color = '#f50062';
el.style.color = '#f50062';
el.innerHTML = `${totalFmt}
( ${sinal} ${diffAbsFmt} )`;
}
// ===== estado (pra não recalcular pesado ao digitar no input) =====
const STATE = (window.__avelMultiState = window.__avelMultiState || {
lastTotalFinalCents: 0
});
function mascararBRLNoInput(input) {
if (!input) return;
const cents = parseBRLToCents(input.value);
input.value = cents ? formatBRLFromCents(cents) : '';
}
// Atualiza SÓ a validação (não repovoa inputs) + ✅ atualiza "Parcela:" baseado no valor digitado
function validarSomente() {
const ativos = getMetodosAtivos();
let somaInputsCents = 0;
ativos.forEach((n) => {
const box = document.querySelector(`[class~="${n}metodoPagamento"]`);
if (!box) return;
const inputValor = getInBoxVisible(
box,
`input#form-field-${n}valorPagamento, input[name="form_fields[${n}valorPagamento]"]`
);
const selectMetodo = getInBoxVisible(
box,
`select#form-field-${n}maisPagamento, select[name="form_fields[${n}maisPagamento]"]`
);
const selectParcelas = getInBoxVisible(
box,
`select#form-field-${n}maisPagamentoParcelas, select[name="form_fields[${n}maisPagamentoParcelas]"]`
);
const valorInputCents = inputValor ? parseBRLToCents(inputValor.value) : 0;
somaInputsCents += valorInputCents;
// ✅ Atualiza "Parcela:" com base no VALOR DIGITADO
const metUpper = String(selectMetodo?.value || '').toUpperCase();
const parcelasVisivel = !!(selectParcelas && selectParcelas.offsetParent !== null);
const parcelasValue = selectParcelas?.value || '';
if (metUpper === 'CREDIT_CARD' && parcelasVisivel) {
const numParc = parseInt(String(parcelasValue || '').replace(/\D/g, ''), 10);
if (!isNaN(numParc) && numParc >= 2 && numParc 0) {
const parcelaCents = Math.round(valorInputCents / numParc);
setParcelaHeading(
n,
box,
`Parcela:
${formatBRLFromCents(parcelaCents)}`
);
} else {
setParcelaHeading(n, box, `Parcela: -`);
}
} else {
setParcelaHeading(n, box, `Parcela: -`);
}
});
setHeading(STATE.lastTotalFinalCents, somaInputsCents);
}
function atualizarParcelasSomente() {
const ativos = getMetodosAtivos();
ativos.forEach((n) => {
const box = document.querySelector(`[class~="${n}metodoPagamento"]`);
if (!box) return;
const inputValor = getInBoxVisible(
box,
`input#form-field-${n}valorPagamento, input[name="form_fields[${n}valorPagamento]"]`
);
const selectMetodo = getInBoxVisible(
box,
`select#form-field-${n}maisPagamento, select[name="form_fields[${n}maisPagamento]"]`
);
const selectParcelas = getInBoxVisible(
box,
`select#form-field-${n}maisPagamentoParcelas, select[name="form_fields[${n}maisPagamentoParcelas]"]`
);
const metUpper = String(selectMetodo?.value || '').toUpperCase();
const parcelasVisivel = !!(selectParcelas && selectParcelas.offsetParent !== null);
const parcelasValue = selectParcelas?.value || '';
const valorInputCents = inputValor ? parseBRLToCents(inputValor.value) : 0;
if (metUpper === 'CREDIT_CARD' && parcelasVisivel) {
const numParc = parseInt(String(parcelasValue || '').replace(/\D/g, ''), 10);
if (!isNaN(numParc) && numParc >= 2 && numParc 0) {
const parcelaCents = Math.round(valorInputCents / numParc);
setParcelaHeading(
n,
box,
`Parcela:
${formatBRLFromCents(parcelaCents)}`
);
} else {
setParcelaHeading(n, box, `Parcela: -`);
}
} else {
setParcelaHeading(n, box, `Parcela: -`);
}
});
}
const PARCELA_HEADING_DATAID = {
2: '271b80113',
3: '371b80113',
4: '471b80113',
5: '571b80113'
};
function setParcelaHeading(nMetodo, box, texto) {
const dataId = PARCELA_HEADING_DATAID[nMetodo];
if (!dataId) return;
// tenta primeiro dentro da box (mais seguro)
const el =
(box && box.querySelector(`[data-id="${dataId}"] .elementor-heading-title`)) ||
document.querySelector(`[data-id="${dataId}"] .elementor-heading-title`);
if (el) el.innerHTML = texto;
}
// Atualização completa (recalcula taxas e, se permitido, autopreenche)
function atualizarUIValores(baseTotal) {
const ativos = getMetodosAtivos();
const qtd = ativos.length;
if (!qtd) return;
const baseTotalCents = Math.round(Number(baseTotal || 0) * 100);
if (!baseTotalCents || baseTotalCents {
const box = document.querySelector(`[class~="${n}metodoPagamento"]`);
if (!box) return;
const baseParteCents = baseParteCentsBase + (idx = 2 && numParc ${formatBRLFromCents(parcelaCents)}`
);
} else {
setParcelaHeading(n, box, `Parcela: -`);
}
} else {
setParcelaHeading(n, box, `Parcela: -`);
}
});
STATE.lastTotalFinalCents = totalFinalCents;
setHeading(totalFinalCents, somaInputsCents);
}
function setHeading(totalCents, somaInputsCents) {
const el = document.querySelector('[data-id="71b80117"] .elementor-heading-title');
if (!el) return;
const diff = totalCents - somaInputsCents; // >0 falta | 0 ? '-' : '+';
el.style.color = '#f50062';
el.innerHTML = `${totalFmt}
( ${sinal} ${diffAbsFmt} )`;
el.dataset.invalid = '1'; // ✅
if (typeof window.atualizarOpacidadeBtn === 'function') window.atualizarOpacidadeBtn(); // ✅
}
// scheduler leve
let raf = 0;
function scheduleFullRecalc() {
if (raf) cancelAnimationFrame(raf);
raf = requestAnimationFrame(() => {
const base = Number(window.__avelBasePrecoAtual || 0);
if (!base || base recalcula completo (taxas)
document.addEventListener('change', (e) => {
const t = e.target;
if (!t) return;
if (t.matches('select#form-field-MaismeioPagamento, select[name="form_fields[MaismeioPagamento]"]')) {
scheduleFullRecalc();
return;
}
if (t.matches('select[id^="form-field-"][id$="maisPagamento"], select[id^="form-field-"][id$="maisPagamentoParcelas"]')) {
scheduleFullRecalc();
// ✅ depois do recalculo (no próximo frame), atualiza "Parcela:" com base no valor atual do input
requestAnimationFrame(() => {
atualizarParcelasSomente();
validarSomente(); // mantém a validação/heading total coerente
});
return;
}
}, true);
// 2) Usuário editou input "Valor" => marca manual e só valida (não sobrescreve)
document.addEventListener('input', (e) => {
const t = e.target;
if (!t) return;
if (t.matches('input[id^="form-field-"][id$="valorPagamento"], input[name^="form_fields["][name$="valorPagamento]"]')) {
t.dataset.manual = '1';
// NÃO mascara aqui (pra não brigar com o cursor). Só valida.
validarSomente();
}
}, true);
// 3) Ao sair do input, aplica máscara BRL e valida
document.addEventListener('blur', (e) => {
const t = e.target;
if (!t) return;
if (t.matches('input[id^="form-field-"][id$="valorPagamento"], input[name^="form_fields["][name$="valorPagamento]"]')) {
mascararBRLNoInput(t);
validarSomente();
}
}, true);
// bootstrap
scheduleFullRecalc();
})();
// ====== Listeners dos seletores (com select visível no parcelado) ======
const seletorPagamento = document.getElementById('form-field-meioPagamentoAVista');
if (seletorPagamento) {
seletorPagamento.addEventListener('change', atualizarValorTresPorCento);
}
const seletorParceladoVisivel = (() => {
const sels = Array.from(document.querySelectorAll(
'select#form-field-meioPagamentoParcelado, select[name="form_fields[meioPagamentoParcelado]"]'
));
return sels.find(s => s.offsetParent !== null) || sels[0] || null;
})();
if (seletorParceladoVisivel) {
seletorParceladoVisivel.addEventListener('change', atualizarValorTresPorCento);
}
// Executa ao carregar a página
somarRendas();
const inputPrecoEditado = document.getElementById('form-field-precoEditado');
if (inputPrecoEditado) {
inputPrecoEditado.addEventListener('blur', atualizarValorTresPorCento);
inputPrecoEditado.addEventListener('change', atualizarValorTresPorCento);
}
[...idsBrutosMensais, ...idsBrutosAnuais].forEach(id => {
const input = document.getElementById(id);
if (input) {
input.addEventListener('input', somarRendas);
}
});
function somarGastos() {
let total = 0;
coletarIdsGastos().forEach(id => {
const input = document.getElementById(id);
if (!input) return;
const valor = input.value.replace(/[R$\s.]/g, '').replace(',', '.');
const numero = parseFloat(valor);
if (!isNaN(numero)) total += numero;
});
const valorMensal = `
${formatarMoeda(total)}`;
const valorAnual = `
${formatarMoeda(total * 12)}`;
campoMensal.innerHTML = `Total mensal de gastos: ${valorMensal}`;
campoAnual.innerHTML = `Total anual de gastos: ${valorAnual}`;
campoTotalAnualExtra.innerHTML = `Total mensal de gastos: ${valorMensal}`;
ultimoTotalGastos = total;
atualizarDiferencaReceitaGastos(ultimoTotalReceitaLiquida, ultimoTotalGastos);
atualizarGraficoGastos();
}
document.addEventListener('input', (e) => {
if (e.target.matches(
`input[id^="form-field-279"],
input[id^="form-field-287"],
input[id^="form-field-296"],
input[id^="form-field-302"]`
)) {
somarGastos();
calcularReceitaLiquidaMensal();
atualizarGraficoGastos();
}
});
function calcularReceitaLiquidaMensal() {
let totalLiquido = 0;
// Rendas líquidas mensais (somadas direto)
idsLiquidoMultiplica12.forEach(id => {
const input = document.getElementById(id);
if (!input) return;
const valor = input.value.replace(/[R$\s.]/g, '').replace(',', '.');
const numero = parseFloat(valor);
if (!isNaN(numero)) totalLiquido += numero;
});
// Rendas brutas adicionais com ou sem desconto de IR
idsLiquidoDescontar.forEach(id => {
const input = document.getElementById(id);
if (!input) return;
const valor = input.value.replace(/[R$\s.]/g, '').replace(',', '.');
const numero = parseFloat(valor);
if (!isNaN(numero)) {
if(id === 'form-field-308' || id === 'form-field-318') {
// 308 e 318 são mensais, sem desconto de IR
totalLiquido += numero;
} else if(id === 'form-field-310' || id === 'form-field-320') {
// 310 e 320 são anuais, sem desconto de IR -> dividir por 12
totalLiquido += numero / 12;
} else if(id === 'form-field-309' || id === 'form-field-311' || id === 'form-field-319' || id === 'form-field-321') {
// Estes campos são tratados como mensais brutos, aplicar IR diretamente
totalLiquido += numero * 0.725;
} else {
// Demais continuam sendo anuais com desconto IR dividido por 12
totalLiquido += (numero * 0.725) / 12;
}
}
});
const valorFormatado = `
${formatarMoeda(totalLiquido)}`;
campoReceitaLiquida.innerHTML = `Receita líquida mensal: ${valorFormatado}`;
ultimoTotalReceitaLiquida = totalLiquido;
atualizarDiferencaReceitaGastos(ultimoTotalReceitaLiquida, ultimoTotalGastos);
}
function atualizarDiferencaReceitaGastos(receitaLiquida, gastosAnuais) {
const diferenca = receitaLiquida - gastosAnuais;
const valorFormatado = `
${formatarMoeda(diferenca)}`;
campoDiferenca.innerHTML = `Diferença mensal: ${valorFormatado}`;
}
function precisaRecalcular(el) {
if (!el || el.tagName !== 'INPUT') return false;
const id = el.id || '';
// Dinâmicos: 279, 287, 296, 302 — com ou sem sufixo -N
const dinamicos = /^(form-field-(279|287|296|302))(?:-\d+)?$/;
return dinamicos.test(id)
|| baseIdsGastos.includes(id)
|| idsLiquidoMultiplica12.includes(id)
|| idsLiquidoDescontar.includes(id);
}
document.addEventListener('input', (e) => {
if (precisaRecalcular(e.target)) {
somarGastos();
calcularReceitaLiquidaMensal();
}
});
// Executar ao carregar
somarGastos();
calcularReceitaLiquidaMensal();
function atualizarObrigatoriedadeCampos() {
Object.entries(camposObrigatoriosPorObjetivo).forEach(([checkboxValue, inputId]) => {
const checkbox = document.querySelector(`input[type="checkbox"][value="${checkboxValue}"]`);
const input = document.getElementById(inputId);
const container = input?.closest('.elementor-field-group');
if (checkbox && input && container) {
if (checkbox.checked) {
input.setAttribute('required', 'required');
input.setAttribute('aria-required', 'true');
container.classList.add('elementor-field-required');
} else {
input.removeAttribute('required');
input.removeAttribute('aria-required');
container.classList.remove('elementor-field-required');
}
}
});
}
// Executa ao carregar
atualizarObrigatoriedadeCampos();
// ====== BLOCO DE SELEÇÃO DE PAGAMENTO (AVista, Parcelado, MaisPagamento) ======
const conteinerAVista = document.querySelector('.conteinerAVista');
const conteinerParcelado = document.querySelector('.conteinerParcelado');
const conteinerMaisPagamento = document.querySelector('.conteinerMaisPagamento');
const formAVista = document.getElementById('formsAvista');
const formParcelado = document.getElementById('formsParcelado');
// Internos da box "Mais Pagamentos"
const formMaisPagamentoInternos = [
document.getElementById('metodoMaisPagamento'),
document.getElementById('2metodo'),
document.getElementById('3metodo'),
document.getElementById('4metodo'),
document.getElementById('5metodo'),
document.getElementById('2Parcelado'),
document.getElementById('3Parcelado'),
document.getElementById('4Parcelado'),
document.getElementById('5Parcelado'),
].filter(Boolean);
const btnContrato = document.getElementById('btncontrato');
// Garante transição suave no botão
if (btnContrato) {
btnContrato.style.transition = 'opacity 1s ease, pointer-events 1s ease';
btnContrato.style.opacity = '0';
btnContrato.style.pointerEvents = 'none';
}
// Lista única de containers de pagamento
const containersPagamento = [
conteinerAVista,
conteinerParcelado,
conteinerMaisPagamento
].filter(Boolean);
// Atualiza opacidade e interatividade do botão
function atualizarOpacidadeBtn() {
if (!btnContrato) return;
const elTotal = document.querySelector('[data-id="71b80117"] .elementor-heading-title');
const avistaAtivo = !!conteinerAVista && conteinerAVista.classList.contains('ativo');
const parceladoAtivo = !!conteinerParcelado && conteinerParcelado.classList.contains('ativo');
const maisPagAtivo = !!conteinerMaisPagamento && conteinerMaisPagamento.classList.contains('ativo');
// inválido quando multi está vermelho / com diff
const totalInvalido =
!!elTotal && (
elTotal.dataset.invalid === '1' ||
getComputedStyle(elTotal).color === 'rgb(245, 0, 98)' // #f50062
);
// ✅ regra final:
// - À vista OU Parcelado: botão aparece direto
// - Mais Pagamento: só aparece se NÃO estiver inválido
const deveMostrar = avistaAtivo || parceladoAtivo || (maisPagAtivo && !totalInvalido);
btnContrato.style.opacity = deveMostrar ? '1' : '0';
btnContrato.style.pointerEvents = deveMostrar ? 'auto' : 'none';
}
// ✅ MUITO IMPORTANTE: deixa global pro setHeading conseguir esconder/mostrar ao mudar o total
window.atualizarOpacidadeBtn = atualizarOpacidadeBtn;
// Alterna o estado do container clicado (suporta 3 opções)
function alternarAtivoPagamento(clicado) {
const jaAtivo = clicado.classList.contains('ativo');
if (jaAtivo) {
// Se clicar novamente no ativo, desativa todos
containersPagamento.forEach(c => c.classList.remove('ativo'));
} else {
// Ativa apenas o clicado
containersPagamento.forEach(c =>
c.classList.toggle('ativo', c === clicado)
);
}
atualizarOpacidadeBtn();
}
// Helper: só alterna se o clique NÃO foi dentro de nenhum interno
function bindCliqueContainer(container, internos = []) {
if (!container) return;
const listaInternos = Array.isArray(internos) ? internos : [internos];
container.addEventListener('click', (e) => {
const clicouDentroInterno = listaInternos.some(
el => el && el.contains(e.target)
);
if (clicouDentroInterno) return;
alternarAtivoPagamento(container);
});
}
// ====== TOGGLE MAIS PAGAMENTO (corrigindo display original) ======
const btnMaisPag = document.querySelector('.btnmaispag');
const containerPagamento = document.querySelector('.containerPagamento');
// ⚠️ aqui você NÃO declara conteinerMaisPagamento de novo (você já tem no script)
// usa a variável existente: conteinerMaisPagamento
if (btnMaisPag && conteinerMaisPagamento && containerPagamento) {
const btnWrapper = btnMaisPag.closest('.btnmaispag') || btnMaisPag;
// --- salva o display original do MaisPagamento (flex/grid/etc) ---
if (!conteinerMaisPagamento.dataset.displayOriginal) {
const displayAtual = getComputedStyle(conteinerMaisPagamento).display;
// se por acaso já estiver "none" no load, usa flex como fallback
conteinerMaisPagamento.dataset.displayOriginal = (displayAtual && displayAtual !== 'none') ? displayAtual : 'flex';
}
function esconderContainerPagamentoSemSumirBotao() {
Array.from(containerPagamento.children).forEach((child) => {
if (child.contains(btnWrapper)) return;
if (!child.dataset._displayOriginal) {
child.dataset._displayOriginal = getComputedStyle(child).display;
}
child.style.pointerEvents = 'none';
child.style.display = 'none';
});
containerPagamento.style.pointerEvents = 'none';
btnWrapper.style.pointerEvents = 'auto';
}
function mostrarContainerPagamentoDeNovo() {
Array.from(containerPagamento.children).forEach((child) => {
if (child.contains(btnWrapper)) return;
const original = child.dataset._displayOriginal || 'block';
child.style.display = original;
child.style.pointerEvents = 'auto';
});
containerPagamento.style.pointerEvents = 'auto';
}
// Estado inicial do MaisPagamento (oculto, mas mantendo displayOriginal salvo)
conteinerMaisPagamento.style.display = 'none';
conteinerMaisPagamento.style.opacity = '0';
conteinerMaisPagamento.style.pointerEvents = 'none';
conteinerMaisPagamento.style.transition = 'opacity 0.5s ease';
conteinerMaisPagamento.dataset.aberto = conteinerMaisPagamento.dataset.aberto || '0';
function abrirMaisPagamento() {
esconderContainerPagamentoSemSumirBotao();
// ✅ volta com o display correto (flex/grid)
conteinerMaisPagamento.style.display = conteinerMaisPagamento.dataset.displayOriginal;
// reflow p/ transição
void conteinerMaisPagamento.offsetHeight;
conteinerMaisPagamento.style.opacity = '1';
conteinerMaisPagamento.style.pointerEvents = 'auto';
conteinerMaisPagamento.dataset.aberto = '1';
}
function fecharMaisPagamento() {
conteinerMaisPagamento.style.opacity = '0';
conteinerMaisPagamento.style.pointerEvents = 'none';
conteinerMaisPagamento.dataset.aberto = '0';
setTimeout(() => {
if (conteinerMaisPagamento.dataset.aberto === '0') {
conteinerMaisPagamento.style.display = 'none';
}
}, 500);
mostrarContainerPagamentoDeNovo();
}
btnMaisPag.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
const aberto = conteinerMaisPagamento.dataset.aberto === '1';
if (aberto) fecharMaisPagamento();
else abrirMaisPagamento();
});
}
// ====== MÉTODOS DE PAGAMENTO (2/3/4) - FUNCIONA COM IDs DUPLICADOS (Elementor) ======
(function () {
const SELECT_CSS = 'select#form-field-MaismeioPagamento, select[name="form_fields[MaismeioPagamento]"]';
function firstByClass(cls) {
return document.getElementsByClassName(cls)[0] || null;
}
function saveDisplay(el) {
if (!el) return;
if (!el.dataset._displayOriginal) {
const d = getComputedStyle(el).display;
el.dataset._displayOriginal = (d && d !== 'none') ? d : 'flex'; // seus containers são e-flex
}
}
function hide(el) {
if (!el) return;
saveDisplay(el);
el.style.display = 'none';
el.style.pointerEvents = 'none';
}
function show(el) {
if (!el) return;
saveDisplay(el);
el.style.display = el.dataset._displayOriginal || 'flex';
el.style.pointerEvents = 'auto';
}
function extrairNumero(valor) {
// "2 Métodos de Pagamento" -> 2
const m = String(valor || '').match(/\d/);
return m ? parseInt(m[0], 10) : null;
}
function aplicarPorQuantidade(qtd) {
const c2 = firstByClass('2metodoPagamento');
const c3 = firstByClass('3metodoPagamento');
const c4 = firstByClass('4metodoPagamento');
const c5 = firstByClass('5metodoPagamento');
const wrap45 = document.querySelector('.metodoPagamento4e5');
// padrão: tudo oculto
hide(c2); hide(c3);
hide(c4); hide(c5);
hide(wrap45);
if (!qtd) return;
// SUA LÓGICA ATUAL
if (qtd === 2) {
show(c2); show(c3);
return;
}
if (qtd === 3) {
show(c2); show(c3);
show(wrap45); show(c4);
hide(c5);
return;
}
// qtd === 4
if (qtd >= 4) {
show(c2); show(c3);
show(wrap45); show(c4); show(c5);
return;
}
}
// pega o select VISÍVEL quando houver duplicados
function getSelectVisivel() {
const sels = Array.from(document.querySelectorAll(SELECT_CSS));
if (!sels.length) return null;
// prioriza o que está visível (offsetParent != null)
const visivel = sels.find(s => s.offsetParent !== null);
return visivel || sels[0];
}
function atualizarAPartirDoSelect(sel) {
const qtd = extrairNumero(sel?.value);
aplicarPorQuantidade(qtd);
}
// 1) EVENT DELEGATION: pega exatamente o select que o usuário mudou (mesmo com ID duplicado)
document.addEventListener('change', (e) => {
const t = e.target;
if (t && t.matches && t.matches(SELECT_CSS)) {
setTimeout(() => atualizarAPartirDoSelect(t), 0);
}
}, true);
document.addEventListener('input', (e) => {
const t = e.target;
if (t && t.matches && t.matches(SELECT_CSS)) {
setTimeout(() => atualizarAPartirDoSelect(t), 0);
}
}, true);
// 2) bootstrap inicial (quando a tela abre / Elementor renderiza)
function bootstrap() {
const sel = getSelectVisivel();
if (sel) atualizarAPartirDoSelect(sel);
}
setTimeout(bootstrap, 0);
// 3) fallback curto (Elementor cria/duplica elementos depois)
let tent = 0;
const timer = setInterval(() => {
bootstrap();
tent++;
if (tent >= 60) clearInterval(timer); // ~6s
}, 100);
})();
// ====== PARCELADO: OCULTAR POR PADRÃO + MOSTRAR SÓ SE CREDIT_CARD (fix classe numérica) ======
(function () {
const REGRAS = [
{
selectId: 'form-field-2maisPagamento',
boxSel: '[class~="2metodoPagamento"]',
parceladoFormId: '2Parcelado',
headingSel: '.elementor-element-270b80113'
},
{
selectId: 'form-field-3maisPagamento',
boxSel: '[class~="3metodoPagamento"]',
parceladoFormId: '3Parcelado',
headingSel: '.elementor-element-370b80113'
},
{
selectId: 'form-field-4maisPagamento',
boxSel: '[class~="4metodoPagamento"]',
parceladoFormId: '4Parcelado',
headingSel: '.elementor-element-470b80113'
},
{
selectId: 'form-field-5maisPagamento',
boxSel: '[class~="5metodoPagamento"]',
parceladoFormId: '5Parcelado',
headingSel: '.elementor-element-570b80113'
}
];
function esconder(el) {
if (!el) return;
el.style.setProperty('display', 'none', 'important');
el.style.setProperty('pointer-events', 'none', 'important');
el.style.setProperty('visibility', 'hidden', 'important');
el.style.setProperty('opacity', '0', 'important');
}
function mostrar(el, fallbackDisplay = 'block') {
if (!el) return;
if (!el.dataset._displayOriginal) {
const d = getComputedStyle(el).display;
el.dataset._displayOriginal = (d && d !== 'none') ? d : fallbackDisplay;
}
el.style.setProperty('display', el.dataset._displayOriginal, 'important');
el.style.setProperty('pointer-events', 'auto', 'important');
el.style.setProperty('visibility', 'visible', 'important');
el.style.setProperty('opacity', '1', 'important');
}
function getTargets(cfg) {
const box = document.querySelector(cfg.boxSel);
// form Parcelado dentro da box
const form = box ? box.querySelector(`#${CSS.escape(cfg.parceladoFormId)}`) : document.getElementById(cfg.parceladoFormId);
const formWrap = form ? form.closest('.elementor-widget-form') : null;
// heading "Parcela:" dentro da box
const heading = box ? box.querySelector(cfg.headingSel) : document.querySelector(cfg.headingSel);
const headingWrap = heading ? heading.closest('.elementor-element') : null;
return { box, form, formWrap, heading, headingWrap };
}
function esconderPadraoTudo() {
REGRAS.forEach(cfg => {
const { formWrap, headingWrap } = getTargets(cfg);
esconder(formWrap);
esconder(headingWrap);
});
}
function aplicar(cfg) {
const select = document.getElementById(cfg.selectId);
if (!select) return;
const { formWrap, headingWrap } = getTargets(cfg);
const isCartao = (select.value || '').toUpperCase() === 'CREDIT_CARD';
if (isCartao) {
// widget-form normalmente é block (mas pode ser flex)
mostrar(formWrap, 'block');
mostrar(headingWrap, 'block');
} else {
esconder(formWrap);
esconder(headingWrap);
}
}
// 1) some com tudo no início (mesmo se Elementor vier setando display inline)
esconderPadraoTudo();
// 2) aplica estado inicial conforme selects já setados
setTimeout(() => {
REGRAS.forEach(aplicar);
}, 0);
// 3) ao mudar o select, atualiza imediatamente
document.addEventListener('change', (e) => {
const t = e.target;
if (!t || !t.id) return;
const cfg = REGRAS.find(r => r.selectId === t.id);
if (!cfg) return;
aplicar(cfg);
}, true);
// 4) Elementor pode re-renderizar e reexibir: garante re-hide + reaplica
const obs = new MutationObserver(() => {
esconderPadraoTudo();
REGRAS.forEach(aplicar);
});
obs.observe(document.body, { childList: true, subtree: true });
})();
// Bind dos containers (SEM duplicar listeners)
bindCliqueContainer(conteinerAVista, formAVista);
bindCliqueContainer(conteinerParcelado, formParcelado);
bindCliqueContainer(conteinerMaisPagamento, formMaisPagamentoInternos);
// ✅ Alias: sua atualizarValorTresPorCento() chama __avelRecalcMulti
// e o multi que criamos expõe atualizarMultiPagamento.
if (typeof window.__avelRecalcMulti !== 'function') {
window.__avelRecalcMulti = function () {
if (typeof window.atualizarMultiPagamento === 'function') {
window.atualizarMultiPagamento(window.__avelBasePrecoAtual || 0);
}
};
}
// ✅ sempre que trocar o ativo, se for "Mais Pagamento", recalc o multi
function afterTogglePagamento() {
atualizarOpacidadeBtn();
if (conteinerMaisPagamento?.classList.contains('ativo')) {
// garante base atualizado e recalcula multi
if (typeof atualizarValorTresPorCento === 'function') atualizarValorTresPorCento();
if (typeof window.atualizarMultiPagamento === 'function') {
window.atualizarMultiPagamento(window.__avelBasePrecoAtual || 0);
}
}
}
// Hook: roda depois que o bindCliqueContainer alternar o "ativo"
function hookAfterToggle(container, internos) {
if (!container) return;
const lista = Array.isArray(internos) ? internos : [internos];
container.addEventListener('click', (e) => {
const clicouDentroInterno = lista.some(el => el && el.contains(e.target));
if (clicouDentroInterno) return;
// roda depois do toggle do bindCliqueContainer
setTimeout(afterTogglePagamento, 0);
});
}
hookAfterToggle(conteinerAVista, formAVista);
hookAfterToggle(conteinerParcelado, formParcelado);
hookAfterToggle(conteinerMaisPagamento, formMaisPagamentoInternos);
// Estado inicial do botão
atualizarOpacidadeBtn();
// ========= Ir para contrato (mantém igual) =========
const containerContrator = document.querySelector('.containercontrator');
const blocoPreco = document.querySelector('.preco');
// Estilos iniciais
containerContrator.style.opacity = '0';
containerContrator.style.pointerEvents = 'none';
containerContrator.style.display = 'none';
blocoPreco.style.opacity = '1';
containerContrator.style.transition = 'opacity 0.5s ease';
blocoPreco.style.transition = 'opacity 0.5s ease';
function restaurarPagamentoAoVoltarDoContrato() {
const containerPagamento = document.querySelector('.containerPagamento');
if (containerPagamento) {
const original = containerPagamento.dataset._displayOriginal || 'block';
containerPagamento.style.setProperty('display', original, 'important');
containerPagamento.style.setProperty('pointer-events', 'auto', 'important');
containerPagamento.style.setProperty('visibility', 'visible', 'important');
containerPagamento.style.setProperty('opacity', '1', 'important');
}
// ✅ garante que a UI “reacenda” corretamente (botão, etc.)
setTimeout(() => {
if (typeof atualizarOpacidadeBtn === 'function') atualizarOpacidadeBtn();
// se quiser manter o cálculo do multi consistente caso o Mais Pagamento esteja ativo
if (conteinerMaisPagamento?.classList.contains('ativo')) {
if (typeof atualizarValorTresPorCento === 'function') atualizarValorTresPorCento();
if (typeof window.atualizarMultiPagamento === 'function') {
window.atualizarMultiPagamento(window.__avelBasePrecoAtual || 0);
}
}
}, 0);
}
function restaurarMaisPagamentoSeNecessario() {
if (!conteinerMaisPagamento) return;
const estavaAberto = conteinerMaisPagamento.dataset._estavaAberto === '1';
if (!estavaAberto) return;
// reativa estado lógico
conteinerMaisPagamento.classList.add('ativo');
conteinerMaisPagamento.dataset.aberto = '1';
// restaura display original salvo no toggle
const displayOriginal =
conteinerMaisPagamento.dataset.displayOriginal || 'flex';
conteinerMaisPagamento.style.setProperty('display', displayOriginal, 'important');
conteinerMaisPagamento.style.setProperty('pointer-events', 'auto', 'important');
conteinerMaisPagamento.style.setProperty('visibility', 'visible', 'important');
conteinerMaisPagamento.style.setProperty('opacity', '1', 'important');
// garante recalculo correto
setTimeout(() => {
if (typeof atualizarValorTresPorCento === 'function') atualizarValorTresPorCento();
if (typeof window.atualizarMultiPagamento === 'function') {
window.atualizarMultiPagamento(window.__avelBasePrecoAtual || 0);
}
if (typeof atualizarOpacidadeBtn === 'function') atualizarOpacidadeBtn();
}, 0);
}
// Abrir container de contrato
// Abrir container de contrato ✅ (corrigido para não “vazar” dropdown do Mais Pagamento)
btnContrato?.addEventListener('click', () => {
// ===== PASSO 1: SALVA SE O MAIS PAGAMENTO ESTAVA ABERTO =====
if (conteinerMaisPagamento) {
conteinerMaisPagamento.dataset._estavaAberto =
conteinerMaisPagamento.dataset.aberto === '1' ? '1' : '0';
}
// ✅ Mata a área de pagamento inteira (evita foco/click em selects invisíveis)
const containerPagamento = document.querySelector('.containerPagamento');
if (containerPagamento) {
if (!containerPagamento.dataset._displayOriginal) {
containerPagamento.dataset._displayOriginal =
getComputedStyle(containerPagamento).display || 'block';
}
containerPagamento.style.setProperty('display', 'none', 'important');
containerPagamento.style.setProperty('pointer-events', 'none', 'important');
containerPagamento.style.setProperty('visibility', 'hidden', 'important');
containerPagamento.style.setProperty('opacity', '0', 'important');
}
// ✅ Garantia extra: o Mais Pagamento não pode ficar “ativo” por baixo
if (conteinerMaisPagamento) {
conteinerMaisPagamento.classList.remove('ativo');
conteinerMaisPagamento.dataset.aberto = '0';
conteinerMaisPagamento.style.setProperty('display', 'none', 'important');
conteinerMaisPagamento.style.setProperty('pointer-events', 'none', 'important');
conteinerMaisPagamento.style.setProperty('visibility', 'hidden', 'important');
conteinerMaisPagamento.style.setProperty('opacity', '0', 'important');
}
// (mantém igual)
blocoPreco.style.opacity = '0';
blocoPreco.style.pointerEvents = 'none';
blocoPreco.style.display = 'none';
containerContrator.style.display = 'flex';
containerContrator.style.opacity = '1';
containerContrator.style.pointerEvents = 'auto';
});
////////////////////////////////////////////
// Armazena os últimos valores enviados por campo
const valoresEnviados = new Map();
// Envia dados ao endpoint com autenticação via API Key
function enviarParaWebhook(id, valor) {
// Captura sempre o valor do campo form-field-168
const urlPipeInput = document.getElementById('form-field-168');
const urlPipe = urlPipeInput ? urlPipeInput.value : '';
const payload = {
id,
valor,
urlPipe
};
// Evita envio duplicado do mesmo campo e mesmo valor
const chaveUnica = `${id}-${valor}-${urlPipe}`;
if (valoresEnviados.get(id) === chaveUnica) return;
valoresEnviados.set(id, chaveUnica);
fetch("https://iah9mm1rj6.execute-api.us-east-2.amazonaws.com/financial_planning/r1", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "II9ao2TV9i4jubEIXpcDC7Owc8ssqEVG1ERZB39Q"
},
body: JSON.stringify(payload)
}).catch(err => {
console.error('Erro ao enviar para o endpoint:', err);
});
}
// Dispara ao sair de campos de texto
document.addEventListener('blur', e => {
const el = e.target;
if ((el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT') && el.type !== 'radio' && el.type !== 'checkbox') {
const id = el.id || el.name;
const valor = el.value.trim();
enviarParaWebhook(id, valor);
}
}, true);
// Dispara ao alterar checkbox, radio ou select
document.addEventListener('change', e => {
const el = e.target;
const id = el.id || el.name;
if (el.tagName === 'INPUT') {
if (el.type === 'radio' && el.checked) {
enviarParaWebhook(id, el.value);
}
if (el.type === 'checkbox') {
enviarParaWebhook(id, el.checked);
}
}
if (el.tagName === 'SELECT') {
enviarParaWebhook(id, el.value);
}
});
const btnEnv = document.getElementById('btnenv');
if (btnEnv) {
btnEnv.addEventListener('click', () => {
conteinerFormulario.style.opacity = '0';
conteinerFormulario.style.pointerEvents = 'none';
const preco = document.querySelector('.preco');
if (preco) {
preco.style.opacity = '1';
}
// Envia o valor de "Fim do forms" com ID 390
const urlPipeInput = document.getElementById('form-field-168');
const urlPipe = urlPipeInput ? urlPipeInput.value : '';
fetch("https://iah9mm1rj6.execute-api.us-east-2.amazonaws.com/financial_planning/r1", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "II9ao2TV9i4jubEIXpcDC7Owc8ssqEVG1ERZB39Q"
},
body: JSON.stringify({
id: 390,
valor: "Fim do forms",
urlPipe: urlPipe
})
}).catch(err => {
console.error('Erro ao enviar "Fim do forms":', err);
});
});
}
});