fix: Beschreibung generieren — AbortController, Readonly, Gold-Styling

- Textarea readonly + visuell gedimmt während Generierung läuft
- AbortController bricht Request ab wenn Modal geschlossen wird
- Stern-Icon entfernt, Button-Text in Gold (Akzentfarbe)
- api.js _request() unterstützt externen AbortSignal
Dieser Commit ist enthalten in:
Claude Dev
2026-03-27 23:46:44 +01:00
Ursprung a84e2c108e
Commit 11d0aadc57
4 geänderte Dateien mit 48 neuen und 7 gelöschten Zeilen

Datei anzeigen

@@ -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) {

Datei anzeigen

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