Line tree: gold outline follows the chain's BuyerCounted events, plus a legend
The Overview tree painted gold on the first N chips (N = buyer count) instead of the members the contract actually counted, so a non-buyer listed first showed as qualified (Hugh's line, 2026-09-12). /api/my/line now flags each member with qualified from BuyerCounted events for the account's ids; the tree uses that flag and shows a legend for gold / plain / open chips. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -335,8 +335,8 @@
|
|||||||
const chip = (m, q) => '<span class="lt-node' + (q ? ' qualified' : (m.email ? '' : ' deep')) + '" title="' + esc(m.name || 'member') + (q ? ' · qualified buyer' : '') + '">' + esc(String(m.name || 'M').replace(/^@/, '').slice(0, 12)) + '</span>';
|
const chip = (m, q) => '<span class="lt-node' + (q ? ' qualified' : (m.email ? '' : ' deep')) + '" title="' + esc(m.name || 'member') + (q ? ' · qualified buyer' : '') + '">' + esc(String(m.name || 'M').replace(/^@/, '').slice(0, 12)) + '</span>';
|
||||||
const rowFor = lvl => {
|
const rowFor = lvl => {
|
||||||
const L = levels.find(x => x.level === lvl); const members = L ? L.members : [];
|
const L = levels.find(x => x.level === lvl); const members = L ? L.members : [];
|
||||||
const qn = lvl === 1 ? (buyerCount || 0) : 0; // highlight your qualified directs
|
// gold = the contract counted this member as one of your qualifying buyers (not "the first N chips")
|
||||||
let html = members.map((m, i) => chip(m, i < qn)).join('');
|
let html = members.map(m => chip(m, !!m.qualified)).join('');
|
||||||
if (lvl <= 2 && members.length < (lvl === 1 ? 2 : 4)) html += '<span class="lt-node open">+ open</span>';
|
if (lvl <= 2 && members.length < (lvl === 1 ? 2 : 4)) html += '<span class="lt-node open">+ open</span>';
|
||||||
if (!html) html = '<span class="lt-node open">+ open</span>';
|
if (!html) html = '<span class="lt-node open">+ open</span>';
|
||||||
return '<div class="lt-row"><span class="lt-cap">L' + lvl + '</span><div class="lt-nodes">' + html + '</div></div>';
|
return '<div class="lt-row"><span class="lt-cap">L' + lvl + '</span><div class="lt-nodes">' + html + '</div></div>';
|
||||||
|
|||||||
@@ -234,6 +234,7 @@
|
|||||||
<div class="card" id="lineTreeCard">
|
<div class="card" id="lineTreeCard">
|
||||||
<div class="card-head"><h3>Your line at a glance</h3><span class="sub" id="treeSub"></span></div>
|
<div class="card-head"><h3>Your line at a glance</h3><span class="sub" id="treeSub"></span></div>
|
||||||
<div id="lineTree" class="line-tree"><p class="muted small">Loading your line…</p></div>
|
<div id="lineTree" class="line-tree"><p class="muted small">Loading your line…</p></div>
|
||||||
|
<p class="small muted" id="treeLegend" style="margin:10px 0 0;line-height:2"><span class="lt-node qualified" style="font-size:11px;padding:2px 8px">name</span> qualifying buyer: bought a $20+ package, counts toward your levels · <span class="lt-node" style="font-size:11px;padding:2px 8px">name</span> joined, no $20+ buy yet · <span class="lt-node open" style="font-size:11px;padding:2px 8px">+ open</span> spot still open</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card" id="badgeCard" hidden>
|
<div class="card" id="badgeCard" hidden>
|
||||||
<div class="card-head"><h3>Your achievements</h3><span class="sub">unlock as you build</span></div>
|
<div class="card-head"><h3>Your achievements</h3><span class="sub">unlock as you build</span></div>
|
||||||
|
|||||||
@@ -1234,10 +1234,12 @@ const server = http.createServer(async (req, res) => {
|
|||||||
const own = (await accounts.positions(s.email)).filter(p => p.memberId);
|
const own = (await accounts.positions(s.email)).filter(p => p.memberId);
|
||||||
const myIds = new Set([myId, ...own.map(p => p.memberId)].filter(Boolean));
|
const myIds = new Set([myId, ...own.map(p => p.memberId)].filter(Boolean));
|
||||||
const earnedBy = {};
|
const earnedBy = {};
|
||||||
|
const qualified = new Set(); // members the contract counted as this account's qualifying buyers ($20+)
|
||||||
if (myIds.size) {
|
if (myIds.size) {
|
||||||
for (const ev of chain.recentEvents(1e9)) {
|
for (const ev of chain.recentEvents(1e9)) {
|
||||||
if (ev.type === 'TierPaid' && myIds.has(ev.recipientId) && ev.buyerId)
|
if (ev.type === 'TierPaid' && myIds.has(ev.recipientId) && ev.buyerId)
|
||||||
earnedBy[ev.buyerId] = (BigInt(earnedBy[ev.buyerId] || '0') + BigInt(ev.amountWei)).toString();
|
earnedBy[ev.buyerId] = (BigInt(earnedBy[ev.buyerId] || '0') + BigInt(ev.amountWei)).toString();
|
||||||
|
if (ev.type === 'BuyerCounted' && myIds.has(ev.sponsorId) && ev.newBuyerId) qualified.add(ev.newBuyerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const out = levels.map(L => ({ level: L.level, members: L.members.map(m => ({
|
const out = levels.map(L => ({ level: L.level, members: L.members.map(m => ({
|
||||||
@@ -1245,6 +1247,7 @@ const server = http.createServer(async (req, res) => {
|
|||||||
name: m.username ? '@' + m.username : m.memberId ? 'member #' + m.memberId : 'member',
|
name: m.username ? '@' + m.username : m.memberId ? 'member #' + m.memberId : 'member',
|
||||||
email: L.level === 1 ? m.email : null, // directs only
|
email: L.level === 1 ? m.email : null, // directs only
|
||||||
joined: m.created,
|
joined: m.created,
|
||||||
|
qualified: !!(m.memberId && qualified.has(m.memberId)),
|
||||||
earnedWei: (m.memberId && earnedBy[m.memberId]) || '0' })) }));
|
earnedWei: (m.memberId && earnedBy[m.memberId]) || '0' })) }));
|
||||||
// the member's own linked positions sit on level 1 too, labelled as theirs
|
// the member's own linked positions sit on level 1 too, labelled as theirs
|
||||||
if (own.length) {
|
if (own.length) {
|
||||||
|
|||||||
Reference in New Issue
Block a user