diff --git a/public/admin.js b/public/admin.js index f9fcc9f..0877c5f 100644 --- a/public/admin.js +++ b/public/admin.js @@ -8,8 +8,8 @@ function statusClass(s){return `status status-${s}`} function pct(n,d){return d?`${Math.round((n/d)*100)}%`:'—'} function renderAnalytics(){ const src=(state.analytics&&state.analytics.sources)||{}; - 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;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}}) + const tot={bridge:0,start:0,click:0,training:0,postback:0,purchase:0,join:0,joinnow:0,engaged:0,ctbCredited: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;tot.engaged+=r.engaged||0;tot.ctbCredited+=r['ctb-offer-credited']||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); document.getElementById('funnelStats').innerHTML= `
Bridge views${tot.bridge}
`+ @@ -21,7 +21,9 @@ function renderAnalytics(){ (tot.joinnow?`
Join-now views${tot.joinnow}
`:'')+ (tot.purchase?`
Confirmed joins${tot.purchase}
`:'')+ (tot.joinnow&&tot.purchase?`
Join-now → Confirmed${pct(tot.purchase,tot.joinnow)}
`:'')+ - (tot.postback?`
BeMob postbacks${tot.postback}
`:''); + (tot.postback?`
BeMob postbacks${tot.postback}
`:'')+ + (tot.engaged?`
Engaged 30s+ visits${tot.engaged}
`:'')+ + (tot.ctbCredited?`
CTB offer credits${tot.ctbCredited}
`:''); const subs=state.submissions||[]; document.getElementById('submissionRows').innerHTML=subs.map(s=>{ const path=s.path==='rotation'?'rotation ':s.path==='leg'?'leg ':''; diff --git a/public/contract.html b/public/contract.html index 8e709d0..31a72db 100644 --- a/public/contract.html +++ b/public/contract.html @@ -30,4 +30,4 @@ - + diff --git a/public/disclaimer.html b/public/disclaimer.html index 8d0a13c..860c125 100644 --- a/public/disclaimer.html +++ b/public/disclaimer.html @@ -25,4 +25,4 @@ - + diff --git a/public/how-pay-works.html b/public/how-pay-works.html index f7a8a66..c55962b 100644 --- a/public/how-pay-works.html +++ b/public/how-pay-works.html @@ -74,4 +74,4 @@ - + diff --git a/public/index.html b/public/index.html index 4fc4f49..8401112 100644 --- a/public/index.html +++ b/public/index.html @@ -23,4 +23,4 @@
Ready to start?

See the current team placement.

The onboarding page automatically shows the sponsor position the team is currently helping. Always use the sponsor shown there instead of an old screenshot or saved link.

Open Getting Started Instructions →
- + diff --git a/public/join.html b/public/join.html index 49c6bf5..b21620c 100644 --- a/public/join.html +++ b/public/join.html @@ -29,4 +29,4 @@
Risk reminder: participation involves cryptocurrency and smart-contract risk. No income is guaranteed. Use only funds you can afford to lose.
- + diff --git a/public/nav-dash.js b/public/nav-dash.js new file mode 100644 index 0000000..0fc4ce7 --- /dev/null +++ b/public/nav-dash.js @@ -0,0 +1,27 @@ +// "My Dashboard" shortcut in the top-right nav. Appears when we know who the +// visitor is: an authenticated msg session (web wallet sign-in OR the Telegram +// Mini App auth bridge) is authoritative; otherwise the last dashboard opened +// on this device (ctb.myId, set by my.js) or the tools-page ID. No identity → +// no button, so prospects never see it. +(function () { + 'use strict'; + if (/^\/my(\/|$)/.test(location.pathname)) return; + var nav = document.querySelector('.nav-actions'); + if (!nav) return; + function add(id) { + if (!id || !/^\d{1,15}$/.test(String(id))) return; + if (document.getElementById('navMyDash')) return; + var a = document.createElement('a'); + a.id = 'navMyDash'; + a.className = 'btn btn-secondary'; + a.href = '/my/' + id; + a.textContent = '📊 My Dashboard'; + nav.insertBefore(a, nav.firstChild); + } + var stored = null; + try { stored = localStorage.getItem('ctb.myId') || localStorage.getItem('rmc.toolsId'); } catch (e) {} + fetch('/api/public/msg-me') + .then(function (r) { return r.ok ? r.json() : null; }) + .then(function (d) { add(d && d.id ? d.id : stored); }) + .catch(function () { add(stored); }); +})(); diff --git a/public/start.html b/public/start.html index f791d1f..74799a3 100644 --- a/public/start.html +++ b/public/start.html @@ -16,4 +16,4 @@
Risk reminder: participation involves cryptocurrency and smart-contract risk. No income is guaranteed. Use only funds you can afford to lose.
RM Circle Premium Team Build Roadmap — core strategy, step-by-step guide, premium levels, and duplication formula
This roadmap is the plan every member follows — tap to view full size.
- + diff --git a/public/tools.html b/public/tools.html index 623501e..3cc5d13 100644 --- a/public/tools.html +++ b/public/tools.html @@ -477,4 +477,4 @@ Watch the short training, look at the live payment feed, then ask me anything. - + diff --git a/public/training.html b/public/training.html index 0ddaa56..1120f14 100644 --- a/public/training.html +++ b/public/training.html @@ -86,4 +86,4 @@ - + diff --git a/server.js b/server.js index ceda29d..ad301c3 100644 --- a/server.js +++ b/server.js @@ -349,7 +349,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 getAnalytics() { try { return readJson(ANALYTICS_FILE); } catch (e) { return { sources: {} }; } } function recordEvent(event, source) { - if (!['bridge','start','click','training','postback','purchase','join','joinnow'].includes(event)) return; + if (!['bridge','start','click','training','postback','purchase','join','joinnow','engaged'].includes(event) && !String(event).startsWith('ctb-offer-')) return; const s = String(source||'').toLowerCase().trim().replace(/[^a-z0-9.()\-_:/ ]/g,'').slice(0,80) || '(direct)'; 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 }; }