diff --git a/public/admin.js b/public/admin.js index 800103c..5356aae 100644 --- a/public/admin.js +++ b/public/admin.js @@ -98,12 +98,20 @@ async function loadCoaching(){ if(!d.ready){out.innerHTML='
Chain snapshot not ready โ€” try again in a minute.
';return} const nm=r=>r.name?`${esc(r.name)} (#${r.id})`:`#${r.id}`; const link=r=>`${nm(r)}`; + const mineRisk=d.atRisk.filter(r=>r.owned),mineRoll=d.rollForward.filter(r=>r.owned); + d.atRisk=d.atRisk.filter(r=>!r.owned);d.rollForward=d.rollForward.filter(r=>!r.owned);d.oneAway=d.oneAway.filter(r=>!r.owned); + const mineHtml=(mineRisk.length||mineRoll.length)? + `

๐Ÿ‘‘ Your positions โ€” your own action items

`+ + mineRisk.map(r=>`
๐Ÿ‘‘
${link(r)} โ€” ${esc(r.levelName)}, ${fmt(r.atRiskPol)} POL forming; needs ${esc(r.neededLevelName||'qualification')} to catch it${r.earnedPol>=(r.upgradeCost||0)&&r.upgradeCost?' โ€” next step covered by its earnings':r.upgradeCost?` (earned ${fmt(r.earnedPol)} vs ${fmt(r.upgradeCost)} next step)`:''}.
`).join('')+ + mineRoll.map(r=>`
๐Ÿ‘‘
${link(r)} โ€” qualified at Scintilla, Ascensus (${fmt(r.ascensusCost)} POL) covered by its ${fmt(r.earnedPol)} POL earned, whenever you choose.
`).join('') + :''; const t1=d.rollForward.map(r=>`
๐Ÿ’ฌ
${link(r)} โ€” qualified, still Scintilla, sitting on ${fmt(r.earnedPol)} POL (Ascensus costs ${fmt(r.ascensusCost)}). One message: โ€œyour entry rewards already cover the upgrade that catches your team's first payments.โ€
`).join('')||'
Nobody stuck at qualified-Scintilla. ๐ŸŽ‰
'; const t2=d.atRisk.filter(r=>r.qualified&&r.level>1).map(r=>`
โš ๏ธ
${link(r)} โ€” ${esc(r.levelName)}, needs ${esc(r.neededLevelName)} to catch ${fmt(r.atRiskPol)} POL forming under them (from ${r.fromIds.map(f=>'#'+f).join(', ')}).
`).join('')||'
Everyone leveled is ahead of their wave. ๐ŸŽ‰
'; const t3=d.oneAway.map(r=>`
๐ŸŽฏ
${link(r)} โ€” 1/2 directs, one placement from qualifying.
`).join('')||'
Nobody stranded at 1/2.
'; const unq=d.atRisk.filter(r=>!r.qualified).map(r=>`
โฐ
${link(r)} โ€” NOT qualified (${r.directCount}/2) with ${fmt(r.atRiskPol)} POL already forming below. Qualification is urgent for them.
`).join(''); out.innerHTML= `
Members scanned${d.scanned}
POL at risk${fmt(d.totals.atRiskPol)}
Easy wins${d.totals.rollForwardCount}
One direct away${d.totals.oneAwayCount}
`+ + mineHtml+ `

Tier 1 ยท Easy wins โ€” one message each

${t1}`+ `

Tier 2 ยท Money at risk โ€” coach the upgrade

${t2}`+ (unq?`

Tier 2b ยท Money forming, not yet qualified

${unq}`:'')+ diff --git a/server.js b/server.js index 9efeff5..4a5212f 100644 --- a/server.js +++ b/server.js @@ -516,7 +516,8 @@ async function handleApi(req,res,pathname){ const names={}; try{for(const s of getSponsors())names[String(s.id)]=s.name;}catch(e){} try{for(const s of readJson(SUBMISSIONS_FILE))if(s.newId&&!names[String(s.newId)])names[String(s.newId)]=s.memberName;}catch(e){} - for(const list of [d.atRisk,d.rollForward,d.oneAway])for(const r of list)if(names[String(r.id)])r.name=names[String(r.id)]; + const ownedIds=new Set(parseOwnerIds()); + for(const list of [d.atRisk,d.rollForward,d.oneAway])for(const r of list){if(names[String(r.id)])r.name=names[String(r.id)];if(ownedIds.has(Number(r.id)))r.owned=true;} } return json(res,200,d); }