// Founder Desk client — builds the weekly pack and manages the API key.
(function () {
'use strict';
var $ = function (id) { return document.getElementById(id); };
function esc(s) {
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
});
}
function gate(msg) {
$('dGate').style.display = 'block';
$('dGate').innerHTML = msg;
}
function copyBtn(getText) {
var b = document.createElement('button');
b.className = 'btn btn-secondary btn-sm d-copy';
b.textContent = 'Copy';
b.addEventListener('click', function () {
if (navigator.clipboard) navigator.clipboard.writeText(getText());
b.textContent = 'Copied';
setTimeout(function () { b.textContent = 'Copy'; }, 1400);
});
return b;
}
function item(tag, subject, body) {
var d = document.createElement('div');
d.className = 'd-item';
var html = '
' + esc(tag) + '
';
if (subject) html += '' + esc(subject) + '
';
html += '' + esc(body) + '
';
d.innerHTML = html;
d.appendChild(copyBtn(function () { return (subject ? subject + '\n\n' : '') + body; }));
return d;
}
function section(title, why) {
var s = document.createElement('div');
s.className = 'd-sec';
s.innerHTML = '' + esc(title) + '
' + esc(why) + '
';
return s;
}
function renderPack(pack) {
var host = $('dPack');
host.innerHTML = '';
if (!pack) return;
if ((pack.posts || []).length) {
var s1 = section('Social posts — one per angle',
'Post one a day. They deliberately take different angles so the week does not read as the same message five times.');
pack.posts.forEach(function (p) { s1.appendChild(item(p.angle, '', p.text)); });
host.appendChild(s1);
}
if ((pack.messages || []).length) {
var s2 = section('Outreach messages',
'For the conversations that actually build the team — a first approach, a follow-up for someone gone quiet, and the pyramid-scheme answer.');
pack.messages.forEach(function (mm) { s2.appendChild(item(mm.label, '', mm.text)); });
host.appendChild(s2);
}
if ((pack.emails || []).length) {
var s3 = section('Email for your list', 'Drop straight into your autoresponder.');
pack.emails.forEach(function (em) { s3.appendChild(item('Email', em.subject, em.body)); });
host.appendChild(s3);
}
if ((pack.textAds || []).length) {
var s4 = section('Text ads', 'Ready to run from the Traffic Desk.');
var grid = document.createElement('div');
grid.className = 'd-ads';
pack.textAds.forEach(function (a) {
var c = document.createElement('div');
c.className = 'd-ad';
var h = '' + esc(a.subject) + '
';
(a.lines || []).forEach(function (l) { if (l) h += '' + esc(l) + '
'; });
c.innerHTML = h;
grid.appendChild(c);
});
s4.appendChild(grid);
host.appendChild(s4);
}
// Be straight about anything the engine could not produce, rather than
// quietly handing over a short pack.
if ((pack.problems || []).length) {
var warn = document.createElement('div');
warn.className = 'd-item';
warn.style.borderColor = '#f0a05a';
warn.innerHTML = 'Incomplete
' +
'The engine could not finish: ' + esc(pack.problems.join(', ')) +
'. Everything else above is fine — build again to retry the missing pieces.
';
host.appendChild(warn);
}
}
function renderKey(info, freshKey) {
var host = $('dKeyState');
if (freshKey) {
host.innerHTML = 'Copy this now — it is shown once and never again.
' +
'' + esc(freshKey) + '
';
var b = copyBtn(function () { return freshKey; });
b.style.position = 'static';
b.style.marginTop = '8px';
host.appendChild(b);
$('dIssue').textContent = 'Replace this key';
$('dRevoke').style.display = '';
return;
}
if (info && info.exists) {
host.innerHTML = 'A key is active (' + esc(info.hint) + '), created ' +
esc(String(info.createdAt || '').slice(0, 10)) +
(info.lastUsedAt ? ', last used ' + esc(String(info.lastUsedAt).slice(0, 10)) : ', never used yet') +
'. The key itself is stored hashed, so it cannot be shown again — create a new one if you have lost it.
';
$('dIssue').textContent = 'Replace this key';
$('dRevoke').style.display = '';
} else {
host.innerHTML = 'No key yet.
';
$('dIssue').textContent = 'Create a key';
$('dRevoke').style.display = 'none';
}
}
async function build(btn) {
btn.disabled = true;
btn.innerHTML = 'Building your week — this takes a minute…';
$('dErr').style.display = 'none';
try {
var r = await fetch('/api/public/suite-founder', {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}'
});
var d = await r.json();
if (!r.ok) {
$('dErr').textContent = d.error || 'That did not build — try again.';
$('dErr').style.display = 'block';
} else {
renderPack(d.pack);
if (d.meter) $('dMeter').textContent = d.meter.remaining + ' of ' + d.meter.limit + ' packs left this month';
}
} catch (e) {
$('dErr').textContent = 'Connection hiccup — try again.';
$('dErr').style.display = 'block';
}
btn.disabled = false;
btn.innerHTML = '👑 Build my week';
}
async function issue() {
if (!window.confirm('Create a new API key? Any existing key stops working immediately.')) return;
try {
var r = await fetch('/api/public/suite-founder', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ issueKey: true })
});
var d = await r.json();
if (r.ok) renderKey(d.info, d.key);
} catch (e) {}
}
async function revoke() {
if (!window.confirm('Revoke your API key? Anything using it stops working.')) return;
try {
var r = await fetch('/api/public/suite-founder', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ revokeKey: true })
});
var d = await r.json();
if (r.ok) renderKey(d.info, null);
} catch (e) {}
}
async function boot() {
try {
var r = await fetch('/api/public/suite-founder');
if (r.status === 401) { gate('You’re not signed in yet. Open the Suite and connect the wallet that holds your position, then come back.'); return; }
if (r.status === 403) {
var g = await r.json();
gate((g.error || 'Not open for this position yet.') + ' See your Suite.');
return;
}
if (!r.ok) return;
var d = await r.json();
$('dBody').style.display = 'block';
renderPack(d.pack);
renderKey(d.key, null);
if (d.meter) $('dMeter').textContent = d.meter.remaining + ' of ' + d.meter.limit + ' packs left this month';
if (d.writerReady === false) {
$('dGo').disabled = true;
$('dGo').textContent = 'The writer is warming up';
}
} catch (e) {}
}
document.addEventListener('DOMContentLoaded', function () {
boot();
$('dGo').addEventListener('click', function () { build(this); });
$('dIssue').addEventListener('click', issue);
$('dRevoke').addEventListener('click', revoke);
});
})();