From 19609e9026610b8f9ae2e25985e8f9ca74cb9344 Mon Sep 17 00:00:00 2001 From: Pawel Date: Thu, 16 Apr 2026 23:48:06 +0300 Subject: [PATCH] fix: block bulk delete when selected sessions are hidden by filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a user selects sessions then applies a search/filter, some selected sessions become invisible. Previously, Delete would silently remove those hidden sessions too — a data loss risk. Now: - updateBulkBar() detects selected IDs not in filteredSessions - Shows warning: "⚠ N hidden by filter — deselect hidden" (amber) - Delete button is disabled until hidden selections are resolved - "deselect hidden" link removes only the invisible selections, preserving the visible ones so the user can continue bulk actions --- src/frontend/app.js | 23 +++++++++++++++++++++++ src/frontend/index.html | 3 ++- src/frontend/styles.css | 16 ++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/frontend/app.js b/src/frontend/app.js index 38bd8bf..3f79e61 100644 --- a/src/frontend/app.js +++ b/src/frontend/app.js @@ -1266,11 +1266,34 @@ function updateBulkBar() { if (selectedIds.size > 0) { bar.style.display = 'flex'; document.getElementById('bulkCount').textContent = selectedIds.size + ' selected'; + + // Warn if some selected sessions are hidden by the current filter + var visibleIds = new Set((filteredSessions || []).map(function(s) { return s.id; })); + var hiddenCount = 0; + selectedIds.forEach(function(id) { if (!visibleIds.has(id)) hiddenCount++; }); + var warning = document.getElementById('bulkHiddenWarning'); + var deleteBtn = document.getElementById('bulkDeleteBtn'); + if (hiddenCount > 0) { + document.getElementById('bulkHiddenCount').textContent = hiddenCount; + if (warning) warning.style.display = 'inline'; + if (deleteBtn) { deleteBtn.disabled = true; deleteBtn.title = 'Clear or deselect hidden sessions first'; } + } else { + if (warning) warning.style.display = 'none'; + if (deleteBtn) { deleteBtn.disabled = false; deleteBtn.title = ''; } + } } else { bar.style.display = 'none'; } } +function clearHiddenSelections(event) { + if (event) event.preventDefault(); + var visibleIds = new Set((filteredSessions || []).map(function(s) { return s.id; })); + selectedIds.forEach(function(id) { if (!visibleIds.has(id)) selectedIds.delete(id); }); + updateBulkBar(); + render(); +} + function clearSelection() { selectedIds.clear(); selectMode = false; diff --git a/src/frontend/index.html b/src/frontend/index.html index 215cad7..deacfb2 100644 --- a/src/frontend/index.html +++ b/src/frontend/index.html @@ -173,7 +173,8 @@

Delete Session?

diff --git a/src/frontend/styles.css b/src/frontend/styles.css index 583ed50..33e52fb 100644 --- a/src/frontend/styles.css +++ b/src/frontend/styles.css @@ -1062,6 +1062,22 @@ body { border-color: var(--accent-red); } .bulk-bar button.bulk-delete:hover { opacity: 0.85; } +.bulk-bar button:disabled { opacity: 0.4; cursor: not-allowed; } +.bulk-bar button:disabled:hover { opacity: 0.4; } + +.bulk-hidden-warning { + font-size: 12px; + color: #f5a623; + display: flex; + align-items: center; + gap: 4px; +} +.bulk-hidden-warning a { + color: #f5a623; + text-decoration: underline; + cursor: pointer; +} +.bulk-hidden-warning a:hover { opacity: 0.8; } [data-theme="light"] .bulk-bar { background: rgba(245,245,247,0.88);