From ef9ffdbe71f2f429e93bc61e08f48168dfd6b603 Mon Sep 17 00:00:00 2001 From: Alexander Sabino <32822107+asabino2@users.noreply.github.com> Date: Thu, 7 May 2026 16:53:37 +0100 Subject: [PATCH] =?UTF-8?q?adiciona=20funcionalidade=20para=20carregar=20e?= =?UTF-8?q?=20exibir=20todas=20as=20execu=C3=A7=C3=B5es=20de=20backup;=20a?= =?UTF-8?q?tualiza=20estat=C3=ADsticas=20de=20sucesso=20e=20falha=20no=20p?= =?UTF-8?q?ainel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot --- public/app.js | 81 ++++++++++++++++++++++++++++++++++++++++------- public/index.html | 17 +++++----- 2 files changed, 79 insertions(+), 19 deletions(-) diff --git a/public/app.js b/public/app.js index 54b61bb..8a9fbf3 100644 --- a/public/app.js +++ b/public/app.js @@ -49,6 +49,9 @@ function navigateTo(viewName) { if (viewName === 'servers') { renderServers(); } + if (viewName === 'runs') { + loadAllRuns(); + } if (viewName === 'backups') { renderBackupsView(); } @@ -99,16 +102,27 @@ async function renderBackupsView() {
- + - ${backups.length ? backups.map((b) => ` + ${backups.length ? backups.map((b) => { + const hasRestorable = (b.containers || []).some((c) => c.status === 'ok'); + return ` + - `).join('') : ''} + `}).join('') : ''}
DataModeStatusContainers
DataModeStatusContainersActions
${escapeHtml(new Date(b.createdAt).toLocaleString('pt-BR'))} ${escapeHtml(b.mode || '—')} ${escapeHtml(b.status)} ${escapeHtml((b.containers || []).map((c) => c.containerName).join(', '))} + +
Nenhum backup realizado.
Nenhum backup realizado.
@@ -127,16 +141,11 @@ async function updateDashboard() { .sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); const lastBackupEl = document.querySelector('#lastBackupTime'); if (lastBackupEl) { - lastBackupEl.textContent = successful.length - ? new Date(successful[0].createdAt).toLocaleString('pt-BR') - : '—'; + lastBackupEl.textContent = String(successful.length); } - // Failed in last 24h - const cutoff = Date.now() - 24 * 60 * 60 * 1000; - const failed = allBackups.filter( - (b) => b.status === 'error' && new Date(b.createdAt).getTime() >= cutoff, - ); + // Failed total + const failed = allBackups.filter((b) => b.status === 'error'); const failedEl = document.querySelector('#failedCount'); if (failedEl) failedEl.textContent = String(failed.length); @@ -191,6 +200,54 @@ function formatBytes(bytes) { return bytes + ' B'; } +async function loadAllRuns() { + const tbody = document.querySelector('#allRunsBody'); + if (!tbody) return; + tbody.innerHTML = 'Carregando...'; + + if (!state.profiles.length) { + await loadProfiles(); + } + + if (!state.profiles.length) { + tbody.innerHTML = 'Nenhum profile encontrado.'; + return; + } + + const allBackups = (await Promise.all( + state.profiles.map((p) => api(`/api/profiles/${p.id}/backups`)), + )).flat(); + + const sorted = [...allBackups].sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); + + if (!sorted.length) { + tbody.innerHTML = 'Nenhum run encontrado.'; + return; + } + + tbody.innerHTML = sorted.map((b, index) => { + const profile = state.profiles.find((p) => p.id === b.profileId); + const profileName = profile ? profile.name : (b.profileName || b.profileId || '—'); + const containers = (b.containers || []).map((c) => escapeHtml(c.containerName || c.containerId?.slice(0, 12) || '?')).join(', '); + const fileCount = (b.containers || []).reduce((sum, c) => sum + (c.fileCount || 0), 0); + const size = (b.containers || []).reduce((sum, c) => sum + (c.archiveSize || 0), 0); + const sizeStr = size > 0 ? formatBytes(size) : '—'; + const statusLabel = b.status === 'ok' ? 'Completed' : b.status === 'partial' ? 'Partial' : 'Error'; + return ` + + ${index + 1} + ${escapeHtml(profileName)} + ${escapeHtml(b.mode || '—')} + ${escapeHtml(statusLabel)} + ${containers || '—'} + ${fileCount || '—'} + ${sizeStr} + ${escapeHtml(new Date(b.createdAt).toLocaleString('pt-BR'))} + + `; + }).join(''); +} + async function api(path, options = {}) { const response = await fetch(path, { headers: { @@ -925,5 +982,7 @@ document.querySelector('#refreshContainers').addEventListener('click', init); document.querySelector('#reloadProfiles').addEventListener('click', loadProfiles); document.querySelector('#clearForm').addEventListener('click', resetForm); elements.profilesList.addEventListener('click', handleProfileAction); +document.querySelector('#backupsViewList').addEventListener('click', handleProfileAction); +document.querySelector('#refreshRuns')?.addEventListener('click', () => loadAllRuns()); init(); \ No newline at end of file diff --git a/public/index.html b/public/index.html index a26c1dd..4f98f7d 100644 --- a/public/index.html +++ b/public/index.html @@ -106,9 +106,9 @@
- Last Backup - - Latest successful run + Successful + 0 + Total com sucesso
@@ -117,9 +117,9 @@
- Failed (24h) + Failed 0 - Needs attention + Total com falha
@@ -234,6 +234,7 @@