Members view: Balance / All-time mode switch (period columns hide in all-time); drop redundant 'active' total card
This commit is contained in:
1 parent
52314ddfe2
commit
8c6f2a85c0
1 file changed
+29
-20
+29
-20
@@ -273,6 +273,7 @@ PAGE_HTML = """<!DOCTYPE html>
|
||||
.range-label { font-size: 12px; color: var(--muted); margin-right: 2px; }
|
||||
.range-btn { cursor: pointer; border: 1px solid var(--border); background: #fff; color: var(--muted); border-radius: 8px; font-size: 12px; font-weight: 600; padding: 5px 12px; }
|
||||
.range-btn.active { background: var(--green-mid); color: #fff; border-color: var(--green-mid); }
|
||||
.members-all-time .balance-col { display: none; }
|
||||
.table-scroll { overflow-x: auto; border-radius: 12px; }
|
||||
.table-scroll table { min-width: 980px; }
|
||||
th.sortable { cursor: pointer; user-select: none; }
|
||||
@@ -291,7 +292,6 @@ PAGE_HTML = """<!DOCTYPE html>
|
||||
|
||||
<div class="totals">
|
||||
<div class="tot"><div class="num" id="tot-members">—</div><div class="lbl">members</div></div>
|
||||
<div class="tot"><div class="num" id="tot-active">—</div><div class="lbl">active</div></div>
|
||||
<div class="tot"><div class="num" id="tot-spend-cycle">—</div><div class="lbl">current balance used</div></div>
|
||||
<div class="tot"><div class="num" id="tot-spend-all">—</div><div class="lbl">all-time spend</div></div>
|
||||
</div>
|
||||
@@ -302,6 +302,10 @@ PAGE_HTML = """<!DOCTYPE html>
|
||||
<h2 id="view-title">Members</h2>
|
||||
<button class="tab-btn active" id="tab-members" onclick="switchView('members')">Members</button>
|
||||
<button class="tab-btn" id="tab-models" onclick="switchView('models')">Model usage</button>
|
||||
<span class="range-group" id="member-mode-group">
|
||||
<button class="range-btn active" id="mmode-balance" onclick="setMemberMode('balance')">Balance</button>
|
||||
<button class="range-btn" id="mmode-all" onclick="setMemberMode('all')">All time</button>
|
||||
</span>
|
||||
<span class="range-group" id="range-group" style="display:none;">
|
||||
<span class="range-label">Period</span>
|
||||
<button class="range-btn" id="range-30" onclick="setRange(30)">Last 30 days</button>
|
||||
@@ -317,11 +321,11 @@ PAGE_HTML = """<!DOCTYPE html>
|
||||
<th class="sortable" onclick="sortMembers('active')">Status<span class="arrow" id="arrow-members-active"></span></th>
|
||||
<th class="sortable" onclick="sortMembers('activated')">Activated<span class="arrow" id="arrow-members-activated"></span></th>
|
||||
<th class="sortable" onclick="sortMembers('manual')">Source<span class="arrow" id="arrow-members-manual"></span></th>
|
||||
<th class="sortable" onclick="sortMembers('balance')">Allowance<span class="arrow" id="arrow-members-balance"></span></th>
|
||||
<th class="sortable" onclick="sortMembers('credit_balance')">Credits<span class="arrow" id="arrow-members-credit_balance"></span></th>
|
||||
<th class="sortable" onclick="sortMembers('spend')" title="Click to toggle: spend from each member's current balance ↔ all-time">Spend (<span id="spend-mode-label">current</span>)<span class="arrow" id="arrow-members-spend"></span></th>
|
||||
<th class="sortable" onclick="sortMembers('remaining')">Remaining<span class="arrow" id="arrow-members-remaining"></span></th>
|
||||
<th class="sortable" onclick="sortMembers('reset_at')">Reset<span class="arrow" id="arrow-members-reset_at"></span></th>
|
||||
<th class="sortable balance-col" onclick="sortMembers('balance')">Allowance<span class="arrow" id="arrow-members-balance"></span></th>
|
||||
<th class="sortable balance-col" onclick="sortMembers('credit_balance')">Credits<span class="arrow" id="arrow-members-credit_balance"></span></th>
|
||||
<th class="sortable" onclick="sortMembers('spend')"><span id="spend-mode-label">Spend</span><span class="arrow" id="arrow-members-spend"></span></th>
|
||||
<th class="sortable balance-col" onclick="sortMembers('remaining')">Remaining<span class="arrow" id="arrow-members-remaining"></span></th>
|
||||
<th class="sortable balance-col" onclick="sortMembers('reset_at')">Reset<span class="arrow" id="arrow-members-reset_at"></span></th>
|
||||
<th>Adjust</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -376,7 +380,6 @@ async function load() {
|
||||
if (r.status === 403) { document.body.innerHTML = '<h1>Forbidden</h1>'; return; }
|
||||
const d = await r.json();
|
||||
$('tot-members').textContent = d.totals.members;
|
||||
$('tot-active').textContent = d.totals.active;
|
||||
$('tot-spend-cycle').textContent = fmt(d.totals.total_spend);
|
||||
$('tot-spend-all').textContent = fmt(d.totals.total_all_time_spend ?? d.totals.total_spend);
|
||||
membersData = d.members || [];
|
||||
@@ -393,7 +396,7 @@ let currentView = 'members';
|
||||
let memberSort = { key: 'email', dir: 1 };
|
||||
let modelSort = { key: 'spend', dir: -1 };
|
||||
let rangeDays = 0; // 0 = all time
|
||||
let spendMode = 'cycle'; // 'cycle' | 'all' — Spend column in Members view ('cycle' = usage from the member's current balance period)
|
||||
let memberMode = 'balance'; // 'balance' | 'all' — Members view: current-balance figures vs all-time spend
|
||||
|
||||
function arrowFor(key, sortState) {
|
||||
if (sortState.key !== key) return '';
|
||||
@@ -417,8 +420,9 @@ function switchView(view) {
|
||||
$('tab-members').className = 'tab-btn' + (view === 'members' ? ' active' : '');
|
||||
$('tab-models').className = 'tab-btn' + (view === 'models' ? ' active' : '');
|
||||
$('view-title').textContent = view === 'members' ? 'Members' : 'Model usage';
|
||||
// Range buttons only apply to the models view (spend logs are model-level).
|
||||
// Mode groups: Balance/All-time for members; Period for models.
|
||||
$('range-group').style.display = view === 'models' ? '' : 'none';
|
||||
$('member-mode-group').style.display = view === 'members' ? '' : 'none';
|
||||
render();
|
||||
}
|
||||
|
||||
@@ -429,20 +433,25 @@ function setRange(days) {
|
||||
loadModels();
|
||||
}
|
||||
|
||||
function setMemberMode(mode) {
|
||||
memberMode = mode;
|
||||
$('mmode-balance').className = 'range-btn' + (mode === 'balance' ? ' active' : '');
|
||||
$('mmode-all').className = 'range-btn' + (mode === 'all' ? ' active' : '');
|
||||
// Header label reflects the mode
|
||||
$('spend-mode-label').textContent = mode === 'all' ? 'All-time spend' : 'Spend';
|
||||
// Balance-only columns (Allowance/Credits/Remaining/Reset) hide in all-time mode
|
||||
$('table-members').parentElement.classList.toggle('members-all-time', mode === 'all');
|
||||
renderMembers();
|
||||
}
|
||||
|
||||
function sortMembers(key) {
|
||||
// Clicking the Spend header toggles the value shown (since-reset ↔ all-time)
|
||||
// before sorting; other headers just sort.
|
||||
if (key === 'spend') {
|
||||
spendMode = spendMode === 'cycle' ? 'all' : 'cycle';
|
||||
$('spend-mode-label').textContent = spendMode === 'cycle' ? 'current' : 'all-time';
|
||||
}
|
||||
memberSort = { key, dir: memberSort.key === key ? -memberSort.dir : 1 };
|
||||
clearArrows('members', memberSort);
|
||||
render();
|
||||
}
|
||||
|
||||
function memberSpend(m) {
|
||||
return spendMode === 'all' ? (m.all_time_spend ?? m.spend ?? 0) : (m.spend ?? 0);
|
||||
return memberMode === 'all' ? (m.all_time_spend ?? m.spend ?? 0) : (m.spend ?? 0);
|
||||
}
|
||||
|
||||
function sortModels(key) {
|
||||
@@ -484,11 +493,11 @@ function renderMembers() {
|
||||
'<td><span class="pill ' + pill + '">' + pill + '</span></td>' +
|
||||
'<td>' + act + '</td>' +
|
||||
'<td>' + src + '</td>' +
|
||||
'<td class="num">' + fmt(m.balance) + '</td>' +
|
||||
'<td>' + creditsPill + '</td>' +
|
||||
'<td class="num balance-col">' + fmt(m.balance) + '</td>' +
|
||||
'<td class="balance-col">' + creditsPill + '</td>' +
|
||||
'<td class="num">' + fmt(memberSpend(m)) + '</td>' +
|
||||
'<td class="num">' + fmt(m.remaining) + '</td>' +
|
||||
'<td>' + esc(reset) + '</td>' +
|
||||
'<td class="num balance-col">' + fmt(m.remaining) + '</td>' +
|
||||
'<td class="balance-col">' + esc(reset) + '</td>' +
|
||||
'<td><div class="adjust">' +
|
||||
'<input id="add-' + i(m.email) + '" placeholder="+$" />' +
|
||||
'<button data-email="' + esc(m.email) + '" onclick="addBalance(this.dataset.email)" title="Add monthly allowance">Allow</button>' +
|
||||
|
||||
Reference in new issue
Block a user