Spend clarity: since-reset vs all-time (per-member resets), toggle on Spend header, both totals shown; rename cycle wording

This commit is contained in:
inference-bot committed 2026-09-30 22:22:13 -06:00
1 parent 51e189ae4d
commit 90d76dc7ab
1 file changed
+18 -4
+18 -4
View File
@@ -292,7 +292,8 @@ 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">—</div><div class="lbl">total spend</div></div>
<div class="tot"><div class="num" id="tot-spend-cycle">—</div><div class="lbl">sp since reset</div></div>
<div class="tot"><div class="num" id="tot-spend-all">—</div><div class="lbl">all-time spend</div></div>
</div>
<div class="flash" id="flash"></div>
@@ -318,7 +319,7 @@ PAGE_HTML = """<!DOCTYPE html>
<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')">Spend<span class="arrow" id="arrow-members-spend"></span></th>
<th class="sortable" onclick="sortMembers('spend')" title="Click to toggle: spend since each member's own reset ↔ all-time">Spend (<span id="spend-mode-label">since reset</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>Adjust</th>
@@ -376,7 +377,8 @@ async function load() {
const d = await r.json();
$('tot-members').textContent = d.totals.members;
$('tot-active').textContent = d.totals.active;
$('tot-spend').textContent = fmt(d.totals.total_spend);
$('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 || [];
if (currentView === 'members') renderMembers();
} catch (e) {
@@ -391,6 +393,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 = since the member's own reset)
function arrowFor(key, sortState) {
if (sortState.key !== key) return '';
@@ -427,11 +430,21 @@ function setRange(days) {
}
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' ? 'since reset' : '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);
}
function sortModels(key) {
modelSort = { key, dir: modelSort.key === key ? -modelSort.dir : -1 };
clearArrows('models', modelSort);
@@ -446,6 +459,7 @@ function render() {
function renderMembers() {
const rows = [...membersData].sort((a, b) => {
let va = a[memberSort.key], vb = b[memberSort.key];
if (memberSort.key === 'spend') { va = memberSpend(a); vb = memberSpend(b); }
if (memberSort.key === 'active' || memberSort.key === 'activated') { va = va ? 1 : 0; vb = vb ? 1 : 0; }
if (typeof va === 'string' || typeof vb === 'string') {
return String(va ?? '').localeCompare(String(vb ?? '')) * memberSort.dir;
@@ -472,7 +486,7 @@ function renderMembers() {
'<td>' + src + '</td>' +
'<td class="num">' + fmt(m.balance) + '</td>' +
'<td>' + creditsPill + '</td>' +
'<td class="num">' + fmt(m.spend) + '</td>' +
'<td class="num">' + fmt(memberSpend(m)) + '</td>' +
'<td class="num">' + fmt(m.remaining) + '</td>' +
'<td>' + esc(reset) + '</td>' +
'<td><div class="adjust">' +