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');