Member dashboard: stat cards, next-move guidance, line roster, share kit

/api/my/dashboard aggregates credits, earned POL (from the chain index),
qualification count, and the referral roster (privacy-masked emails,
joined-free vs wallet-linked status, code+id referrer match). Members area
leads with four stat cards, a next-move card with a qualification progress
bar (2 and 5 buyer marks), the line roster, and a copy-ready share message
beside the invite link. Assets v=20260904j.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-04 14:47:07 -05:00
parent 6680e154d0
commit a386c8e3a3
8 changed files with 158 additions and 20 deletions
+36
View File
@@ -300,6 +300,42 @@ const server = http.createServer(async (req, res) => {
return json(res, 200, out);
}
if (p === '/api/my/dashboard' && req.method === 'GET') {
const s = await auth.fromRequest(req);
if (!s) return json(res, 401, { error: 'Sign in first.' });
const memberId = await auth.refreshMemberId(s);
const acct = (s.email && await accounts.byEmail(s.email)) || (s.address && await accounts.byAddress(s.address)) || null;
const out = { memberId, email: s.email || (acct && acct.email) || null,
address: s.address || (acct && acct.address) || null,
refCode: (acct && acct.code) || null, credits: 0, buyerCount: 0,
earnedWei: '0', earnCount: 0, referrals: [] };
if (memberId) {
try {
const mm = await chain.member(memberId);
out.buyerCount = mm.buyerCount;
out.credits = await ads.availableCredits(memberId);
} catch (e) { out.chainReadError = true; }
let earned = 0n, n = 0;
for (const ev of chain.recentEvents(600)) {
if ((ev.type === 'TierPaid' && ev.recipientId === memberId) || (ev.type === 'AwardPaid' && ev.toId === memberId)) {
earned += BigInt(ev.amountWei); n += 1;
}
}
out.earnedWei = earned.toString();
out.earnCount = n;
}
// who joined through this member (code and, once on-chain, numeric id)
const refs = [];
if (acct && acct.code) refs.push(acct.code);
if (memberId) refs.push(String(memberId));
const joined = await accounts.listByReferrer(refs);
out.referrals = joined.map(r => ({
email: r.email.replace(/^(.).*(@.*)$/, '$1***$2'), // privacy mask
joined: r.created,
status: r.address ? 'wallet linked' : 'joined free'
}));
return json(res, 200, out);
}
if (p === '/api/my/activity' && req.method === 'GET') {
const s = await auth.fromRequest(req);
if (!s) return json(res, 401, { error: 'Sign in first.' });