fix: reset flow now fetches photo before deleting, shows preview + manual entry
- resetPhoto() now downloads the photo blob before calling the reset endpoint, then re-shows it in the detail view with preview and manual entry - resetAndReupload() same treatment, with fallback to server fetch - Added Reset button to ALL server results (not just duplicates) - Preview in Previous Photos cards with lightbox (prev-thumb + openLightbox)
This commit is contained in:
+60
-6
@@ -604,11 +604,37 @@ function renderPrevCard(p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function resetPhoto(photoId) {
|
async function resetPhoto(photoId) {
|
||||||
if (!confirm('Reset this entry? The photo will be deleted from the database and can be re-uploaded as new.')) return;
|
if (!confirm('Reset this entry and re-process from scratch?')) return;
|
||||||
|
|
||||||
|
// Find the card to get the original filename
|
||||||
|
const card = document.querySelector(`#reEng${photoId}`)?.closest('.prev-photo-card');
|
||||||
|
const filename = card?.querySelector('.prev-filename')?.textContent?.trim() || 'photo.jpg';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`/api/photos/${photoId}/reset`, { method: 'POST' });
|
// 1. Fetch the original file BEFORE reset deletes it
|
||||||
if (!resp.ok) throw new Error('Reset failed');
|
const fileResp = await fetch(`/api/photos/${photoId}/file`);
|
||||||
loadPreviousPhotos();
|
if (!fileResp.ok) throw new Error('Could not load photo file');
|
||||||
|
const blob = await fileResp.blob();
|
||||||
|
|
||||||
|
// 2. Reset in DB (deletes DB record + file from disk)
|
||||||
|
const resetResp = await fetch(`/api/photos/${photoId}/reset`, { method: 'POST' });
|
||||||
|
if (!resetResp.ok) throw new Error('Reset failed');
|
||||||
|
|
||||||
|
// 3. Rebuild a File object and scan it like a fresh upload
|
||||||
|
const file = new File([blob], filename, { type: blob.type || 'image/jpeg' });
|
||||||
|
const photoData = await scanPhoto(file);
|
||||||
|
photoData.file = file;
|
||||||
|
|
||||||
|
// 4. Clear current state and add to gallery
|
||||||
|
resetAll();
|
||||||
|
allPhotos = [photoData];
|
||||||
|
updateSummary();
|
||||||
|
renderGallery();
|
||||||
|
|
||||||
|
// 5. Show the detail view with preview image + manual entry
|
||||||
|
showDetail(0);
|
||||||
|
document.getElementById('serverSection').style.display = 'none';
|
||||||
|
document.getElementById('detail').scrollIntoView({ behavior: 'smooth' });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert('Reset failed: ' + e.message);
|
alert('Reset failed: ' + e.message);
|
||||||
}
|
}
|
||||||
@@ -618,9 +644,31 @@ async function resetAndReupload(photoId) {
|
|||||||
if (!photoId) { alert('No photo ID available'); return; }
|
if (!photoId) { alert('No photo ID available'); return; }
|
||||||
if (!confirm('Reset this entry and re-process as fresh?')) return;
|
if (!confirm('Reset this entry and re-process as fresh?')) return;
|
||||||
try {
|
try {
|
||||||
|
// Try to use local file first, fall back to fetching from server
|
||||||
|
let file = null;
|
||||||
|
if (selectedIdx >= 0 && allPhotos[selectedIdx]?.file) {
|
||||||
|
file = allPhotos[selectedIdx].file;
|
||||||
|
} else {
|
||||||
|
const fileResp = await fetch(`/api/photos/${photoId}/file`);
|
||||||
|
if (fileResp.ok) {
|
||||||
|
const blob = await fileResp.blob();
|
||||||
|
file = new File([blob], 'photo.jpg', { type: blob.type || 'image/jpeg' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!file) throw new Error('No photo file available');
|
||||||
|
|
||||||
await fetch(`/api/photos/${photoId}/reset`, { method: 'POST' });
|
await fetch(`/api/photos/${photoId}/reset`, { method: 'POST' });
|
||||||
// Re-trigger the upload
|
|
||||||
uploadSelected();
|
// Scan and show in detail view
|
||||||
|
const photoData = await scanPhoto(file);
|
||||||
|
photoData.file = file;
|
||||||
|
resetAll();
|
||||||
|
allPhotos = [photoData];
|
||||||
|
updateSummary();
|
||||||
|
renderGallery();
|
||||||
|
showDetail(0);
|
||||||
|
document.getElementById('serverSection').style.display = 'none';
|
||||||
|
document.getElementById('detail').scrollIntoView({ behavior: 'smooth' });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert('Reset failed: ' + e.message);
|
alert('Reset failed: ' + e.message);
|
||||||
}
|
}
|
||||||
@@ -905,6 +953,12 @@ async function uploadSelected() {
|
|||||||
'<div id="manualResult" style="margin-top:4px;"></div>' +
|
'<div id="manualResult" style="margin-top:4px;"></div>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
|
|
||||||
|
// Reset button — always available to start fresh
|
||||||
|
html += '<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--border);text-align:center;">' +
|
||||||
|
'<button class="btn btn-outline btn-xs" onclick="resetAndReupload(' + (data.photo_id || 'null') + ')" style="color:var(--amber);border-color:var(--amber);">🔄 Reset & Re-process from scratch</button>' +
|
||||||
|
'<div style="font-size:10px;color:var(--text3);margin-top:4px;">Clears DB entry and lets you re-analyze with manual entry</div>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
div.innerHTML = html;
|
div.innerHTML = html;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
div.innerHTML = '<div class="empty">❌ Upload failed: ' + esc(e.message) + '</div>';
|
div.innerHTML = '<div class="empty">❌ Upload failed: ' + esc(e.message) + '</div>';
|
||||||
|
|||||||
Reference in New Issue
Block a user