diff --git a/admin-script.js b/admin-script.js new file mode 100644 index 0000000..6a3ee32 --- /dev/null +++ b/admin-script.js @@ -0,0 +1,172 @@ +'use strict'; +const SB_URL = 'https://sdrmbrrhgovkzzlmdqul.supabase.co'; +const SB_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InNkcm1icnJoZ292a3p6bG1kcXVsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzM2MTcwNjEsImV4cCI6MjA4OTE5MzA2MX0.YIcN9PQRcKAvCq-3_wpom3Ir-uD8tCZh2efg7Xasyyg'; + +// ⬇ CHANGE THIS PASSWORD +const ADMIN_PASSWORD = 'promptai2025'; + +async function sbSelect(table, params) { + const url = `${SB_URL}/rest/v1/${table}?${params || ''}`; + const r = await fetch(url, { + headers: { + 'apikey': SB_KEY, + 'Authorization': 'Bearer ' + SB_KEY, + 'Range': '0-999' + } + }); + if (!r.ok) throw new Error('Failed to load ' + table); + return r.json(); +} + +function adminLogin() { + const pass = document.getElementById('adminPass').value; + if (pass === ADMIN_PASSWORD) { + document.getElementById('loginPage').style.display = 'none'; + document.getElementById('adminPanel').style.display = 'block'; + loadData(); + } else { + document.getElementById('loginErr').style.display = 'block'; + } +} + +function adminLogout() { + document.getElementById('adminPanel').style.display = 'none'; + document.getElementById('loginPage').style.display = ''; + document.getElementById('adminPass').value = ''; + document.getElementById('loginErr').style.display = 'none'; +} + +function showView(name) { + document.querySelectorAll('.view').forEach(v => v.classList.remove('active')); + document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active')); + document.getElementById('view-' + name).classList.add('active'); + document.getElementById('nav-' + name).classList.add('active'); +} + +async function loadData() { + document.getElementById('lastUpdated').textContent = 'Refreshing...'; + try { + const [users, events, waitlist] = await Promise.all([ + sbSelect('users', 'order=created_at.desc'), + sbSelect('events', 'order=created_at.desc&limit=200'), + sbSelect('waitlist', 'order=created_at.desc') + ]); + + document.getElementById('lastUpdated').textContent = 'Live Β· ' + new Date().toLocaleTimeString(); + + const gens = events.filter(e => e.type === 'generation'); + const todayStr = new Date().toDateString(); + const todayGens = gens.filter(e => new Date(e.created_at).toDateString() === todayStr); + + // Stats + set('statUsers', users.length); + set('statWaitlist', waitlist.length); + set('statGen', gens.length); + set('statToday', todayGens.length); + + // Categories + const catCounts = {}; + gens.forEach(e => { catCounts[e.category] = (catCounts[e.category]||0)+1; }); + const catEntries = Object.entries(catCounts).sort((a,b)=>b[1]-a[1]); + const catEl = document.getElementById('catTable'); + if (!catEntries.length) { + catEl.innerHTML = '
πŸ“Š
No generations yet
'; + } else { + const colors = ['blue','purple','green','yellow','blue','purple']; + let h = ''; + catEntries.forEach((e,i) => { + const pct = Math.round(e[1]/gens.length*100); + h += ``; + }); + catEl.innerHTML = h + '
CategoryCountShare
${catEmoji(e[0])} ${cap(e[0])}${e[1]}${pct}%
'; + } + + // Recent signups + set('recentCount', users.length); + const rsEl = document.getElementById('recentSignups'); + if (!users.length) { + rsEl.innerHTML = '
πŸ‘€
No sign-ups yet
'; + } else { + let h = ''; + users.slice(0,10).forEach(u => { + const pc = u.provider==='google'?'blue':'purple'; + h += ``; + }); + rsEl.innerHTML = h + '
NameEmailProviderJoined
${esc(u.name)}${esc(u.email)}${esc(u.provider||'email')}${fmt(u.created_at)}
'; + } + + // Activity + set('eventsCount', events.length); + const feedEl = document.getElementById('activityFeed'); + if (!events.length) { + feedEl.innerHTML = '
πŸ“‘
No events yet
'; + } else { + let h = ''; + events.slice(0,100).forEach(ev => { + let text = ''; + let dotClass = ev.type; + if (ev.type === 'generation') { + text = `${esc(ev.email||'User')} generated a ${esc(ev.category)} prompt (${esc(ev.tone)})`; + } else if (ev.type === 'waitlist') { + text = `${esc(ev.email)} joined the waitlist`; + } else { + text = `${esc(ev.type)}${ev.email?' β€” '+esc(ev.email):''}`; + dotClass = 'other'; + } + h += `
${text}
${fmt(ev.created_at)}
`; + }); + feedEl.innerHTML = h; + } + + // Users table + set('usersCount', users.length); + const ub = document.getElementById('usersBody'); + if (!users.length) { + ub.innerHTML = '
πŸ‘€
No users yet
'; + } else { + let h = ''; + users.forEach(u => { + const pc = u.provider==='google'?'blue':'purple'; + h += `${esc(u.name)}${esc(u.email)}${esc(u.provider||'email')}${fmt(u.created_at)}`; + }); + ub.innerHTML = h; + } + + // Waitlist table + set('waitlistCount', waitlist.length); + const wb = document.getElementById('waitlistBody'); + if (!waitlist.length) { + wb.innerHTML = '
πŸ“‹
Waitlist is empty
'; + } else { + let h = ''; + waitlist.forEach(w => { + h += `${esc(w.email)}${esc(w.whatsapp)}${fmt(w.created_at)}Pending`; + }); + wb.innerHTML = h; + } + + } catch(err) { + document.getElementById('lastUpdated').textContent = 'Error: ' + err.message; + } +} + +function set(id, val) { document.getElementById(id).textContent = val; } +function fmt(ts) { + if (!ts) return 'β€”'; + const d = new Date(ts); + return d.toLocaleDateString('en-GB',{day:'numeric',month:'short',year:'numeric'}) + ' ' + + d.toLocaleTimeString('en-GB',{hour:'2-digit',minute:'2-digit'}); +} +function esc(s) { + if (!s) return 'β€”'; + return String(s).replace(/&/g,'&').replace(//g,'>'); +} +function cap(s) { return s ? s[0].toUpperCase()+s.slice(1) : ''; } +function catEmoji(c) { + return {general:'✦',writing:'✍️',coding:'πŸ’»',business:'πŸ’Ό',image:'🎨',marketing:'πŸ“£',education:'πŸ“š',research:'πŸ”¬'}[c]||'β€’'; +} + +// Auto-refresh every 30s +setInterval(() => { + if (document.getElementById('adminPanel').style.display !== 'none') loadData(); +}, 30000); diff --git a/admin-styles.css b/admin-styles.css new file mode 100644 index 0000000..59fadba --- /dev/null +++ b/admin-styles.css @@ -0,0 +1,84 @@ +@import url('https://fonts.googleapis.com/css2?family=Syne:wght@600;700;800&family=DM+Sans:wght@300;400;500&display=swap'); +:root{--bg:#060a12;--surface:#0c1220;--card:#101825;--border:#1a2840;--accent:#3b82f6;--accent2:#a78bfa;--text:#dce8f8;--muted:#4e6a90;--success:#34d399;--danger:#f87171;--warning:#fbbf24;} +*{margin:0;padding:0;box-sizing:border-box;} +body{background:var(--bg);color:var(--text);font-family:'DM Sans',sans-serif;min-height:100vh;} + +.login-wrap{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px;} +.login-card{background:var(--card);border:1px solid var(--border);border-radius:20px;padding:44px 36px;width:100%;max-width:360px;box-shadow:0 40px 80px rgba(0,0,0,0.5);text-align:center;} +.login-icon{font-size:38px;margin-bottom:14px;} +.login-title{font-family:'Syne',sans-serif;font-weight:700;font-size:22px;margin-bottom:6px;} +.login-sub{color:var(--muted);font-size:13px;margin-bottom:24px;} +.login-card input{width:100%;background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:12px 15px;color:var(--text);font-family:'DM Sans',sans-serif;font-size:14px;outline:none;transition:border-color 0.2s;margin-bottom:10px;} +.login-card input:focus{border-color:var(--accent);} +.login-card input::placeholder{color:var(--muted);} +.btn-login{width:100%;padding:12px;border-radius:10px;border:none;background:linear-gradient(135deg,#3b82f6,#7c3aed);color:#fff;font-family:'Syne',sans-serif;font-weight:700;font-size:14px;cursor:pointer;transition:all 0.2s;} +.btn-login:hover{transform:translateY(-1px);box-shadow:0 8px 24px rgba(59,130,246,0.4);} +.login-err{color:var(--danger);font-size:12px;margin-top:8px;display:none;} + +.admin-wrap{display:flex;min-height:100vh;} +.sidebar{width:216px;background:var(--card);border-right:1px solid var(--border);padding:20px 0;flex-shrink:0;display:flex;flex-direction:column;} +.sidebar-logo{display:flex;align-items:center;gap:9px;padding:0 18px 22px;border-bottom:1px solid var(--border);margin-bottom:18px;} +.logo-icon{width:30px;height:30px;border-radius:9px;background:linear-gradient(135deg,var(--accent),var(--accent2));display:flex;align-items:center;justify-content:center;font-size:15px;} +.logo-label{font-family:'Syne',sans-serif;font-weight:800;font-size:15px;background:linear-gradient(90deg,var(--accent),var(--accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent;} +.sidebar-section{font-size:9px;font-weight:600;letter-spacing:2px;text-transform:uppercase;color:var(--muted);padding:0 18px;margin-bottom:6px;} +.nav-item{display:flex;align-items:center;gap:9px;padding:9px 18px;color:var(--muted);font-size:13px;cursor:pointer;transition:all 0.15s;border-right:2px solid transparent;} +.nav-item:hover{color:var(--text);background:rgba(59,130,246,0.05);} +.nav-item.active{color:var(--accent);background:rgba(59,130,246,0.09);border-right-color:var(--accent);} +.sidebar-bottom{margin-top:auto;padding:14px 18px;border-top:1px solid var(--border);} +.logout-btn{width:100%;padding:8px;border-radius:8px;border:1px solid var(--border);background:transparent;color:var(--muted);font-size:13px;cursor:pointer;transition:all 0.2s;} +.logout-btn:hover{border-color:var(--danger);color:var(--danger);} + +.main{flex:1;padding:28px;overflow-y:auto;} +.page-header{margin-bottom:28px;display:flex;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;gap:12px;} +.page-title{font-family:'Syne',sans-serif;font-weight:800;font-size:24px;letter-spacing:-0.5px;} +.page-sub{color:var(--muted);font-size:13px;margin-top:4px;} +.refresh-btn{display:flex;align-items:center;gap:6px;padding:7px 14px;border-radius:8px;border:1px solid var(--border);background:transparent;color:var(--muted);font-size:12px;cursor:pointer;transition:all 0.2s;} +.refresh-btn:hover{border-color:var(--accent);color:var(--accent);} + +.stats-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:14px;margin-bottom:28px;} +.stat-card{background:var(--card);border:1px solid var(--border);border-radius:13px;padding:18px;} +.stat-label{font-size:10px;font-weight:600;letter-spacing:1.5px;text-transform:uppercase;color:var(--muted);margin-bottom:10px;} +.stat-value{font-family:'Syne',sans-serif;font-weight:800;font-size:30px;line-height:1;margin-bottom:5px;} +.stat-sub{font-size:11px;color:var(--muted);} +.stat-card.blue .stat-value{color:var(--accent);} +.stat-card.purple .stat-value{color:var(--accent2);} +.stat-card.green .stat-value{color:var(--success);} +.stat-card.yellow .stat-value{color:var(--warning);} + +.section{background:var(--card);border:1px solid var(--border);border-radius:13px;margin-bottom:20px;overflow:hidden;} +.section-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border);} +.section-title{font-family:'Syne',sans-serif;font-weight:700;font-size:14px;} +.badge{display:inline-flex;align-items:center;padding:2px 9px;border-radius:100px;font-size:11px;font-weight:600;} +.badge.blue{background:rgba(59,130,246,0.12);color:var(--accent);} +.badge.purple{background:rgba(167,139,250,0.12);color:var(--accent2);} +.badge.green{background:rgba(52,211,153,0.12);color:var(--success);} +.badge.yellow{background:rgba(251,191,36,0.12);color:var(--warning);} + +.table-wrap{overflow-x:auto;} +table{width:100%;border-collapse:collapse;} +th{text-align:left;font-size:9px;font-weight:600;letter-spacing:1.5px;text-transform:uppercase;color:var(--muted);padding:11px 20px;border-bottom:1px solid var(--border);} +td{padding:12px 20px;font-size:13px;color:var(--text);border-bottom:1px solid rgba(26,40,64,0.4);vertical-align:middle;} +tr:last-child td{border-bottom:none;} +tr:hover td{background:rgba(59,130,246,0.02);} +.empty-state{text-align:center;padding:40px 20px;color:var(--muted);font-size:13px;} +.empty-icon{font-size:32px;margin-bottom:10px;} + +.activity-item{display:flex;align-items:flex-start;gap:12px;padding:12px 20px;border-bottom:1px solid rgba(26,40,64,0.35);} +.activity-item:last-child{border-bottom:none;} +.dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;margin-top:4px;} +.dot.generation{background:var(--accent);} +.dot.waitlist{background:var(--warning);} +.dot.signup{background:var(--success);} +.dot.other{background:var(--muted);} +.activity-text{font-size:13px;flex:1;line-height:1.5;} +.activity-time{font-size:11px;color:var(--muted);flex-shrink:0;white-space:nowrap;} + +.view{display:none;}.view.active{display:block;} + +@media(max-width:768px){ + .sidebar{width:52px;} + .sidebar-logo,.sidebar-section,.nav-item span,.sidebar-bottom{display:none;} + .nav-item{padding:10px;justify-content:center;} + .main{padding:16px;} + .stats-grid{grid-template-columns:repeat(2,1fr);} +} diff --git a/admin.html b/admin.html index 9599dd2..df5fff2 100644 --- a/admin.html +++ b/admin.html @@ -1,392 +1,339 @@ - + - - - -Prompt AI β€” Admin - - - - -
-
- -
-
- -