++ fix grafico

This commit is contained in:
2026-03-18 17:22:33 +01:00
parent 405b8d24bd
commit 2a249e16d4
2 changed files with 125 additions and 15 deletions

View File

@@ -35,16 +35,22 @@
<section class="log-panel mt-4 p-3 p-md-4">
<div class="d-flex align-items-center justify-content-between mb-3">
<h2 class="h4 mb-0">Attivita Recenti</h2>
<span class="pill">UTC</span>
<div class="d-flex align-items-center gap-2">
<h2 class="h4 mb-0">Attivita Recenti</h2>
<span id="liveIndicator" class="live-dot d-none"></span>
</div>
<div class="d-flex align-items-center gap-2">
<span id="logCount" class="pill">0 eventi</span>
<span class="pill">UTC</span>
</div>
</div>
<div class="table-responsive">
<div class="log-scroll">
<table class="table table-dark table-borderless align-middle mb-0" id="logsTable">
<thead>
<tr>
<th>Ora</th>
<th>Livello</th>
<th>Job</th>
<th style="width:160px">Ora</th>
<th style="width:90px">Livello</th>
<th style="width:130px">Job</th>
<th>Messaggio</th>
<th>Dettagli</th>
</tr>
@@ -69,15 +75,20 @@
return new Date(value).toLocaleString();
}
function fmtSummary(summary) {
if (!summary || Object.keys(summary).length === 0) {
return 'Nessuna esecuzione';
function fmtSummary(summary, running) {
if (running) {
return '<span class="sync-in-progress">&#9654; Sincronizzazione in corso&hellip;</span>';
}
if (!summary || Object.keys(summary).length === 0) {
return '<span class="text-secondary">Nessuna esecuzione ancora</span>';
}
const labels = { downloaded: 'scaricati', kept: 'invariati', deleted: 'eliminati', errors: 'errori', trigger: 'avviato da', error: 'errore' };
const parts = [];
for (const [key, val] of Object.entries(summary)) {
parts.push(`${key}: ${val}`);
const label = labels[key] || key;
parts.push(`<span class="summary-kv"><span class="summary-key">${label}</span> ${val}</span>`);
}
return parts.join(' | ');
return parts.join('');
}
function statusClass(status, running) {
@@ -112,7 +123,7 @@
<div>Ultima fine: ${fmtDate(job.last_end)}</div>
</div>
<div class="mt-3 summary-box mono small">${fmtSummary(job.last_summary)}</div>
<div class="mt-3 summary-box mono small">${fmtSummary(job.last_summary, job.running)}</div>
<div class="mt-3 d-flex gap-2">
<button class="btn btn-outline-light btn-sm" data-job="${job.name}">Avvia ora</button>
@@ -135,21 +146,37 @@
});
}
let prevLogTime = null;
function renderLogs(logs) {
const tbody = document.querySelector('#logsTable tbody');
tbody.innerHTML = '';
const entries = logs.slice(0, 200);
const newTopTime = entries.length > 0 ? entries[0].time : null;
const hasNew = newTopTime && newTopTime !== prevLogTime;
prevLogTime = newTopTime;
logs.slice(0, 120).forEach((entry) => {
tbody.innerHTML = '';
entries.forEach((entry, i) => {
const tr = document.createElement('tr');
if (hasNew && i < 3) tr.classList.add('row-new');
tr.innerHTML = `
<td class="mono small">${fmtDate(entry.time)}</td>
<td><span class="pill level-${entry.level.toLowerCase()}">${entry.level}</span></td>
<td class="mono small">${entry.job || '-'}</td>
<td>${entry.message}</td>
<td class="mono small">${JSON.stringify(entry.details || {})}</td>
<td class="mono small text-truncate" style="max-width:260px" title="${encodeURIComponent(JSON.stringify(entry.details || {})).replace(/'/g,"&apos;").replace(/%/g,'%')}">` +
JSON.stringify(entry.details || {}) +
`</td>
`;
tbody.appendChild(tr);
});
document.getElementById('logCount').textContent = entries.length + ' eventi';
if (hasNew) {
const scroll = document.querySelector('.log-scroll');
scroll.scrollTop = 0;
}
}
async function reload() {
@@ -158,6 +185,13 @@
document.getElementById('serverTime').textContent = `Server UTC: ${fmtDate(data.server_time)}`;
renderJobs(data.jobs || []);
renderLogs(data.logs || []);
const anyRunning = (data.jobs || []).some(j => j.running);
const dot = document.getElementById('liveIndicator');
if (anyRunning) {
dot.classList.remove('d-none');
} else {
dot.classList.add('d-none');
}
} catch (err) {
document.getElementById('serverTime').textContent = `Errore: ${err.message}`;
}