Rebuild /my as a two-purpose tabbed page

Turns the member page into a persistent, tabbed dual-purpose page:
- Persistent context strip: "Member #X's RM Circle page · Verified · Qualified"
- Tab 1 "The Team Build": pitch for promoted-link visitors — hook, embedded
  overview video, 3 steps, live proof (member's real on-chain numbers), Join
  under #X CTA. Persistent (replaces the dismissible cold-intro banner).
- Tab 2 "Position Dashboard": the full dashboard as-is (facts, plan, matrix,
  pipeline, alerts, share, payments, lineage).
- Default: cold visitor opens on the pitch, the member opens on their dashboard;
  both always one tap apart. Retired the coldIntro banner + its handler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-17 08:31:07 -05:00
parent c91a530b7e
commit 45797bdf9a
3 changed files with 73 additions and 22 deletions
+30 -13
View File
@@ -4,6 +4,32 @@
const date=ts=>ts?new Date(ts*1000).toLocaleDateString(undefined,{year:'numeric',month:'short',day:'numeric'}):'—';
const prompt=document.getElementById('idPrompt'),dash=document.getElementById('dash');
// Two-purpose page: a "Team Build" pitch tab (for cold visitors who clicked a
// promoted link) and the "Position Dashboard" tab. Persistent — the pitch is
// always one tap away, never dismissed.
function setTab(name){
document.querySelectorAll('.mp-tab').forEach(t=>t.classList.toggle('on',t.dataset.tab===name));
const tp=document.getElementById('tabPitch'),td=document.getElementById('tabDash');
if(tp)tp.classList.toggle('on',name==='pitch');
if(td)td.classList.toggle('on',name==='dash');
}
document.querySelectorAll('.mp-tab').forEach(t=>t.addEventListener('click',()=>{setTab(t.dataset.tab);window.scrollTo({top:0,behavior:'smooth'});}));
function renderPitch(d){
const set=(id,v)=>{const el=document.getElementById(id);if(el)el.textContent=v;};
set('pInvId','#'+d.id); set('pProofId','#'+d.id);
set('pEarned',Math.round(d.totalEarnedPol||0).toLocaleString());
set('pTeam',(d.subtree&&d.subtree.downCount!=null)?String(d.subtree.downCount):'—');
set('pLevel',d.levelName||'—');
const jb=document.getElementById('pJoinBtn'); if(jb){jb.href='/join/'+d.id;jb.textContent='Join under #'+d.id+' →';}
const pp=document.getElementById('pPayouts');
if(pp){
const inc=(d.income||[]).slice(0,3);
pp.innerHTML=inc.length
? '<div class="pitch-l-lab">Real payouts landing on this team — verifiable on Polygonscan</div>'+inc.map(p=>`<div class="pitch-l-row"><span>${esc(p.desc)} — from #${p.fromId}</span><b>+${fmt(p.pol)} POL</b></div>`).join('')
: '';
}
}
function pathId(){const m=location.pathname.match(/^\/my\/(\d+)$/);if(m)return m[1];const q=new URLSearchParams(location.search).get('id');if(q&&/^\d+$/.test(q))return q;try{return localStorage.getItem('ctb.myId')||''}catch(e){return ''}}
const treeUI={byId:{},parent:{},homeId:0,rootId:0,mode:'pyramid'};
@@ -58,17 +84,10 @@
});
function render(d){
prompt.classList.add('hidden');dash.classList.remove('hidden');
// cold-visitor context banner: shown unless this looks like the member's own
// dashboard (their stored ID matches) or they dismissed it this session
try{
const intro=document.getElementById('coldIntro');
const seenBefore=d._priorId===String(d.id); // they've viewed this position before (likely their own)
const dismissed=sessionStorage.getItem('ctb.introDismissed')==='1';
if(intro){
if(!seenBefore&&!dismissed){intro.classList.remove('hidden');const cij=document.getElementById('coldIntroJoin');cij.href='/join/'+d.id;cij.textContent='See how to join under #'+d.id+' →';}
else intro.classList.add('hidden');
}
}catch(e){}
// default tab: a cold visitor (hasn't viewed this ID before) opens on the
// Team Build pitch; the member (their own saved ID) opens on their dashboard.
renderPitch(d);
try{ setTab(d._priorId===String(d.id)?'dash':'pitch'); }catch(e){ setTab('pitch'); }
document.getElementById('dTitle').textContent='#'+d.id;
const qb=document.getElementById('dQualified');
if(qb)qb.outerHTML=d.directCount>=2
@@ -298,8 +317,6 @@
document.getElementById('idError').textContent=x.message;
}
}
const introX=document.getElementById('coldIntroClose');
if(introX)introX.addEventListener('click',()=>{try{sessionStorage.setItem('ctb.introDismissed','1')}catch(e){};document.getElementById('coldIntro').classList.add('hidden')});
document.getElementById('dJoinForm').addEventListener('submit',async e=>{
e.preventDefault();
const form=e.currentTarget,msg=document.getElementById('dJoinMsg');