7c3e98eaa9
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
125 lines
5.0 KiB
JavaScript
125 lines
5.0 KiB
JavaScript
// Email Engine client — sequence picker, brief, then a card per email with
|
||
// its own copy button (people paste them one at a time into an autoresponder).
|
||
(function () {
|
||
'use strict';
|
||
var KINDS = [
|
||
{ k: 'welcome', label: 'Welcome series (4)' },
|
||
{ k: 'followup', label: 'Follow-up series (4)' },
|
||
{ k: 'reengage', label: 'Re-engagement (3)' },
|
||
{ k: 'broadcast', label: 'Single broadcast' }
|
||
];
|
||
var PLACEHOLDERS = {
|
||
welcome: "e.g. New members who joined from my warm market — most are brand new to crypto and a little nervous",
|
||
followup: "e.g. People who watched my video and said they'd think about it — no pressure, keep it useful",
|
||
reengage: "e.g. Members who joined a month ago, got their wallet set up, then went quiet",
|
||
broadcast: "e.g. My marketing list — they know me from my content, not from crypto. Lead with the toolkit angle."
|
||
};
|
||
var $ = function (id) { return document.getElementById(id); };
|
||
var kind = 'welcome', busy = false;
|
||
|
||
function renderKinds() {
|
||
var host = $('emKinds');
|
||
host.innerHTML = '';
|
||
KINDS.forEach(function (x) {
|
||
var b = document.createElement('button');
|
||
b.type = 'button';
|
||
b.className = 'em-kind' + (x.k === kind ? ' on' : '');
|
||
b.textContent = x.label;
|
||
b.addEventListener('click', function () {
|
||
kind = x.k;
|
||
$('emBrief').placeholder = PLACEHOLDERS[kind] || '';
|
||
renderKinds();
|
||
});
|
||
host.appendChild(b);
|
||
});
|
||
}
|
||
|
||
function showMeter(m) {
|
||
if (!m || !m.limit) { $('emMeter').textContent = ''; return; }
|
||
$('emMeter').innerHTML = '<b>' + m.remaining + '</b> of ' + m.limit + ' emails left this month';
|
||
}
|
||
|
||
function gate(msg) {
|
||
var g = $('emGate');
|
||
g.style.display = 'block';
|
||
g.innerHTML = msg;
|
||
$('emCard').style.opacity = '.55';
|
||
$('emGo').disabled = true;
|
||
$('emBrief').disabled = true;
|
||
}
|
||
|
||
function renderEmails(list) {
|
||
var out = $('emOut');
|
||
out.innerHTML = '';
|
||
list.forEach(function (e, i) {
|
||
var d = document.createElement('div');
|
||
d.className = 'em-mail';
|
||
d.innerHTML = '<div class="n">Email ' + (i + 1) + '</div><div class="s"></div><div class="b"></div>' +
|
||
'<div class="act"><button class="btn btn-teal btn-sm">📋 Copy this email</button> <span class="em-hint ok" style="display:none;color:var(--teal)">Copied.</span></div>';
|
||
d.querySelector('.s').textContent = e.subject;
|
||
d.querySelector('.b').textContent = e.body;
|
||
d.querySelector('button').addEventListener('click', function () {
|
||
navigator.clipboard.writeText('Subject: ' + e.subject + '\n\n' + e.body).then(function () {
|
||
var ok = d.querySelector('.ok');
|
||
ok.style.display = '';
|
||
setTimeout(function () { ok.style.display = 'none'; }, 2200);
|
||
});
|
||
});
|
||
out.appendChild(d);
|
||
});
|
||
out.style.display = 'block';
|
||
}
|
||
|
||
async function boot() {
|
||
renderKinds();
|
||
try {
|
||
var r = await fetch('/api/public/suite-meters');
|
||
if (r.status === 401) { gate('You’re not signed in yet. <a href="/suite" style="color:var(--gold);font-weight:700">Open the Suite</a> and connect the wallet that holds your position, then come back.'); return; }
|
||
if (r.status === 403) { gate('The Email Engine isn’t open for this position yet. <a href="/suite" style="color:var(--gold);font-weight:700">See your Suite</a>.'); return; }
|
||
if (!r.ok) return;
|
||
var d = await r.json();
|
||
var m = d.meters && d.meters.email;
|
||
if (m && m.reason === 'locked') {
|
||
gate('The Email Engine unlocks at <b>Fabrica (level 3)</b>. You’re at level ' + d.level + ' — keep climbing and it opens, along with the Video Maker. <a href="/how-pay-works#exact-costs" style="color:var(--gold);font-weight:700">See what upgrades cost →</a>');
|
||
return;
|
||
}
|
||
showMeter(m);
|
||
} catch (e) {}
|
||
}
|
||
|
||
async function run() {
|
||
if (busy) return;
|
||
busy = true;
|
||
$('emErr').style.display = 'none';
|
||
$('emGo').disabled = true;
|
||
$('emGo').innerHTML = '<span class="spin"></span>Writing the sequence… (up to two minutes)';
|
||
try {
|
||
var r = await fetch('/api/public/suite-email', {
|
||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ kind: kind, brief: $('emBrief').value.trim() })
|
||
});
|
||
var d = await r.json();
|
||
if (!r.ok) {
|
||
$('emErr').textContent = d.error || 'That didn’t go through — try again.';
|
||
$('emErr').style.display = 'block';
|
||
if (d.meter) showMeter(d.meter);
|
||
} else {
|
||
renderEmails(d.emails || []);
|
||
showMeter(d.meter);
|
||
$('emOut').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||
}
|
||
} catch (e) {
|
||
$('emErr').textContent = 'Connection hiccup — try again.';
|
||
$('emErr').style.display = 'block';
|
||
}
|
||
busy = false;
|
||
$('emGo').disabled = false;
|
||
$('emGo').textContent = '✉️ Write the sequence';
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
boot();
|
||
$('emGo').addEventListener('click', run);
|
||
});
|
||
})();
|