Fix dashboard review speed: DB-only decisions (0.03s vs 11.6s); add process-staging flow for moves
This commit is contained in:
@@ -40,6 +40,7 @@
|
||||
.reset { background: #444; color: #eee; }
|
||||
.none { color: #666; font-style: italic; padding: 2rem; text-align: center; }
|
||||
.selall { display: inline-flex; align-items: center; gap: .35rem; background: #222; border: 1px solid #444; border-radius: 6px; padding: .4rem .8rem; cursor: pointer; }
|
||||
.toast { position: fixed; bottom: 1.2rem; right: 1.2rem; background: #1d4; color: #031; padding: .7rem 1rem; border-radius: 8px; display: none; z-index: 50; font-size: .9rem; box-shadow: 0 2px 12px rgba(0,0,0,.5); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -70,6 +71,7 @@
|
||||
<button class="btn-reject" id="bulk-reject">🗑 Bulk Reject</button>
|
||||
<button class="btn-reset" id="bulk-reset">↺ Reset</button>
|
||||
</div>
|
||||
<div class="toast" id="toast"></div>
|
||||
<p class="count">{{ items|length }} images</p>
|
||||
<main>
|
||||
<div class="grid" id="review-grid">
|
||||
@@ -120,22 +122,56 @@
|
||||
async function bulk(action) {
|
||||
if (selected.size === 0) return;
|
||||
const shas = [...selected];
|
||||
const resp = await fetch('/review/bulk', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ action, shas })
|
||||
});
|
||||
const data = await resp.json();
|
||||
alert(`Bulk ${action}: ${data.moved} processed, ${data.missing} missing`);
|
||||
// refresh: remove done cards
|
||||
shas.forEach(s => {
|
||||
const card = document.querySelector(`[data-name="${s}"]`)?.closest('.card');
|
||||
if (card) card.remove();
|
||||
});
|
||||
selected.clear();
|
||||
refresh();
|
||||
location.reload();
|
||||
// disable buttons during the operation (cross-filesystem moves can be slow)
|
||||
setBulkBusy(true);
|
||||
try {
|
||||
const resp = await fetch('/review/bulk', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ action, shas })
|
||||
});
|
||||
const data = await resp.json();
|
||||
// remove processed cards from DOM immediately (match by sha in value attr)
|
||||
shas.forEach(s => {
|
||||
const cb = document.querySelector(`.item-check[value="${s}"]`);
|
||||
if (cb) cb.closest('.card')?.remove();
|
||||
});
|
||||
selected.clear();
|
||||
refresh();
|
||||
updateCount();
|
||||
showToast(`Bulk ${action}: ${data.moved} done, ${data.missing} missing`);
|
||||
} catch (e) {
|
||||
showToast('Bulk action failed: ' + e, true);
|
||||
} finally {
|
||||
setBulkBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
function setBulkBusy(busy) {
|
||||
['bulk-keep','bulk-reject','bulk-reset'].forEach(id => {
|
||||
const b = document.getElementById(id);
|
||||
if (b) { b.disabled = busy; b.textContent = busy ? 'Working…' : b.dataset.orig; }
|
||||
});
|
||||
}
|
||||
|
||||
function updateCount() {
|
||||
const el = document.querySelector('.count');
|
||||
const n = document.querySelectorAll('.card').length;
|
||||
if (el) el.textContent = n + ' images';
|
||||
}
|
||||
|
||||
function showToast(msg, isErr) {
|
||||
const t = document.getElementById('toast');
|
||||
t.textContent = msg;
|
||||
t.style.background = isErr ? '#d43' : '#1d4';
|
||||
t.style.color = isErr ? '#fff' : '#031';
|
||||
t.style.display = 'block';
|
||||
setTimeout(() => { t.style.display = 'none'; }, 3000);
|
||||
}
|
||||
|
||||
// stash original labels for busy-state restore
|
||||
document.querySelectorAll('.bulk-bar button').forEach(b => { b.dataset.orig = b.textContent; });
|
||||
|
||||
document.getElementById('bulk-keep').addEventListener('click', () => bulk('approve'));
|
||||
document.getElementById('bulk-reject').addEventListener('click', () => bulk('reject'));
|
||||
document.getElementById('bulk-reset').addEventListener('click', () => bulk('reset'));
|
||||
|
||||
Reference in New Issue
Block a user