| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>HenAi Admin Dashboard</title> |
| <style> |
| * { margin: 0; padding: 0; box-sizing: border-box; } |
| body { |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
| background: #0f0f12; |
| color: #d4d4e0; |
| padding: 20px; |
| } |
| .container { max-width: 1400px; margin: 0 auto; } |
| h1 { font-size: 28px; margin-bottom: 20px; color: #eeeef5; } |
| h2 { font-size: 20px; margin: 24px 0 16px; color: #eeeef5; } |
| .stats-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); |
| gap: 16px; |
| margin-bottom: 24px; |
| } |
| .stat-card { |
| background: #141418; |
| border: 1px solid rgba(255,255,255,0.08); |
| border-radius: 16px; |
| padding: 20px; |
| } |
| .stat-number { |
| font-size: 36px; |
| font-weight: 700; |
| color: #7c6aff; |
| } |
| .stat-label { |
| font-size: 13px; |
| color: #6b6b80; |
| margin-top: 8px; |
| } |
| table { |
| width: 100%; |
| border-collapse: collapse; |
| background: #141418; |
| border-radius: 16px; |
| overflow: hidden; |
| } |
| th, td { |
| padding: 12px 16px; |
| text-align: left; |
| border-bottom: 1px solid rgba(255,255,255,0.08); |
| } |
| th { |
| background: #1a1a20; |
| font-weight: 600; |
| color: #9191a8; |
| } |
| tr:hover { background: rgba(124,106,255,0.05); } |
| .btn { |
| padding: 6px 12px; |
| border-radius: 8px; |
| border: none; |
| cursor: pointer; |
| font-size: 12px; |
| } |
| .btn-danger { |
| background: rgba(248,113,113,0.2); |
| color: #f87171; |
| } |
| .btn-danger:hover { background: rgba(248,113,113,0.4); } |
| .nav-links { |
| display: flex; |
| gap: 16px; |
| margin-bottom: 20px; |
| border-bottom: 1px solid rgba(255,255,255,0.08); |
| padding-bottom: 12px; |
| } |
| .nav-link { |
| color: #9191a8; |
| text-decoration: none; |
| font-size: 14px; |
| padding: 4px 0; |
| cursor: pointer; |
| } |
| .nav-link.active { |
| color: #7c6aff; |
| border-bottom: 2px solid #7c6aff; |
| } |
| .nav-link:hover { color: #d4d4e0; } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>HenAi Admin Dashboard</h1> |
| |
| <div class="nav-links"> |
| <span class="nav-link active" data-tab="overview">Overview</span> |
| <span class="nav-link" data-tab="users">Users</span> |
| </div> |
| |
| <div id="overview-tab"> |
| <div class="stats-grid" id="stats-container"> |
| <div class="stat-card"> |
| <div class="stat-number" id="total-users">-</div> |
| <div class="stat-label">Total Users</div> |
| </div> |
| <div class="stat-card"> |
| <div class="stat-number" id="total-conversations">-</div> |
| <div class="stat-label">Total Conversations</div> |
| </div> |
| </div> |
| </div> |
| |
| <div id="users-tab" style="display:none;"> |
| <table id="users-table"> |
| <thead> |
| <tr><th>User ID</th><th>Username</th><th>Email</th><th>Created</th><th>Last Active</th><th>Actions</th></tr> |
| </thead> |
| <tbody id="users-tbody"></tbody> |
| </table> |
| </div> |
| </div> |
| |
| <script> |
| async function loadStats() { |
| const res = await fetch('/admin/api/stats'); |
| const data = await res.json(); |
| if (data.success) { |
| document.getElementById('total-users').textContent = data.stats.total_users; |
| document.getElementById('total-conversations').textContent = data.stats.total_conversations; |
| } |
| } |
| |
| async function loadUsers() { |
| const res = await fetch('/api/users'); |
| const data = await res.json(); |
| if (data.success) { |
| const tbody = document.getElementById('users-tbody'); |
| tbody.innerHTML = ''; |
| data.users.forEach(user => { |
| const row = tbody.insertRow(); |
| row.insertCell(0).textContent = user.user_id; |
| row.insertCell(1).textContent = user.username; |
| row.insertCell(2).textContent = user.email; |
| row.insertCell(3).textContent = user.created_at ? new Date(user.created_at).toLocaleDateString() : '-'; |
| row.insertCell(4).textContent = user.last_active ? new Date(user.last_active).toLocaleDateString() : '-'; |
| const actionsCell = row.insertCell(5); |
| const deleteBtn = document.createElement('button'); |
| deleteBtn.textContent = 'Delete'; |
| deleteBtn.className = 'btn btn-danger'; |
| deleteBtn.onclick = () => { |
| if (confirm(`Delete user ${user.username}?`)) { |
| fetch(`/api/users/${user.user_id}`, { method: 'DELETE' }) |
| .then(() => { loadUsers(); loadStats(); }); |
| } |
| }; |
| actionsCell.appendChild(deleteBtn); |
| }); |
| } |
| } |
| |
| document.querySelectorAll('.nav-link').forEach(link => { |
| link.addEventListener('click', () => { |
| document.querySelectorAll('.nav-link').forEach(l => l.classList.remove('active')); |
| link.classList.add('active'); |
| const tab = link.dataset.tab; |
| document.getElementById('overview-tab').style.display = tab === 'overview' ? 'block' : 'none'; |
| document.getElementById('users-tab').style.display = tab === 'users' ? 'block' : 'none'; |
| if (tab === 'overview') loadStats(); |
| if (tab === 'users') loadUsers(); |
| }); |
| }); |
| |
| loadStats(); |
| </script> |
| </body> |
| </html> |