Track the join funnel end-to-end through the self-enroll page
- Self-enroll pageviews now tracked as the "joinnow" event (track.js loaded on the page; server whitelist + admin tiles/columns extended) - A confirmed on-chain registration in the wallet-connect flow now auto-posts to the submit-id API, so every self-enroll join records a submission with source + clickid attribution and fires the purchase event and BeMob postback with no manual ID entry - Optional name/handle field on the join page feeds the submission and the team Telegram alert - Admin Traffic panel: Join-now views tile, Join-now -> Confirmed conversion, and a per-source Confirmed column for campaign-level ROI reading Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,9 @@
|
|||||||
<h2 style="margin:0 0 10px;font-size:18px">2 · Join</h2>
|
<h2 style="margin:0 0 10px;font-size:18px">2 · Join</h2>
|
||||||
<div class="dj-kv"><span>Tier</span><b>Premium (the tier the team builds at)</b></div>
|
<div class="dj-kv"><span>Tier</span><b>Premium (the tier the team builds at)</b></div>
|
||||||
<div class="dj-kv"><span>You will pay</span><b id="joinCost">—</b></div>
|
<div class="dj-kv"><span>You will pay</span><b id="joinCost">—</b></div>
|
||||||
|
<label style="display:block;margin-top:14px;font-size:13px;color:var(--muted)">Your name or Telegram handle <span style="opacity:.7">(optional — helps the team welcome you)</span>
|
||||||
|
<input id="memberName" maxlength="60" placeholder="e.g. @yourhandle" style="display:block;width:100%;margin-top:6px;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:#08192880;color:var(--text);font-size:14px">
|
||||||
|
</label>
|
||||||
<button id="joinBtn" class="btn btn-primary" style="margin-top:14px;width:100%">Join the team →</button>
|
<button id="joinBtn" class="btn btn-primary" style="margin-top:14px;width:100%">Join the team →</button>
|
||||||
<p style="color:var(--muted);font-size:12px;margin:10px 0 0">Confirm the amount in your wallet before signing. If anything is off, the contract simply rejects it and you keep your POL.</p>
|
<p style="color:var(--muted);font-size:12px;margin:10px 0 0">Confirm the amount in your wallet before signing. If anything is off, the contract simply rejects it and you keep your POL.</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,4 +52,4 @@
|
|||||||
<p class="micro" style="text-align:center">No income is guaranteed. Cryptocurrency and smart-contract participation carry risk. Only use funds you can afford to lose.</p>
|
<p class="micro" style="text-align:center">No income is guaranteed. Cryptocurrency and smart-contract participation carry risk. Only use funds you can afford to lose.</p>
|
||||||
</main>
|
</main>
|
||||||
<footer class="wrap disclaimer">All figures are read live from the RM Circle smart contract on Polygon. Not an earnings guarantee or investment advice. Participation carries risk of loss.<div class="footer-links"><a href="/">Home</a><a href="/training">Training</a><a href="/how-pay-works">How You Get Paid</a><a href="/contract">Contract Security</a><a href="/disclaimer">Disclaimers</a></div></footer>
|
<footer class="wrap disclaimer">All figures are read live from the RM Circle smart contract on Polygon. Not an earnings guarantee or investment advice. Participation carries risk of loss.<div class="footer-links"><a href="/">Home</a><a href="/training">Training</a><a href="/how-pay-works">How You Get Paid</a><a href="/contract">Contract Security</a><a href="/disclaimer">Disclaimers</a></div></footer>
|
||||||
<script src="/rmc-wallet.js"></script><script src="/join-now.js"></script></body></html>
|
<script src="/track.js"></script><script src="/rmc-wallet.js"></script><script src="/join-now.js"></script></body></html>
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
<section id="loginView" class="login-panel"><div class="brand" style="margin-bottom:22px"><div class="brand-mark">RM</div><span>RM Circle<small>Sponsor Router Admin</small></span></div><h1>Team Admin</h1><p>Manage the current sponsor, qualification queue, and onboarding settings.</p><form id="loginForm"><div class="field"><label for="password">Admin password</label><input id="password" class="input" type="password" autocomplete="current-password" required></div><button class="btn btn-primary" style="width:100%">Sign in</button><div id="loginError" class="micro" style="color:var(--danger)"></div></form></section>
|
<section id="loginView" class="login-panel"><div class="brand" style="margin-bottom:22px"><div class="brand-mark">RM</div><span>RM Circle<small>Sponsor Router Admin</small></span></div><h1>Team Admin</h1><p>Manage the current sponsor, qualification queue, and onboarding settings.</p><form id="loginForm"><div class="field"><label for="password">Admin password</label><input id="password" class="input" type="password" autocomplete="current-password" required></div><button class="btn btn-primary" style="width:100%">Sign in</button><div id="loginError" class="micro" style="color:var(--danger)"></div></form></section>
|
||||||
<section id="adminView" class="admin hidden"><div class="admin-top"><div><div class="eyebrow">Sponsor router</div><h1>RM Circle Team Build Admin</h1></div><div class="nav-actions"><a class="btn btn-secondary" href="/start" target="_blank">View Live Page ↗</a><button id="logoutBtn" class="btn btn-secondary">Log out</button></div></div>
|
<section id="adminView" class="admin hidden"><div class="admin-top"><div><div class="eyebrow">Sponsor router</div><h1>RM Circle Team Build Admin</h1></div><div class="nav-actions"><a class="btn btn-secondary" href="/start" target="_blank">View Live Page ↗</a><button id="logoutBtn" class="btn btn-secondary">Log out</button></div></div>
|
||||||
<div class="admin-grid"><div class="stack"><div class="table-card"><div style="display:flex;justify-content:space-between;gap:12px;align-items:center;margin-bottom:12px"><div><h2 style="margin:0">Sponsor Queue</h2><p style="color:var(--muted);margin:4px 0 0;font-size:13px">Mark a sponsor qualified to automatically activate the next waiting position.</p></div></div><div class="table-wrap"><table class="table"><thead><tr><th>Order</th><th>Sponsor</th><th>Parent</th><th>Directs</th><th>Level</th><th>Status</th><th>Clicks</th><th>Actions</th></tr></thead><tbody id="sponsorRows"></tbody></table></div></div>
|
<div class="admin-grid"><div class="stack"><div class="table-card"><div style="display:flex;justify-content:space-between;gap:12px;align-items:center;margin-bottom:12px"><div><h2 style="margin:0">Sponsor Queue</h2><p style="color:var(--muted);margin:4px 0 0;font-size:13px">Mark a sponsor qualified to automatically activate the next waiting position.</p></div></div><div class="table-wrap"><table class="table"><thead><tr><th>Order</th><th>Sponsor</th><th>Parent</th><th>Directs</th><th>Level</th><th>Status</th><th>Clicks</th><th>Actions</th></tr></thead><tbody id="sponsorRows"></tbody></table></div></div>
|
||||||
<div class="table-card"><div style="margin-bottom:12px"><h2 style="margin:0">Traffic & Conversions</h2><p style="color:var(--muted);margin:4px 0 0;font-size:13px">First-touch source per visitor session (referring domain or utm_source). Funnel: bridge page → start page → join click.</p></div><div id="funnelStats" class="funnel"></div><div class="table-wrap"><table class="table"><thead><tr><th>Source</th><th>Bridge views</th><th>Start views</th><th>Training views</th><th>Invite views</th><th>Join clicks</th><th>Start → Join</th></tr></thead><tbody id="trafficRows"></tbody></table></div></div>
|
<div class="table-card"><div style="margin-bottom:12px"><h2 style="margin:0">Traffic & Conversions</h2><p style="color:var(--muted);margin:4px 0 0;font-size:13px">First-touch source per visitor session (referring domain or utm_source). Funnel: bridge page → start page → join click.</p></div><div id="funnelStats" class="funnel"></div><div class="table-wrap"><table class="table"><thead><tr><th>Source</th><th>Bridge views</th><th>Start views</th><th>Training views</th><th>Invite views</th><th>Join-now views</th><th>Join clicks</th><th>Confirmed</th><th>Start → Join</th></tr></thead><tbody id="trafficRows"></tbody></table></div></div>
|
||||||
<div class="table-card"><div style="margin-bottom:12px"><h2 style="margin:0">Member ID Submissions</h2><p style="color:var(--muted);margin:4px 0 0;font-size:13px">New members who confirmed their purchase on the start page. Each one was posted to your Hermes Telegram chat — add them to the rotation.</p></div><div class="table-wrap"><table class="table"><thead><tr><th>When</th><th>Name / Handle</th><th>New ID</th><th>Joined under</th><th>Source</th><th>On-chain</th></tr></thead><tbody id="submissionRows"></tbody></table></div></div>
|
<div class="table-card"><div style="margin-bottom:12px"><h2 style="margin:0">Member ID Submissions</h2><p style="color:var(--muted);margin:4px 0 0;font-size:13px">New members who confirmed their purchase on the start page. Each one was posted to your Hermes Telegram chat — add them to the rotation.</p></div><div class="table-wrap"><table class="table"><thead><tr><th>When</th><th>Name / Handle</th><th>New ID</th><th>Joined under</th><th>Source</th><th>On-chain</th></tr></thead><tbody id="submissionRows"></tbody></table></div></div>
|
||||||
<div class="table-card"><div style="display:flex;justify-content:space-between;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap"><div><h2 style="margin:0">Your Organization vs. the Network</h2><p style="color:var(--muted);margin:4px 0 0;font-size:13px">How your team — rooted at your top ID — stacks up against the entire RM Circle smart contract. Live on-chain.</p></div><form id="orgShareForm" style="display:flex;gap:8px"><input id="orgRoot" class="input" style="max-width:110px" placeholder="21" inputmode="numeric"><button class="btn btn-secondary btn-sm">Refresh</button></form></div><div id="orgShare"><div class="empty">Reading the blockchain…</div></div></div>
|
<div class="table-card"><div style="display:flex;justify-content:space-between;gap:12px;align-items:center;margin-bottom:12px;flex-wrap:wrap"><div><h2 style="margin:0">Your Organization vs. the Network</h2><p style="color:var(--muted);margin:4px 0 0;font-size:13px">How your team — rooted at your top ID — stacks up against the entire RM Circle smart contract. Live on-chain.</p></div><form id="orgShareForm" style="display:flex;gap:8px"><input id="orgRoot" class="input" style="max-width:110px" placeholder="21" inputmode="numeric"><button class="btn btn-secondary btn-sm">Refresh</button></form></div><div id="orgShare"><div class="empty">Reading the blockchain…</div></div></div>
|
||||||
<div class="table-card"><div style="margin-bottom:12px"><h2 style="margin:0">My Positions — Income</h2><p style="color:var(--muted);margin:4px 0 0;font-size:13px">Every payment received by your own positions, live from the contract. Comma-separated IDs — saved for next time.</p></div><form id="incomeForm" style="display:flex;gap:8px;margin-bottom:14px;flex-wrap:wrap"><input id="incomeIds" class="input" style="max-width:260px" placeholder="21,24,25" inputmode="numeric"><button class="btn btn-teal">Load</button></form><div id="incomeAlert"></div><div id="incomeRouting" style="margin-bottom:14px"></div><div id="incomeSummary" class="facts" style="grid-template-columns:repeat(4,1fr);margin-bottom:12px"></div><div id="incomeResult"></div></div>
|
<div class="table-card"><div style="margin-bottom:12px"><h2 style="margin:0">My Positions — Income</h2><p style="color:var(--muted);margin:4px 0 0;font-size:13px">Every payment received by your own positions, live from the contract. Comma-separated IDs — saved for next time.</p></div><form id="incomeForm" style="display:flex;gap:8px;margin-bottom:14px;flex-wrap:wrap"><input id="incomeIds" class="input" style="max-width:260px" placeholder="21,24,25" inputmode="numeric"><button class="btn btn-teal">Load</button></form><div id="incomeAlert"></div><div id="incomeRouting" style="margin-bottom:14px"></div><div id="incomeSummary" class="facts" style="grid-template-columns:repeat(4,1fr);margin-bottom:12px"></div><div id="incomeResult"></div></div>
|
||||||
|
|||||||
+5
-3
@@ -8,8 +8,8 @@ function statusClass(s){return `status status-${s}`}
|
|||||||
function pct(n,d){return d?`${Math.round((n/d)*100)}%`:'—'}
|
function pct(n,d){return d?`${Math.round((n/d)*100)}%`:'—'}
|
||||||
function renderAnalytics(){
|
function renderAnalytics(){
|
||||||
const src=(state.analytics&&state.analytics.sources)||{};
|
const src=(state.analytics&&state.analytics.sources)||{};
|
||||||
const tot={bridge:0,start:0,click:0,training:0,postback:0,purchase:0,join:0};
|
const tot={bridge:0,start:0,click:0,training:0,postback:0,purchase:0,join:0,joinnow:0};
|
||||||
const list=Object.entries(src).map(([name,r])=>{tot.bridge+=r.bridge||0;tot.start+=r.start||0;tot.click+=r.click||0;tot.training+=r.training||0;tot.postback+=r.postback||0;tot.purchase+=r.purchase||0;tot.join+=r.join||0;return{name,bridge:r.bridge||0,start:r.start||0,click:r.click||0,training:r.training||0,join:r.join||0}})
|
const list=Object.entries(src).map(([name,r])=>{tot.bridge+=r.bridge||0;tot.start+=r.start||0;tot.click+=r.click||0;tot.training+=r.training||0;tot.postback+=r.postback||0;tot.purchase+=r.purchase||0;tot.join+=r.join||0;tot.joinnow+=r.joinnow||0;return{name,bridge:r.bridge||0,start:r.start||0,click:r.click||0,training:r.training||0,join:r.join||0,joinnow:r.joinnow||0,purchase:r.purchase||0}})
|
||||||
.sort((a,b)=>b.click-a.click||b.start-a.start||b.bridge-a.bridge);
|
.sort((a,b)=>b.click-a.click||b.start-a.start||b.bridge-a.bridge);
|
||||||
document.getElementById('funnelStats').innerHTML=
|
document.getElementById('funnelStats').innerHTML=
|
||||||
`<div class="fact"><small>Bridge views</small><strong>${tot.bridge}</strong></div>`+
|
`<div class="fact"><small>Bridge views</small><strong>${tot.bridge}</strong></div>`+
|
||||||
@@ -18,7 +18,9 @@ function renderAnalytics(){
|
|||||||
`<div class="fact"><small>Bridge → Start</small><strong>${pct(tot.start,tot.bridge)}</strong></div>`+
|
`<div class="fact"><small>Bridge → Start</small><strong>${pct(tot.start,tot.bridge)}</strong></div>`+
|
||||||
`<div class="fact"><small>Start → Join</small><strong>${pct(tot.click,tot.start)}</strong></div>`+
|
`<div class="fact"><small>Start → Join</small><strong>${pct(tot.click,tot.start)}</strong></div>`+
|
||||||
(tot.join?`<div class="fact"><small>Invite-page views</small><strong>${tot.join}</strong></div>`:'')+
|
(tot.join?`<div class="fact"><small>Invite-page views</small><strong>${tot.join}</strong></div>`:'')+
|
||||||
|
(tot.joinnow?`<div class="fact"><small>Join-now views</small><strong>${tot.joinnow}</strong></div>`:'')+
|
||||||
(tot.purchase?`<div class="fact"><small>Confirmed joins</small><strong>${tot.purchase}</strong></div>`:'')+
|
(tot.purchase?`<div class="fact"><small>Confirmed joins</small><strong>${tot.purchase}</strong></div>`:'')+
|
||||||
|
(tot.joinnow&&tot.purchase?`<div class="fact"><small>Join-now → Confirmed</small><strong>${pct(tot.purchase,tot.joinnow)}</strong></div>`:'')+
|
||||||
(tot.postback?`<div class="fact"><small>BeMob postbacks</small><strong>${tot.postback}</strong></div>`:'');
|
(tot.postback?`<div class="fact"><small>BeMob postbacks</small><strong>${tot.postback}</strong></div>`:'');
|
||||||
const subs=state.submissions||[];
|
const subs=state.submissions||[];
|
||||||
document.getElementById('submissionRows').innerHTML=subs.map(s=>{
|
document.getElementById('submissionRows').innerHTML=subs.map(s=>{
|
||||||
@@ -26,7 +28,7 @@ function renderAnalytics(){
|
|||||||
const oc=s.onchain?(s.onchain.registered?`${path}<span style="color:var(--ok)">✓ ${esc(s.onchain.tier||'')} · ref #${esc(String(s.onchain.referrerId??'?'))}</span>`:'<span style="color:var(--danger)">✗ not found</span>'):'—';
|
const oc=s.onchain?(s.onchain.registered?`${path}<span style="color:var(--ok)">✓ ${esc(s.onchain.tier||'')} · ref #${esc(String(s.onchain.referrerId??'?'))}</span>`:'<span style="color:var(--danger)">✗ not found</span>'):'—';
|
||||||
return `<tr><td>${esc((s.ts||'').replace('T',' ').slice(0,16))}</td><td>${esc(s.memberName||'—')}</td><td><strong>${esc(s.newId)}</strong></td><td>ID ${esc(s.sponsorId)}</td><td>${esc(s.source)}</td><td>${oc}</td></tr>`;
|
return `<tr><td>${esc((s.ts||'').replace('T',' ').slice(0,16))}</td><td>${esc(s.memberName||'—')}</td><td><strong>${esc(s.newId)}</strong></td><td>ID ${esc(s.sponsorId)}</td><td>${esc(s.source)}</td><td>${oc}</td></tr>`;
|
||||||
}).join('')||'<tr><td colspan="6" class="empty">No submissions yet.</td></tr>';
|
}).join('')||'<tr><td colspan="6" class="empty">No submissions yet.</td></tr>';
|
||||||
document.getElementById('trafficRows').innerHTML=list.map(r=>`<tr><td><strong>${esc(r.name)}</strong></td><td>${r.bridge}</td><td>${r.start}</td><td>${r.training}</td><td>${r.join}</td><td>${r.click}</td><td>${pct(r.click,r.start)}</td></tr>`).join('')||'<tr><td colspan="7" class="empty">No traffic recorded yet.</td></tr>';
|
document.getElementById('trafficRows').innerHTML=list.map(r=>`<tr><td><strong>${esc(r.name)}</strong></td><td>${r.bridge}</td><td>${r.start}</td><td>${r.training}</td><td>${r.join}</td><td>${r.joinnow}</td><td>${r.click}</td><td>${r.purchase?`<strong style="color:var(--ok)">${r.purchase}</strong>`:0}</td><td>${pct(r.click,r.start)}</td></tr>`).join('')||'<tr><td colspan="9" class="empty">No traffic recorded yet.</td></tr>';
|
||||||
}
|
}
|
||||||
function esc(s){return String(s??'').replace(/[&<>'"]/g,c=>({'&':'&','<':'<','>':'>',"'":''','"':'"'}[c]))}
|
function esc(s){return String(s??'').replace(/[&<>'"]/g,c=>({'&':'&','<':'<','>':'>',"'":''','"':'"'}[c]))}
|
||||||
function render(){
|
function render(){
|
||||||
|
|||||||
@@ -93,6 +93,7 @@
|
|||||||
if(rc.status==='0x0'){ log('⚠️ The transaction reverted — no position created, and your POL was returned (minus gas). Please try again.','err'); $('joinBtn').disabled=false; return; }
|
if(rc.status==='0x0'){ log('⚠️ The transaction reverted — no position created, and your POL was returned (minus gas). Please try again.','err'); $('joinBtn').disabled=false; return; }
|
||||||
var newId=receiptEventId(rc);
|
var newId=receiptEventId(rc);
|
||||||
if(newId){
|
if(newId){
|
||||||
|
reportJoin(newId);
|
||||||
$('result').style.display='block';
|
$('result').style.display='block';
|
||||||
$('resultId').textContent='#'+newId;
|
$('resultId').textContent='#'+newId;
|
||||||
$('resultSub').innerHTML='Welcome to the team! Taking you to your position page…';
|
$('resultSub').innerHTML='Welcome to the team! Taking you to your position page…';
|
||||||
@@ -108,6 +109,21 @@
|
|||||||
}
|
}
|
||||||
function el_scroll(id){ var e=$(id); if(e&&e.scrollIntoView) e.scrollIntoView({behavior:'smooth',block:'center'}); }
|
function el_scroll(id){ var e=$(id); if(e&&e.scrollIntoView) e.scrollIntoView({behavior:'smooth',block:'center'}); }
|
||||||
|
|
||||||
|
// Auto-confirm the join server-side: records the submission (source + clickid
|
||||||
|
// attribution), fires the purchase event + BeMob postback, enqueues rotation
|
||||||
|
// joins, and alerts the team — same pipeline as the manual submit-ID form.
|
||||||
|
function reportJoin(newId){
|
||||||
|
try{
|
||||||
|
var nameEl=$('memberName');
|
||||||
|
var name=(nameEl&&nameEl.value?nameEl.value.trim().slice(0,60):'')||('Self-enrolled #'+newId);
|
||||||
|
var src=(window.ctbGetSource?window.ctbGetSource():'(direct)');
|
||||||
|
var cid=(window.ctbGetClickId?window.ctbGetClickId():'');
|
||||||
|
fetch('/api/public/submit-id',{method:'POST',headers:{'Content-Type':'application/json'},
|
||||||
|
body:JSON.stringify({newId:String(newId),memberName:name,sponsorId:String(sponsorId||'?'),source:src,clickid:cid})
|
||||||
|
}).catch(function(){});
|
||||||
|
}catch(e){}
|
||||||
|
}
|
||||||
|
|
||||||
function attachWalletEvents(){
|
function attachWalletEvents(){
|
||||||
if(!eth||!eth.on||eth.__rmcBound) return; eth.__rmcBound=true;
|
if(!eth||!eth.on||eth.__rmcBound) return; eth.__rmcBound=true;
|
||||||
eth.on('accountsChanged',function(accs){ account=(accs&&accs[0])||null; if(!account){ $('connected').style.display='none'; $('connectBtn').style.display=''; } else { $('wallet').textContent=account.slice(0,6)+'…'+account.slice(-4); refreshBalance(); } });
|
eth.on('accountsChanged',function(accs){ account=(accs&&accs[0])||null; if(!account){ $('connected').style.display='none'; $('connectBtn').style.display=''; } else { $('wallet').textContent=account.slice(0,6)+'…'+account.slice(-4); refreshBalance(); } });
|
||||||
|
|||||||
+1
-1
@@ -19,6 +19,6 @@
|
|||||||
}catch(e){}
|
}catch(e){}
|
||||||
window.ctbGetClickId=function(){try{return sessionStorage.getItem('ctb.clickid')||''}catch(e){return ''}};
|
window.ctbGetClickId=function(){try{return sessionStorage.getItem('ctb.clickid')||''}catch(e){return ''}};
|
||||||
const path=location.pathname.replace(/\/$/,'')||'/';
|
const path=location.pathname.replace(/\/$/,'')||'/';
|
||||||
const page=path==='/'?'bridge':(path==='/start'?'start':(path==='/training'?'training':(/^\/join\/\d+$/.test(path)?'join':null)));
|
const page=path==='/'?'bridge':(path==='/start'?'start':(path==='/training'?'training':(path==='/join-now'?'joinnow':(/^\/join\/\d+$/.test(path)?'join':null))));
|
||||||
if(page)fetch('/api/public/track',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({event:page,source:getSource()})}).catch(()=>{});
|
if(page)fetch('/api/public/track',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({event:page,source:getSource()})}).catch(()=>{});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ function getConfig() { return readJson(CONFIG_FILE); }
|
|||||||
function activeSponsor(sponsors) { return sponsors.find(s=>s.status==='active') || sponsors.find(s=>s.status==='waiting') || null; }
|
function activeSponsor(sponsors) { return sponsors.find(s=>s.status==='active') || sponsors.find(s=>s.status==='waiting') || null; }
|
||||||
function getAnalytics() { try { return readJson(ANALYTICS_FILE); } catch (e) { return { sources: {} }; } }
|
function getAnalytics() { try { return readJson(ANALYTICS_FILE); } catch (e) { return { sources: {} }; } }
|
||||||
function recordEvent(event, source) {
|
function recordEvent(event, source) {
|
||||||
if (!['bridge','start','click','training','postback','purchase','join'].includes(event)) return;
|
if (!['bridge','start','click','training','postback','purchase','join','joinnow'].includes(event)) return;
|
||||||
const s = String(source||'').toLowerCase().trim().replace(/[^a-z0-9.()\-_:/ ]/g,'').slice(0,80) || '(direct)';
|
const s = String(source||'').toLowerCase().trim().replace(/[^a-z0-9.()\-_:/ ]/g,'').slice(0,80) || '(direct)';
|
||||||
const a = getAnalytics(); if (!a.sources) a.sources = {};
|
const a = getAnalytics(); if (!a.sources) a.sources = {};
|
||||||
if (!a.sources[s]) { if (Object.keys(a.sources).length >= 500) return; a.sources[s] = { bridge:0, start:0, click:0 }; }
|
if (!a.sources[s]) { if (Object.keys(a.sources).length >= 500) return; a.sources[s] = { bridge:0, start:0, click:0 }; }
|
||||||
|
|||||||
Reference in New Issue
Block a user