From 04ac2fd7c7338eebe25ccefd8cdcd7e55b9e73b3 Mon Sep 17 00:00:00 2001 From: Shawn Date: Mon, 25 May 2026 20:35:41 -0400 Subject: [PATCH] fix: show manual entry when OCR digits found but lookup fails - Always show manual entry field regardless of OCR result or GPS presence - Context-sensitive label: different text when OCR found digits vs none - Add Assign button to manual lookup result (POST /api/assign-machine-id) - Add Push GPS button when GPS available after assign - Store _lastPhotoId / _lastPhotoGps for manual entry functions - Add lightbox: full-screen photo viewer with pinch/scroll/double-tap zoom --- static/index.html | 285 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 273 insertions(+), 12 deletions(-) diff --git a/static/index.html b/static/index.html index 218f2dd..20f257f 100644 --- a/static/index.html +++ b/static/index.html @@ -302,6 +302,41 @@ if ('serviceWorker' in navigator) { .walk-timeline { font-size: 10px; color: var(--text2); margin-top: 4px; max-height: 120px; overflow-y: auto; } .walk-timeline div { padding: 1px 0; } + /* Lightbox โ€” full-screen photo viewer with zoom */ + #lightbox { + display: none; position: fixed; inset: 0; z-index: 99999; + background: rgba(0,0,0,0.95); + touch-action: none; user-select: none; -webkit-user-select: none; + } + #lightbox.active { display: flex; align-items: center; justify-content: center; } + #lightboxClose { + position: fixed; top: 12px; right: 12px; z-index: 100000; + width: 36px; height: 36px; border-radius: 50%; + background: rgba(255,255,255,0.15); border: none; + color: #fff; font-size: 20px; cursor: pointer; + display: flex; align-items: center; justify-content: center; + -webkit-tap-highlight-color: transparent; + } + #lightboxClose:active { background: rgba(255,255,255,0.3); } + #lightboxContainer { + width: 100%; height: 100%; + display: flex; align-items: center; justify-content: center; + overflow: hidden; position: relative; + } + #lightboxImage { + max-width: 100%; max-height: 100%; + object-fit: contain; cursor: zoom-in; + transition: none; + will-change: transform; + } + #lightboxImage.zoomed { cursor: grab; } + #lightboxImage.zoomed:active { cursor: grabbing; } + #lightboxZoomHint { + position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); + color: rgba(255,255,255,0.4); font-size: 11px; + pointer-events: none; transition: opacity 0.5s; + } + @@ -359,7 +394,7 @@ if ('serviceWorker' in navigator) {
- +
@@ -433,6 +468,8 @@ let selectedIdx = -1; let currentFilter = 'all'; let bulkMap = null; let bulkData = []; +let _lastPhotoId = null; // stored by uploadSelected for manualLookup assign +let _lastPhotoGps = null; // {lat, lng} for manualLookup push // On load: fetch previously processed photos document.addEventListener('DOMContentLoaded', loadPreviousPhotos); @@ -843,17 +880,23 @@ async function uploadSelected() { '' + '
'; - // Manual entry when GPS exists but OCR failed - if ((!ocr.match_5dash6 && !ocr.match_5plus) && data.exif && data.exif.gps) { - html += '
' + - '
โœ๏ธ GPS found but no machine ID in OCR. Enter it manually:
' + - '
' + - '' + - '' + - '
' + - '
' + - '
'; - } + // Store for manualLookup assign + _lastPhotoId = data.photo_id; + _lastPhotoGps = data.exif && data.exif.gps ? {lat: data.exif.gps.lat, lng: data.exif.gps.lng} : null; + + // Always show manual entry field โ€” user can correct OCR or enter ID manually + html += '
' + + '
' + + (ocr.match_5dash6 || ocr.match_5plus + ? 'โœ๏ธ Try a different ID if the match above is wrong:' + : 'โœ๏ธ No machine ID found in OCR. Enter it manually:') + + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '
'; div.innerHTML = html; } catch (e) { @@ -876,6 +919,12 @@ function manualLookup() { '
' + esc(a.name) + '
' + '
๐Ÿ†” ' + esc(a.machine_id) + '๐Ÿ“ฆ ' + esc(a.category) + '' + (a.status === 'active' ? '๐ŸŸข Active' : 'โšช ' + esc(a.status)) + + '
' + + '
' + + '' + + (_lastPhotoGps + ? '' + : '') + '
'; } else { resultDiv.innerHTML = 'โš ๏ธ No asset found for ' + esc(val) + ''; @@ -886,6 +935,54 @@ function manualLookup() { }); } +// Assign a machine ID to the last analyzed photo (manual entry) +async function assignManualId(machineId) { + if (!_lastPhotoId || !machineId) { alert('No photo loaded'); return; } + const resultDiv = document.getElementById('manualResult'); + try { + const r = await fetch('/api/assign-machine-id', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({photo_id: _lastPhotoId, machine_id: machineId}) + }); + const d = await r.json(); + if (d.asset) { + let extra = ''; + if (d.needs_gps && _lastPhotoGps) { + extra = ''; + } + resultDiv.innerHTML = '
โœ… Assigned: ' + esc(d.asset.name) + ' ๐Ÿ†” ' + esc(d.asset.machine_id) + '
' + extra + + '
Saved to photo #' + _lastPhotoId + '
'; + } else { + resultDiv.innerHTML = 'โš ๏ธ Could not assign: ' + esc(d.reason || 'unknown error') + ''; + } + } catch (e) { + resultDiv.innerHTML = 'โŒ Error: ' + esc(e.message) + ''; + } +} + +// Push GPS from the last analyzed photo to a canteen asset +async function pushGpsManual(assetId) { + if (!_lastPhotoGps || !assetId) { alert('No GPS data available'); return; } + const resultDiv = document.getElementById('manualResult'); + try { + const r = await fetch('/api/push-gps', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({asset_id: assetId, latitude: _lastPhotoGps.lat, longitude: _lastPhotoGps.lng}) + }); + const d = await r.json(); + if (d.updated) { + resultDiv.innerHTML = '
โœ… GPS pushed successfully! ๐Ÿ“ ' + Number(_lastPhotoGps.lat).toFixed(5) + ', ' + Number(_lastPhotoGps.lng).toFixed(5) + '
' + + '
Canteen DB updated
'; + } else { + resultDiv.innerHTML = 'โš ๏ธ Push failed: ' + esc(d.reason || 'unknown') + ''; + } + } catch (e) { + resultDiv.innerHTML = 'โŒ Error: ' + esc(e.message) + ''; + } +} + async function reprocessCurrent() { const eng = document.getElementById('reRunEng').value; const model = document.getElementById('reRunModel').value; @@ -1650,5 +1747,169 @@ if (navigator.serviceWorker) { // Initialize banner on load setTimeout(updateOfflineBanner, 500); + + +