Files
TokenWatch/public/app.js
T
Jannik b312ac38ef
Build and Push Docker Image / build-and-push (push) Canceled after 0s
Fix database connection, add postgres healthcheck, update deployment templates
2026-08-28 18:45:51 +02:00

200 lines
6.9 KiB
JavaScript

let historyChart = null;
document.addEventListener('DOMContentLoaded', () => {
fetchQuotas();
fetchHistory();
setupThemeDropdown();
});
function setupThemeDropdown() {
const dropdown = document.getElementById('theme-dropdown');
dropdown.addEventListener('change', (e) => {
// Change body class
document.body.className = e.target.value;
// Update chart colors if chart exists
if (historyChart) {
updateChartTheme(e.target.value);
}
});
}
async function fetchQuotas() {
try {
const response = await fetch('/api/quotas');
const quotas = await response.json();
// Mock data if DB is empty
const activeQuotas = quotas.length > 0 ? quotas : [
{ provider: 'Antigravity', used: 68240, total: 100000, percentage: 68.24 },
{ provider: 'POE', used: 33750, total: 75000, percentage: 45 },
{ provider: 'MiniMax', used: 60, total: 100, percentage: 60 }
];
activeQuotas.forEach(quota => {
updateRow(quota.provider.toLowerCase(), quota);
});
} catch (error) {
console.error('Error fetching quotas:', error);
}
}
function updateRow(providerId, quota) {
const fill = document.getElementById(`fill-${providerId}`);
const statusText = document.getElementById(`status-${providerId}`);
const percentText = document.getElementById(`percentage-${providerId}`);
if (!fill || !statusText || !percentText) return;
const percentage = quota.percentage;
fill.style.width = `${percentage}%`;
// Update percentage text
percentText.innerText = `${Math.round(percentage)}%`;
// Status coloring
percentText.className = 'percentage'; // reset
if (percentage > 90) {
percentText.classList.add('status-danger');
} else if (percentage > 70) {
percentText.classList.add('status-warn');
} else {
percentText.classList.add('status-good');
}
// Detail text
if (quota.provider.toLowerCase() === 'minimax' && quota.meta) {
const reset5h = new Date(quota.meta.intervalReset).toLocaleTimeString();
statusText.innerHTML = `5h: ${quota.meta.intervalUsed}% (Resets: ${reset5h})`;
} else if (quota.provider.toLowerCase() === 'antigravity' && quota.meta) {
statusText.innerHTML = `5h: ${quota.meta.intervalUsed}% (Resets: ${quota.meta.intervalResetStr})`;
} else {
statusText.innerText = `${(quota.used).toLocaleString()} / ${(quota.total).toLocaleString()}`;
}
if (percentage >= 100) {
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6 }
});
statusText.innerText = 'LIMIT REACHED!';
statusText.style.color = 'var(--danger)';
}
}
async function fetchHistory() {
try {
const response = await fetch('/api/history');
const history = await response.json();
const labels = [...new Set(history.map(item => new Date(item.timestamp).toLocaleTimeString()))].sort();
const antigravityData = history.filter(item => item.provider === 'Antigravity').map(item => item.percentage);
const poeData = history.filter(item => item.provider === 'POE').map(item => item.percentage);
const minimaxData = history.filter(item => item.provider === 'MiniMax').map(item => item.percentage);
const ctx = document.getElementById('historyChart').getContext('2d');
historyChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels.length ? labels : ['10:00', '11:00', '12:00', '13:00'],
datasets: [
{
label: 'Antigravity (%)',
data: antigravityData.length ? antigravityData : [10, 30, 50, 68],
borderColor: '#58a6ff',
backgroundColor: 'rgba(88, 166, 255, 0.1)',
borderWidth: 2,
tension: 0.3,
fill: true
},
{
label: 'POE (%)',
data: poeData.length ? poeData : [5, 10, 20, 45],
borderColor: '#3fb950',
backgroundColor: 'rgba(63, 185, 80, 0.1)',
borderWidth: 2,
tension: 0.3,
fill: true
},
{
label: 'MiniMax (%)',
data: minimaxData.length ? minimaxData : [0, 0, 30, 60],
borderColor: '#d29922',
backgroundColor: 'rgba(210, 153, 34, 0.1)',
borderWidth: 2,
tension: 0.3,
fill: true
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
labels: { color: '#c9d1d9' }
}
},
scales: {
y: {
beginAtZero: true,
max: 100,
grid: { color: '#30363d' },
ticks: { color: '#8b949e' }
},
x: {
grid: { color: '#30363d' },
ticks: { color: '#8b949e' }
}
}
}
});
// Apply initial theme to chart
updateChartTheme(document.body.className);
} catch (error) {
console.error('Error fetching history:', error);
}
}
function updateChartTheme(themeClass) {
if (!historyChart) return;
let colors = ['#58a6ff', '#3fb950', '#d29922']; // Default
switch (themeClass) {
case 'style-pacman':
colors = ['#f1c40f', '#f39c12', '#e67e22'];
break;
case 'style-nyancat':
colors = ['#ff0000', '#33ff00', '#0099ff'];
break;
case 'style-matrix':
colors = ['#0f0', '#0a0', '#050'];
break;
case 'style-lava':
colors = ['#ff4e00', '#ec9f05', '#bf3a00'];
break;
}
historyChart.data.datasets.forEach((dataset, index) => {
dataset.borderColor = colors[index % colors.length];
// Convert hex to rgba for background
let hex = colors[index % colors.length].replace('#', '');
if (hex.length === 3) {
hex = hex.split('').map(char => char + char).join('');
}
const r = parseInt(hex.substring(0, 2), 16);
const g = parseInt(hex.substring(2, 4), 16);
const b = parseInt(hex.substring(4, 6), 16);
dataset.backgroundColor = `rgba(${r}, ${g}, ${b}, 0.1)`;
});
historyChart.update();
}