Extend run / visits top-up: budget = charged + this buy (release parked credits); featured row shows days left

Hugh extended his featured link and his balance fell 1,133 -> 0: the row still carried the 1,400
parked by the old add-credits button, and reactivating it reserved that budget again. Both flat
paths now set budget to spent + accrued + cost. Featured Run column shows "N of M days left"
(Hugh: "says 14 but it's actually 7") and "M days done" after the run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-16 08:19:33 -05:00
parent 28bce75913
commit ffacf741da
3 changed files with 9 additions and 2 deletions
+8 -1
View File
@@ -819,7 +819,7 @@
+ '<td class="num" data-l="Network views">' + (['banner', 'text'].includes(c.type) ? (c.impsNas || 0).toLocaleString() : '<span class="muted small" title="only banner and text ads syndicate to the network">n/a</span>') + '</td>'
+ '<td class="num" data-l="Clicks">' + c.clicks + (r.clickSources && r.clickSources[c.id] ? '<div class="muted small clk-src" title="where the clicks happened">' + Object.entries(r.clickSources[c.id]).sort((a, b) => b[1] - a[1]).map(([k, v]) => '<span>' + esc(k) + ' ' + v + '</span>').join('') + (c.impsNas ? ' · network: see Network views' : '') + '</div>' : '') + '</td>'
+ (c.type === 'visits' ? '<td class="num" data-l="Spent"><span class="muted small" title="visit packs are paid in full when created; nothing more is charged as visits arrive">paid up front</span></td><td class="num" data-l="Delivered"><b>' + c.imps.toLocaleString() + '</b> of ' + Number(c.body || 0).toLocaleString() + ' visits</td>'
: c.type === 'featured' ? '<td class="num" data-l="Spent"><span class="muted small" title="featured runs are paid in full when booked (' + (r.rates.featuredPerDay || 40) + ' credits a day); nothing more is charged while the run is live">paid up front</span></td><td class="num" data-l="Run"><b>' + Number(c.body || 0) + '</b> day' + (Number(c.body || 0) === 1 ? '' : 's') + '</td>'
: c.type === 'featured' ? '<td class="num" data-l="Spent"><span class="muted small" title="featured runs are paid in full when booked (' + (r.rates.featuredPerDay || 40) + ' credits a day); nothing more is charged while the run is live">paid up front</span></td><td class="num" data-l="Run">' + featRun(c) + '</td>'
: '<td class="num" data-l="Spent">' + c.spent + '</td><td class="num" data-l="Budget">' + c.budget + '</td>')
+ '<td data-l="Status">' + (c.status === 'out' ? (c.type === 'visits' ? '<span class="badge">pack delivered</span>' : '<span class="badge amber">budget spent</span>') : c.status === 'done' ? '<span class="muted">' + (c.type === 'featured' ? 'run ended' : 'ended') + '</span>' : c.scheduled ? '<span class="badge">scheduled</span>' : c.status) + '</td>'
+ '<td class="act">' + (c.status === 'active' ? '<button class="btn small sec" data-camp="' + c.id + '" data-act="pause">Pause</button>'
@@ -1328,6 +1328,13 @@
}
// schedule chips on the campaign table (local time) + by-hour view bars
const fmtWhen = ms => new Date(ms).toLocaleString([], { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
// featured run column: days left of the booked total (Hugh, 2026-09-16: "says 14 but it's actually 7")
function featRun(c) {
const total = Number(c.body || 0), now = Date.now();
if (c.status === 'done' || !c.expires || c.expires <= now) return '<span class="muted">' + total + ' day' + (total === 1 ? '' : 's') + ' done</span>';
const left = Math.max(1, Math.ceil((c.expires - now) / 86400000));
return '<b>' + left + '</b> of ' + total + ' day' + (total === 1 ? '' : 's') + ' left';
}
function schedChips(c) {
const now = Date.now(); let s = '';
if (c.starts && c.starts > now) s += ' <span class="muted small">starts ' + fmtWhen(c.starts) + '</span>';
+1 -1
View File
@@ -1021,7 +1021,7 @@
<script src="/assets/common.js?v=20260914a"></script>
<script src="/assets/wallet.js?v=20260911a"></script>
<script src="/assets/promo.js?v=20260911a"></script>
<script src="/assets/my.js?v=20260916a"></script>
<script src="/assets/my.js?v=20260916b"></script>
<script src="/assets/chat.js?v=20260907l"></script>
</body>
</html>