Your position's matrix — the rollup line on each card counts everyone underneath, all the way down. Click a position to drill into that leg. Open slots are where the next placements land.
✓ qualified (2/2 directs) · P Premium · S Standard · ⬇ everyone below that position (all generations) and the POL they've earned · ↧ spillover = placed there by upline activity; only members who join with a position's own ID count toward its 2/2
Share this position
+
Just joined under this position?
Welcome to the team! Enter the new member ID the RM Circle dApp gave you — we'll verify it on the blockchain and let the team know you're in.
Payments received
Every payment your position has received, straight from the smart contract.
Your lineage
Team reminder: once your two directs are placed, retire your link and help your two get their two — always send new members through the current team sponsor page.
diff --git a/public/my.js b/public/my.js
index 14a5126..99b547f 100644
--- a/public/my.js
+++ b/public/my.js
@@ -136,6 +136,27 @@
document.getElementById('idError').textContent=x.message;
}
}
+ document.getElementById('dJoinForm').addEventListener('submit',async e=>{
+ e.preventDefault();
+ const form=e.currentTarget,msg=document.getElementById('dJoinMsg');
+ const newId=form.elements.newId.value.trim(),memberName=form.elements.memberName.value.trim();
+ if(!/^\d{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.';return}
+ msg.style.color='var(--muted)';msg.textContent='Verifying on the blockchain…';
+ try{
+ const pageId=(location.pathname.match(/^\/my\/(\d+)$/)||[])[1]||'?';
+ const r=await fetch('/api/public/submit-id',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({newId,memberName,sponsorId:pageId,source:'dashboard-'+pageId,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=` Open your own dashboard →`;
+ if(d.duplicate)msg.innerHTML=`✓ ID ${newId} was already submitted — you're on the list.${dash}`;
+ else if(d.onchain&&d.onchain.registered)msg.innerHTML=`✓ Verified on the blockchain! ID ${newId} is registered under sponsor #${d.onchain.referrerId} (${d.onchain.tier}). The team has been notified — welcome aboard.${dash}`;
+ else if(d.path==='notfound'){msg.style.color='var(--danger)';msg.innerHTML=`⚠ ID ${newId} 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 — submitted. The team will verify your placement.${dash}`;
+ }catch(x){msg.style.color='var(--danger)';msg.textContent=x.message}
+ });
document.getElementById('idForm').addEventListener('submit',e=>{
e.preventDefault();
const v=document.getElementById('memberId').value.trim();
diff --git a/public/start.js b/public/start.js
index dc14d02..f5c651d 100644
--- a/public/start.js
+++ b/public/start.js
@@ -40,10 +40,12 @@ document.getElementById('idSubmitForm').addEventListener('submit',async e=>{
if(!r.ok)throw new Error(d.error||'Submission failed');
form.classList.add('hidden');
msg.style.color='var(--ok)';
- let chainNote='';
- if(d.onchain&&d.onchain.registered)chainNote=` ✓ Verified on the blockchain: ${d.onchain.tier} tier, ${d.onchain.level} level, referred by ID ${d.onchain.referrerId}.`;
- else if(d.onchain&&d.onchain.registered===false)chainNote=` ⚠ We couldn't find this ID on the smart contract yet — double-check the number. The team will verify it manually.`;
- msg.innerHTML=d.duplicate?`✓ ID ${newId} was already submitted — you're on the list.`:`✓ Got it! ID ${newId} is submitted under sponsor ID ${sponsorId}. The team has been notified and you'll be added to the rotation.${chainNote}`;
+ const dash=` Open your Member Dashboard →`;
+ if(d.duplicate)msg.innerHTML=`✓ ID ${newId} was already submitted — you're on the list.${dash}`;
+ else if(d.path==='rotation')msg.innerHTML=`✓ Verified on the blockchain! ID ${newId} joined through the team rotation under sponsor #${d.onchain.referrerId} (${d.onchain.tier}). The team has been notified and you're in line for the rotation that gets your 2.${dash}`;
+ else if(d.path==='leg')msg.innerHTML=`✓ Verified on the blockchain! ID ${newId} is registered under sponsor #${d.onchain.referrerId}'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 ${newId} 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 ${newId} 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()=>{
diff --git a/server.js b/server.js
index 7f4505c..fa76a73 100644
--- a/server.js
+++ b/server.js
@@ -135,17 +135,29 @@ async function handleSubmitId(req, res) {
new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), 6000))
]);
} catch (e) { onchain = null; }
- subs.push({ newId, memberName, sponsorId, source: source||'(direct)', clickid, ts: new Date().toISOString(),
+ // the chain decides the path: rotation join (referrer = active rotation sponsor)
+ // vs leg join (someone's personal team build) — regardless of which page they used
+ const active = activeSponsor(getSponsors());
+ let joinPath = 'unknown';
+ if (onchain && onchain.registered) joinPath = (active && String(onchain.referrerId) === String(active.id)) ? 'rotation' : 'leg';
+ else if (onchain && !onchain.registered) joinPath = 'notfound';
+ subs.push({ newId, memberName, sponsorId, source: source||'(direct)', clickid, ts: new Date().toISOString(), path: joinPath,
onchain: onchain ? { registered: onchain.registered, tier: onchain.tierName, level: onchain.levelName, referrerId: onchain.referrerId, uplineId: onchain.uplineId } : undefined });
writeJson(SUBMISSIONS_FILE, subs.slice(-1000));
recordEvent('purchase', source);
firePostback(clickid, `purchase-${clickid}`, source);
- const chainLine = onchain === null ? '⏳ On-chain check unavailable — verify manually in admin.'
- : onchain.registered
- ? `✅ VERIFIED ON-CHAIN: ${onchain.tierName} tier, level ${onchain.levelName}, referred by ID ${onchain.referrerId}${String(onchain.referrerId)!==sponsorId?` ⚠ (submitted sponsor was ${sponsorId})`:''}`
- : `❌ NOT FOUND ON-CHAIN — ID ${newId} has no registration on the contract yet.`;
- sendTelegram(`🔔 RM Circle: NEW MEMBER CONFIRMED\nName: ${memberName}\nNew ID: ${newId}\nJoined under sponsor: ${sponsorId}\nSource: ${source||'(direct)'}\n${chainLine}\n→ Add ${memberName} (ID ${newId}) to the rotation queue.`);
- return json(res, 200, { ok: true, onchain: onchain ? { registered: onchain.registered, tier: onchain.tierName, level: onchain.levelName, referrerId: onchain.referrerId } : null });
+ let msg;
+ if (joinPath === 'rotation') {
+ msg = `🔔 RM Circle: ROTATION JOIN CONFIRMED ✅\nName: ${memberName}\nNew ID: ${newId} (${onchain.tierName}, verified on-chain)\nJoined under rotation sponsor: #${onchain.referrerId}\nSource: ${source||'(direct)'}\n→ +1 direct for ID ${active.id}; add ${memberName} (ID ${newId}) to the rotation queue.`;
+ } else if (joinPath === 'leg') {
+ msg = `🌱 RM Circle: TEAM-BUILD JOIN (not rotation)\nName: ${memberName}\nNew ID: ${newId} (${onchain.tierName}, verified on-chain)\nActual sponsor on-chain: #${onchain.referrerId}${sponsorId!=='?'&&String(onchain.referrerId)!==sponsorId?` (form said ${sponsorId})`:''}\nSource: ${source||'(direct)'}\n→ Leg growth under #${onchain.referrerId} — no rotation action needed. Add them to the rotation queue only if they want the team effort.`;
+ } else if (joinPath === 'notfound') {
+ msg = `🔔 RM Circle: ID SUBMITTED — ❌ NOT FOUND ON-CHAIN\nName: ${memberName}\nNew ID: ${newId}\nClaimed sponsor: ${sponsorId}\nSource: ${source||'(direct)'}\n→ ID has no registration on the contract — double-check with them before any queue action.`;
+ } else {
+ msg = `🔔 RM Circle: NEW MEMBER SUBMITTED\nName: ${memberName}\nNew ID: ${newId}\nClaimed sponsor: ${sponsorId}\nSource: ${source||'(direct)'}\n⏳ On-chain check unavailable — verify manually in admin (member lookup).`;
+ }
+ sendTelegram(msg);
+ return json(res, 200, { ok: true, path: joinPath, onchain: onchain ? { registered: onchain.registered, tier: onchain.tierName, level: onchain.levelName, referrerId: onchain.referrerId } : null });
}
async function handleChat(req, res) {
const ip = String(req.headers['x-forwarded-for']||req.socket.remoteAddress||'').split(',')[0].trim();