From fb0c47eee40243cb424aa682f3dc837c27f94a63 Mon Sep 17 00:00:00 2001 From: Claude Dev Date: Thu, 9 Apr 2026 20:50:30 +0200 Subject: [PATCH] Fix: Abbrechen-Dialog, Overlay-Stacking, Queue-Cancel 1. Confirm-Dialog z-index ueber Progress-Popup (10000 > 9000) 2. Progress-Popup wird ausgeblendet waehrend Confirm-Dialog offen 3. Kein dunkler-werdendes Overlay bei mehrfachem Abbrechen-Klick 4. Abbrechen funktioniert jetzt auch fuer Lagen in der Warteschlange (werden direkt aus der Queue entfernt statt auf Start zu warten) 5. Cancel-Status wird im Popup-Titel angezeigt Co-Authored-By: Claude Opus 4.6 (1M context) --- src/agents/orchestrator.py | 69 +++++++++++++++++++++++++++++--------- src/static/css/style.css | 2 +- src/static/js/app.js | 32 ++++++++++++++---- 3 files changed, 79 insertions(+), 24 deletions(-) diff --git a/src/agents/orchestrator.py b/src/agents/orchestrator.py index b9839e1..099b0fd 100644 --- a/src/agents/orchestrator.py +++ b/src/agents/orchestrator.py @@ -437,22 +437,59 @@ class AgentOrchestrator: return True async def cancel_refresh(self, incident_id: int) -> bool: - """Fordert Abbruch eines laufenden Refreshes an.""" - if self._current_task != incident_id: - return False - self._cancel_requested.add(incident_id) - logger.info(f"Cancel angefordert fuer Lage {incident_id}") - if self._ws_manager: - try: - vis, cb, tid = await self._get_incident_visibility(incident_id) - except Exception: - vis, cb, tid = "public", None, None - await self._ws_manager.broadcast_for_incident({ - "type": "status_update", - "incident_id": incident_id, - "data": {"status": "cancelling", "detail": "Wird abgebrochen..."}, - }, vis, cb, tid) - return True + """Fordert Abbruch eines laufenden oder wartenden Refreshes an.""" + # Check if it's the currently running task + if self._current_task == incident_id: + self._cancel_requested.add(incident_id) + logger.info(f"Cancel angefordert fuer laufende Lage {incident_id}") + if self._ws_manager: + try: + vis, cb, tid = await self._get_incident_visibility(incident_id) + except Exception: + vis, cb, tid = "public", None, None + await self._ws_manager.broadcast_for_incident({ + "type": "status_update", + "incident_id": incident_id, + "data": {"status": "cancelling", "detail": "Wird abgebrochen..."}, + }, vis, cb, tid) + return True + + # Check if it's in the queue (not yet started) + if incident_id in self._queued_ids: + self._queued_ids.discard(incident_id) + # Remove from asyncio queue (rebuild without this ID) + removed = False + new_items = [] + while not self._queue.empty(): + try: + item = self._queue.get_nowait() + iid = item[0] if isinstance(item, tuple) else item + if iid == incident_id: + removed = True + self._queue.task_done() + else: + new_items.append(item) + except Exception: + break + for item in new_items: + self._queue.put_nowait(item) + + logger.info(f"Lage {incident_id} aus Warteschlange entfernt (removed={removed})") + + # Send cancelled event + if self._ws_manager: + try: + vis, cb, tid = await self._get_incident_visibility(incident_id) + except Exception: + vis, cb, tid = "public", None, None + await self._ws_manager.broadcast_for_incident({ + "type": "refresh_cancelled", + "incident_id": incident_id, + "data": {"status": "cancelled"}, + }, vis, cb, tid) + return True + + return False def _check_cancelled(self, incident_id: int): """Prüft ob Abbruch angefordert wurde und wirft CancelledError.""" diff --git a/src/static/css/style.css b/src/static/css/style.css index 1236995..7c5b4fe 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -1710,7 +1710,7 @@ a:hover { inset: 0; background: var(--backdrop); backdrop-filter: blur(4px); - z-index: 100; + z-index: 10000; align-items: center; justify-content: center; } diff --git a/src/static/js/app.js b/src/static/js/app.js index 37d8f42..8947bd5 100644 --- a/src/static/js/app.js +++ b/src/static/js/app.js @@ -2224,23 +2224,41 @@ async handleRefresh() { async cancelRefresh() { if (!this.currentIncidentId) return; - const ok = await confirmDialog('Laufende Recherche abbrechen?'); - if (!ok) return; + // Temporarily hide progress popup so confirm dialog is fully visible + const progressOverlay = document.getElementById('progress-overlay'); + if (progressOverlay) progressOverlay.style.display = 'none'; + + const ok = await confirmDialog('Laufende Recherche abbrechen?'); + + // Restore progress popup if not confirmed + if (!ok) { + const state = UI._progressState[this.currentIncidentId]; + if (state && progressOverlay) progressOverlay.style.display = 'flex'; + return; + } + + // Show cancelling state in popup + if (progressOverlay) progressOverlay.style.display = 'flex'; const btn = document.getElementById('progress-cancel-btn'); if (btn) { btn.textContent = 'Wird abgebrochen...'; btn.disabled = true; } + const titleEl = document.getElementById('progress-popup-title'); + if (titleEl) titleEl.textContent = 'Wird abgebrochen...'; try { - await API.cancelRefresh(this.currentIncidentId); + const result = await API.cancelRefresh(this.currentIncidentId); + if (!result) { + UI.showToast('Kein aktiver Refresh zum Abbrechen gefunden.', 'info'); + if (btn) { btn.textContent = 'Abbrechen'; btn.disabled = false; } + if (titleEl) titleEl.textContent = 'Aktualisierung l\u00e4uft'; + } } catch (err) { UI.showToast('Abbrechen fehlgeschlagen: ' + err.message, 'error'); - if (btn) { - btn.textContent = 'Abbrechen'; - btn.disabled = false; - } + if (btn) { btn.textContent = 'Abbrechen'; btn.disabled = false; } + if (titleEl) titleEl.textContent = 'Aktualisierung l\u00e4uft'; } },