diff --git a/src/static/css/style.css b/src/static/css/style.css index 034f8da..b245b0d 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -2082,6 +2082,22 @@ a:hover { align-items: center; } +#btn-enhance-description { + color: var(--accent-primary); + border-color: var(--accent-primary); + font-weight: 600; +} + +#btn-enhance-description:hover:not(:disabled) { + background: var(--accent-primary); + color: #fff; +} + +.textarea--loading { + opacity: 0.5; + cursor: wait; +} + .spinner-inline { display: inline-block; width: 14px; diff --git a/src/static/dashboard.html b/src/static/dashboard.html index d7348b5..62932d7 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -337,7 +337,7 @@
diff --git a/src/static/js/api.js b/src/static/js/api.js index 615fff0..07db045 100644 --- a/src/static/js/api.js +++ b/src/static/js/api.js @@ -12,10 +12,15 @@ const API = { }; }, - async _request(method, path, body = null) { + async _request(method, path, body = null, externalSignal = null) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 30000); + // Externen Abort weiterleiten an internen Controller + if (externalSignal) { + externalSignal.addEventListener('abort', () => controller.abort(), { once: true }); + } + const options = { method, headers: this._getHeaders(), @@ -70,8 +75,8 @@ const API = { return this._request('GET', `/incidents${query}`); }, - enhanceDescription(title, description, type) { - return this._request('POST', '/incidents/enhance-description', { title, description, type }); + enhanceDescription(title, description, type, signal = null) { + return this._request('POST', '/incidents/enhance-description', { title, description, type }, signal); }, createIncident(data) { diff --git a/src/static/js/app.js b/src/static/js/app.js index 8e4b49c..0acb5b1 100644 --- a/src/static/js/app.js +++ b/src/static/js/app.js @@ -1617,21 +1617,34 @@ async generateDescription() { const btn = document.getElementById('btn-enhance-description'); const btnText = document.getElementById('enhance-btn-text'); const spinner = document.getElementById('enhance-spinner'); + const textarea = document.getElementById('inc-description'); if (title.length < 3 || !btn) return; + + // Vorherigen Request abbrechen falls noch aktiv + if (this._enhanceController) this._enhanceController.abort(); + this._enhanceController = new AbortController(); + btn.disabled = true; btnText.style.display = 'none'; spinner.style.display = ''; + textarea.readOnly = true; + textarea.classList.add('textarea--loading'); try { - const result = await API.enhanceDescription(title, description || null, type); - document.getElementById('inc-description').value = result.description; + const result = await API.enhanceDescription(title, description || null, type, this._enhanceController.signal); + textarea.value = result.description; } catch (err) { - UI.showToast('Beschreibung konnte nicht generiert werden', 'error'); + if (err.name !== 'AbortError') { + UI.showToast('Beschreibung konnte nicht generiert werden', 'error'); + } } finally { btnText.style.display = ''; spinner.style.display = 'none'; btn.disabled = title.length < 3; + textarea.readOnly = false; + textarea.classList.remove('textarea--loading'); + this._enhanceController = null; } }, @@ -3029,6 +3042,13 @@ function openModal(id) { } function closeModal(id) { + // Laufenden Beschreibung-generieren-Request abbrechen + if (id === 'modal-new' && App._enhanceController) { + App._enhanceController.abort(); + App._enhanceController = null; + const ta = document.getElementById('inc-description'); + if (ta) { ta.readOnly = false; ta.classList.remove('textarea--loading'); } + } const modal = document.getElementById(id); releaseFocus(modal); modal.classList.remove('active');