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:
@@ -2082,6 +2082,22 @@ a:hover {
|
|||||||
align-items: center;
|
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 {
|
.spinner-inline {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
|
|||||||
@@ -337,7 +337,7 @@
|
|||||||
<textarea id="inc-description" placeholder="Weitere Details zum Vorfall (optional)"></textarea>
|
<textarea id="inc-description" placeholder="Weitere Details zum Vorfall (optional)"></textarea>
|
||||||
<div class="description-enhance-row">
|
<div class="description-enhance-row">
|
||||||
<button type="button" class="btn btn-secondary btn-small" id="btn-enhance-description" onclick="App.generateDescription()" disabled>
|
<button type="button" class="btn btn-secondary btn-small" id="btn-enhance-description" onclick="App.generateDescription()" disabled>
|
||||||
<span id="enhance-btn-text">✦ Beschreibung generieren</span>
|
<span id="enhance-btn-text">Beschreibung generieren</span>
|
||||||
<span id="enhance-spinner" class="spinner-inline" style="display:none;"></span>
|
<span id="enhance-spinner" class="spinner-inline" style="display:none;"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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 controller = new AbortController();
|
||||||
const timeout = setTimeout(() => controller.abort(), 30000);
|
const timeout = setTimeout(() => controller.abort(), 30000);
|
||||||
|
|
||||||
|
// Externen Abort weiterleiten an internen Controller
|
||||||
|
if (externalSignal) {
|
||||||
|
externalSignal.addEventListener('abort', () => controller.abort(), { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
method,
|
method,
|
||||||
headers: this._getHeaders(),
|
headers: this._getHeaders(),
|
||||||
@@ -70,8 +75,8 @@ const API = {
|
|||||||
return this._request('GET', `/incidents${query}`);
|
return this._request('GET', `/incidents${query}`);
|
||||||
},
|
},
|
||||||
|
|
||||||
enhanceDescription(title, description, type) {
|
enhanceDescription(title, description, type, signal = null) {
|
||||||
return this._request('POST', '/incidents/enhance-description', { title, description, type });
|
return this._request('POST', '/incidents/enhance-description', { title, description, type }, signal);
|
||||||
},
|
},
|
||||||
|
|
||||||
createIncident(data) {
|
createIncident(data) {
|
||||||
|
|||||||
@@ -1617,21 +1617,34 @@ async generateDescription() {
|
|||||||
const btn = document.getElementById('btn-enhance-description');
|
const btn = document.getElementById('btn-enhance-description');
|
||||||
const btnText = document.getElementById('enhance-btn-text');
|
const btnText = document.getElementById('enhance-btn-text');
|
||||||
const spinner = document.getElementById('enhance-spinner');
|
const spinner = document.getElementById('enhance-spinner');
|
||||||
|
const textarea = document.getElementById('inc-description');
|
||||||
|
|
||||||
if (title.length < 3 || !btn) return;
|
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;
|
btn.disabled = true;
|
||||||
btnText.style.display = 'none';
|
btnText.style.display = 'none';
|
||||||
spinner.style.display = '';
|
spinner.style.display = '';
|
||||||
|
textarea.readOnly = true;
|
||||||
|
textarea.classList.add('textarea--loading');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await API.enhanceDescription(title, description || null, type);
|
const result = await API.enhanceDescription(title, description || null, type, this._enhanceController.signal);
|
||||||
document.getElementById('inc-description').value = result.description;
|
textarea.value = result.description;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
UI.showToast('Beschreibung konnte nicht generiert werden', 'error');
|
if (err.name !== 'AbortError') {
|
||||||
|
UI.showToast('Beschreibung konnte nicht generiert werden', 'error');
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
btnText.style.display = '';
|
btnText.style.display = '';
|
||||||
spinner.style.display = 'none';
|
spinner.style.display = 'none';
|
||||||
btn.disabled = title.length < 3;
|
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) {
|
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);
|
const modal = document.getElementById(id);
|
||||||
releaseFocus(modal);
|
releaseFocus(modal);
|
||||||
modal.classList.remove('active');
|
modal.classList.remove('active');
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren