diff --git a/static/index.html b/static/index.html
index dd6dc5f..4a0d5d7 100644
--- a/static/index.html
+++ b/static/index.html
@@ -278,6 +278,21 @@ if ('serviceWorker' in navigator) {
border-radius: var(--radius-sm); margin-bottom: 8px;
cursor: zoom-in; display: block; background: var(--card2);
}
+ /* Inline machine ID editing in Previous Photos */
+ .prev-mid-editable {
+ cursor: text; border-bottom: 1px dashed var(--text3);
+ transition: border-color 0.15s, color 0.15s;
+ padding: 0 2px;
+ }
+ .prev-mid-editable:hover { border-color: var(--accent); color: var(--text); }
+ .prev-mid-editable::after { content: ' ✏️'; font-size: 9px; opacity: 0.5; }
+ .prev-mid-editable:hover::after { opacity: 1; }
+ .prev-mid-input {
+ background: var(--card2); color: var(--text); border: 1px solid var(--accent);
+ border-radius: 6px; padding: 2px 6px; font-size: 11px; font-weight: 700;
+ width: 100px; outline: none;
+ }
+ .prev-mid-input:focus { border-color: var(--accent2); box-shadow: 0 0 0 2px rgba(59,130,246,0.3); }
.export-modal-backdrop { position: fixed; inset: 0; z-index: 999; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; }
.export-modal { background: var(--card); border-radius: var(--radius); padding: 20px; max-width: 320px; width: 90%; text-align: center; }
@@ -568,7 +583,7 @@ function renderPrevCard(p) {
${time}
- Machine: ${p.machine_id ? esc(p.machine_id) : '—'}
+ Machine: ${esc(p.machine_id || '—')}
${esc(matchText)}
${esc(engine)}${p.ocr_model ? ' ' + esc(p.ocr_model) : ''}
${colorHtml}
@@ -603,6 +618,92 @@ function renderPrevCard(p) {
`;
}
+/* ── Inline machine ID editing in Previous Photos ── */
+function editMachineIdInline(photoId) {
+ const span = document.getElementById('midSpan' + photoId);
+ if (!span) return;
+ const currentVal = span.dataset.mid || '';
+ const input = document.createElement('input');
+ input.type = 'text';
+ input.className = 'prev-mid-input';
+ input.value = currentVal;
+ input.placeholder = 'Enter machine ID';
+ input.dataset.photoId = photoId;
+ input.dataset.original = currentVal;
+ span.replaceWith(input);
+ input.focus();
+ input.select();
+ input.addEventListener('blur', () => saveMachineIdInline(input, photoId));
+ input.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter') input.blur();
+ if (e.key === 'Escape') { saveMachineIdInline(input, photoId, true); }
+ });
+}
+
+function saveMachineIdInline(input, photoId, cancel) {
+ const orig = input.dataset.original || '';
+ const val = cancel ? orig : input.value.trim();
+ // Restore span first so UI is consistent
+ function restoreSpan(mid, label) {
+ const span = document.createElement('span');
+ span.id = 'midSpan' + photoId;
+ span.className = 'prev-mid-editable';
+ span.dataset.mid = mid || '';
+ span.textContent = label || mid || '—';
+ span.onclick = () => editMachineIdInline(photoId);
+ input.replaceWith(span);
+ return span;
+ }
+ if (cancel || val === orig) {
+ restoreSpan(orig, orig || '—');
+ return;
+ }
+ fetch('/api/assign-machine-id', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ photo_id: photoId, machine_id: val })
+ })
+ .then(r => r.json())
+ .then(data => {
+ restoreSpan(val, val);
+ // Update the badge
+ const card = document.getElementById('midSpan' + photoId)?.closest('.prev-photo-card');
+ if (card) {
+ const badge = card.querySelector('.match-badge');
+ if (badge) {
+ badge.textContent = val;
+ badge.className = 'match-badge matched';
+ }
+ }
+ // Update asset info
+ const assetEl = document.getElementById('prevAsset' + photoId);
+ const infoEl = document.getElementById('prevMachineInfo' + photoId);
+ if (data.asset) {
+ const a = data.asset;
+ if (assetEl) {
+ assetEl.innerHTML = '🆔 ' + esc(a.machine_id) + ' · ' + esc(a.name) + '' +
+ (a.category ? ' · 📦 ' + esc(a.category) : '') +
+ (a.status === 'active' ? ' 🟢 Active' : '');
+ }
+ if (infoEl) {
+ const parts = [a.location_name, a.building, a.floor, a.zone].filter(Boolean);
+ infoEl.innerHTML = parts.join(' · ') || '(No location details)';
+ }
+ } else {
+ if (assetEl) assetEl.innerHTML = '⚠️ No asset match for ' + esc(val) + '';
+ }
+ })
+ .catch(() => {
+ restoreSpan(orig, orig || '—');
+ // Show error toast
+ const toast = document.createElement('div');
+ toast.style.cssText = 'position:fixed;bottom:60px;left:50%;transform:translateX(-50%);background:var(--red);color:#fff;padding:8px 16px;border-radius:8px;font-size:13px;z-index:9999;animation:fadeIn 0.2s;';
+ toast.textContent = 'Failed to save machine ID';
+ document.body.appendChild(toast);
+ setTimeout(() => toast.remove(), 2000);
+ });
+}
+
async function resetPhoto(photoId) {
if (!confirm('Reset this entry and re-process from scratch?')) return;