fix(admin-ui): improve D&D confirm UX – spinner, success/error feedback, auto-reset
This commit is contained in:
parent
e88e2d04c0
commit
506374f35b
1 changed files with 41 additions and 9 deletions
|
|
@ -890,8 +890,9 @@
|
||||||
card.innerHTML = `
|
card.innerHTML = `
|
||||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;">
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;">
|
||||||
<strong style="color:#E8D5B0;">${item.fileFormat.toUpperCase()} – ${formatBytes(item.fileSize)}</strong>
|
<strong style="color:#E8D5B0;">${item.fileFormat.toUpperCase()} – ${formatBytes(item.fileSize)}</strong>
|
||||||
<button class="primary" onclick="confirmAnalyzedResource(${idx})">✓ Übernehmen</button>
|
<button class="primary rv-confirm-btn" onclick="confirmAnalyzedResource(${idx})">✓ Übernehmen</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="rv-status" style="display:none; padding:8px 12px; margin-bottom:10px; border-radius:3px; background:#242119; font-size:.9rem;"></div>
|
||||||
<div style="display:grid; grid-template-columns:1fr 1fr; gap:10px;">
|
<div style="display:grid; grid-template-columns:1fr 1fr; gap:10px;">
|
||||||
<div><label>Titel *</label><input class="rv-title" value="${escHtml(item.title)}" style="width:100%"></div>
|
<div><label>Titel *</label><input class="rv-title" value="${escHtml(item.title)}" style="width:100%"></div>
|
||||||
<div><label>Autor</label><input class="rv-author" value="${escHtml(item.author || '')}" style="width:100%"></div>
|
<div><label>Autor</label><input class="rv-author" value="${escHtml(item.author || '')}" style="width:100%"></div>
|
||||||
|
|
@ -943,6 +944,15 @@
|
||||||
const card = cards[idx];
|
const card = cards[idx];
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
|
|
||||||
|
const btn = card.querySelector('.rv-confirm-btn');
|
||||||
|
const statusEl = card.querySelector('.rv-status');
|
||||||
|
|
||||||
|
// Show spinner
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.textContent = '⏳ Wird gespeichert…';
|
||||||
|
statusEl.innerHTML = '';
|
||||||
|
statusEl.style.display = 'none';
|
||||||
|
|
||||||
const item = JSON.parse(card.dataset.item);
|
const item = JSON.parse(card.dataset.item);
|
||||||
const payload = {
|
const payload = {
|
||||||
filename: item.filename,
|
filename: item.filename,
|
||||||
|
|
@ -965,28 +975,50 @@
|
||||||
body: JSON.stringify(payload)
|
body: JSON.stringify(payload)
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
card.style.opacity = '0.4';
|
const created = await res.json();
|
||||||
card.querySelector('button').disabled = true;
|
card.style.opacity = '0.5';
|
||||||
card.querySelector('button').textContent = '✓ Gespeichert';
|
btn.textContent = '✓ Gespeichert';
|
||||||
|
statusEl.style.display = 'block';
|
||||||
|
statusEl.style.color = '#7CBA3C';
|
||||||
|
statusEl.innerHTML = `✓ Ressource angelegt:<br><strong>${escHtml(created.title)}</strong><br><code style="font-size:.75rem;color:#A89070">${escHtml(created.guid)}</code>`;
|
||||||
|
// Refresh list + reset D&D after 2s
|
||||||
|
resourcesLoaded = false;
|
||||||
|
loadResources();
|
||||||
|
cachedTags = null; // refresh tags
|
||||||
|
setTimeout(() => resetDndZone(), 2000);
|
||||||
} else {
|
} else {
|
||||||
const body = await res.json().catch(() => ({}));
|
const body = await res.json().catch(() => ({}));
|
||||||
alert('Fehler: ' + (body.message || res.status));
|
btn.textContent = '✓ Übernehmen';
|
||||||
|
btn.disabled = false;
|
||||||
|
statusEl.style.display = 'block';
|
||||||
|
statusEl.style.color = '#D95A20';
|
||||||
|
statusEl.innerHTML = `Fehler: ${escHtml(body.message || 'HTTP ' + res.status)}<br><button class="secondary" style="margin-top:6px" onclick="resetDndZone()">↻ Retry</button>`;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert('Verbindungsfehler: ' + e.message);
|
btn.textContent = '✓ Übernehmen';
|
||||||
|
btn.disabled = false;
|
||||||
|
statusEl.style.display = 'block';
|
||||||
|
statusEl.style.color = '#D95A20';
|
||||||
|
statusEl.innerHTML = `Verbindungsfehler: ${escHtml(e.message)}<br><button class="secondary" style="margin-top:6px" onclick="resetDndZone()">↻ Retry</button>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetDndZone() {
|
||||||
|
const review = document.getElementById('dnd-review');
|
||||||
|
review.style.display = 'none';
|
||||||
|
review.innerHTML = '';
|
||||||
|
document.getElementById('dnd-area').style.display = 'block';
|
||||||
|
document.getElementById('dnd-zone').style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
async function confirmAllAnalyzed() {
|
async function confirmAllAnalyzed() {
|
||||||
const review = document.getElementById('dnd-review');
|
const review = document.getElementById('dnd-review');
|
||||||
const cards = review.querySelectorAll('[data-item]');
|
const cards = review.querySelectorAll('[data-item]');
|
||||||
for (let i = 0; i < cards.length; i++) {
|
for (let i = 0; i < cards.length; i++) {
|
||||||
if (cards[i].style.opacity !== '0.4') {
|
if (cards[i].style.opacity !== '0.5') {
|
||||||
await confirmAnalyzedResource(i);
|
await confirmAnalyzedResource(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resourcesLoaded = false;
|
|
||||||
loadResources();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function escHtml(str) {
|
function escHtml(str) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue