233 lines
12 KiB
HTML
233 lines
12 KiB
HTML
|
|
<div id="sothis-leads">
|
||
|
|
<style>
|
||
|
|
#sothis-leads { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; color: #1f2933; line-height: 1.5; }
|
||
|
|
#sothis-leads * { box-sizing: border-box; }
|
||
|
|
#sothis-leads .sl-wrap { max-width: 880px; margin: 0 auto; }
|
||
|
|
#sothis-leads h1 { font-size: 22px; margin-bottom: 4px; }
|
||
|
|
#sothis-leads .sl-sub { color: #61707d; font-size: 14px; margin-bottom: 20px; }
|
||
|
|
#sothis-leads .sl-card {
|
||
|
|
background: #fff; border: 1px solid #e1e7ed; border-radius: 12px;
|
||
|
|
padding: 24px; box-shadow: 0 1px 3px rgba(0,0,0,.05); margin-bottom: 24px;
|
||
|
|
}
|
||
|
|
#sothis-leads .sl-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||
|
|
#sothis-leads .sl-full { grid-column: 1 / -1; }
|
||
|
|
#sothis-leads label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 6px; }
|
||
|
|
#sothis-leads label .sl-req { color: #d93025; }
|
||
|
|
#sothis-leads input, #sothis-leads select, #sothis-leads textarea {
|
||
|
|
width: 100%; padding: 10px 12px; border: 1px solid #cbd5dd; border-radius: 8px;
|
||
|
|
font-size: 14px; font-family: inherit; background: #fff; color: #1f2933;
|
||
|
|
}
|
||
|
|
#sothis-leads input:focus, #sothis-leads select:focus, #sothis-leads textarea:focus { outline: 2px solid #ff7a59; border-color: #ff7a59; outline-offset: -1px; }
|
||
|
|
#sothis-leads textarea { min-height: 110px; resize: vertical; }
|
||
|
|
#sothis-leads .sl-hint { font-size: 12px; color: #8795a1; margin-top: 4px; }
|
||
|
|
#sothis-leads .sl-actions { display: flex; gap: 12px; align-items: center; margin-top: 20px; }
|
||
|
|
#sothis-leads button {
|
||
|
|
background: #31a3b5; color: #fff; border: none; border-radius: 20px;
|
||
|
|
padding: 12px 28px; font-size: 15px; font-weight: 600; cursor: pointer;
|
||
|
|
}
|
||
|
|
#sothis-leads button:hover { background: #1d6570; }
|
||
|
|
#sothis-leads button:disabled { background: #cbd5dd; cursor: not-allowed; }
|
||
|
|
#sothis-leads .sl-btn-sec { background: #fff; color: #45535e; border: 1px solid #cbd5dd; }
|
||
|
|
#sothis-leads .sl-btn-sec:hover { background: #f5f7fa; }
|
||
|
|
#sothis-leads #sl-status { font-size: 14px; }
|
||
|
|
#sothis-leads .sl-ok { color: #0b8043; } #sothis-leads .sl-err { color: #d93025; white-space: pre-wrap; }
|
||
|
|
#sothis-leads .sl-spinner { display: inline-block; width: 14px; height: 14px; border: 2px solid #ff7a59;
|
||
|
|
border-top-color: transparent; border-radius: 50%; animation: sl-sp .7s linear infinite; vertical-align: -2px; margin-right: 6px; }
|
||
|
|
@keyframes sl-sp { to { transform: rotate(360deg); } }
|
||
|
|
#sothis-leads h2 { font-size: 16px; margin-bottom: 12px; }
|
||
|
|
#sothis-leads table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||
|
|
#sothis-leads th, #sothis-leads td { text-align: left; padding: 8px 10px; border-bottom: 1px solid #eef1f4; vertical-align: top; }
|
||
|
|
#sothis-leads th { color: #61707d; font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .03em; }
|
||
|
|
#sothis-leads .sl-badge { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; }
|
||
|
|
#sothis-leads .sl-b-lead { background: #e8f0fe; color: #1a56db; }
|
||
|
|
#sothis-leads .sl-b-prospect { background: #fef3e2; color: #b45309; }
|
||
|
|
#sothis-leads .sl-empty { color: #8795a1; text-align: center; padding: 16px; }
|
||
|
|
#sothis-leads .sl-steps { font-size: 13px; color: #61707d; margin-top: 10px; }
|
||
|
|
#sothis-leads .sl-steps span { margin-right: 14px; }
|
||
|
|
#sothis-leads .sl-done::before { content: "✓ "; color: #0b8043; }
|
||
|
|
#sothis-leads .sl-pend::before { content: "○ "; color: #cbd5dd; }
|
||
|
|
#sothis-leads .sl-fail::before { content: "✗ "; color: #d93025; }
|
||
|
|
#sothis-leads .sl-hp { position: absolute; left: -9999px; top: -9999px; height: 0; width: 0; overflow: hidden; }
|
||
|
|
</style>
|
||
|
|
|
||
|
|
<div class="sl-wrap">
|
||
|
|
<h1>Cadastro de Leads</h1>
|
||
|
|
<div class="sl-sub">Preencha os dados do novo lead ou prospect. Ao enviar, serão criados no HubSpot: a <b>Empresa</b>, o <b>Contato</b> (vinculado à empresa) e um <b>Negócio</b> no pipeline Sales Process.</div>
|
||
|
|
|
||
|
|
<div class="sl-card">
|
||
|
|
<form id="sl-leadForm">
|
||
|
|
<div class="sl-grid">
|
||
|
|
<div>
|
||
|
|
<label>Nome do Cliente (empresa) Oportunidade: <span class="sl-req">*</span></label>
|
||
|
|
<input id="sl-empresa" required placeholder="Ex.: ACME Ltda">
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label>Tipo: <span class="sl-req">*</span></label>
|
||
|
|
<select id="sl-tipo">
|
||
|
|
<option value="lead">Lead</option>
|
||
|
|
<option value="prospect">Prospect</option>
|
||
|
|
</select>
|
||
|
|
<div class="sl-hint">Define o estágio inicial do negócio no pipeline</div>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label>Nome do Contato da Oportunidade: <span class="sl-req">*</span></label>
|
||
|
|
<input id="sl-contato" required placeholder="Ex.: Maria Silva">
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label>Telefone / WhatsApp: <span class="sl-req">*</span></label>
|
||
|
|
<input type="number" id="sl-telefone" required placeholder="Ex.: +55 11 99999-9999">
|
||
|
|
</div>
|
||
|
|
<div class="sl-full">
|
||
|
|
<label>E-mail do Contato: <span class="sl-req">*</span></label>
|
||
|
|
<input id="sl-email" type="email" required placeholder="Ex.: maria@acme.com.br">
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label>Nome do Canal de Venda:</label>
|
||
|
|
<input id="sl-canalNome" placeholder="Ex.: Parceiro / Revenda XYZ">
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label>Contato do Canal de Venda:</label>
|
||
|
|
<input id="sl-canalContato" placeholder="Ex.: João Souza">
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label>Telefone / WhatsApp:</label>
|
||
|
|
<input type="number" id="sl-telefoneOpp" placeholder="Ex.: +55 11 99999-9999">
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<label>Telefone / WhatsApp do Canal de Venda:</label>
|
||
|
|
<input type="number" id="sl-canalTelefone" placeholder="Ex.: +55 11 99999-9999">
|
||
|
|
</div>
|
||
|
|
<div class="sl-full">
|
||
|
|
<label>Escopo / Serviço / Necessidade: <span class="sl-req">*</span></label>
|
||
|
|
<textarea id="sl-escopo" required placeholder="Detalhe o que o cliente precisa: serviço de interesse, contexto, urgência, orçamento estimado, etc."></textarea>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="sl-hp"><label>Não preencha<input id="sl-_gotcha" tabindex="-1" autocomplete="off"></label></div>
|
||
|
|
<div class="sl-actions">
|
||
|
|
<button type="submit" id="sl-btnSalvar">Cadastrar</button>
|
||
|
|
<button type="button" class="sl-btn-sec" id="sl-btnLimpar">Apagar tudo</button>
|
||
|
|
<span id="sl-status"></span>
|
||
|
|
</div>
|
||
|
|
<div class="sl-steps" id="sl-steps" style="display:none">
|
||
|
|
<span id="sl-st-emp" class="sl-pend">Empresa</span>
|
||
|
|
<span id="sl-st-con" class="sl-pend">Contato</span>
|
||
|
|
<span id="sl-st-neg" class="sl-pend">Negócio</span>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="sl-card">
|
||
|
|
<h2>Leads cadastrados nessa sessão</h2>
|
||
|
|
<table>
|
||
|
|
<thead><tr><th>Data</th><th>Cliente</th><th>Contato</th><th>Tipo</th><th>Escopo</th><th>Status</th></tr></thead>
|
||
|
|
<tbody id="sl-histBody"><tr><td colspan="6" class="sl-empty">Nenhum lead cadastrado ainda.</td></tr></tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
(function () {
|
||
|
|
// Endpoint criado pelo snippet PHP (leads-backend.php) via WPCode.
|
||
|
|
// Como a pagina e o endpoint estao no mesmo dominio, o caminho relativo funciona.
|
||
|
|
var ENDPOINT = "/wp-json/sothis/v1/leads";
|
||
|
|
|
||
|
|
var TIPO_LABEL = { lead: "Lead", prospect: "Prospect" };
|
||
|
|
var TIPO_CLASS = { lead: "sl-b-lead", prospect: "sl-b-prospect" };
|
||
|
|
|
||
|
|
function getHist() { try { return JSON.parse(localStorage.getItem("sothisLeadsHist") || "[]"); } catch (e) { return []; } }
|
||
|
|
function saveHist(h) { localStorage.setItem("sothisLeadsHist", JSON.stringify(h.slice(0, 50))); }
|
||
|
|
function esc(s) { var d = document.createElement("div"); d.textContent = s || ""; return d.innerHTML; }
|
||
|
|
|
||
|
|
function renderHist() {
|
||
|
|
var h = getHist();
|
||
|
|
var tb = document.getElementById("sl-histBody");
|
||
|
|
if (!h.length) { tb.innerHTML = '<tr><td colspan="6" class="sl-empty">Nenhum lead cadastrado ainda.</td></tr>'; return; }
|
||
|
|
tb.innerHTML = h.map(function (r) {
|
||
|
|
return '<tr>' +
|
||
|
|
'<td>' + esc(r.data) + '</td><td>' + esc(r.empresa) + '</td>' +
|
||
|
|
'<td>' + esc(r.contato) + '<br><span style="color:#8795a1">' + esc(r.email) + '</span></td>' +
|
||
|
|
'<td><span class="sl-badge ' + (TIPO_CLASS[r.tipo] || "sl-b-lead") + '">' + esc(TIPO_LABEL[r.tipo] || r.tipo) + '</span></td>' +
|
||
|
|
'<td>' + esc((r.escopo || "").length > 80 ? r.escopo.slice(0, 80) + "…" : r.escopo) + '</td>' +
|
||
|
|
'<td class="' + (r.ok ? "sl-ok" : "sl-err") + '">' + (r.ok ? "Enviado ✓" : "Falhou") + '</td>' +
|
||
|
|
'</tr>';
|
||
|
|
}).join("");
|
||
|
|
}
|
||
|
|
|
||
|
|
function setStep(id, cls) { document.getElementById(id).className = cls; }
|
||
|
|
|
||
|
|
document.getElementById("sl-btnLimpar").onclick = function () {
|
||
|
|
document.getElementById("sl-leadForm").reset();
|
||
|
|
document.getElementById("sl-status").textContent = "";
|
||
|
|
document.getElementById("sl-steps").style.display = "none";
|
||
|
|
};
|
||
|
|
|
||
|
|
document.getElementById("sl-leadForm").addEventListener("submit", function (e) {
|
||
|
|
e.preventDefault();
|
||
|
|
var payload = {
|
||
|
|
empresa: document.getElementById("sl-empresa").value.trim(),
|
||
|
|
contato: document.getElementById("sl-contato").value.trim(),
|
||
|
|
telefone: document.getElementById("sl-telefone").value.trim(),
|
||
|
|
email: document.getElementById("sl-email").value.trim(),
|
||
|
|
escopo: document.getElementById("sl-escopo").value.trim(),
|
||
|
|
tipo: document.getElementById("sl-tipo").value,
|
||
|
|
canalNome: document.getElementById("sl-canalNome").value.trim(),
|
||
|
|
canalContato: document.getElementById("sl-canalContato").value.trim(),
|
||
|
|
telefoneOpp: document.getElementById("sl-telefoneOpp").value.trim(),
|
||
|
|
canalTelefone: document.getElementById("sl-canalTelefone").value.trim(),
|
||
|
|
_gotcha: document.getElementById("sl-_gotcha").value
|
||
|
|
};
|
||
|
|
|
||
|
|
var btn = document.getElementById("sl-btnSalvar");
|
||
|
|
var status = document.getElementById("sl-status");
|
||
|
|
var steps = document.getElementById("sl-steps");
|
||
|
|
btn.disabled = true;
|
||
|
|
steps.style.display = "block";
|
||
|
|
["sl-st-emp", "sl-st-con", "sl-st-neg"].forEach(function (i) { setStep(i, "sl-pend"); });
|
||
|
|
status.className = "";
|
||
|
|
status.innerHTML = '<span class="sl-spinner"></span>Enviando para a Sothis…';
|
||
|
|
|
||
|
|
var ok = false;
|
||
|
|
fetch(ENDPOINT, {
|
||
|
|
method: "POST",
|
||
|
|
headers: { "Content-Type": "application/json" },
|
||
|
|
body: JSON.stringify(payload)
|
||
|
|
}).then(function (r) {
|
||
|
|
return r.json().catch(function () { return {}; }).then(function (res) { return { r: r, res: res }; });
|
||
|
|
}).then(function (result) {
|
||
|
|
var r = result.r, res = result.res;
|
||
|
|
var st = res.steps || {};
|
||
|
|
setStep("sl-st-emp", st.company ? "sl-done" : "sl-fail");
|
||
|
|
setStep("sl-st-con", st.contact ? "sl-done" : "sl-fail");
|
||
|
|
setStep("sl-st-neg", st.deal ? "sl-done" : "sl-fail");
|
||
|
|
|
||
|
|
if (r.ok && res.ok) {
|
||
|
|
ok = true;
|
||
|
|
status.className = "sl-ok";
|
||
|
|
status.textContent = "✓ Lead enviado com sucesso!";
|
||
|
|
document.getElementById("sl-leadForm").reset();
|
||
|
|
} else {
|
||
|
|
status.className = "sl-err";
|
||
|
|
status.textContent = "Erro ao enviar: " + String(res.error || ("HTTP " + r.status)).slice(0, 300);
|
||
|
|
}
|
||
|
|
}).catch(function (err) {
|
||
|
|
["sl-st-emp", "sl-st-con", "sl-st-neg"].forEach(function (i) {
|
||
|
|
if (document.getElementById(i).className === "sl-pend") setStep(i, "sl-fail");
|
||
|
|
});
|
||
|
|
status.className = "sl-err";
|
||
|
|
status.textContent = "Erro de conexão: " + String((err && err.message) || err).slice(0, 300);
|
||
|
|
}).finally(function () {
|
||
|
|
var h = getHist();
|
||
|
|
h.unshift({
|
||
|
|
data: new Date().toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" }),
|
||
|
|
empresa: payload.empresa, contato: payload.contato, email: payload.email, tipo: payload.tipo, escopo: payload.escopo, ok: ok
|
||
|
|
});
|
||
|
|
saveHist(h);
|
||
|
|
renderHist();
|
||
|
|
btn.disabled = false;
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
renderHist();
|
||
|
|
})();
|
||
|
|
</script>
|
||
|
|
</div>
|