Member identity, promo tools, back-office color, seeded inventory
Real people, not numbers: usernames (unique, profile pane to set them), shown across the ledger, activity, rosters, and the sidebar chip; vanity invite links (/join/<username>) with late chain binding intact. Promo tools pane ships Branded Voice share posts and an email swipe personalized with each member's link. The earn viewer excludes a member's own campaigns, so nobody earns from their own spend. Back office gains the cyan/violet/amber accent family over the green base. Three house campaigns seeded so every surface shows live inventory. Assets v=20260905i. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -102,9 +102,10 @@ const J = {
|
||||
this.save();
|
||||
return { ok: true, campaign: pubC(c) };
|
||||
},
|
||||
async serve(type) {
|
||||
async serve(type, opts) {
|
||||
const r = rates();
|
||||
const pool = this.db.campaigns.filter(c => c.type === type && c.status === 'active');
|
||||
const ex = opts && opts.excludeEmail;
|
||||
const pool = this.db.campaigns.filter(c => c.type === type && c.status === 'active' && (!ex || c.owner !== ex));
|
||||
if (!pool.length) return null;
|
||||
const c = pool[Math.floor(Math.random() * pool.length)];
|
||||
c.imps += 1;
|
||||
@@ -194,9 +195,10 @@ const D = {
|
||||
const rows = await db.q('SELECT * FROM campaigns WHERE id=?', [Number(id)]);
|
||||
return { ok: true, campaign: pubC(rowC(rows[0])) };
|
||||
},
|
||||
async serve(type) {
|
||||
async serve(type, opts) {
|
||||
const r = rates();
|
||||
const rows = await db.q('SELECT * FROM campaigns WHERE type=? AND status=\'active\' ORDER BY RAND() LIMIT 1', [type]);
|
||||
const ex = (opts && opts.excludeEmail) || '';
|
||||
const rows = await db.q('SELECT * FROM campaigns WHERE type=? AND status=\'active\' AND owner_email<>? ORDER BY RAND() LIMIT 1', [type, ex]);
|
||||
if (!rows.length) return null;
|
||||
const c = rowC(rows[0]);
|
||||
await db.q('UPDATE campaigns SET imps=imps+1, batch_imps=batch_imps+1 WHERE id=?', [c.id]);
|
||||
@@ -411,7 +413,7 @@ async function setStatus(owner, id, status) {
|
||||
if (!['active', 'paused'].includes(status)) return { error: 'Bad status.' };
|
||||
return impl().setStatus(owner, id, status);
|
||||
}
|
||||
async function serve(type) { return TYPES.includes(type) ? impl().serve(type) : null; }
|
||||
async function serve(type, opts) { return TYPES.includes(type) ? impl().serve(type, opts) : null; }
|
||||
async function click(id) { return impl().click(id); }
|
||||
async function dailySweep() { return impl().dailySweep(); }
|
||||
async function pendingBurns() { return impl().pendingBurns(); }
|
||||
|
||||
Reference in New Issue
Block a user