Overview: Getting-started stepper (username, link wallet, switch on payouts, first package) with one guided button per step; soft nudge, collapsible, never a gate

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-14 05:53:02 -05:00
parent 825d0de6c3
commit b09e5dce8b
4 changed files with 54 additions and 2 deletions
+34
View File
@@ -472,12 +472,45 @@
$('lbList').innerHTML = h; $('lbCard').hidden = false;
} catch (e) {}
}
// Getting-started stepper: username -> wallet -> payouts -> first package. The current step gets one big
// button that opens the right tab and pulses the control (Jim: "it wasn't obvious", 2026-09-14)
function renderSteps(d) {
const card = $('gsCard'); if (!card) return;
const hasUser = !!d.username, hasWallet = !!d.address, hasPayouts = !!d.memberId, hasPackage = !!(d.memberId && (d.credits || 0) > 0);
const steps = [
{ k: 'user', t: 'Pick a username', done: hasUser },
{ k: 'wallet', t: 'Link your wallet', done: hasWallet },
{ k: 'payouts', t: 'Switch on payouts', done: hasPayouts },
{ k: 'package', t: 'Your first package', done: hasPackage }
];
if (steps.every(x => x.done)) { card.hidden = true; return; }
const cur = steps.find(x => !x.done);
let collapsed = false; try { collapsed = localStorage.getItem('iap.gs.collapsed') === '1'; } catch (e) {}
$('gsSub').innerHTML = steps.filter(x => x.done).length + ' of 4 done · <a href="#" id="gsToggle" style="color:var(--mint);text-decoration:none">' + (collapsed ? 'show' : 'hide') + '</a>';
$('gsToggle').addEventListener('click', e => { e.preventDefault(); try { localStorage.setItem('iap.gs.collapsed', collapsed ? '0' : '1'); } catch (er) {} renderSteps(d); });
$('gsSteps').hidden = collapsed; $('gsNow').hidden = collapsed;
$('gsSteps').innerHTML = steps.map((x, i) => '<div class="gs-step' + (x.done ? ' done' : x === cur ? ' now' : '') + '"><span class="n">' + (x.done ? '✓' : i + 1) + '</span><span>' + x.t + '</span></div>').join('');
const copy = {
user: ['Pick your username', 'It becomes your invite link and your public page, and it is permanent.', 'Choose a username', () => showOnboard(true)],
wallet: ['Link your wallet', 'Your payouts land in a wallet you control, so the site needs to know which one is yours. One free signature, no purchase. MetaMask recommended; Phantom users switch Polygon on first. Never held crypto? The wallet guide walks you through it.', 'Link my wallet', () => jumpTo('wallet', 'linkBtn')],
payouts: ['Switch on payouts', 'One small transaction registers your wallet with the contract so commissions can reach it. Buying any package does this automatically.', 'Activate payouts', () => jumpTo('wallet', 'activateBtn')],
package: ['Your first package', 'From $5. The $20 Activation package is the one that counts you as a qualifying buyer for your sponsor and unlocks adopting from the holding tank.', 'See packages', () => jumpTo('buy', null)]
}[cur.k];
$('gsNow').innerHTML = '<p><b>Next: ' + copy[0] + '</b>' + copy[1] + (cur.k === 'wallet' ? ' <a href="/wallets" target="_blank" rel="noopener" style="color:var(--mint)">Wallet guide</a>' : '') + '</p><button type="button" class="btn" id="gsGo">' + copy[2] + '</button>';
$('gsGo').addEventListener('click', copy[3]);
card.hidden = false;
}
function jumpTo(pane, id) {
setPane(pane);
setTimeout(() => { const el = id && $(id); if (!el) return; el.scrollIntoView({ behavior: 'smooth', block: 'center' }); el.classList.remove('pulse'); void el.offsetWidth; el.classList.add('pulse'); try { el.focus({ preventScroll: true }); } catch (e) {} }, 250);
}
async function loadDashboard() {
try {
loadNews(); loadLeaderboard();
const d = await (await fetch('/api/my/dashboard')).json();
if (d.error) return;
chatSync(d);
try { renderSteps(d); } catch (e) {}
MYID = d.memberId || MYID;
startLiveFeed();
if (!window.__lbChecked) { // daily login bonus, once per session (server guards once/day)
@@ -671,6 +704,7 @@
setPane(location.hash.slice(1) || 'overview');
$('campGate').hidden = !!me.memberId;
$('earnGate').hidden = !!me.memberId;
if (me.username && !me.address) { let shown = ''; try { shown = sessionStorage.getItem('iap.gs.walletHint'); sessionStorage.setItem('iap.gs.walletHint', '1'); } catch (e) {} if (shown !== '1') setTimeout(() => IAP.status('Next step when you are ready: link your wallet so your payouts have somewhere to land. The Getting started card at the top shows how.', 'ok'), 1200); }
loadDashboard();
const who = [];
+12
View File
@@ -678,3 +678,15 @@ img{max-width:100%}
.wo-slot.locked{opacity:.6}
.wo-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}
.wo-slot img{max-width:100%;border-radius:8px}
/* getting-started stepper on the Overview (Jim could not find the wallet step, 2026-09-14) */
.gs{border-color:rgba(67,232,195,.45)}
.gs-steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px;margin:4px 0 12px}
.gs-step{display:flex;gap:10px;align-items:center;border:1px solid var(--line);border-radius:12px;padding:10px 12px;color:var(--muted);font-size:14px}
.gs-step .n{flex:0 0 26px;width:26px;height:26px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-weight:700;border:1px solid var(--line);font-size:13px}
.gs-step.done{color:var(--ink)} .gs-step.done .n{background:var(--mint);color:var(--mint-ink);border-color:var(--mint)}
.gs-step.now{color:var(--ink);border-color:var(--mint);background:rgba(67,232,195,.07)} .gs-step.now .n{border-color:var(--mint);color:var(--mint)}
.gs-now{display:flex;gap:14px;align-items:center;flex-wrap:wrap;border-top:1px solid var(--line);padding-top:12px}
.gs-now p{margin:0;flex:1;min-width:240px;max-width:60ch} .gs-now p b{display:block;font-family:var(--disp);font-size:16px;margin-bottom:2px}
@keyframes gsPulse{0%{box-shadow:0 0 0 0 rgba(67,232,195,.7)}70%{box-shadow:0 0 0 14px rgba(67,232,195,0)}100%{box-shadow:0 0 0 0 rgba(67,232,195,0)}}
.pulse{animation:gsPulse 1.2s ease-out 4}