84 lines
5.0 KiB
HTML
84 lines
5.0 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>RecordGod shop</title>
|
||
<style>
|
||
:root{--p:#ff5db1;--a:#46d18a;--bg:#0c0c0e;--pn:#141418;--tx:#f0f0f2;--r:12px}
|
||
*{box-sizing:border-box}
|
||
html,body{margin:0;min-height:100%;background:var(--bg);color:var(--tx);font:15px/1.5 var(--font,system-ui),sans-serif}
|
||
a{color:inherit;text-decoration:none}
|
||
.head{display:flex;align-items:center;gap:20px;padding:14px 24px;border-bottom:1px solid #ffffff14;position:sticky;top:0;background:var(--bg);z-index:5}
|
||
.logo{height:32px;max-width:170px;object-fit:contain}.logo.txt{font-weight:700;color:var(--p);font-size:21px}
|
||
nav{display:flex;gap:18px;flex:1;flex-wrap:wrap}nav a{opacity:.85;font-size:14px}
|
||
.srch{display:flex;gap:8px;padding:16px 24px}
|
||
.srch input{flex:1;max-width:420px;padding:10px;border:1px solid #ffffff22;border-radius:8px;background:#0e0e11;color:var(--tx)}
|
||
.srch button,.cartbtn{background:var(--p);color:#160a10;border:0;border-radius:8px;padding:9px 16px;font-weight:600;cursor:pointer}
|
||
.grid{display:grid;gap:16px;padding:8px 24px 40px}
|
||
.card{background:var(--pn);border-radius:var(--r);padding:10px;display:flex;flex-direction:column;gap:6px}
|
||
.cover{width:100%;aspect-ratio:1;border-radius:calc(var(--r) - 4px);object-fit:cover;background:#222}
|
||
.title{font-weight:600;font-size:14px;line-height:1.2}.artist{color:#ffffff99;font-size:13px}
|
||
.price{font-weight:600;color:var(--p)}
|
||
.pill{display:inline-block;padding:1px 8px;border-radius:14px;background:#ffffff1a;font-size:11px;width:fit-content}
|
||
.pill.ok{background:#46d18a22;color:var(--a)}
|
||
.cart{background:var(--p);color:#160a10;border:0;border-radius:8px;padding:7px;font-weight:600;cursor:pointer;margin-top:2px}
|
||
.pg{display:flex;gap:10px;align-items:center;justify-content:center;padding:0 0 40px}
|
||
.pg button{background:#1d1d22;color:#ccc;border:1px solid #ffffff22;border-radius:8px;padding:8px 14px;cursor:pointer}
|
||
.muted{color:#ffffff88}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="head"><span id="logo"></span><nav id="nav"></nav><button class="cartbtn">Cart</button></div>
|
||
<div class="srch"><input id="q" placeholder="search the shop…"><button onclick="search()">Search</button></div>
|
||
<div id="grid" class="grid muted" style="padding:24px">loading…</div>
|
||
<div id="pg" class="pg"></div>
|
||
|
||
<script>
|
||
let cfg=null, page=1, q='';
|
||
const num=(v,d)=>v==null?d:v;
|
||
const el={
|
||
cover:r=>r.thumb?`<img class=cover src="${r.thumb}">`:`<div class=cover></div>`,
|
||
title:r=>`<div class="title">${esc(r.title||r.sku)}</div>`,
|
||
artist:r=>`<div class="artist">${esc(r.artist||'')}</div>`,
|
||
price:r=>`<div class="price">$${r.price==null?'—':r.price.toFixed(2)}</div>`,
|
||
condition:r=>r.condition?`<span class="pill">${esc(r.condition)}</span>`:'',
|
||
format:r=>`<span class="pill">${esc(r.kind||'vinyl')}</span>`,
|
||
genre:()=>'',year:()=>'',
|
||
stock_badge:()=>`<span class="pill ok">in stock</span>`,
|
||
wishlist:()=>`<button class="cart" style="background:#ffffff14;color:var(--tx)">♡ Wishlist</button>`,
|
||
cart:()=>`<button class="cart">Add to cart</button>`,
|
||
};
|
||
function esc(s){return (s||'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c]));}
|
||
|
||
async function boot(){
|
||
cfg=await fetch('/shop/config').then(r=>r.json());
|
||
const t=cfg.theme||{}, root=document.documentElement;
|
||
root.style.setProperty('--p',t.primary); root.style.setProperty('--a',t.accent);
|
||
root.style.setProperty('--bg',t.bg); root.style.setProperty('--pn',t.panel);
|
||
root.style.setProperty('--tx',t.text); root.style.setProperty('--r',(t.radius??12)+'px');
|
||
if(t.font) root.style.setProperty('--font',t.font);
|
||
document.getElementById('logo').innerHTML = t.logo
|
||
? `<img class="logo" src="${t.logo}" onerror="this.replaceWith(Object.assign(document.createElement('span'),{className:'logo txt',textContent:'RecordGod'}))">`
|
||
: `<span class="logo txt">RecordGod</span>`;
|
||
document.getElementById('nav').innerHTML=(cfg.menu||[]).map(m=>`<a href="${m.href}">${esc(m.label)}</a>`).join('');
|
||
load();
|
||
}
|
||
async function load(){
|
||
document.getElementById('grid').className='grid muted';
|
||
const d=await fetch(`/shop/catalog?q=${encodeURIComponent(q)}&page=${page}`).then(r=>r.json());
|
||
const cols=(cfg.theme&&cfg.theme.cardCols)||4;
|
||
const g=document.getElementById('grid');
|
||
g.className='grid'; g.style.gridTemplateColumns=`repeat(${cols},1fr)`;
|
||
g.innerHTML=d.items.map(r=>`<div class="card">${(cfg.card||[]).map(k=>el[k]?el[k](r):'').join('')}</div>`).join('')||'<div class="muted">nothing found</div>';
|
||
const pages=Math.max(1,Math.ceil(d.total/d.per));
|
||
document.getElementById('pg').innerHTML=`<button ${page<=1?'disabled':''} onclick="go(-1)">‹ prev</button><span class="muted">page ${page} / ${pages} · ${d.total} items</span><button ${page>=pages?'disabled':''} onclick="go(1)">next ›</button>`;
|
||
}
|
||
function search(){ q=document.getElementById('q').value.trim(); page=1; load(); }
|
||
function go(n){ page+=n; load(); window.scrollTo(0,0); }
|
||
document.getElementById('q').addEventListener('keydown',e=>{if(e.key==='Enter')search();});
|
||
boot();
|
||
</script>
|
||
</body>
|
||
</html>
|