Share card rebuilt for person-to-person referrals: always-present personal QR (moving link handles rotation), tap-to-fullscreen scan mode, guidance adapts to 2/2; render pipeline hardened so one panel error can never blank later panels

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-20 07:33:19 -05:00
parent 2e0620a251
commit bf7e9b7ea5
+40 -23
View File
@@ -123,14 +123,9 @@
document.getElementById('dIncome').innerHTML=inc.length document.getElementById('dIncome').innerHTML=inc.length
?`<div class="table-wrap"><table class="table" style="min-width:520px"><thead><tr><th>When</th><th>From member</th><th>For</th><th>Amount</th></tr></thead><tbody>${inc.map(p=>`<tr><td>${date(p.ts)}</td><td>#${p.fromId}</td><td>${esc(p.desc)}</td><td><strong>${fmt(p.pol)} POL</strong></td></tr>`).join('')}</tbody></table></div>` ?`<div class="table-wrap"><table class="table" style="min-width:520px"><thead><tr><th>When</th><th>From member</th><th>For</th><th>Amount</th></tr></thead><tbody>${inc.map(p=>`<tr><td>${date(p.ts)}</td><td>#${p.fromId}</td><td>${esc(p.desc)}</td><td><strong>${fmt(p.pol)} POL</strong></td></tr>`).join('')}</tbody></table></div>`
:'<div class="empty">No payments yet — they appear here the moment they land on-chain.</div>'; :'<div class="empty">No payments yet — they appear here the moment they land on-chain.</div>';
renderNextStep(d); // each panel renders independently — one panel's error must never blank
renderUpgradeCTA(d); // the ones after it (bit us: share-card QR vanished behind an earlier throw)
renderGens(d); [renderNextStep,renderUpgradeCTA,renderGens,renderPipeline,renderCoach,renderMessages,renderAlerts,renderShare].forEach(function(f){try{f(d);}catch(e){try{console.error('panel',f.name,e);}catch(_){}}});
renderPipeline(d);
renderCoach(d);
renderMessages(d);
renderAlerts(d);
renderShare(d);
document.getElementById('dLineage').innerHTML=d.uplineChain&&d.uplineChain.length document.getElementById('dLineage').innerHTML=d.uplineChain&&d.uplineChain.length
?`<strong style="color:var(--text)">#${d.id}</strong> → ${d.uplineChain.map(i=>'#'+i).join(' → ')} <span style="color:#8498aa">(root)</span>` ?`<strong style="color:var(--text)">#${d.id}</strong> → ${d.uplineChain.map(i=>'#'+i).join(' → ')} <span style="color:#8498aa">(root)</span>`
:'You are at the top of your line.'; :'You are at the top of your line.';
@@ -462,25 +457,47 @@
const el=document.getElementById('dShare'); const el=document.getElementById('dShare');
if(!el)return; if(!el)return;
const dashUrl=`https://rmcircle.team/join/${d.id}`; const dashUrl=`https://rmcircle.team/join/${d.id}`;
if(d.directCount>=2){ const q2=d.directCount>=2, nx=d.nextInLine;
const nx=d.nextInLine; // The personal QR is ALWAYS shown: /join/<id> self-rotates via the moving
if(nx){ // link, so a scan is always placed correctly — even after 2/2. This is the
const nxDash=`https://rmcircle.team/join/${nx.id}`; // person-to-person flow: open your page, tap the QR, they scan, they see
el.innerHTML=`<p style="color:var(--muted);font-size:14px;line-height:1.6;margin:6px 0 14px"><span class="q-badge q-yes" style="margin-right:6px">★ Qualified</span> The team play now moves down. <strong style="color:var(--text)">Next in line: #${nx.id}</strong> (${nx.directCount}/2 directs). Help them get their 2 — share their <strong>invite page</strong> (the full team-build pitch, personalized to them) or send prospects the join button, which carries <strong style="color:var(--gold)">#${nx.id}</strong> as sponsor. (If someone still joins through your own link, that's a bonus — the entry reward is yours and they spill down your leg — but only #${nx.id}'s own directs can qualify them.)</p><div style="display:flex;gap:16px;align-items:center;flex-wrap:wrap"><span id="dQr" class="qr-img"></span><div style="min-width:220px"><div style="display:flex;gap:8px;flex-wrap:wrap"><button id="copyShare" class="btn btn-secondary btn-sm">Copy #${nx.id}'s page link</button><a class="btn btn-primary btn-sm" href="${esc(nx.referralUrl||'#')}" target="_blank" rel="noopener noreferrer">Join under #${nx.id} →</a></div><p class="micro" style="margin:10px 0 0;word-break:break-all">${esc(nxDash)}</p><p class="micro" style="margin:6px 0 0">When #${nx.id} reaches 2/2 this rotates to the next position in your leg — the qualification wave keeps moving down. General traffic can still go through the <a href="/start" style="color:var(--teal)">team rotation</a>.</p></div></div>`; // your pitch page with the video and the join button.
const qr=document.getElementById('dQr');if(qr)qr.innerHTML=qrSvg(nxDash); const guidance=q2
const cp=document.getElementById('copyShare'); ?(nx?`<span class="q-badge q-yes" style="margin-right:6px">★ Qualified</span> Your page now routes new joins to <strong style="color:var(--text)">#${nx.id}</strong> (${nx.directCount||0}/2) — the team play moving down your leg automatically. Anyone who scans still lands on YOUR pitch page; placement is handled for you. A signup on your own link is always a bonus: the entry reward is yours.`
if(cp)cp.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(nxDash);cp.textContent='Copied ✓';setTimeout(()=>cp.textContent=`Copy #${nx.id}'s page link`,1400)}catch(e){}}); :`<span class="q-badge q-yes" style="margin-right:6px">★ Qualified</span> Your whole leg is qualified — your page routes new joins to the team rotation automatically.`)
return; :`Share this with your 2 — anyone who scans or clicks lands on your personal pitch page (video, live proof, join button) and joins under <strong style="color:var(--gold)">you</strong>.`;
} el.innerHTML=`<p style="color:var(--muted);font-size:14px;line-height:1.6;margin:6px 0 14px">${guidance}</p>
el.innerHTML=`<p style="color:var(--muted);font-size:14px;line-height:1.6;margin:6px 0 14px"><span class="q-badge q-yes" style="margin-right:6px">★ Qualified</span> This position has its 2 directs and everyone in your leg is qualified too — outstanding. Send new members through the team rotation — the current sponsor is always live on the start page.</p><div style="display:flex;gap:16px;align-items:center;flex-wrap:wrap"><img src="/qr-start.svg" alt="Team QR code" class="qr-img"><div><a class="btn btn-primary" href="/start">Open the Team Sponsor Page →</a><p class="micro" style="margin:10px 0 0">This QR sends prospects to the team rotation — show it or print it.</p></div></div>`; <div style="display:flex;gap:18px;align-items:center;flex-wrap:wrap">
return; <button id="dQrBtn" title="Tap to enlarge for scanning" style="background:#fff;border:0;border-radius:14px;padding:10px;cursor:pointer;line-height:0"><span id="dQr" style="display:block;width:150px;height:150px"></span></button>
} <div style="min-width:220px;flex:1">
const need=2-d.directCount; <div style="font-weight:800;margin-bottom:6px">📲 Show this QR to anyone, anywhere.</div>
el.innerHTML=`<p style="color:var(--muted);font-size:14px;line-height:1.6;margin:6px 0 14px">You need <strong style="color:var(--text)">${need} more direct${need===1?'':'s'}</strong> to qualify. Until then, share your <strong>personal invite page</strong> — the link and QR below. A prospect who opens it gets the full team-build story (video, strategy, live blockchain payouts) with <strong style="color:var(--gold)">your ID #${d.id}</strong> pinned as sponsor and your position's real results as proof. This dashboard stays yours; the invite page is what you share.</p><div style="display:flex;gap:16px;align-items:center;flex-wrap:wrap"><span id="dQr" class="qr-img"></span><div style="min-width:220px"><div style="display:flex;gap:8px;flex-wrap:wrap"><button id="copyShare" class="btn btn-secondary btn-sm">Copy my page link</button><a class="btn btn-primary btn-sm" href="${esc(d.referralUrl||'#')}" target="_blank" rel="noopener noreferrer">Join under #${d.id} →</a></div><p class="micro" style="margin:10px 0 0;word-break:break-all">${esc(dashUrl)}</p><p class="micro" style="margin:6px 0 0">When you reach 2/2 this page automatically retires your link from the team's focus and promotes the next position in your leg. Any late signup on your own link is a bonus — it pays you and spills down as depth.</p></div></div>`; <p class="micro" style="margin:0 0 10px">Tap the code to blow it up full-screen — they scan it with their phone camera and land on your invite page.</p>
<div style="display:flex;gap:8px;flex-wrap:wrap"><button id="copyShare" class="btn btn-secondary btn-sm">Copy my page link</button><a class="btn btn-teal btn-sm" href="${esc(dashUrl)}" target="_blank" rel="noopener">Preview my page →</a></div>
<p class="micro" style="margin:10px 0 0;word-break:break-all">${esc(dashUrl)}</p>
</div>
</div>`;
const qr=document.getElementById('dQr');if(qr)qr.innerHTML=qrSvg(dashUrl); const qr=document.getElementById('dQr');if(qr)qr.innerHTML=qrSvg(dashUrl);
const qb=document.getElementById('dQrBtn');
if(qb)qb.addEventListener('click',function(){
const ov=document.createElement('div');
ov.style.cssText='position:fixed;inset:0;background:#fff;z-index:99999;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;padding:24px;cursor:pointer';
const big=document.createElement('div');
big.style.cssText='width:min(78vw,78vh);height:min(78vw,78vh)';
big.innerHTML=qrSvg(dashUrl);
const cap=document.createElement('div');
cap.style.cssText='color:#0a1620;font-weight:800;font-size:18px;text-align:center';
cap.textContent='Scan to see the RM Circle team build — join page for #'+d.id;
const hint=document.createElement('div');
hint.style.cssText='color:#667;font-size:13px';
hint.textContent='Tap anywhere to close';
ov.appendChild(big);ov.appendChild(cap);ov.appendChild(hint);
ov.addEventListener('click',function(){try{document.body.removeChild(ov);}catch(e){}});
document.body.appendChild(ov);
});
const cp=document.getElementById('copyShare'); const cp=document.getElementById('copyShare');
if(cp)cp.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(dashUrl);cp.textContent='Copied ✓';setTimeout(()=>cp.textContent='Copy my page link',1400)}catch(e){}}); if(cp)cp.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(dashUrl);cp.textContent='Copied ✓';setTimeout(()=>cp.textContent='Copy my page link',1400)}catch(e){}});
} }
async function load(id){ async function load(id){
try{ try{
// after our own join/upgrade tx, force one cache-bypassing read so the // after our own join/upgrade tx, force one cache-bypassing read so the