Add files via upload

This commit is contained in:
James 2025-10-04 19:21:45 -07:00 committed by GitHub
parent 4f16ac7a44
commit a534b65833
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 64 additions and 38 deletions

View File

@ -338,7 +338,7 @@
<div class="wrap"> <div class="wrap">
<div class="row"> <div class="row">
<h1>BESTWCOAST Shop</h1> <h1>BESTWCOAST Shop</h1>
<a class="cart-btn" href="/auth/discord" id="loginDiscord">Login with Discord</a> <a class="cart-btn" href="https://discord.gg/kQrAQSSrez" id="loginDiscord">Login to our Discord</a>
<span id="whoami" class="muted"></span> <span id="whoami" class="muted"></span>
<div class="spacer"></div> <div class="spacer"></div>
<nav class="links" aria-label="Primary"> <nav class="links" aria-label="Primary">
@ -463,7 +463,8 @@
<script> <script>
const API = "https://affiliated-lets-automatic-oak.trycloudflare.com"; const API = "https://affiliated-lets-automatic-oak.trycloudflare.com";
const CAPACITY_IDS = new Set(['server1', 'server2', 'server3']);
let remaining = 12;
/* ===== Login banner (whoami) ===== */ /* ===== Login banner (whoami) ===== */
async function getWhoAmI() { async function getWhoAmI() {
@ -510,36 +511,41 @@
function cartTotal() { return Object.entries(state.cart).reduce((s, [id, q]) => { const p = products.find(p => p.id === id); return s + (p ? p.price * q : 0) }, 0); } function cartTotal() { return Object.entries(state.cart).reduce((s, [id, q]) => { const p = products.find(p => p.id === id); return s + (p ? p.price * q : 0) }, 0); }
const isServer = id => id === 'server1' || id === 'server2' || id === 'server3'; const isServer = id => id === 'server1' || id === 'server2' || id === 'server3';
function serversInCart() { function serversInCart() {
return Object.entries(state.cart) return Object.entries(state.cart || {})
.filter(([id]) => isServer(id)) .reduce((n, [id, qty]) => n + (CAPACITY_IDS.has(id) ? Number(qty || 0) : 0), 0);
.reduce((a, [, q]) => a + q, 0);
} }
function canAddServers(qtyToAdd) { function canAddServers(qtyToAdd) {
return serversInCart() + qtyToAdd <= remaining; return serversInCart() + qtyToAdd <= remaining;
} }
/* ===== Render ===== */ /* ===== Render ===== */
function cardHtml(p) { function cardHtml(p) {
const inCart = state.cart[p.id] || 0; const remHtml = CAPACITY_IDS.has(p.id)
let infoBtn = ''; ? `<div class="rem-row">Remaining: <span class="remN" data-rem-for="${p.id}">12</span>/12</div>`
if (p.name === 'Server Class 1') infoBtn = `<button class="btn btn-secondary" data-info="server-class-1">Info</button> <span class="chip" data-remaining>Remaining: <span class="remN">?</span>/12</span>`; : `<div class="rem-row text-muted">Unlimited</div>`;
if (p.name === 'Server Class 2') infoBtn = `<button class="btn btn-secondary" data-info="server-class-2">Info</button> <span class="chip" data-remaining>Remaining: <span class="remN">?</span>/12</span>`;
if (p.name === 'Server Class 3') infoBtn = `<button class="btn btn-secondary" data-info="server-class-3">Info</button> <span class="chip" data-remaining>Remaining: <span class="remN">?</span>/12</span>`; const inCart = state.cart[p.id] || 0;
let infoBtn = '';
if (p.name === 'Server Class 1') infoBtn = `<button class="btn btn-secondary" data-info="server-class-1">Info</button>`;
if (p.name === 'Server Class 2') infoBtn = `<button class="btn btn-secondary" data-info="server-class-2">Info</button>`;
if (p.name === 'Server Class 3') infoBtn = `<button class="btn btn-secondary" data-info="server-class-3">Info</button>`;
return `<article class="card" data-product="${p.id}">
<div class="img">${p.img ? `<img src="${p.img}" alt="${p.name}">` : `<canvas data-id="${p.id}" width="320" height="150"></canvas>`}</div>
<div class="footer">
<h3>${p.name}</h3>
<div class="meta">${p.tag || ''}</div>
<div class="price">${fmt(p.price)}</div>
<div class="chip">${p.category}</div>
${remHtml}
<div class="actions">
<button class="btn" data-add="${p.id}">${inCart ? 'Add another' : 'Add to cart'}</button>
${infoBtn}
</div>
</div>
</article>`;
}
return `<article class="card">
<div class="img">${p.img ? `<img src="${p.img}" alt="${p.name}">` : `<canvas data-id="${p.id}" width="320" height="150"></canvas>`}</div>
<div class="footer">
<h3>${p.name}</h3>
<div class="meta">${p.tag || ''}</div>
<div class="price">${fmt(p.price)}</div>
<div class="chip">${p.category}</div>
<div class="actions">
<button class="btn" data-add="${p.id}">${inCart ? 'Add another' : 'Add to cart'}</button>
${infoBtn}
</div>
</div>
</article>`;
}
@ -560,6 +566,7 @@
case 'name': list.sort((a, b) => a.name.localeCompare(b.name)); break; case 'name': list.sort((a, b) => a.name.localeCompare(b.name)); break;
} }
grid.innerHTML = list.map(cardHtml).join(''); grid.innerHTML = list.map(cardHtml).join('');
refreshRemaining();
updateCartUi(); updateCartUi();
} }
@ -581,6 +588,8 @@
$('#cartTotal').textContent = fmt(cartTotal()); $('#cartTotal').textContent = fmt(cartTotal());
const rows = Object.entries(state.cart); const rows = Object.entries(state.cart);
$('#cartItems').innerHTML = rows.length ? rows.map(([id, qty]) => rowHtml(id, qty)).join('') : '<div class="muted">Cart is empty.</div>'; $('#cartItems').innerHTML = rows.length ? rows.map(([id, qty]) => rowHtml(id, qty)).join('') : '<div class="muted">Cart is empty.</div>';
} }
/* ===== Events ===== */ /* ===== Events ===== */
@ -601,7 +610,8 @@
return; return;
} }
state.cart[id] = (state.cart[id] || 0) + 1; state.cart[id] = (state.cart[id] || 0) + 1;
saveCart(); render(); saveCart(); render(); refreshRemaining();
} }
if (inc) { if (inc) {
@ -612,14 +622,16 @@
return; return;
} }
state.cart[id] = (state.cart[id] || 0) + 1; state.cart[id] = (state.cart[id] || 0) + 1;
saveCart(); updateCartUi(); saveCart(); updateCartUi(); refreshRemaining();
} }
if (dec) { if (dec) {
const id = dec.getAttribute('data-dec'); const id = dec.getAttribute('data-dec');
state.cart[id] = Math.max(0, (state.cart[id] || 0) - 1); state.cart[id] = Math.max(0, (state.cart[id] || 0) - 1);
if (state.cart[id] === 0) delete state.cart[id]; if (state.cart[id] === 0) delete state.cart[id];
saveCart(); render(); saveCart(); render(); refreshRemaining();
} }
}); });
@ -729,7 +741,7 @@
} }
let remaining = 12;
async function refreshRemaining() { async function refreshRemaining() {
try { try {
@ -738,18 +750,32 @@
const j = await r.json(); const j = await r.json();
if (typeof j.remaining === 'number') remaining = j.remaining; if (typeof j.remaining === 'number') remaining = j.remaining;
// update badges // For each card, set remaining or ∞ and enable/disable add button
document.querySelectorAll('[data-remaining] .remN') document.querySelectorAll('[data-rem-for]').forEach(el => {
.forEach(s => s.textContent = remaining); const id = el.dataset.remFor;
const card = el.closest('[data-product]');
const addBtn = card?.querySelector(`[data-add="${id}"]`);
// disable server buttons if sold out relative to what's already in cart if (CAPACITY_IDS.has(id)) {
const left = Math.max(0, remaining - serversInCart()); const left = Math.max(0, remaining - serversInCart());
const disable = left <= 0; el.textContent = left; // just the current number
document.querySelectorAll('[data-add="server1"],[data-add="server2"],[data-add="server3"]') if (addBtn) {
.forEach(btn => { btn.disabled = disable; btn.textContent = disable ? 'Sold out' : 'Add to cart'; }); addBtn.disabled = left <= 0;
} catch { } addBtn.textContent = left <= 0 ? 'Sold out' : 'Add to cart';
}
} else {
el.textContent = '∞';
if (addBtn) {
addBtn.disabled = false;
addBtn.textContent = 'Add to cart';
}
}
});
} catch { /* ignore */ }
} }
</script> </script>