From 97ecde87c248dc98626c7a12692a713a02348c49 Mon Sep 17 00:00:00 2001 From: Claude Dev Date: Thu, 9 Apr 2026 20:21:52 +0200 Subject: [PATCH] Fortschritt: Auto-Minimize, Action-Lock, Queue-Anzeige in Sidebar 1. Aktualisierungen starten minimiert (Mini-Bar), Popup nur per Klick. Verhindert Ueberlagerung von Bearbeiten/Export-Buttons. 2. Erster Durchlauf: Bearbeiten/Export/Archivieren/Loeschen gesperrt, nur Abbrechen moeglich. 3. Sidebar: Warteschlange-Lagen zeigen Position (#1, #2...) mit eigenem visuellen Stil (gedimmt, pulsierender Dot). 4. Sidebar-Status (Recherchiert/Analysiert/Faktencheck) wird fuer ALLE laufenden Lagen angezeigt, nicht nur die aktuelle. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/static/css/style.css | 35 +++++++++++++++++++++++++++++ src/static/js/app.js | 12 ++++++++-- src/static/js/components.js | 45 ++++++++++++++++++++++++++++++++----- 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/src/static/css/style.css b/src/static/css/style.css index 60db7d7..1236995 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -2071,6 +2071,41 @@ a:hover { transition: filter 0.4s ease; } +/* === Disabled Actions During First Refresh === */ +.incident-header-actions.first-refresh-locked .btn:not(#refresh-btn) { + opacity: 0.3; + pointer-events: none; + cursor: not-allowed; +} +.incident-header-actions.first-refresh-locked #refresh-btn { + opacity: 0.3; + pointer-events: none; +} + +/* === Sidebar Queue Position Badge === */ +.incident-queue-badge { + font-size: 9px; + font-weight: 700; + color: var(--bg-primary); + background: var(--text-disabled); + border-radius: 4px; + padding: 1px 5px; + letter-spacing: 0.3px; + white-space: nowrap; + animation: fadeIn 0.3s ease; +} + +.incident-item.queued-item { + opacity: 0.7; +} +.incident-item.queued-item .incident-dot { + background: var(--text-disabled); + animation: pulse 2s ease-in-out infinite; +} +.incident-refresh-status.queued-status { + color: var(--text-disabled); +} + /* === Sidebar Refreshing Indicator === */ .incident-item.refreshing-item { border: 1px solid transparent; diff --git a/src/static/js/app.js b/src/static/js/app.js index 601880c..277f3de 100644 --- a/src/static/js/app.js +++ b/src/static/js/app.js @@ -577,7 +577,10 @@ const App = { if (data.refreshing && data.refreshing.length > 0) { data.refreshing.forEach(id => this._refreshingIncidents.add(id)); // Sidebar-Dots aktualisieren - data.refreshing.forEach(id => this._updateSidebarDot(id)); + data.refreshing.forEach(id => { + this._updateSidebarDot(id); + UI._updateSidebarRefreshStatus(id, 'researching', {}); + }); } } catch (e) { /* Kein kritischer Fehler */ } @@ -711,6 +714,9 @@ const App = { const step = state ? state.step : 'researching'; const isFirst = state ? state.isFirst : false; UI.showProgress(step, {}, id, isFirst); + } else { + // Ensure actions are unlocked when viewing non-refreshing incident + UI._lockActionsIfFirst(false); } // Alte Inhalte sofort leeren um Flackern beim Wechsel zu vermeiden @@ -2035,8 +2041,10 @@ async handleRefresh() { // Detect first refresh: no summary means first run const inc = this.incidents.find(i => i.id === msg.incident_id); const isFirst = inc && !inc.summary; - UI.showProgress(status, msg.data, msg.incident_id, isFirst); + // Always update sidebar status (visible for all incidents) + UI._updateSidebarRefreshStatus(msg.incident_id, status, msg.data); if (msg.incident_id === this.currentIncidentId) { + UI.showProgress(status, msg.data, msg.incident_id, isFirst); this._updateRefreshButton(status !== 'idle'); } }, diff --git a/src/static/js/components.js b/src/static/js/components.js index 3e477fd..b5347ec 100644 --- a/src/static/js/components.js +++ b/src/static/js/components.js @@ -285,6 +285,11 @@ const UI = { // Update sidebar status text this._updateSidebarRefreshStatus(incidentId, status, extra); + // For updates (not first refresh): default to minimized to avoid overlaying buttons + if (!state.isFirst && !state._userOpenedPopup) { + state.minimized = true; + } + if (state.minimized) { this._showMiniProgress(status, state); return; @@ -373,6 +378,19 @@ const UI = { // Hide mini bar const mini = document.getElementById('progress-mini'); if (mini) mini.style.display = 'none'; + + // Lock action buttons during first refresh + this._lockActionsIfFirst(state.isFirst); + }, + + _lockActionsIfFirst(isFirst) { + const actions = document.querySelector('.incident-header-actions'); + if (!actions) return; + if (isFirst) { + actions.classList.add('first-refresh-locked'); + } else { + actions.classList.remove('first-refresh-locked'); + } }, _showMiniProgress(status, state) { @@ -393,6 +411,7 @@ const UI = { const state = this._progressState[incidentId]; if (!state) return; state.minimized = true; + state._userOpenedPopup = false; this._showMiniProgress(state.step, state); }, @@ -401,6 +420,7 @@ const UI = { const state = this._progressState[incidentId]; if (!state) return; state.minimized = false; + state._userOpenedPopup = true; this._showPopupProgress(state.step, {}, state); }, @@ -523,6 +543,9 @@ const UI = { if (mini) mini.style.display = 'none'; } + // Unlock action buttons + this._lockActionsIfFirst(false); + // Remove sidebar status this._removeSidebarRefreshStatus(incidentId); @@ -564,8 +587,11 @@ const UI = { const item = document.querySelector('.incident-item[data-id="' + incidentId + '"]'); if (!item) return; - // Add refreshing class for animated border - item.classList.add('refreshing-item'); + const isQueued = (status === 'queued'); + + // Add appropriate class + item.classList.remove('refreshing-item', 'queued-item'); + item.classList.add(isQueued ? 'queued-item' : 'refreshing-item'); // Add or update status text below meta let statusEl = document.getElementById('sidebar-refresh-' + incidentId); @@ -574,18 +600,25 @@ const UI = { if (!textCol) return; statusEl = document.createElement('div'); statusEl.id = 'sidebar-refresh-' + incidentId; - statusEl.className = 'incident-refresh-status'; textCol.appendChild(statusEl); } - const label = this._getStepLabel(status); - statusEl.innerHTML = '' + label + ''; + + if (isQueued) { + const pos = (extra && extra.queue_position) ? extra.queue_position : ''; + statusEl.className = 'incident-refresh-status queued-status'; + statusEl.innerHTML = 'Warteschlange' + (pos ? ' (#' + pos + ')' : '') + ''; + } else { + statusEl.className = 'incident-refresh-status'; + const label = this._getStepLabel(status); + statusEl.innerHTML = '' + label + ''; + } }, _removeSidebarRefreshStatus(incidentId) { const statusEl = document.getElementById('sidebar-refresh-' + incidentId); if (statusEl) statusEl.remove(); const item = document.querySelector('.incident-item[data-id="' + incidentId + '"]'); - if (item) item.classList.remove('refreshing-item'); + if (item) item.classList.remove('refreshing-item', 'queued-item'); },