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
+16 -1
View File
@@ -82,6 +82,13 @@ const J = {
async byEmail(e) { return pub(this.db.byEmail[e]); },
async byAddress(a) { const e = this.db.byAddress[a]; return e ? pub(this.db.byEmail[e]) : null; },
async byCode(c) { const e = this.db.byCode[c]; return e ? pub(this.db.byEmail[e]) : null; },
async listByReferrer(refs) {
const set = new Set(refs.filter(Boolean).map(String));
return Object.values(this.db.byEmail)
.filter(a => set.has(String(a.sponsorRef || '')))
.sort((a, b) => b.created - a.created)
.slice(0, 200).map(pub);
},
async linkWallet(e, a) {
const acct = this.db.byEmail[e];
if (!acct) return { error: 'No such account.' };
@@ -133,6 +140,13 @@ const D = {
async byEmail(e) { const r = await db.q('SELECT * FROM accounts WHERE email=?', [e]); return rowPub(r[0]); },
async byAddress(a) { const r = await db.q('SELECT * FROM accounts WHERE address=?', [a]); return rowPub(r[0]); },
async byCode(c) { const r = await db.q('SELECT * FROM accounts WHERE code=?', [c]); return rowPub(r[0]); },
async listByReferrer(refs) {
const clean = refs.filter(Boolean).map(String);
if (!clean.length) return [];
const rows = await db.q('SELECT * FROM accounts WHERE sponsor_ref IN (' + clean.map(() => '?').join(',')
+ ') ORDER BY created DESC LIMIT 200', clean);
return rows.map(rowPub);
},
async linkWallet(e, a) {
const cur = await this.byEmail(e);
if (!cur) return { error: 'No such account.' };
@@ -166,6 +180,7 @@ async function ensure(email, sponsorRef) {
async function byEmail(email) { return impl().byEmail(normEmail(email)); }
async function byAddress(address) { return impl().byAddress(normAddr(address)); }
async function byCode(code) { return impl().byCode(String(code || '').toLowerCase()); }
async function listByReferrer(refs) { return impl().listByReferrer(refs || []); }
async function linkWallet(email, address) {
const a = normAddr(address);
if (!/^0x[0-9a-f]{40}$/.test(a)) return { error: 'Bad wallet address.' };
@@ -173,4 +188,4 @@ async function linkWallet(email, address) {
}
async function count() { return impl().count(); }
module.exports = { init, signup, login, ensure, byEmail, byAddress, byCode, linkWallet, count };
module.exports = { init, signup, login, ensure, byEmail, byAddress, byCode, listByReferrer, linkWallet, count };