7c8a43a055
Marty (2026-09-19): delete it if it no longer serves a purpose. The public link has run on the chain walk since the mode flip and the queue's active head never received a join. Gone: active/waiting/qualified statuses, normalizeStatuses, activeSponsor, publicSponsorPayload, the increment/activate/qualify/reset actions, the click counter, the auto-count/auto-advance/ level-sync block, the legacy queue branches in current-sponsor and the submit-id classifier, the chatbot's "placements waiting" facts, and the queue columns and buttons in admin. What stays is a member directory (name, parent, level, notes, contact email) because the payout alerts and the feed's name map read it. The chain walk is the only company rotation now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
117 lines
8.5 KiB
JavaScript
117 lines
8.5 KiB
JavaScript
let currentSponsor=null;
|
||
// Who invited this visitor, if anyone. A prospect who arrived on /join/<id> and then tapped
|
||
// a nav link used to land here and be offered the COMPANY rotation position instead — the
|
||
// inviter lost the referral just because the prospect clicked "Training" first. The invite
|
||
// is remembered in the rmc_ref cookie (set when the invite page was served) and can also be
|
||
// passed explicitly as ?ref=<id>.
|
||
function invitedBy(){
|
||
try{
|
||
const q=new URLSearchParams(location.search).get('ref');
|
||
if(q&&/^\d{1,15}$/.test(q))return q;
|
||
const m=document.cookie.match(/(?:^|;\s*)rmc_ref=(\d{1,15})/);
|
||
if(m)return m[1];
|
||
}catch(e){}
|
||
return null;
|
||
}
|
||
function placementChoice(){
|
||
try{
|
||
const q=new URLSearchParams(location.search).get('direct');
|
||
if(q==='1'||q==='0')return q;
|
||
const m=document.cookie.match(/(?:^|;\s*)rmc_direct=([01])/);
|
||
if(m)return m[1];
|
||
}catch(e){}
|
||
return null;
|
||
}
|
||
async function load(){
|
||
try{
|
||
const ref=invitedBy();
|
||
// an explicit ?direct=1/0 from the invite link wins over the position's default, and it
|
||
// has to be in scope for the join button further down — not just the display block
|
||
const dq=placementChoice();
|
||
const [cr,sr]=await Promise.all([
|
||
fetch('/api/public/config'),
|
||
fetch(ref?('/api/public/member?id='+encodeURIComponent(ref)):'/api/public/current-sponsor')
|
||
]);
|
||
const c=await cr.json(); const s=await sr.json();
|
||
if(!sr.ok)throw new Error(s.error||'No sponsor assigned');
|
||
if(ref&&s.registered){
|
||
// Route exactly the way that member's own invite page would: their moving link sends
|
||
// the join to the next position in their leg, unless their position is set to take
|
||
// direct placements. Same rules, so the two pages can never disagree.
|
||
const goDirect=dq==='1'||(dq!=='0'&&(s.directDefault||!s.joinTarget));
|
||
const t=goDirect?{id:s.id,referralUrl:s.referralUrl,directCount:s.directCount}:s.joinTarget;
|
||
currentSponsor={id:t.id,name:null,directs:(t.directCount!==undefined?t.directCount:0),goal:2,referralUrl:t.referralUrl};
|
||
// say whose team this is, so it never reads as an anonymous "current placement"
|
||
const badge=document.querySelector('.live-badge');
|
||
if(badge){
|
||
const dot=badge.querySelector('.dot');
|
||
badge.textContent=' '+(String(t.id)===String(ref)?('Invited by Member #'+ref):('Member #'+ref+"'s team"));
|
||
if(dot)badge.insertBefore(dot,badge.firstChild);
|
||
}
|
||
const jb=document.getElementById('joinButton');
|
||
if(jb)jb.textContent=(String(t.id)===String(ref)?('Join Member #'+ref+"'s Team →"):('Join '+'#'+ref+"'s Team →"));
|
||
const note=document.getElementById('queueText');
|
||
if(note&&String(t.id)!==String(ref)){
|
||
note.textContent='Member #'+ref+' is already qualified, so your entry fills the next open spot in their team (#'+t.id+').';
|
||
}
|
||
}else{
|
||
currentSponsor=s.sponsor;
|
||
}
|
||
if(c.siteName){document.title=`Get Started | ${c.siteName}`;const bn=document.getElementById('brandName');if(bn)bn.textContent=c.siteName}
|
||
document.getElementById('sponsorId').textContent=`ID ${currentSponsor.id}`;
|
||
document.getElementById('sponsorIdInline').textContent=currentSponsor.id;
|
||
document.getElementById('sponsorName').textContent=currentSponsor.name||'Current RM Circle placement';
|
||
document.getElementById('progressText').textContent=`${currentSponsor.directs} / 2`;
|
||
document.getElementById('progressBar').style.width=`${Math.min(100,(currentSponsor.directs/2)*100)}%`;
|
||
document.getElementById('entryPol').textContent=c.premiumEntryPol;
|
||
document.getElementById('entryPol2').textContent=c.premiumEntryPol;
|
||
if(c.polUsd>0){
|
||
var usd=Math.round(c.premiumEntryPol*c.polUsd);
|
||
['entryUsd','entryUsd2'].forEach(function(id){var el=document.getElementById(id);if(el)el.textContent=' (~$'+usd+' today — POL’s price moves)';});
|
||
}
|
||
// carry the inviter through to enrolment — /join-now already knows how to place a
|
||
// ?ref properly (direct placement or next open spot in their leg). Without this the
|
||
// button dropped the referral on the floor and enrolled under the company rotation.
|
||
document.getElementById('joinButton').href='/join-now'+(ref?('?ref='+encodeURIComponent(ref)+(dq?('&direct='+dq):'')):'');
|
||
{const dl=document.getElementById('dappJoinLink');if(dl&¤tSponsor.referralUrl){dl.href=currentSponsor.referralUrl;dl.classList.remove('hidden');}}
|
||
// the company-queue line only means anything on the company-rotation path
|
||
// only on the company-rotation path — the invited path already wrote its own line here
|
||
if(!ref)document.getElementById('queueText').textContent='The company rotation offers the next open position in the tree; it moves the moment that spot fills.';
|
||
document.getElementById('supportBox').textContent=c.supportLabel||'Contact your team sponsor if you need help before joining.';
|
||
if(c.telegramUrl){const w=document.getElementById('supportLinkWrap'),a=document.getElementById('supportLink');a.href=c.telegramUrl;w.classList.remove('hidden')}
|
||
}catch(e){
|
||
document.getElementById('sponsorName').textContent=e.message;
|
||
document.getElementById('joinButton').classList.add('hidden');
|
||
document.getElementById('copyButton').classList.add('hidden');
|
||
}
|
||
}
|
||
document.getElementById('joinButton').addEventListener('click',()=>{try{if(currentSponsor)sessionStorage.setItem('ctb.joinedSponsor',currentSponsor.id)}catch(e){};fetch('/api/public/join-click',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({source:window.ctbGetSource?window.ctbGetSource():'(direct)',clickid:window.ctbGetClickId?window.ctbGetClickId():''})}).catch(()=>{})});
|
||
document.getElementById('idSubmitForm').addEventListener('submit',async e=>{
|
||
e.preventDefault();
|
||
const form=e.currentTarget,input=form.elements.newId,msg=document.getElementById('idSubmitMsg');
|
||
const newId=input.value.trim();
|
||
const memberName=form.elements.memberName.value.trim();
|
||
if(!/^[0-9]{1,10}$/.test(newId)){msg.style.color='var(--danger)';msg.textContent='Numbers only — the ID shown by the RM Circle dApp.';return}
|
||
if(!memberName){msg.style.color='var(--danger)';msg.textContent='Add your name or Telegram handle so the team can reach you.';return}
|
||
let joinedSponsor='';try{joinedSponsor=sessionStorage.getItem('ctb.joinedSponsor')||''}catch(e){}
|
||
const sponsorId=joinedSponsor||(currentSponsor?currentSponsor.id:'?');
|
||
msg.style.color='var(--muted)';msg.textContent='Submitting…';
|
||
try{
|
||
const r=await fetch('/api/public/submit-id',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({newId,memberName,sponsorId,source:window.ctbGetSource?window.ctbGetSource():'(direct)',clickid:window.ctbGetClickId?window.ctbGetClickId():''})});
|
||
const d=await r.json();
|
||
if(!r.ok)throw new Error(d.error||'Submission failed');
|
||
form.classList.add('hidden');
|
||
msg.style.color='var(--ok)';
|
||
const dash=`<br><a href="/my/${newId}" style="color:var(--gold)">Open your Member Dashboard →</a>`;
|
||
if(d.duplicate)msg.innerHTML=`✓ ID <strong>${newId}</strong> was already submitted — you're on the list.${dash}`;
|
||
else if(d.path==='rotation')msg.innerHTML=`✓ Verified on the blockchain! ID <strong>${newId}</strong> joined through the team rotation under sponsor <strong>#${d.onchain.referrerId}</strong> (${d.onchain.tier}). The team has been notified and you're in line for the rotation that gets <strong>your</strong> 2.${dash}`;
|
||
else if(d.path==='leg')msg.innerHTML=`✓ Verified on the blockchain! ID <strong>${newId}</strong> is registered under sponsor <strong>#${d.onchain.referrerId}</strong>'s team build (${d.onchain.tier}) — your placement is recorded and the team has been notified. Heads up: the sponsor shown on this page is the site's team rotation, a separate group effort — your own team hub is your dashboard.${dash}`;
|
||
else if(d.path==='notfound'){msg.style.color='var(--danger)';msg.innerHTML=`⚠ ID <strong>${newId}</strong> isn't on the smart contract yet — double-check the number from the RM Circle dApp. Your submission is saved and the team will verify it manually.`;}
|
||
else msg.innerHTML=`✓ Got it! ID <strong>${newId}</strong> is submitted. The team has been notified and will verify your placement.${dash}`;
|
||
}catch(x){msg.style.color='var(--danger)';msg.textContent=x.message}
|
||
});
|
||
document.getElementById('copyButton').addEventListener('click',async()=>{
|
||
if(!currentSponsor)return; await navigator.clipboard.writeText(currentSponsor.id); const b=document.getElementById('copyButton'); const old=b.textContent;b.textContent='Copied ✓';setTimeout(()=>b.textContent=old,1400)
|
||
});
|
||
load();
|