Home: Renewal status tab with expiry card

- Check tab renamed Renewal status / Show renewal status
- Result card: expiry date, days left, renewal window open or opening date, position, balance, donations, server page link
- Terms FAQ 6 follows the new name
This commit is contained in:
2026-11-06 19:22:24 +01:00
parent a13b93b8e0
commit 40d9b4ed80
2 changed files with 66 additions and 26 deletions

View File

@@ -223,6 +223,19 @@ tbody tr:hover{background:color-mix(in srgb,var(--accent-soft) 50%,transparent)}
background:var(--accent-soft);color:var(--accent);font-family:var(--mono);font-size:12px;display:flex;align-items:center;justify-content:center}
.mini-steps b{display:block;color:var(--ink);font-size:13.5px;margin-bottom:2px}
.side-link{color:var(--accent);text-decoration:none;font-weight:600}
/* Renewal status result */
.renewal-card{margin-top:14px;border:1px solid var(--line);border-radius:var(--radius);background:var(--bg);padding:18px 20px}
.rc-main{display:flex;flex-direction:column;gap:2px;margin-bottom:14px}
.rc-label{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.rc-date{font-family:var(--display);font-size:30px;font-weight:600;line-height:1.1;color:var(--ink)}
.rc-sub{font-size:13px;color:var(--muted)}
.rc-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px 18px;font-size:13px}
.rc-grid div{display:flex;flex-direction:column;gap:2px;border-top:1px solid var(--line);padding-top:8px}
.rc-grid span{color:var(--muted)}
.rc-grid b{font-family:var(--mono);font-weight:600}
.rc-link{display:inline-block;margin-top:14px;color:var(--accent);text-decoration:none;font-weight:600;font-size:13px}
.rc-link:hover{text-decoration:underline}
.rc-open{color:var(--ok)}
.side-link:hover{text-decoration:underline}
@media (max-width:1024px){
.panel-body{grid-template-columns:1fr}
@@ -438,15 +451,15 @@ footer a{color:var(--accent);text-decoration:none}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>Add a server
</button>
<button type="button" role="tab" aria-selected="false" data-mode="check">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="6"/><path d="M20 20l-4.5-4.5"/></svg>Check a server
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 10h18M8 3v4M16 3v4"/></svg>Renewal status
</button>
</div>
<div class="panel-body">
<div class="panel-form">
<h2 data-add>Add your Will Executor</h2>
<h2 data-check hidden>Check your Will Executor</h2>
<h2 data-check hidden>Renewal status of your Will Executor</h2>
<p class="lead" data-add>Enter your server URL. It will appear in the list once your deposit is confirmed (typically about 30 minutes).</p>
<p class="lead" data-check hidden>Enter the exact URL of your server as it appears in the list. You get its position, balance and expiry date. Nothing is charged.</p>
<p class="lead" data-check hidden>Enter the exact URL of your server as it appears in the list: you see its expiry date and when you can renew. Nothing is charged.</p>
<div class="form-row">
<input type="text" id="url" placeholder="https://we.bitcoin-after.life">
</div>
@@ -455,11 +468,27 @@ footer a{color:var(--accent);text-decoration:none}
</div>
<div class="btn-row">
<button class="btn" id="submit" data-add>Add server</button>
<button class="btn" id="check" data-check hidden>Check server</button>
<button class="btn" id="check" data-check hidden>Show renewal status</button>
</div>
<div class="ok-line" id="iv-status"></div>
<!-- Renewal status result: filled by the "Show renewal status" button. -->
<div class="renewal-card" id="renewal-card" hidden>
<div class="rc-main">
<span class="rc-label">Expiry date</span>
<b class="rc-date" id="rc-expiry">&mdash;</b>
<span class="rc-sub" id="rc-days">&mdash;</span>
</div>
<div class="rc-grid">
<div><span>Renewal window</span><b id="rc-window">&mdash;</b></div>
<div><span>Position</span><b id="rc-pos">&mdash;</b></div>
<div><span>Balance</span><b id="rc-balance">&mdash;</b></div>
<div><span>Donations</span><b id="rc-donations">&mdash;</b></div>
</div>
<a class="rc-link" id="rc-link" href="#">View the server page &rarr;</a>
</div>
<div class="terms-box" id="terms-box">
<p class="lead">You are about to add this server:</p>
<p><a id="terms-url" href="#" target="_blank" rel="noopener" class="mono"></a></p>
@@ -503,9 +532,9 @@ footer a{color:var(--accent);text-decoration:none}
<div data-check hidden>
<p class="side-title">What you get</p>
<ol class="mini-steps">
<li><b>Position in the list</b><span>and the server page with its WeTime activity</span></li>
<li><b>Balance and donations</b><span>what the server has paid so far</span></li>
<li><b>Expiry date</b><span>shown only here and on the server page; the renewal window opens 6 months before</span></li>
<li><b>Expiry date</b><span>shown only here and on the server page, not in the main table</span></li>
<li><b>Renewal window</b><span>it opens 6 months before expiry; you see whether it is open or when it opens</span></li>
<li><b>Position, balance, donations</b><span>and a link to the server page with its WeTime activity</span></li>
</ol>
</div>
<a class="side-link" href="/terms.html">How listing works (15 s) &rarr;</a>
@@ -520,7 +549,7 @@ footer a{color:var(--accent);text-decoration:none}
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Listing period</span><b>365 days</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Renewal window</span><b>from 6 months before expiry</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;font-size:13px"><span style="color:var(--muted)">Plugin integration</span><b>automatic</b></div>
<p class="lead" style="margin:16px 0 0">Paying more than the yearly fee moves your server up: the excess counts first in the order and never decays. Points are only a tie-breaker. <a href="/terms.html#faq" style="color:var(--accent);text-decoration:none;font-weight:600">Details in the Terms &rarr;</a></p>
</div>
</div>
@@ -792,6 +821,7 @@ document.getElementById('check').addEventListener('click', async () => {
const status = document.getElementById('iv-status');
const inv = document.getElementById('invoice');
inv.classList.remove('show');
document.getElementById('terms-box').classList.remove('show');
document.getElementById('renewal-card').hidden = true;
status.className = '';
status.textContent = 'Checking…';
@@ -801,30 +831,39 @@ document.getElementById('check').addEventListener('click', async () => {
const list = await listResp.json();
const idx = list.findIndex(d => normalizeUrl(d.url || '').toLowerCase() === url.toLowerCase());
if (idx < 0) {
status.className = 'err-line';
status.className = 'err-line';
status.textContent = `Not in the ${chain} list: no server with this exact URL. To list it, use the "Add a server" tab.`;
return;
}
const d = list[idx];
const infoResp = await fetch(`/weinfodata/${chain}/${d.id}`);
const info = infoResp.ok ? await infoResp.json() : {};
const info = infoResp.ok ? await infoResp.json() : {};
const expiry = (info.expiry_date || '').slice(0, 10); // '2027-07-16 00:00:00' -> the date
const bal = info.balance != null ? fmt(info.balance) : null;
const unc = info.unconfirmed_balance != null ? fmt(info.unconfirmed_balance) : null;
document.getElementById('iv-chain').textContent = info.chain || chain;
document.getElementById('iv-balance').textContent = bal ? (unc ? bal + ' (' + unc + ')' : bal) : '—';
document.getElementById('iv-donations').textContent = info.donation != null ? fmt(info.donation) : '—';
document.getElementById('iv-contact').textContent = '—';
document.getElementById('iv-url').textContent = d.url;
const weinfoEl = document.getElementById('iv-weinfo');
weinfoEl.href = `/weinfo/${chain}/${d.id}`;
weinfoEl.textContent = 'View page';
document.getElementById('iv-nextdep').textContent = expiry;
document.getElementById('iv-nextdep-row').style.display = 'flex';
const unc = info.unconfirmed_balance != null ? fmt(info.unconfirmed_balance) : null;
// Renewal window: from 6 months before expiry (Terms, Section 1).
let daysText = '—', windowText = '—', windowOpen = false;
if (expiry) {
const exp = new Date(expiry + 'T00:00:00Z');
const win = new Date(exp); win.setUTCMonth(win.getUTCMonth() - 6);
const days = Math.ceil((exp - Date.now()) / 86400000);
daysText = days > 0 ? `${days} days left` : 'expired';
windowOpen = Date.now() >= win.getTime() && days > 0;
windowText = windowOpen ? 'open now: you can renew' : `opens on ${win.toISOString().slice(0, 10)}`;
}
document.getElementById('rc-expiry').textContent = expiry || '—';
document.getElementById('rc-days').textContent = daysText;
const winEl = document.getElementById('rc-window');
winEl.textContent = windowText;
winEl.className = windowOpen ? 'rc-open' : '';
document.getElementById('rc-pos').textContent = `#${idx + 1} of ${list.length}`;
document.getElementById('rc-balance').textContent = bal ? (unc ? bal + ' (' + unc + ')' : bal) : '—';
document.getElementById('rc-donations').textContent = info.donation != null ? fmt(info.donation) : '—';
document.getElementById('rc-link').href = `/weinfo/${chain}/${d.id}`;
status.className = 'ok-line';
status.textContent = `Server is listed (#${idx + 1} of ${list.length}). Expiry: ${expiry}. Renewal opens 6 months before.`;
inv.classList.add('show');
status.className = 'ok-line';
status.textContent = windowOpen ? '✓ Listed. The renewal window is open.' : '✓ Listed.';
const card = document.getElementById('renewal-card');
card.hidden = false;
card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
} catch (err) {
status.className = 'err-line';
@@ -988,6 +1027,7 @@ document.getElementById('terms-accept').addEventListener('click', async function
const status = document.getElementById('iv-status');
status.className = ''; status.textContent = '';
document.getElementById('terms-box').classList.remove('show');
document.getElementById('invoice').classList.remove('show');
document.getElementById('renewal-card').hidden = true;
}
tabs.forEach(b => b.addEventListener('click', () => setMode(b.dataset.mode)));