From a0642516e1958134a21d4079e1e96c63145a5c28 Mon Sep 17 00:00:00 2001 From: martbost Date: Mon, 24 Aug 2026 07:10:52 -0500 Subject: [PATCH] My Dashboard nav shortcut + analytics whitelist fix for engaged/ctb-offer nav-dash.js on all header pages: a top-right My Dashboard button when the visitor is known (msg session from wallet sign-in or the Mini App bridge is authoritative; else the last dashboard opened on the device). Prospects with no identity never see it. recordEvent was silently dropping the new engaged + ctb-offer-* events (whitelist); now recorded per source, with admin funnel tiles for both. Co-Authored-By: Claude Fable 5 --- public/admin.js | 8 +++++--- public/contract.html | 2 +- public/disclaimer.html | 2 +- public/how-pay-works.html | 2 +- public/index.html | 2 +- public/join.html | 2 +- public/nav-dash.js | 27 +++++++++++++++++++++++++++ public/start.html | 2 +- public/tools.html | 2 +- public/training.html | 2 +- server.js | 2 +- 11 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 public/nav-dash.js 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.
This independent team page is educational and is not an earnings guarantee or investment advice. Cryptocurrency and smart-contract participation involve risk, including possible loss of funds. Never use funds you cannot afford to lose. Results depend on actual participation, qualification, upgrades, smart-contract behavior, and the market value of POL.
- + 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 }; }