From 5300620330dfbb487e9548b606ba06ef38bd0d00 Mon Sep 17 00:00:00 2001 From: DeLo1585 <174631727+DeLo1585@users.noreply.github.com> Date: Fri, 2 Oct 2026 23:45:32 +0000 Subject: [PATCH 1/4] Show total size of selected files in maintenance cards (#220) Untracked Files, Backup Cleanup and Duplicates now show the combined size next to the Selected: N counter, so the amount of data an action will touch is visible before clicking. Sizes come from the rows' existing data-size attributes. Group rows in Untracked Files carry the whole group's size in data-size (used for sorting) and their children are selected too, so group rows expose their own file in a new data-file-size attribute to avoid counting children twice. Duplicate file rows get a data-size attribute. formatBytes() mirrors core.system_utils.format_bytes so the figure matches the operation pill. --- web/templates/maintenance/index.html | 37 +++++++++++++++++++ .../maintenance/partials/audit_results.html | 6 +-- .../maintenance/partials/duplicate_card.html | 4 +- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/web/templates/maintenance/index.html b/web/templates/maintenance/index.html index 2c4eabd2..8024f081 100644 --- a/web/templates/maintenance/index.html +++ b/web/templates/maintenance/index.html @@ -119,6 +119,40 @@

Confirm Action

formData.append('paths_json', JSON.stringify(Array.from(paths))); } +// Format a byte count like core.system_utils.format_bytes (1024-based, 2 decimals) +// so the pre-action selection size matches the size shown once the operation runs. +function formatBytes(bytes) { + let size = Number(bytes) || 0; + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + let i = 0; + while (size >= 1024 && i < units.length - 1) { + size /= 1024; + i++; + } + return i === 0 ? Math.round(size) + ' B' : size.toFixed(2) + ' ' + units[i]; +} + +// Total size of the checked boxes matching `selector`. Every checkbox is one file, so the +// size comes from its own row. A group row's data-size is the whole group (primary + children, +// used for sorting), so group rows expose their own file in data-file-size to avoid +// counting the children twice when the group checkbox selects them too. +function sumCheckedSize(selector) { + let total = 0; + document.querySelectorAll(selector + ':checked').forEach(cb => { + const row = cb.closest('tr'); + if (!row) return; + const size = parseInt(row.dataset.fileSize || row.dataset.size, 10); + if (size > 0) total += size; + }); + return total; +} + +// Write " · 42.93 GB" next to a "Selected: N" counter (empty when no size is known). +function setSelectedSize(spanId, bytes) { + const span = document.getElementById(spanId); + if (span) span.textContent = bytes > 0 ? ' \u00b7 ' + formatBytes(bytes) : ''; +} + // Selected items tracking let selectedUntrackedFiles = new Set(); let selectedOrphanedBackups = new Set(); @@ -155,6 +189,7 @@

Confirm Action

if (count > 0) { bulkActions.style.display = 'flex'; countSpan.textContent = count; + setSelectedSize('untracked-selected-size', sumCheckedSize('.untracked-checkbox')); } else { bulkActions.style.display = 'none'; } @@ -193,6 +228,7 @@

Confirm Action

if (count > 0) { bulkActions.style.display = 'flex'; countSpan.textContent = count; + setSelectedSize('orphaned-selected-size', sumCheckedSize('.orphaned-checkbox')); // Only show restore button if at least one orphaned (not redundant) backup is selected if (restoreBtn) { @@ -675,6 +711,7 @@

if (count > 0) { bulkActions.style.display = 'flex'; if (countSpan) countSpan.textContent = count; + setSelectedSize('dup-selected-size', sumCheckedSize('.dup-file-checkbox')); } else { bulkActions.style.display = 'none'; } diff --git a/web/templates/maintenance/partials/audit_results.html b/web/templates/maintenance/partials/audit_results.html index c04d01fa..f9151bf7 100644 --- a/web/templates/maintenance/partials/audit_results.html +++ b/web/templates/maintenance/partials/audit_results.html @@ -119,7 +119,7 @@

Untracked Files ({{ results.untracked_files|length }}{% if results.grouped_u