// Page Builder client. Loads the member's existing page (if any), takes a // four-question brief, and rebuilds at the same /p/ URL so shared links // never break. (function () { 'use strict'; var $ = function (id) { return document.getElementById(id); }; var angle = 'overview', busy = false, myId = null; // null = building a NEW page; '' = rebuilding the main one; 'slug' = that page var replacing = null, pages = [], cap = 0; function renderAngles(angles) { var host = $('pgAngles'); host.innerHTML = ''; Object.keys(angles).forEach(function (k) { var b = document.createElement('button'); b.type = 'button'; b.className = 'pg-angle' + (k === angle ? ' on' : ''); b.textContent = angles[k].label; b.addEventListener('click', function () { angle = k; renderAngles(angles); }); host.appendChild(b); }); } function esc(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); } function setMode(mode, label) { replacing = mode; $('pgGo').textContent = mode === null ? '🧱 Build a new page' : '🔁 Rebuild ' + (label || 'this page'); if (mode !== null) $('pgCard').scrollIntoView({ behavior: 'smooth', block: 'start' }); } function renderPages() { var wrap = $('pgPagesWrap'), host = $('pgPages'); if (!pages.length) { wrap.style.display = 'none'; return; } wrap.style.display = 'block'; host.innerHTML = ''; pages.forEach(function (p) { var row = document.createElement('div'); row.className = 'pg-page' + (p.slug ? '' : ' main'); var left = document.createElement('div'); left.style.minWidth = '210px'; left.innerHTML = '
' + esc(p.name || 'Your page') + (p.slug ? '' : ' MAIN') + '
' + '
rmcircle.team' + esc(p.url) + '
' + '
' + esc(p.angle || '') + (p.updatedAt ? ' · updated ' + esc(String(p.updatedAt).slice(0, 10)) : '') + '
'; row.appendChild(left); var acts = document.createElement('div'); acts.className = 'pg-acts'; var view = document.createElement('a'); view.className = 'btn btn-secondary btn-sm'; view.href = p.url; view.target = '_blank'; view.rel = 'noopener'; view.textContent = 'View'; acts.appendChild(view); var copy = document.createElement('button'); copy.className = 'btn btn-secondary btn-sm'; copy.textContent = 'Copy link'; copy.addEventListener('click', function () { if (navigator.clipboard) navigator.clipboard.writeText('https://rmcircle.team' + p.url); copy.textContent = 'Copied'; setTimeout(function () { copy.textContent = 'Copy link'; }, 1400); }); acts.appendChild(copy); var re = document.createElement('button'); re.className = 'btn btn-secondary btn-sm'; re.textContent = 'Rebuild'; re.addEventListener('click', function () { setMode(p.slug || '', p.name || 'this page'); }); acts.appendChild(re); // The main page keeps its address forever — it is on banners and printed // flyers — so only the extra pages can be removed. if (p.slug) { var del = document.createElement('button'); del.className = 'btn btn-secondary btn-sm'; del.textContent = 'Delete'; del.addEventListener('click', function () { removePage(p.slug, p.name, del); }); acts.appendChild(del); } row.appendChild(acts); host.appendChild(row); }); var left = Math.max(0, cap - pages.length); $('pgCapNote').innerHTML = 'Holding ' + pages.length + ' of ' + cap + ' pages your level allows' + (left ? ' — room for ' + left + ' more.' : ' — delete one to add another, or upgrade to hold more.'); if (!left && replacing === null) setMode(pages.length ? (pages[0].slug || '') : null, pages[0] && pages[0].name); } async function removePage(slug, name, btn) { if (!window.confirm('Delete "' + (name || slug) + '"? Anything pointing at that address will fall back to your main page.')) return; btn.disabled = true; try { var r = await fetch('/api/public/suite-page', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ remove: slug }) }); var d = await r.json(); if (r.ok) { pages = d.pages || []; cap = d.cap || cap; renderPages(); } else btn.disabled = false; } catch (e) { btn.disabled = false; } } function showMeter(m) { if (!m || !m.limit) { $('pgMeter').textContent = ''; return; } $('pgMeter').innerHTML = '' + m.remaining + ' of ' + m.limit + ' page builds left this month'; } var built = false; function showLive(url) { built = true; $('pgUrl').textContent = url.replace(/^https:\/\//, ''); $('pgOpen').href = url; $('pgLive').style.display = 'block'; // cache-bust so a rebuild always shows the NEW page, not the old one $('pgFrame').src = url + '?preview=' + Date.now(); $('pgPrev').style.display = 'block'; } // Live status under the button. A silent 40-second wait reads as "frozen", // so we narrate it and count the seconds. var progTimer = null; function startProgress() { var el = $('pgProg'), t0 = Date.now(); var steps = [ 'Reading your notes…', 'Writing your headline and story…', 'Choosing your angle video…', 'Laying out your page…', 'Almost there — finishing up…' ]; el.style.display = 'block'; var tick = function () { var s = Math.round((Date.now() - t0) / 1000); var i = Math.min(steps.length - 1, Math.floor(s / 9)); el.innerHTML = '' + steps[i] + ' (' + s + 's)'; }; tick(); progTimer = setInterval(tick, 1000); } function stopProgress() { if (progTimer) clearInterval(progTimer); progTimer = null; $('pgProg').style.display = 'none'; } function gate(msg) { var g = $('pgGate'); g.style.display = 'block'; g.innerHTML = msg; $('pgCard').style.opacity = '.55'; $('pgGo').disabled = true; } async function boot() { try { var r = await fetch('/api/public/suite-page'); 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.'); renderAngles({ overview: { label: 'General overview' } }); return; } if (r.status === 403) { gate('The Page Builder isn’t open for this position yet. See your Suite.'); return; } if (!r.ok) return; var d = await r.json(); myId = d.id; renderAngles(d.angles || {}); if (d.meter && d.meter.reason === 'locked') { gate('The Page Builder unlocks at Ascensus (level 2). You’re at level ' + d.level + ' — your next upgrade opens it, along with the Copy Engine. See what an upgrade costs →'); return; } showMeter(d.meter); pages = d.pages || []; cap = d.cap || 0; // First-time member: no pages yet, so the button builds their main one. // Anyone with pages starts in "add another" mode; renderPages flips that // to rebuild if they are already at their level's limit. setMode(pages.length ? null : '', null); renderPages(); if (d.page) { if (d.page.name) $('pgName').value = d.page.name; if (d.page.audience) $('pgAudience').value = d.page.audience; if (d.page.story) $('pgStory').value = d.page.story; if (d.page.angle) { angle = d.page.angle; renderAngles(d.angles || {}); } showLive('https://rmcircle.team/p/' + d.id); } } catch (e) {} } async function run() { if (busy) return; busy = true; $('pgErr').style.display = 'none'; $('pgGo').disabled = true; // Hide any previous preview while the new one is being written, so a stale // or half-loaded frame can't look like a broken result. $('pgPrev').style.display = 'none'; $('pgFrame').removeAttribute('src'); $('pgGo').innerHTML = 'Writing your page…'; startProgress(); try { var r = await fetch('/api/public/suite-page', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: $('pgName').value.trim(), audience: $('pgAudience').value.trim(), story: $('pgStory').value.trim(), angle: angle, replace: replacing === null ? undefined : replacing }) }); var d = await r.json(); if (!r.ok) { $('pgErr').textContent = d.error || 'That didn’t go through — try again.'; $('pgErr').style.display = 'block'; if (d.meter) showMeter(d.meter); } else { showMeter(d.meter); showLive(d.url); if (d.pages) { pages = d.pages; cap = d.cap || cap; renderPages(); } setMode(d.canAddMore ? null : (d.slug || ''), null); $('pgLive').scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } } catch (e) { $('pgErr').textContent = 'Connection hiccup — try again.'; $('pgErr').style.display = 'block'; } stopProgress(); busy = false; $('pgGo').disabled = false; setMode(replacing, null); } document.addEventListener('DOMContentLoaded', function () { boot(); $('pgGo').addEventListener('click', run); $('pgCopyUrl').addEventListener('click', function () { navigator.clipboard.writeText($('pgOpen').href).then(function () { $('pgCopied').style.display = ''; setTimeout(function () { $('pgCopied').style.display = 'none'; }, 2500); }); }); }); })();