302 lines
8.8 KiB
JavaScript
302 lines
8.8 KiB
JavaScript
// Linux BenchTools - Dashboard Logic
|
|
|
|
const { formatDate, formatRelativeTime, createScoreBadge, getScoreBadgeText, escapeHtml, showError, showEmptyState, copyToClipboard, showToast, debounce } = window.BenchUtils;
|
|
const api = window.BenchAPI;
|
|
|
|
// Global state
|
|
let allDevices = [];
|
|
let isLoading = false;
|
|
|
|
// Load dashboard data
|
|
async function loadDashboard() {
|
|
if (isLoading) return;
|
|
|
|
isLoading = true;
|
|
updateRefreshButton(true);
|
|
|
|
try {
|
|
await Promise.all([
|
|
loadStats(),
|
|
loadTopDevices()
|
|
]);
|
|
|
|
// Update last refresh time
|
|
updateLastRefreshTime();
|
|
|
|
} catch (error) {
|
|
console.error('Failed to load dashboard:', error);
|
|
showToast('Erreur lors du chargement des données', 'error');
|
|
} finally {
|
|
isLoading = false;
|
|
updateRefreshButton(false);
|
|
}
|
|
}
|
|
|
|
// Update refresh button state
|
|
function updateRefreshButton(loading) {
|
|
const btn = document.getElementById('refreshBtn');
|
|
if (!btn) return;
|
|
|
|
if (loading) {
|
|
btn.disabled = true;
|
|
btn.innerHTML = '⏳ Chargement...';
|
|
} else {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '🔄 Actualiser';
|
|
}
|
|
}
|
|
|
|
// Update last refresh time
|
|
function updateLastRefreshTime() {
|
|
const element = document.getElementById('lastUpdate');
|
|
if (!element) return;
|
|
|
|
const now = new Date();
|
|
element.textContent = `Mis à jour: ${now.toLocaleTimeString('fr-FR')}`;
|
|
}
|
|
|
|
// Load statistics
|
|
async function loadStats() {
|
|
try {
|
|
const devices = await api.getDevices({ page_size: 1000 });
|
|
|
|
const totalDevices = devices.total || 0;
|
|
let totalBenchmarks = 0;
|
|
let scoreSum = 0;
|
|
let scoreCount = 0;
|
|
let lastBenchDate = null;
|
|
|
|
// Calculate stats from devices
|
|
devices.items.forEach(device => {
|
|
if (device.last_benchmark) {
|
|
totalBenchmarks++;
|
|
|
|
if (device.last_benchmark.global_score !== null) {
|
|
scoreSum += device.last_benchmark.global_score;
|
|
scoreCount++;
|
|
}
|
|
|
|
const benchDate = new Date(device.last_benchmark.run_at);
|
|
if (!lastBenchDate || benchDate > lastBenchDate) {
|
|
lastBenchDate = benchDate;
|
|
}
|
|
}
|
|
});
|
|
|
|
const avgScore = scoreCount > 0 ? Math.round(scoreSum / scoreCount) : 0;
|
|
|
|
// Update UI
|
|
document.getElementById('totalDevices').textContent = totalDevices;
|
|
document.getElementById('totalBenchmarks').textContent = totalBenchmarks;
|
|
document.getElementById('avgScore').textContent = avgScore;
|
|
document.getElementById('lastBench').textContent = lastBenchDate
|
|
? formatRelativeTime(lastBenchDate.toISOString())
|
|
: 'Aucun';
|
|
|
|
} catch (error) {
|
|
console.error('Failed to load stats:', error);
|
|
// Set default values on error
|
|
document.getElementById('totalDevices').textContent = '0';
|
|
document.getElementById('totalBenchmarks').textContent = '0';
|
|
document.getElementById('avgScore').textContent = '0';
|
|
document.getElementById('lastBench').textContent = 'N/A';
|
|
}
|
|
}
|
|
|
|
// Load top devices
|
|
async function loadTopDevices() {
|
|
const container = document.getElementById('devicesTable');
|
|
|
|
try {
|
|
const data = await api.getDevices({ page_size: 50 });
|
|
|
|
if (!data.items || data.items.length === 0) {
|
|
showEmptyState(container, 'Aucun device trouvé. Exécutez un benchmark sur une machine pour commencer.', '📊');
|
|
allDevices = [];
|
|
return;
|
|
}
|
|
|
|
// Store all devices for filtering
|
|
allDevices = data.items;
|
|
|
|
// Sort by global_score descending
|
|
const sortedDevices = allDevices.sort((a, b) => {
|
|
const scoreA = a.last_benchmark?.global_score ?? -1;
|
|
const scoreB = b.last_benchmark?.global_score ?? -1;
|
|
return scoreB - scoreA;
|
|
});
|
|
|
|
// Render devices
|
|
renderDevicesTable(sortedDevices);
|
|
|
|
} catch (error) {
|
|
console.error('Failed to load devices:', error);
|
|
container.innerHTML = `
|
|
<div class="error" style="text-align: center;">
|
|
<p style="margin-bottom: 1rem;">❌ Impossible de charger les devices</p>
|
|
<p style="font-size: 0.9rem; margin-bottom: 1rem;">${escapeHtml(error.message)}</p>
|
|
<button class="btn btn-primary btn-sm" onclick="loadTopDevices()">🔄 Réessayer</button>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
// Render devices table
|
|
function renderDevicesTable(devices) {
|
|
const container = document.getElementById('devicesTable');
|
|
|
|
if (devices.length === 0) {
|
|
container.innerHTML = `
|
|
<div style="text-align: center; padding: 2rem; color: var(--text-secondary);">
|
|
<p>Aucun device trouvé avec ces critères de recherche.</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = `
|
|
<div class="table-wrapper">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Hostname</th>
|
|
<th>Description</th>
|
|
<th>Score Global</th>
|
|
<th>CPU</th>
|
|
<th>MEM</th>
|
|
<th>DISK</th>
|
|
<th>NET</th>
|
|
<th>GPU</th>
|
|
<th>Dernier Bench</th>
|
|
<th>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${devices.map((device, index) => createDeviceRow(device, index + 1)).join('')}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// Create device row HTML
|
|
function createDeviceRow(device, rank) {
|
|
const bench = device.last_benchmark;
|
|
|
|
const globalScore = bench?.global_score;
|
|
const cpuScore = bench?.cpu_score;
|
|
const memScore = bench?.memory_score;
|
|
const diskScore = bench?.disk_score;
|
|
const netScore = bench?.network_score;
|
|
const gpuScore = bench?.gpu_score;
|
|
const runAt = bench?.run_at;
|
|
|
|
const globalScoreHtml = globalScore !== null && globalScore !== undefined
|
|
? `<span class="${window.BenchUtils.getScoreBadgeClass(globalScore)}">${getScoreBadgeText(globalScore)}</span>`
|
|
: '<span class="badge">N/A</span>';
|
|
|
|
return `
|
|
<tr onclick="window.location.href='device_detail.html?id=${device.id}'">
|
|
<td><strong>${rank}</strong></td>
|
|
<td>
|
|
<strong style="color: var(--color-success);">${escapeHtml(device.hostname)}</strong>
|
|
</td>
|
|
<td style="color: var(--text-secondary);">
|
|
${escapeHtml(device.description || 'Aucune description')}
|
|
</td>
|
|
<td>${globalScoreHtml}</td>
|
|
<td><span class="${window.BenchUtils.getScoreBadgeClass(cpuScore)}">${getScoreBadgeText(cpuScore)}</span></td>
|
|
<td><span class="${window.BenchUtils.getScoreBadgeClass(memScore)}">${getScoreBadgeText(memScore)}</span></td>
|
|
<td><span class="${window.BenchUtils.getScoreBadgeClass(diskScore)}">${getScoreBadgeText(diskScore)}</span></td>
|
|
<td><span class="${window.BenchUtils.getScoreBadgeClass(netScore)}">${getScoreBadgeText(netScore)}</span></td>
|
|
<td><span class="${window.BenchUtils.getScoreBadgeClass(gpuScore)}">${getScoreBadgeText(gpuScore)}</span></td>
|
|
<td style="color: var(--text-secondary); font-size: 0.85rem;">
|
|
${runAt ? formatRelativeTime(runAt) : 'Jamais'}
|
|
</td>
|
|
<td>
|
|
<a href="device_detail.html?id=${device.id}" class="btn btn-sm btn-primary">Voir</a>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
}
|
|
|
|
// Copy bench command to clipboard
|
|
async function copyBenchCommand() {
|
|
const command = document.getElementById('benchCommand').textContent;
|
|
const success = await copyToClipboard(command);
|
|
|
|
if (success) {
|
|
showToast('Commande copiée dans le presse-papier !', 'success');
|
|
} else {
|
|
showToast('Erreur lors de la copie', 'error');
|
|
}
|
|
}
|
|
|
|
// Filter devices based on search query
|
|
function filterDevices(query) {
|
|
if (!query || query.trim() === '') {
|
|
renderDevicesTable(allDevices);
|
|
return;
|
|
}
|
|
|
|
const lowerQuery = query.toLowerCase();
|
|
const filtered = allDevices.filter(device => {
|
|
const hostname = (device.hostname || '').toLowerCase();
|
|
const description = (device.description || '').toLowerCase();
|
|
const location = (device.location || '').toLowerCase();
|
|
|
|
return hostname.includes(lowerQuery) ||
|
|
description.includes(lowerQuery) ||
|
|
location.includes(lowerQuery);
|
|
});
|
|
|
|
renderDevicesTable(filtered);
|
|
}
|
|
|
|
// Debounced search
|
|
const debouncedSearch = debounce((query) => {
|
|
filterDevices(query);
|
|
}, 300);
|
|
|
|
// Handle search input
|
|
function handleSearch(event) {
|
|
const query = event.target.value;
|
|
debouncedSearch(query);
|
|
}
|
|
|
|
// Clear search
|
|
function clearSearch() {
|
|
const searchInput = document.getElementById('searchInput');
|
|
if (searchInput) {
|
|
searchInput.value = '';
|
|
filterDevices('');
|
|
}
|
|
}
|
|
|
|
// Refresh dashboard manually
|
|
function refreshDashboard() {
|
|
if (!isLoading) {
|
|
loadDashboard();
|
|
}
|
|
}
|
|
|
|
// Initialize dashboard on page load
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
loadDashboard();
|
|
|
|
// Setup search input listener
|
|
const searchInput = document.getElementById('searchInput');
|
|
if (searchInput) {
|
|
searchInput.addEventListener('input', handleSearch);
|
|
}
|
|
|
|
// Refresh every 30 seconds
|
|
setInterval(loadDashboard, 30000);
|
|
});
|
|
|
|
// Make functions available globally
|
|
window.copyBenchCommand = copyBenchCommand;
|
|
window.clearSearch = clearSearch;
|
|
window.refreshDashboard = refreshDashboard;
|