Storefront /wantlist form (themed, email-keyed, guest-friendly) posts to /shop/wantlist → wantlist table (lazy DDL). Release pages deep-link to it (prefilled; 'not in stock — request this record' when no copies). Admin gets a Wantlist tab: pending/found/fulfilled queue with one-click status actions. Default storefront menu → Records + Wanted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
93 lines
5.4 KiB
HTML
93 lines
5.4 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Request a record</title>
|
||
<style>
|
||
:root{--primary:#ff5db1;--accent:#46d18a;--bg:#0c0c0e;--panel:#141418;--text:#f0f0f2;--mut:#8a8a98;--line:#26262c;--radius:12px;--font:system-ui}
|
||
*{box-sizing:border-box}
|
||
html,body{margin:0;background:var(--bg);color:var(--text);font-family:var(--font),system-ui,sans-serif}
|
||
a{color:inherit;text-decoration:none}
|
||
header{display:flex;align-items:center;gap:16px;padding:14px 22px;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--bg);z-index:10}
|
||
.logo{font-weight:800;font-size:20px}.logo b{color:var(--primary)}.logo img{height:30px;vertical-align:middle}
|
||
nav.menu{display:flex;gap:16px;flex:1}nav.menu a{color:var(--mut);font-size:14px}nav.menu a:hover{color:var(--text)}
|
||
.wrap{max-width:560px;margin:0 auto;padding:30px 22px}
|
||
h1{font-size:26px;margin:0 0 6px}.sub{color:var(--mut);margin-bottom:22px;line-height:1.5}
|
||
label{display:block;font-size:13px;color:var(--mut);margin:14px 0 5px}
|
||
input,select,textarea{width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:9px;background:var(--panel);color:var(--text);font:inherit}
|
||
textarea{min-height:70px;resize:vertical}
|
||
.row{display:grid;grid-template-columns:1fr 1fr;gap:12px}
|
||
button{cursor:pointer;border:0;border-radius:9px;font:600 15px var(--font),system-ui;padding:13px 18px;background:var(--primary);color:#10070c;width:100%;margin-top:22px}
|
||
.ok{background:var(--panel);border:1px solid var(--accent);border-radius:var(--radius);padding:26px;text-align:center}
|
||
.ok h2{color:var(--accent);margin:0 0 8px}
|
||
.err{color:#ff6b6b;font-size:13px;margin-top:10px;min-height:16px}
|
||
.req:after{content:" *";color:var(--primary)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<a class="logo" href="/records" id="logo">Record<b>God</b></a>
|
||
<nav class="menu" id="menu"></nav>
|
||
</header>
|
||
<div class="wrap" id="app">
|
||
<h1>Request a record</h1>
|
||
<div class="sub">Can't find what you're after? Tell us what you want and we'll hunt it down — we'll email you when it lands.</div>
|
||
<form id="f" onsubmit="return submitWant(event)">
|
||
<div class="row">
|
||
<div><label class="req">Artist</label><input id="artist" required></div>
|
||
<div><label>Title</label><input id="title"></div>
|
||
</div>
|
||
<div class="row">
|
||
<div><label>Format</label><input id="format" placeholder="LP, 7", CD…"></div>
|
||
<div><label>Max price (AUD)</label><input id="max_price" type="number" step="0.01" min="0" placeholder="optional"></div>
|
||
</div>
|
||
<label class="req">Your email</label><input id="email" type="email" required>
|
||
<div class="row">
|
||
<div><label>Your name</label><input id="name"></div>
|
||
<div><label>Phone</label><input id="phone"></div>
|
||
</div>
|
||
<div class="row">
|
||
<div><label>Delivery</label><select id="delivery_preference"><option value="either">Either</option><option value="pickup">Pickup</option><option value="post">Post</option></select></div>
|
||
<div><label>Postcode</label><input id="postcode"></div>
|
||
</div>
|
||
<label>Notes</label><textarea id="notes" placeholder="Pressing, condition, year — anything that helps us find the right copy."></textarea>
|
||
<button type="submit" id="btn">Send request</button>
|
||
<div class="err" id="err"></div>
|
||
</form>
|
||
</div>
|
||
<script>
|
||
const $=s=>document.querySelector(s);
|
||
const esc=s=>(s||'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c]));
|
||
const QP=new URLSearchParams(location.search);
|
||
let CFG={};
|
||
|
||
async function boot(){
|
||
CFG=await fetch('/shop/config').then(r=>r.json()).catch(()=>({}));
|
||
const t=CFG.theme||{}, R=document.documentElement.style;
|
||
for(const [k,v] of Object.entries({'--primary':t.primary,'--accent':t.accent,'--bg':t.bg,'--panel':t.panel,'--text':t.text,'--font':t.font})) if(v) R.setProperty(k,v);
|
||
if(t.radius) R.setProperty('--radius',t.radius+'px');
|
||
if(t.logo) $('#logo').innerHTML=`<img src="${esc(t.logo)}">`;
|
||
$('#menu').innerHTML=(CFG.menu||[]).map(m=>`<a href="${esc(m.href||'#')}">${esc(m.label||'')}</a>`).join('');
|
||
// prefill from a release page deep-link (/wantlist?artist=…&title=…&format=…&release_id=…)
|
||
['artist','title','format','postcode'].forEach(k=>{ if(QP.get(k)) $('#'+k).value=QP.get(k); });
|
||
}
|
||
async function submitWant(e){
|
||
e.preventDefault();
|
||
$('#err').textContent=''; $('#btn').disabled=true; $('#btn').textContent='Sending…';
|
||
const body={ release_id: QP.get('release_id')?+QP.get('release_id'):null,
|
||
delivery_preference: $('#delivery_preference').value };
|
||
['artist','title','format','email','name','phone','postcode','notes'].forEach(k=> body[k]=$('#'+k).value.trim());
|
||
const mp=$('#max_price').value; if(mp) body.max_price=+mp;
|
||
try{
|
||
const r=await fetch('/shop/wantlist',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
|
||
if(!r.ok){ const d=await r.json().catch(()=>({})); throw new Error(d.detail||'Something went wrong'); }
|
||
$('#app').innerHTML=`<div class="ok"><h2>Request received ✓</h2><p class="sub">We'll email <b>${esc(body.email)}</b> as soon as we track down <b>${esc(body.artist)}${body.title?' – '+esc(body.title):''}</b>.</p><a href="/records"><button>Back to records</button></a></div>`;
|
||
}catch(err){ $('#err').textContent=err.message; $('#btn').disabled=false; $('#btn').textContent='Send request'; }
|
||
return false;
|
||
}
|
||
boot();
|
||
</script>
|
||
</body>
|
||
</html>
|