← back to Dw Photo Capture
Look Up SKU pill: camera-first pure-identify flow — shoot FRONT pattern → auto-prompts/opens BACK label (the text/SKU) → runs identify-multi (visual+OCR+code fusion) and shows the SKU. Skips the sample check-in (that's the separate Check Sample In pill)
e56063d5a4fc508009b8aad57553d80028a8c42a · 2026-07-07 13:18:26 -0700 · Steve Abrams
Files touched
Diff
commit e56063d5a4fc508009b8aad57553d80028a8c42a
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jul 7 13:18:26 2026 -0700
Look Up SKU pill: camera-first pure-identify flow — shoot FRONT pattern → auto-prompts/opens BACK label (the text/SKU) → runs identify-multi (visual+OCR+code fusion) and shows the SKU. Skips the sample check-in (that's the separate Check Sample In pill)
---
public/index.html | 24 +++++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/public/index.html b/public/index.html
index f7c6588..67e1716 100644
--- a/public/index.html
+++ b/public/index.html
@@ -149,6 +149,7 @@
.fb-tiles{display:flex;gap:12px;margin-bottom:14px}
.fb-tile{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px;background:#0f0e0b;border:1px dashed var(--line);border-radius:14px;padding:12px 6px;color:#b7ad98;font-size:13px;cursor:pointer}
.fb-tile.set{border-style:solid;border-color:#2b7a3f;color:#fff}
+ .fb-tile.need{border-style:solid;border-color:#b8902f;color:#fff;box-shadow:0 0 0 2px rgba(184,144,47,.35);animation:micpulse 1.1s infinite}
.fb-thumb{width:100%;aspect-ratio:1/1;border-radius:10px;background:#000 center/cover no-repeat;display:flex;align-items:center;justify-content:center;font-size:30px}
#fbResult{margin-top:12px}
.fb-hit{display:flex;gap:10px;align-items:center;padding:10px;border:1px solid var(--line);border-radius:12px;margin-top:8px}
@@ -254,6 +255,7 @@
<div class="home-title">DW Sample Station</div>
<button class="home-pill" data-act="add">Add New SKU</button>
<button class="home-pill" data-act="update">Update SKU</button>
+ <button class="home-pill" data-act="lookup">Look Up SKU</button>
<button class="home-pill" data-act="check">Check Sample In</button>
<button class="home-skip" data-act="skip">skip →</button>
</div>
@@ -1438,7 +1440,7 @@ $('#sampPrintSku').addEventListener('click',()=>sampPrint('sku'));
$('#sampPrintAddr').addEventListener('click',()=>sampPrint('address'));
// ── Front + Back identify: fuse back-label code + vendor + QR (client-decoded) with front visual ──
-let _fbBack=null,_fbFront=null,_fbQR=null,_jsqr=null;
+let _fbBack=null,_fbFront=null,_fbQR=null,_jsqr=null,_fbLookup=false;
function loadJsQR(){ if(_jsqr) return Promise.resolve(_jsqr); return new Promise((res)=>{ const s=document.createElement('script'); s.src='https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.js'; s.onload=()=>{_jsqr=window.jsQR;res(_jsqr);}; s.onerror=()=>res(null); document.head.appendChild(s); }); }
async function decodeQR(dataUrl){ const jsQR=await loadJsQR(); if(!jsQR) return null;
return new Promise(r=>{ const im=new Image(); im.onload=()=>{ try{ const c=document.createElement('canvas'); c.width=im.naturalWidth; c.height=im.naturalHeight; const x=c.getContext('2d'); x.drawImage(im,0,0); const d=x.getImageData(0,0,c.width,c.height); const q=jsQR(d.data,c.width,c.height); r(q?q.data:null);}catch(e){r(null);} }; im.onerror=()=>r(null); im.src=dataUrl; }); }
@@ -1448,6 +1450,12 @@ async function fbRead(file,isBack){ if(!file)return;
$('#fbNote').textContent='Reading label + QR…'; _fbQR=await decodeQR(url).catch(()=>null); $('#fbNote').textContent=_fbQR?'🔗 QR code read':''; }
else { _fbFront=url; const t=$('#fbFrontThumb'); t.style.backgroundImage=`url('${url}')`; t.textContent=''; $('#fbFrontTile').classList.add('set'); }
$('#fbGo').disabled=!(_fbBack||_fbFront);
+ // LOOK-UP SKU flow: front first → prompt/open the BACK (label) → then auto-identify
+ if(_fbLookup){
+ if(!isBack && !_fbBack){ $('#fbFrontTile').classList.remove('need'); $('#fbNote').innerHTML='✓ Front captured — <b>now shoot the BACK / label</b>'; $('#fbBackTile').classList.add('need');
+ try{ $('#fbBackInput').click(); }catch(e){} } // auto-open back cam (falls back to the highlighted tile if iOS blocks)
+ else if(isBack){ $('#fbBackTile').classList.remove('need'); fbIdentify(); } // both in → look it up
+ }
}
function fbVisualHtml(vis){ if(!vis||!vis.length) return ''; return '<div class="samp-meta" style="margin-top:12px">Visual matches:</div>'+vis.slice(0,4).map(v=>`<div class="fb-hit">${v.image?`<img src="${v.image}">`:''}<div><b>${v.pattern||v.dw_sku||''}</b> <small>${Math.round((v.score||0)*100)}%</small><br><small>${[v.vendor,v.dw_sku].filter(Boolean).join(' · ')}</small></div></div>`).join(''); }
async function fbIdentify(){
@@ -1459,11 +1467,20 @@ async function fbIdentify(){
const via=r.source==='qr'?'🔗 QR':r.source==='code'?'📄 label code':'🎨 visual';
$('#fbNote').innerHTML=`Source: ${via}${r.corroborated?' · ✓ front+back agree':''}`;
$('#fbResult').innerHTML=`<div class="fb-hit">${p.image?`<img src="${p.image}">`:''}<div><b>${p.pattern||p.mfr_sku||p.dw_sku||'—'}</b><span class="fb-conf ${conf}">${r.confidence}</span><br><small>${[p.vendor,p.dw_sku,p.mfr_code].filter(Boolean).join(' · ')}</small></div></div>`+fbVisualHtml(r.visual);
- checkSampleRequest(p.dw_sku||p.mfr_code, p.mfr_code); // → who ordered this sample (opens the print popup)
+ if(!_fbLookup) checkSampleRequest(p.dw_sku||p.mfr_code, p.mfr_code); // Check-Sample-In only → print popup; pure Look-Up SKU skips it
}catch(e){ $('#fbNote').textContent='Failed: '+e.message; }
$('#fbGo').disabled=false;
}
-function openFbModal(){ _fbBack=_fbFront=_fbQR=null; $('#fbBackThumb').style.backgroundImage=''; $('#fbBackThumb').textContent='📄'; $('#fbFrontThumb').style.backgroundImage=''; $('#fbFrontThumb').textContent='🎨'; $('#fbBackTile').classList.remove('set'); $('#fbFrontTile').classList.remove('set'); $('#fbGo').disabled=true; $('#fbNote').textContent=''; $('#fbResult').innerHTML=''; $('#fbModal').hidden=false; document.body.style.overflow='hidden'; }
+function openFbModal(){ _fbBack=_fbFront=_fbQR=null; _fbLookup=false; $('#fbBackThumb').style.backgroundImage=''; $('#fbBackThumb').textContent='📄'; $('#fbFrontThumb').style.backgroundImage=''; $('#fbFrontThumb').textContent='🎨'; $('#fbBackTile').classList.remove('set','need'); $('#fbFrontTile').classList.remove('set','need'); $('#fbGo').disabled=true; $('#fbNote').textContent=''; $('#fbResult').innerHTML='';
+ $('#fbModal').querySelector('.samp-pat').textContent='Identify a sample';
+ $('#fbModal').querySelector('.samp-meta').textContent='Photograph the BACK label, the FRONT pattern, or both — best ID uses both.';
+ $('#fbModal').hidden=false; document.body.style.overflow='hidden'; }
+// LOOK UP SKU: camera-first — shoot the FRONT pattern, then it prompts/opens the BACK label, then IDs.
+function openLookup(){ openFbModal(); _fbLookup=true;
+ $('#fbModal').querySelector('.samp-pat').textContent='Look up SKU';
+ $('#fbModal').querySelector('.samp-meta').textContent='Shoot the FRONT pattern first — then the BACK label (the text/SKU) — and it identifies the item.';
+ $('#fbFrontTile').classList.add('need');
+ setTimeout(()=>{ try{ $('#fbFrontInput').click(); }catch(e){} }, 250); } // camera on, front first
$('#fbBtn').addEventListener('click',openFbModal);
$('#fbClose').addEventListener('click',()=>{ $('#fbModal').hidden=true; document.body.style.overflow=''; });
$('#fbBackTile').addEventListener('click',()=>$('#fbBackInput').click());
@@ -1739,6 +1756,7 @@ function routeHome(act){ $('#homeScreen').hidden=true;
// onload → straight to the camera; details show after the first scan
if(act==='add') openAddModal(null,{mode:'add',camera:true});
else if(act==='update') openAddModal(null,{mode:'update',camera:true});
+ else if(act==='lookup') openLookup(); // camera-first front→back → identify
else if(act==='check') openFbModal();
// 'skip' → just show the catalog
}
← 27b572c Fix aug-training crash (cannot write empty image): guard aug
·
back to Dw Photo Capture
·
auto-save: 2026-07-07T13:30:45 (1 files) — visual-search/tra 2acc59c →