AeThex-Bot-Master/aethex-bot/public/dashboard.html
sirpiglr 7ca85f433a Add persistent storage for federation mappings and tickets
Integrates Supabase for persistent storage of federation mappings and active tickets, along with adding new commands for announcements, audit logs, and polls.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: 110a0afc-77c3-48ac-afca-8e969438dafc
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/hHBt1No
Replit-Helium-Checkpoint-Created: true
2025-12-08 03:38:13 +00:00

183 lines
6.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AeThex Bot Dashboard</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
color: #fff;
}
.container { max-width: 1200px; margin: 0 auto; padding: 2rem; }
header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.logo { width: 48px; height: 48px; background: #7c3aed; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; }
h1 { font-size: 1.75rem; font-weight: 600; }
.status-badge { padding: 0.25rem 0.75rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; }
.status-online { background: #10b981; }
.status-offline { background: #ef4444; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-bottom: 2rem; }
.card {
background: rgba(255,255,255,0.05);
border-radius: 16px;
padding: 1.5rem;
border: 1px solid rgba(255,255,255,0.1);
}
.card h3 { font-size: 0.875rem; color: #a1a1aa; margin-bottom: 0.5rem; text-transform: uppercase; letter-spacing: 0.05em; }
.card .value { font-size: 2rem; font-weight: 700; }
.server-list { list-style: none; }
.server-list li {
padding: 0.75rem 0;
border-bottom: 1px solid rgba(255,255,255,0.05);
display: flex;
justify-content: space-between;
align-items: center;
}
.server-list li:last-child { border-bottom: none; }
.server-members { color: #a1a1aa; font-size: 0.875rem; }
.commands-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0.75rem; }
.command-item {
background: rgba(124, 58, 237, 0.2);
padding: 0.75rem 1rem;
border-radius: 8px;
font-family: monospace;
font-size: 0.875rem;
}
.section { margin-bottom: 2rem; }
.section h2 { font-size: 1.25rem; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; }
.refresh-btn {
background: #7c3aed;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 8px;
cursor: pointer;
font-size: 0.875rem;
}
.refresh-btn:hover { background: #6d28d9; }
#lastUpdated { color: #a1a1aa; font-size: 0.75rem; }
</style>
</head>
<body>
<div class="container">
<header>
<div class="logo">A</div>
<div>
<h1>AeThex Bot Dashboard</h1>
<span id="lastUpdated">Loading...</span>
</div>
<span id="statusBadge" class="status-badge status-offline">Offline</span>
<button class="refresh-btn" onclick="fetchData()">Refresh</button>
</header>
<div class="grid">
<div class="card">
<h3>Servers</h3>
<div class="value" id="serverCount">-</div>
</div>
<div class="card">
<h3>Total Members</h3>
<div class="value" id="memberCount">-</div>
</div>
<div class="card">
<h3>Commands</h3>
<div class="value" id="commandCount">-</div>
</div>
<div class="card">
<h3>Uptime</h3>
<div class="value" id="uptime">-</div>
</div>
</div>
<div class="section">
<h2>Connected Servers</h2>
<div class="card">
<ul class="server-list" id="serverList">
<li>Loading...</li>
</ul>
</div>
</div>
<div class="section">
<h2>Available Commands</h2>
<div class="commands-grid" id="commandsList"></div>
</div>
<div class="section">
<h2>Sentinel Status</h2>
<div class="grid">
<div class="card">
<h3>Heat Map Size</h3>
<div class="value" id="heatMapSize">0</div>
</div>
<div class="card">
<h3>Active Tickets</h3>
<div class="value" id="activeTickets">0</div>
</div>
<div class="card">
<h3>Federation Mappings</h3>
<div class="value" id="federationMappings">0</div>
</div>
</div>
</div>
</div>
<script>
async function fetchData() {
try {
const [health, stats] = await Promise.all([
fetch('/health').then(r => r.json()),
fetch('/stats').then(r => r.json())
]);
document.getElementById('statusBadge').textContent = health.status === 'online' ? 'Online' : 'Offline';
document.getElementById('statusBadge').className = `status-badge ${health.status === 'online' ? 'status-online' : 'status-offline'}`;
document.getElementById('serverCount').textContent = health.guilds;
document.getElementById('commandCount').textContent = health.commands;
document.getElementById('heatMapSize').textContent = health.heatMapSize;
const hours = Math.floor(health.uptime / 3600);
const minutes = Math.floor((health.uptime % 3600) / 60);
document.getElementById('uptime').textContent = `${hours}h ${minutes}m`;
document.getElementById('memberCount').textContent = stats.totalMembers.toLocaleString();
document.getElementById('activeTickets').textContent = stats.activeTickets;
const serverList = document.getElementById('serverList');
serverList.innerHTML = stats.guilds.map(g => `
<li>
<span>${g.name}</span>
<span class="server-members">${g.memberCount.toLocaleString()} members</span>
</li>
`).join('');
document.getElementById('lastUpdated').textContent = `Last updated: ${new Date().toLocaleTimeString()}`;
const commands = ['verify', 'unlink', 'profile', 'stats', 'set-realm', 'verify-role', 'refresh-roles',
'post', 'leaderboard', 'help', 'admin', 'federation', 'ticket', 'status', 'announce', 'poll', 'auditlog'];
document.getElementById('commandsList').innerHTML = commands.map(c => `
<div class="command-item">/${c}</div>
`).join('');
} catch (error) {
console.error('Failed to fetch data:', error);
document.getElementById('statusBadge').textContent = 'Error';
document.getElementById('statusBadge').className = 'status-badge status-offline';
}
}
fetchData();
setInterval(fetchData, 30000);
</script>
</body>
</html>