Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>FriendlyBot — Premium</title> | |
| <link rel="icon" href="/logo.png" /> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" /> | |
| <style> | |
| :root { | |
| --bg: #0a0a1a; --surface: #12122a; --border: rgba(255,255,255,0.08); | |
| --text: #fff; --text-dim: rgba(255,255,255,0.6); --gold: #FFD700; | |
| --accent: #5865F2; --radius: 16px; | |
| } | |
| * { box-sizing: border-box; margin:0; padding:0; } | |
| body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; padding: 40px 20px; } | |
| header { max-width: 1000px; margin: 0 auto 40px; display: flex; justify-content: space-between; align-items: center; } | |
| h1 { font-size: 28px; font-weight: 700; } | |
| .btn { | |
| display: inline-block; padding: 10px 20px; background: var(--accent); | |
| color: #fff; text-decoration: none; border-radius: 8px; font-weight: 600; font-size: 14px; | |
| } | |
| .container { max-width: 1000px; margin: 0 auto; } | |
| .hero { | |
| background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); | |
| color: #000; padding: 40px; border-radius: var(--radius); margin-bottom: 30px; text-align: center; | |
| } | |
| .hero h2 { font-size: 28px; font-weight: 800; margin-bottom: 10px; } | |
| .hero p { font-size: 16px; opacity: 0.8; } | |
| .features { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 20px; margin-bottom: 30px; } | |
| .card { | |
| background: rgba(18,18,42,0.85); backdrop-filter: blur(8px); | |
| border: 1px solid var(--border); border-radius: var(--radius); | |
| padding: 24px; transition: all 0.25s; | |
| } | |
| .card:hover { border-color: var(--gold); transform: translateY(-3px); } | |
| .card .icon { font-size: 32px; margin-bottom: 12px; } | |
| .card h3 { font-size: 18px; font-weight: 700; margin-bottom: 8px; } | |
| .card p { font-size: 14px; color: var(--text-dim); line-height: 1.6; } | |
| .status { | |
| background: rgba(18,18,42,0.85); backdrop-filter: blur(8px); | |
| border: 1px solid var(--border); border-radius: var(--radius); | |
| padding: 24px; margin-bottom: 30px; display: flex; align-items: center; gap: 16px; | |
| } | |
| .status .badge { | |
| padding: 8px 20px; border-radius: 100px; font-weight: 700; font-size: 14px; | |
| } | |
| .badge.active { background: rgba(255,215,0,0.2); color: var(--gold); border: 1px solid var(--gold); } | |
| .badge.inactive { background: rgba(255,255,255,0.05); color: var(--text-dim); border: 1px solid var(--border); } | |
| .user-row { display: flex; align-items: center; gap: 12px; } | |
| .user-avatar { width: 32px; height: 32px; border-radius: 50%; background: var(--accent); display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700; color: white; } | |
| .user-avatar img { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; } | |
| table { width: 100%; border-collapse: collapse; margin-top: 12px; } | |
| th, td { text-align: left; padding: 12px; border-bottom: 1px solid var(--border); } | |
| th { color: var(--text-dim); font-size: 12px; text-transform: uppercase; letter-spacing: 1px; } | |
| @media (max-width: 700px) { .features { grid-template-columns: 1fr; } } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1>⭐ Premium</h1> | |
| <a href="/" class="btn">Back to Dashboard</a> | |
| </header> | |
| <div class="container"> | |
| <div class="hero"> | |
| <h2>⭐ FriendlyBot Premium</h2> | |
| <p>Unlock exclusive features by winning a league match. Premium is granted automatically when you record a win with <code>!win</code>.</p> | |
| </div> | |
| <div class="status" id="status"> | |
| <div class="badge inactive" id="premium-badge">Checking...</div> | |
| <span id="premium-text">Loading your premium status...</span> | |
| </div> | |
| <div class="features" id="features"> | |
| <div class="card locked" data-feature="0"> | |
| <div class="icon">📊</div> | |
| <h3>Advanced Stats & Analytics</h3> | |
| <p>Track goals, assists, clean sheets, and weekly leaderboards for every player.</p> | |
| </div> | |
| <div class="card locked" data-feature="1"> | |
| <div class="icon">📅</div> | |
| <h3>Match Scheduling & Reminders</h3> | |
| <p>Auto-post scheduled matches with DM reminders 60 minutes before kickoff.</p> | |
| </div> | |
| <div class="card locked" data-feature="2"> | |
| <div class="icon">🎴</div> | |
| <h3>Player Profile Cards</h3> | |
| <p>Advanced <code>profileimage</code> with extra stats and custom backgrounds.</p> | |
| </div> | |
| <div class="card locked" data-feature="3"> | |
| <div class="icon">🔧</div> | |
| <h3>Custom Command Aliases</h3> | |
| <p>Rename bot commands to fit your server (e.g. <code>!ff</code> for <code>!friendly</code>).</p> | |
| </div> | |
| </div> | |
| <div class="card" style="padding:24px"> | |
| <h3>🏆 Premium Users</h3> | |
| <table> | |
| <thead><tr><th>User</th><th>Since</th><th>Reason</th></tr></thead> | |
| <tbody id="premium-list"><tr><td colspan="3">Loading...</td></tr></tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <script> | |
| async function fetchUser(userId) { | |
| try { | |
| const r = await fetch(`https://cdn.discordapp.com/embed/avatars/0.png`); // placeholder — we use ID | |
| return null; // We use a fallback below | |
| } catch(e) { return null; } | |
| } | |
| async function load() { | |
| const statusEl = document.getElementById('premium-badge'); | |
| const textEl = document.getElementById('premium-text'); | |
| const listEl = document.getElementById('premium-list'); | |
| const cards = document.querySelectorAll('#features .card'); | |
| try { | |
| // Check login status first to show user-specific premium status | |
| let userPremium = false; | |
| try { | |
| const authR = await fetch('/api/me'); | |
| const authData = await authR.json(); | |
| if (authData.loggedIn) { | |
| const premiumR = await fetch('/api/premium'); | |
| const premiumData = await premiumR.json(); | |
| userPremium = premiumData.premium; | |
| } | |
| } catch (e) { /* not logged in, that's fine */ } | |
| if (userPremium) { | |
| statusEl.className = 'badge active'; | |
| statusEl.textContent = '⭐ ACTIVE'; | |
| textEl.textContent = 'You have Premium access! Enjoy all features.'; | |
| cards.forEach(c => { c.style.borderColor = 'var(--gold)'; c.style.opacity = '1'; c.classList.remove('locked'); }); | |
| } else { | |
| statusEl.className = 'badge inactive'; | |
| statusEl.textContent = '— LOCKED'; | |
| textEl.textContent = 'Log in to the dashboard and win a league match with !win to unlock Premium.'; | |
| cards.forEach(c => { c.style.borderColor = 'var(--border)'; c.style.opacity = '0.55'; c.classList.add('locked'); }); | |
| } | |
| // Load public premium list | |
| const listR = await fetch('/api/premium/list'); | |
| const listData = await listR.json(); | |
| if (listData.list && listData.list.length > 0) { | |
| listEl.innerHTML = listData.list.map(u => { | |
| const date = new Date(u.grantedAt).toLocaleDateString(); | |
| const avatarUrl = u.avatarUrl || ''; | |
| const displayName = u.userTag || u.userId; | |
| const initial = (displayName || '?')[0].toUpperCase(); | |
| const avatarHtml = avatarUrl | |
| ? `<img src="${avatarUrl}" onerror="this.parentElement.textContent='${initial}'" alt="">` | |
| : `<span style="font-size:14px;font-weight:700">${initial}</span>`; | |
| return `<tr><td><div class="user-row"><div class="user-avatar">${avatarHtml}</div><span style="font-size:13px;font-weight:500">${displayName}</span></div></td><td style="font-size:13px">${date}</td><td style="font-size:13px;color:var(--text-dim)">${u.reason || 'manual'}</td></tr>`; | |
| }).join(''); | |
| } else { | |
| listEl.innerHTML = '<tr><td colspan="3" style="text-align:center;color:var(--text-dim);padding:24px">No premium users yet. Win a league match to be the first!</td></tr>'; | |
| } | |
| } catch (e) { | |
| statusEl.className = 'badge inactive'; | |
| statusEl.textContent = '❌ ERROR'; | |
| textEl.textContent = 'Could not load premium data.'; | |
| listEl.innerHTML = '<tr><td colspan="3" style="text-align:center;color:var(--red)">Failed to load.</td></tr>'; | |
| } | |
| } | |
| load(); | |
| </script> | |
| </body> | |
| </html> |