Coaching Radar: separate Marty-owned positions (config.ownerIds) into their own action-item block, exclude from member-nudge tiers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-19 06:24:22 -05:00
parent 2872a30b96
commit 43753dda4e
2 changed files with 10 additions and 1 deletions
+8
View File
@@ -98,12 +98,20 @@ async function loadCoaching(){
if(!d.ready){out.innerHTML='<div class="empty">Chain snapshot not ready — try again in a minute.</div>';return}
const nm=r=>r.name?`${esc(r.name)} (#${r.id})`:`#${r.id}`;
const link=r=>`<a href="/my/${r.id}" target="_blank" rel="noopener" style="color:var(--teal)">${nm(r)}</a>`;
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)?
`<h3 style="margin:12px 0 6px;font-size:14px">👑 Your positions — your own action items</h3>`+
mineRisk.map(r=>`<div class="pp-row" style="padding:8px 12px;border-color:rgba(240,197,109,.4)"><div class="pp-icon">👑</div><div class="pp-body">${link(r)} — ${esc(r.levelName)}, <strong style="color:var(--gold)">${fmt(r.atRiskPol)} POL</strong> forming; needs <strong>${esc(r.neededLevelName||'qualification')}</strong> to catch it${r.earnedPol>=(r.upgradeCost||0)&&r.upgradeCost?' — <span style="color:var(--ok)">next step covered by its earnings</span>':r.upgradeCost?` (earned ${fmt(r.earnedPol)} vs ${fmt(r.upgradeCost)} next step)`:''}.</div></div>`).join('')+
mineRoll.map(r=>`<div class="pp-row" style="padding:8px 12px;border-color:rgba(240,197,109,.4)"><div class="pp-icon">👑</div><div class="pp-body">${link(r)} — qualified at Scintilla, Ascensus (${fmt(r.ascensusCost)} POL) covered by its ${fmt(r.earnedPol)} POL earned, whenever you choose.</div></div>`).join('')
:'';
const t1=d.rollForward.map(r=>`<div class="pp-row" style="padding:8px 12px"><div class="pp-icon">💬</div><div class="pp-body">${link(r)} — qualified, still Scintilla, sitting on <strong>${fmt(r.earnedPol)} POL</strong> (Ascensus costs ${fmt(r.ascensusCost)}). One message: “your entry rewards already cover the upgrade that catches your team's first payments.”</div></div>`).join('')||'<div class="empty">Nobody stuck at qualified-Scintilla. 🎉</div>';
const t2=d.atRisk.filter(r=>r.qualified&&r.level>1).map(r=>`<div class="pp-row" style="padding:8px 12px"><div class="pp-icon">⚠️</div><div class="pp-body">${link(r)} — ${esc(r.levelName)}, needs <strong>${esc(r.neededLevelName)}</strong> to catch <strong style="color:var(--gold)">${fmt(r.atRiskPol)} POL</strong> forming under them (from ${r.fromIds.map(f=>'#'+f).join(', ')}).</div></div>`).join('')||'<div class="empty">Everyone leveled is ahead of their wave. 🎉</div>';
const t3=d.oneAway.map(r=>`<div class="pp-row" style="padding:8px 12px"><div class="pp-icon">🎯</div><div class="pp-body">${link(r)} — 1/2 directs, one placement from qualifying.</div></div>`).join('')||'<div class="empty">Nobody stranded at 1/2.</div>';
const unq=d.atRisk.filter(r=>!r.qualified).map(r=>`<div class="pp-row" style="padding:8px 12px"><div class="pp-icon">⏰</div><div class="pp-body">${link(r)} — NOT qualified (${r.directCount}/2) with <strong style="color:var(--gold)">${fmt(r.atRiskPol)} POL</strong> already forming below. Qualification is urgent for them.</div></div>`).join('');
out.innerHTML=
`<div class="facts" style="grid-template-columns:repeat(4,1fr);margin-bottom:14px"><div class="fact"><small>Members scanned</small><strong>${d.scanned}</strong></div><div class="fact"><small>POL at risk</small><strong style="color:var(--gold)">${fmt(d.totals.atRiskPol)}</strong></div><div class="fact"><small>Easy wins</small><strong>${d.totals.rollForwardCount}</strong></div><div class="fact"><small>One direct away</small><strong>${d.totals.oneAwayCount}</strong></div></div>`+
mineHtml+
`<h3 style="margin:12px 0 6px;font-size:14px">Tier 1 · Easy wins — one message each</h3>${t1}`+
`<h3 style="margin:16px 0 6px;font-size:14px">Tier 2 · Money at risk — coach the upgrade</h3>${t2}`+
(unq?`<h3 style="margin:16px 0 6px;font-size:14px">Tier 2b · Money forming, not yet qualified</h3>${unq}`:'')+
+2 -1
View File
@@ -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);
}