/* admin.js — shared auth + API helpers for admin pages. */ export async function api(path, method = 'GET', body) { const res = await fetch(path, { method, headers: { 'Content-Type': 'application/json', 'x-exhibit-token': localStorage.getItem('exhibitToken') || '', }, body: body === undefined ? undefined : JSON.stringify(body), }); if (res.status === 401) { location.href = '/admin/login.html?next=' + encodeURIComponent(location.pathname); throw new Error('unauthorized'); } if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || res.statusText); return res.json(); } export async function requireAuth() { const info = await (await fetch('/api/info')).json(); if (!info.authRequired) return info; // probe a guarded endpoint const res = await fetch('/api/client-errors', { headers: { 'x-exhibit-token': localStorage.getItem('exhibitToken') || '' } }); if (res.status === 401) location.href = '/admin/login.html?next=' + encodeURIComponent(location.pathname); return info; } export const styles = ` :root { color-scheme: dark; } body { margin:0; background:#0a0d18; color:#e8ecff; font-family:system-ui,sans-serif; } header { display:flex; gap:14px; align-items:center; padding:10px 16px; background:#111730; } header a { color:#8fb8ff; text-decoration:none; font-size:.9rem; } header a.active { color:#51eaf1; } h1 { font-size:1.05rem; margin:0 auto 0 0; } button { background:#2a3a6e; color:#fff; border:0; border-radius:6px; padding:6px 14px; cursor:pointer; } button.primary { background:linear-gradient(135deg,#51eaf1,#529eff); color:#04121a; font-weight:600; } button.danger { background:#7a2437; } input, select { background:#141a33; color:#fff; border:1px solid #2a3a6e; border-radius:5px; padding:4px 8px; } label { font-size:.8rem; opacity:.85; } `; export function nav(active) { return `

Newbury Exhibit — Admin

${['layout', 'plan', 'playlist', 'print', 'calibrate', 'errors'].map(p => `${p}`).join('')} viewer ↗
`; }