// /account.html — customer order history. Visually mirrors the storefront: // dot-pulse status indicators, octagonal CornerCuts on cards, spotlight-style // hero strip for the account header, and the same sec-head/sec-tag rhythm // used on the catalog & FAQ sections. const { useState, useEffect } = React; function AccountApp() { const { user, isAdmin } = window.useAuthState(); const ready = window.ExecAuth && window.ExecAuth.isReady(); const [orders, setOrders] = useState(null); const [revealed, setRevealed] = useState({}); const [banner, setBanner] = useState(null); useEffect(() => { const params = new URLSearchParams(window.location.search); const paid = params.get('paid'); if (paid === 'ok' || paid === 'cancel') { setBanner(paid); params.delete('paid'); params.delete('sid'); const qs = params.toString(); window.history.replaceState({}, '', window.location.pathname + (qs ? '?' + qs : '')); } }, []); useEffect(() => { if (!user) { setOrders([]); return; } let cancelled = false; const load = () => fetch('/api/orders', { credentials: 'same-origin' }) .then((r) => r.ok ? r.json() : Promise.reject(r.status)) .then((d) => { if (!cancelled) setOrders(d.orders || []); }) .catch(() => { if (!cancelled) setOrders([]); }); load(); // Stripe webhook may land a beat after the redirect; second fetch catches it. if (banner === 'ok') { const t = setTimeout(load, 2500); return () => { cancelled = true; clearTimeout(t); }; } return () => { cancelled = true; }; }, [user, banner]); const reveal = async (itemId) => { try { const res = await fetch(`/api/orders/${itemId}/reveal-key`, { method: 'POST', credentials: 'same-origin' }); const data = await res.json().catch(() => ({})); if (!res.ok) { alert('Reveal failed: ' + (data.error || res.status)); return; } setRevealed((r) => ({ ...r, [itemId]: data.key })); } catch { alert('Reveal failed: network error'); } }; if (!ready) { return ; } if (!user) { return ( ); } const totalSpent = (orders || []) .filter((o) => o.status === 'paid') .reduce((s, o) => s + (o.amount_cents || 0), 0); const pendingKeys = pendingKeyCount(orders); return ( {banner && setBanner(null)}/>} 0}/>
// 01

ORDER HISTORY / {orders === null ? '…' : orders.length}

↶ STORE
{orders === null && } {orders !== null && orders.length === 0 && ( BROWSE CATALOG} /> )} {orders !== null && orders.length > 0 && (
{orders.map((o) => ( ))}
)}
); } // ---------- Order card ---------- function OrderCard({ order, revealed, onReveal }) { const dotClass = order.status === 'paid' ? 'pulse' : ''; return (
{order.status.toUpperCase()} ORDER · #{String(order.id).padStart(4, '0')} {fmtDate(order.created_at)} {window.fmt((order.amount_cents || 0) / 100)}
); } // ---------- Hero strip (spotlight-style) ---------- function HeroStrip({ user, isAdmin, children }) { // Some admin sub-views (e.g. WEBSITE tab) reuse the stat slot without a // logged-in user prop. Guard so passing null doesn't crash the entire // panel; the identity block just renders as anonymous. const name = user ? (user.global_name || user.username || '—') : 'ADMIN'; return (
ACCOUNT // AUTHENTICATED
{user ? (
{user.avatar_url ? (
) : (
{(name[0] || '?').toUpperCase()}
)}
{name}
@{user.username} {isAdmin && ADMIN}
DISCORD · {user.discord_id}
) : (
{name}
{isAdmin && ADMIN}
)}
{children}
); } function StatColumn({ label, value, accent }) { return (
{label}
{value}
); } // ---------- Page chrome (nav + main) ---------- function PageChrome({ user, isAdmin, currentTab, children }) { const [navOpen, setNavOpen] = useState(false); return (
[EXE]cutables v26.5
{user && ( )}
{children}
); } function LoadingState() { return (
LOADING…
); } function EmptyPanel({ art, line, sub, cta }) { return (
{art}
{line}
{sub &&
{sub}
} {cta &&
{cta}
}
); } function LoggedOutCTA() { return (
LOGIN REQUIRED
Authenticate with Discord to view your orders.
); } function InlineBanner({ kind, onClose }) { useEffect(() => { if (kind !== 'ok') return; const t = setTimeout(onClose, 8000); return () => clearTimeout(t); }, [kind, onClose]); return (
{kind === 'ok' ? 'PAYMENT RECEIVED — your order is below' : 'CHECKOUT CANCELLED — no charge was made'}
); } // ---------- helpers ---------- function fmtDate(epoch) { if (!epoch) return '—'; const d = new Date(epoch * 1000); return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }).toUpperCase(); } function pendingKeyCount(orders) { if (!orders) return 0; let n = 0; for (const o of orders) { if (o.status !== 'paid') continue; for (const it of o.items) if (!it.has_key) n++; } return n; } // Expose shared shell pieces so admin.jsx can reuse them. window.PageChrome = PageChrome; window.HeroStrip = HeroStrip; window.StatColumn = StatColumn; window.LoadingState = LoadingState; window.EmptyPanel = EmptyPanel; window.LoggedOutCTA = LoggedOutCTA; window.InlineBanner = InlineBanner; window.fmtDate = fmtDate; const __root = document.getElementById('account-root'); if (__root) ReactDOM.createRoot(__root).render();