Back-office polish: Overview command center, quick actions, pane motion

Overview gains a recent-activity mini-feed (chain events + line joins) and
a quick-actions card jumping straight to buy/earn/campaigns plus one-tap
invite copy. Panes fade in (reduced-motion safe). First slice of the
back-office polish pass. Assets v=20260905f.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-05 06:09:37 -05:00
parent a4c7028c0e
commit 7331eac244
6 changed files with 74 additions and 23 deletions
+31
View File
@@ -50,6 +50,28 @@
+ '<td><span class="badge' + (r.status === 'joined free' ? ' amber' : '') + '">' + r.status + '</span></td></tr>').join('');
wrap.appendChild(t);
}
// overview recent-activity widget: chain events + line joins, newest first
try {
const c = await IAP.getConfig();
const ov = $('ovFeed');
const rows = [];
if (d.memberId) {
const a = await (await fetch('/api/my/activity')).json();
for (const ev of [...(a.earnings || []), ...(a.purchases || [])]
.sort((x, y) => y.block - x.block).slice(0, 5)) rows.push(IAP.feedRow(ev, c));
}
for (const r of (d.referrals || []).slice(0, 3)) {
const div = document.createElement('div');
div.className = 'row';
div.innerHTML = '<span>🤝 ' + r.email + ' joined your line</span><span class="tx muted small">'
+ new Date(r.joined).toLocaleDateString() + '</span>';
rows.push(div);
}
if (rows.length) {
ov.innerHTML = '';
rows.slice(0, 6).forEach(r => ov.appendChild(r));
}
} catch (e) {}
// ready-to-send share message
const link = location.origin + '/join/' + (d.refCode || d.memberId || '');
if (d.refCode || d.memberId) {
@@ -85,6 +107,15 @@
}
document.querySelectorAll('.bo-menu [data-pane]').forEach(b =>
b.addEventListener('click', () => setPane(b.dataset.pane)));
document.querySelectorAll('.qa [data-goto]').forEach(b =>
b.addEventListener('click', () => setPane(b.dataset.goto)));
const qaCopy = document.getElementById('qaCopyInvite');
if (qaCopy) qaCopy.addEventListener('click', async () => {
const link = document.getElementById('inviteLine').textContent;
if (!link || !link.startsWith('http')) { setPane('line'); return; }
try { await navigator.clipboard.writeText(link); IAP.status('Invite link copied.', 'ok'); }
catch (e) { setPane('line'); }
});
window.addEventListener('hashchange', () => setPane(location.hash.slice(1)));
if ($('boBurger')) $('boBurger').addEventListener('click', () =>
document.getElementById('memberArea').classList.toggle('side-open'));