Download static/admin.js from Corin1998/QRcheckin: direct link, hf CLI and curl.
- Browser
- Download file 3.7 kB
-
https://huggingface.co/spaces/Corin1998/QRcheckin/resolve/main/static/admin.js
- Command line
-
hf download hf://spaces/Corin1998/QRcheckin/static/admin.js
-
curl -L -o admin.js https://huggingface.co/spaces/Corin1998/QRcheckin/resolve/main/static/admin.js
3.7 kB
| const venuesEl = document.getElementById('venues'); | |
| const seedBtn = document.getElementById('seed'); | |
| const addBtn = document.getElementById('v-add'); | |
| const nameIn = document.getElementById('v-name'); | |
| const slugIn = document.getElementById('v-slug'); | |
| const makeBtn = document.getElementById('session-make'); | |
| const slugSessionIn = document.getElementById('session-slug'); | |
| const qrArea = document.getElementById('qr-area'); | |
| const crmSee = document.getElementById('crm-see'); | |
| const crmJson = document.getElementById('crm-json'); | |
| async function listVenues(){ | |
| const r = await fetch('/api/admin/venues', {cache:'no-store'}); | |
| const items = await r.json(); | |
| if (!Array.isArray(items) || items.length === 0){ | |
| venuesEl.textContent = '(登録なし)'; | |
| return; | |
| } | |
| venuesEl.innerHTML = '<ul>' + items.map(v => `<li><code>${v.slug}</code> — ${v.name}</li>`).join('') + '</ul>'; | |
| } | |
| async function seedDemo(){ | |
| const r = await fetch('/api/admin/seed', {method:'POST'}); | |
| const j = await r.json(); | |
| alert(j.created ? 'デモ店舗を作成しました' : 'デモ店舗は既に存在します'); | |
| await listVenues(); | |
| } | |
| async function addVenue(){ | |
| const name = nameIn.value.trim(); | |
| const slug = slugIn.value.trim(); | |
| if (!name || !slug){ alert('店名とslugを入力してください'); return; } | |
| const r = await fetch('/api/admin/venues', { | |
| method:'POST', | |
| headers:{'Content-Type':'application/json'}, | |
| body: JSON.stringify({name, slug}) | |
| }); | |
| if (r.ok){ nameIn.value=''; slugIn.value=''; await listVenues(); } | |
| else { const e = await r.json().catch(()=>({})); alert('登録失敗: ' + (e.detail || r.status)); } | |
| } | |
| async function fetchQRObjectURL(sessionId){ | |
| const url = `/qrcode/${sessionId}`; | |
| const r = await fetch(url, {cache:'no-store'}); | |
| if (!r.ok) throw new Error(`QR取得失敗: ${r.status}`); | |
| const blob = await r.blob(); | |
| return URL.createObjectURL(blob); | |
| } | |
| async function makeSession(){ | |
| const slug = slugSessionIn.value.trim(); | |
| if (!slug){ alert('店舗slugを入力してください'); return; } | |
| const r = await fetch('/api/checkin/session', { | |
| method:'POST', | |
| headers:{'Content-Type':'application/json'}, | |
| body: JSON.stringify({venue_slug: slug}) | |
| }); | |
| const j = await r.json(); | |
| if (!r.ok){ alert('発行失敗: ' + (j.detail || r.status)); return; } | |
| const checkinUrl = `/checkin/${j.session_id}`; | |
| // QRはBlob→ObjectURLで確実に表示 | |
| let qrObjectUrl = ''; | |
| try { | |
| qrObjectUrl = await fetchQRObjectURL(j.session_id); | |
| } catch (e) { | |
| console.error(e); | |
| } | |
| qrArea.innerHTML = ` | |
| <div style="display:flex;gap:16px;align-items:center;flex-wrap:wrap"> | |
| <img src="${qrObjectUrl || ''}" alt="QR" style="width:180px;height:180px;background:#fff;padding:8px;border-radius:8px;object-fit:contain;border:1px solid #333"/> | |
| <div> | |
| <div>セッション: <code>${j.session_id}</code></div> | |
| <div style="margin-top:8px"> | |
| <a class="btn" target="_blank" href="${checkinUrl}">チェックインページを開く</a> | |
| </div> | |
| <p class="muted" style="margin-top:8px"> | |
| 画像が表示されない場合は、右のボタンでページを開き、URLを手元のQR生成アプリに貼ってください。 | |
| </p> | |
| </div> | |
| </div> | |
| <p class="muted">※ QRは5分で失効します。必要に応じて再発行してください。</p> | |
| `; | |
| } | |
| async function seeCRM(){ | |
| const r = await fetch('/api/crm/segments', {cache:'no-store'}); | |
| const j = await r.json(); | |
| crmJson.textContent = JSON.stringify(j, null, 2); | |
| } | |
| seedBtn.onclick = seedDemo; | |
| addBtn.onclick = addVenue; | |
| makeBtn.onclick = makeSession; | |
| crmSee.onclick = seeCRM; | |
| listVenues(); | |