diff --git a/public/admin-tabs.js b/public/admin-tabs.js new file mode 100644 index 0000000..5b5d545 --- /dev/null +++ b/public/admin-tabs.js @@ -0,0 +1,60 @@ +// Admin tab bar. Separate file because the site's CSP is script-src 'self'. +// +// The admin page had fourteen panels in a fixed 1.5fr/.75fr split — ten tall +// ones on the left, four short forms on the right. The right column ran out a +// quarter of the way down and you scrolled past dead space for the rest. +// Grouping them into tabs lets every panel use the full width. +(function () { + 'use strict'; + var KEY = 'rmc.admin.tab'; + + function panes() { return [].slice.call(document.querySelectorAll('.tabpane')); } + function tabs() { return [].slice.call(document.querySelectorAll('#adminTabs .tab')); } + + function show(key) { + var found = false; + panes().forEach(function (p) { + var on = p.getAttribute('data-pane') === key; + p.classList.toggle('on', on); + if (on) found = true; + }); + if (!found) return false; + tabs().forEach(function (t) { + var on = t.getAttribute('data-tab') === key; + t.classList.toggle('on', on); + t.setAttribute('aria-selected', on ? 'true' : 'false'); + }); + try { localStorage.setItem(KEY, key); } catch (e) {} + return true; + } + + document.addEventListener('DOMContentLoaded', function () { + var bar = document.getElementById('adminTabs'); + if (!bar) return; + + bar.addEventListener('click', function (e) { + var t = e.target.closest('.tab'); + if (t) show(t.getAttribute('data-tab')); + }); + + // Left/right arrows move between tabs, as a tablist should. + bar.addEventListener('keydown', function (e) { + if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return; + var all = tabs(); + var i = all.indexOf(document.activeElement); + if (i < 0) return; + var next = all[(i + (e.key === 'ArrowRight' ? 1 : all.length - 1)) % all.length]; + next.focus(); + show(next.getAttribute('data-tab')); + e.preventDefault(); + }); + + // Come back to whichever tab was last open. + var saved = null; + try { saved = localStorage.getItem(KEY); } catch (e) {} + if (!saved || !show(saved)) { + var first = tabs()[0]; + if (first) show(first.getAttribute('data-tab')); + } + }); +})(); diff --git a/public/admin.html b/public/admin.html index 087ae7c..cc0bcea 100644 --- a/public/admin.html +++ b/public/admin.html @@ -1,18 +1,5 @@
Manage the current sponsor, qualification queue, and onboarding settings.
Mark a sponsor qualified to automatically activate the next waiting position.
| Order | Sponsor | Parent | Directs | Level | Status | Clicks | Actions |
|---|
Live triage of your whole org: who to nudge, what to tell them, and how much POL is on the line. Computed fresh from the chain index on every refresh.
| To | What | Amount | Reason | When |
|---|
Wallet-verified member-to-member messages, newest first. Admin can review for abuse — members are told this in the UI.
| When | From | To | Message | Read by |
|---|---|---|---|---|
| Loading… | ||||
First-touch source per visitor session (referring domain or utm_source). Funnel: bridge page → start page → join click.
| Source | Bridge views | Start views | Training views | Invite views | Join-now views | Join clicks | Confirmed | Start → Join |
|---|
How your team — rooted at your top ID — stacks up against the entire RM Circle smart contract. Live on-chain.
New members who confirmed their purchase on the start page. Each one was posted to your Hermes Telegram chat — add them to the rotation.
| When | Name / Handle | New ID | Joined under | Source | On-chain |
|---|
Enter an RM Circle ID to read its registration, lineage, and every payment it has received — live from the smart contract.
The entire on-chain matrix — who landed where, with tier, level, directs, and earnings per position. Click a position to drill down.
Every payment received by your own positions, live from the contract. Comma-separated IDs — saved for next time.
Paste an OpenRouter API key to switch the help chat from canned answers to AI (). Clear it to switch back.
When a payout hits a member whose sponsor record has a contact email, they get a "you've been paid" email automatically. Paste your SendGrid API key (Branded Voice Coolify app → Environment Variables → SENDGRID_API_KEY).