From f30a462ca86d5f23e6608ea2546325292dffb3ac Mon Sep 17 00:00:00 2001 From: martbost Date: Sun, 6 Sep 2026 09:16:24 -0500 Subject: [PATCH] Featured rotation -> day-slot booking: 10 slots/day, pick your start day, live occupancy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Per-day occupancy shown in composer (Today 1/10, Tomorrow 2/10, …), full days unpickable - Book a specific start day + duration; every covered day must have an open slot - Slot cap turns dilution disclosure into a hard ceiling; featured skips frame-check (new-tab) Co-Authored-By: Claude Fable 5 --- ads.js | Bin 50301 -> 53487 bytes db.js | 1 + public/assets/my.js | 28 ++++++++++++++++++++-------- public/assets/site.css | 9 +++++++++ public/contract.html | 8 ++++---- public/index.html | 10 +++++----- public/ledger.html | 8 ++++---- public/my.html | 14 ++++++++------ public/shorts.html | 4 ++-- public/tx.html | 6 +++--- public/view.html | 4 ++-- public/wall.html | 6 +++--- server.js | 2 +- 13 files changed, 62 insertions(+), 38 deletions(-) diff --git a/ads.js b/ads.js index 99d511b8199763914e92d1b55b1c807ddf1a6371..5cc0df6aac9b38e90b3fa1c898f0a50181f4708b 100644 GIT binary patch delta 3314 zcmbtXOK%%h6c$BLhzbHCAV7eVQq?oqnIt?~aOzZ14Wf%a)D)pA1avcVC%$AnW6i_C zq`_>EV1tB)1&CE8en6`rB$}=ftoRLO#R`yEA|$?Z#$zXKC=0Ae*S?Q)&-u=GzH{^U zCwKpTcX!2BVjPNN9zGXr$+JL#FPRGSuE7~Am#iUwVd8GIRJ>aU4RyD0g(Yp4w z@4xS-iNKA*B&8%(aY_x|wZva76LzxHk!e`9=h;K~H68iWZ0pU7rzrBpLAMnp8irw& zea5QzW_+>+b{h=}vmnT?9C>o7s{-Qb7_0x4R}dfRh{-@E6b!X{Ye7UbCeyfQw1V2tu_>NAIji*d1- z&`CO*wO4fv6eWi3pX$v6_txZleyCB)k2St~v{o8R7*{6BianoCwV&QTwD7ds2lL9n zD9*w}h(<|AyT&&zM>ox(B$DK=j*Z_Vjj=~=gaOfF3o$F=C~5;-&UM#uowV1{)Brat zrTq#WJxaV(P5{X@n>KgxQ*{BY+E6APdbLu3>k?N*Bp=X)2Sr&QO8)<+~p zkYp0_0R-1PGYC8QXA^;dFba{_sc7&qVOz}r50<<*IPQJl@iOYfdp0stlN=f`f<1q;Gp3%}G*5kySt zuzLxg=`&}hn@wE70#{>qQ7|S<^_q^^TIBnQPMM1g6Zvt}rnJwh;LrsP`2+(LwG&EB zTZ2gLju>IuaAvcxqvAxX)j&IAeiphSojAQT(zPO^Oat*uIFN;&_DzVW!NJkAn7w?t zziX;evCCgH%SFkCm`jku(ZNb6#ILZqnRnjysz+v=6#iOE5_oa5ZSN)5s!G5}I-}B- z;|Ea`TTN7>6Gr_F%cdDB@MqyEz7>)X3o0ast7XB7Cwq4?4T?Oramma(*4y6ZOI!9d zk1vcyII7>*?jA=E*}_+jbgG?S`{Jp-IEGwX)%Nn-N{ukQutL)`CtqJg1>?4XXN3kh zCVAb2Ce>DILl{!3ORdk3ZE@Na(pQ!aG_=qkG{V$iAqIK73N*KvpFPlHvd}=!t_jy7 z*UdUAbbHhWcKOdc69*TA0GMs&GoQYk|F&{4f9CpY$LRD~vMLIk=xStf@obX@TDoxZ zaE+G3Xk*2;own*&akOztV%QqX8&{vWw+JPF_v$nGU)N9f#b@rZZEnd!$3>q+cEHg! z+#)Qd<&u9Q#=^uZGH8zoIt<`kXay!ou`wyQF~E=~9p41#T9!-w#sXOb2^Q6KokF=i zn@->;RANx85HxHoI|j&zUbVo+oj`|cX=?!g@P2`lpB-g8-;k$8ot8JbAnO&W;pe1? zE2uoFRw#IsK|lBrgSigE-df=-JaDc|jO60;uP3a6f&AKO(5ScWdJ%EO%+1c{f9yOq zd@xBenNh`Q__XKr==sBkkB>H@D`MmF3eRxQvmk$oxG-$X8&W)Jpltzy1DX{^!p4?S$R8 zeD74fcq&6y_Agtk*g`OoD3{pICK->PW5%mdnzwg8JJ8kn-#e#yc;m*!rJ5X&7gG5j n#~sQJ)mv695ZLp~8I^K=*H@2iU%T<+!N&pc;9r{IDg*luG+I*o delta 445 zcmaFAkoj*1^9DYS&GR{qNKDpJmWWc&*H=g>ElMoO%+FKE%u`57tSnY2%1lWuRw&6x zRY=NDsZ>bL&neB#<5B>E{L&J=)QW=4BA~pjf=gmas$O1xxrU~KHbkeEf`yrh0SH)6 zzOTG_@{y%N8cC&WC7N_uJ4O{)q&9+udlaJVIPyT4XW%G6i+vLgfcd>2mSZW{<26S^;YGO$#$graP z60qZ~6cUq5GRsmyR^{nJ+?aw;mXn#6U96x{o>`KikYApcTBMMdn47AQmS3chl384k zlUS)aIdP)sU7Nr7xkq5CY;rKG9;*!Z6Cp '').join(''); - fetch('/api/featured/stats').then(r => r.json()).then(s => { - const dys = Number($('cFeatDays').value) || (lastRates.featuredDurations[0]); - $('cFeatHint').textContent = (s.activeCount || 0) + ' link' + (s.activeCount === 1 ? '' : 's') - + ' currently share the rotation — yours would make ' + ((s.activeCount || 0) + 1) - + '. Runs ' + dys + ' day' + (dys > 1 ? 's' : '') + ' for ' + (dys * lastRates.featuredPerDay) + ' credits.'; - }).catch(() => {}); + let s = null; + try { s = await (await fetch('/api/featured/stats')).json(); } catch (e) {} + if (s && s.occupancy) { + const grid = $('cFeatDaysGrid'); + grid.innerHTML = s.occupancy.map(d => { + const label = d.offset === 0 ? 'Today' : d.offset === 1 ? 'Tomorrow' : new Date(d.day + 'T00:00:00Z').toLocaleDateString(undefined, { weekday: 'short', month: 'numeric', day: 'numeric' }); + const full = d.open <= 0; + return '
' + + '
' + label + '
' + d.count + '/' + d.cap + (full ? ' full' : ' left ' + d.open) + '
'; + }).join(''); + grid.querySelectorAll('.feat-day:not(.full)').forEach(el => + el.addEventListener('click', () => { featStartDay = Number(el.dataset.off); featHint(); })); + } + const dys = Number($('cFeatDays').value) || (lastRates.featuredDurations[0]); + const dayLabel = featStartDay === 0 ? 'today' : featStartDay === 1 ? 'tomorrow' : 'in ' + featStartDay + ' days'; + $('cFeatHint').textContent = 'Runs ' + dys + ' day' + (dys > 1 ? 's' : '') + ' starting ' + dayLabel + + ' for ' + (dys * lastRates.featuredPerDay) + ' credits. Max ' + (s ? s.slotsPerDay : 10) + ' links share any day.'; } document.addEventListener('change', e => { if (e.target && e.target.id === 'cFeatDays') featHint(); }); // video composer: tier dropdown + upload + price hint @@ -566,7 +578,7 @@ title: isVideo ? $('cVideoTitle').value : isFeat ? $('cFeatTitle').value : $('cTitle').value, body: t === 'solo' ? soloBody : $('cBody').value, videoUrl: $('cVideoUrl').value, watchSecs: Number($('cWatchSecs').value), - days: Number($('cFeatDays').value), + days: Number($('cFeatDays').value), startDay: featStartDay, ctaLabel: isVideo ? $('cVideoCta').value : $('cCtaLabel').value, budget: isFeat ? (Number($('cFeatDays').value) * (lastRates.featuredPerDay || 40)) : Number($('cBudget').value) }); IAP.status('Campaign is live. It starts serving right away.', 'ok'); diff --git a/public/assets/site.css b/public/assets/site.css index cd6e341..84acd5e 100644 --- a/public/assets/site.css +++ b/public/assets/site.css @@ -259,6 +259,15 @@ textarea{resize:vertical;font:inherit} #boBurger{display:block} .bo-content{padding:18px} } +/* ── featured day-slot picker ── */ +.feat-days{display:flex;gap:8px;flex-wrap:wrap} +.feat-day{flex:0 0 auto;min-width:82px;padding:8px 10px;border:1px solid var(--line-strong);border-radius:10px; + background:var(--panel);cursor:pointer;text-align:center} +.feat-day.on{border-color:var(--mint);box-shadow:0 0 0 1px var(--mint)} +.feat-day.full{opacity:.45;cursor:not-allowed} +.feat-day .fd-day{font-weight:700;font-size:12px} +.feat-day .fd-occ{font-family:var(--mono);font-size:11px;color:var(--muted)} +.feat-day.open2 .fd-occ{color:var(--mint)} /* ── featured rotation strip ── */ .feat-strip{display:flex;flex-direction:column;gap:8px;margin-top:10px} .feat-strip a{display:flex;justify-content:space-between;gap:10px;padding:11px 14px;border-radius:10px; diff --git a/public/contract.html b/public/contract.html index a42efe2..1e9d182 100644 --- a/public/contract.html +++ b/public/contract.html @@ -5,7 +5,7 @@ The contract | InstantAdPay - +
@@ -129,8 +129,8 @@
Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never spend what you cannot afford.
- - - + + + diff --git a/public/index.html b/public/index.html index 6ce1f09..8a3e6fd 100644 --- a/public/index.html +++ b/public/index.html @@ -5,7 +5,7 @@ InstantAdPay: advertise and earn, locked in code - + @@ -410,9 +410,9 @@ - - - - + + + + diff --git a/public/ledger.html b/public/ledger.html index f376f1a..fea6ca9 100644 --- a/public/ledger.html +++ b/public/ledger.html @@ -5,7 +5,7 @@ Live ledger | InstantAdPay - +
@@ -25,8 +25,8 @@
- - - + + + diff --git a/public/my.html b/public/my.html index f13d526..d32d90e 100644 --- a/public/my.html +++ b/public/my.html @@ -4,7 +4,7 @@ Member area | InstantAdPay - + @@ -344,7 +344,9 @@ - - - - + + + + diff --git a/public/shorts.html b/public/shorts.html index 7eb5406..b702bed 100644 --- a/public/shorts.html +++ b/public/shorts.html @@ -4,7 +4,7 @@ Shorts | InstantAdPay - +