Fix powder catalog lookup: exact match auto-fills, partials show picker modal

- CatalogLookup now returns all partial color name matches ranked by
  specificity (exact vendor+color first, same-vendor partial, cross-vendor)
  with isExact flag so JS can decide to auto-fill vs show modal
- Removed cross-vendor fallback that was silently overwriting manufacturer
  field with wrong brand when vendor-scoped search found nothing
- Picker modal now includes "Not listed — search online" option that
  triggers AI lookup as an escape hatch from the catalog results

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-14 14:18:52 -04:00
parent 2ad6df1195
commit 7e79a13cb1
2 changed files with 75 additions and 61 deletions
@@ -62,23 +62,25 @@
const items = await resp.json();
if (items.length === 0) {
// No catalog match — fall back to AI if available
hideStatus();
if (typeof window._runInventoryAiLookup === 'function') {
showStatus('info', '<span class="spinner-border spinner-border-sm me-1"></span>Not in catalog — searching with AI…');
await window._runInventoryAiLookup();
} else {
showStatus('warning', 'No match found in the catalog. Enter details manually or enable AI Lookup.');
}
// Nothing in catalog — go straight to AI
await runAiOrWarn();
return;
}
if (items.length === 1) {
// Single exact match (vendor + color name both match precisely) — auto-fill
if (items.length === 1 && items[0].isExact) {
await fillFields(items[0]);
return;
}
// Multiple matches — let the user pick via modal
// Exact match exists but so do other results — auto-fill the exact one
const exactMatches = items.filter(i => i.isExact);
if (exactMatches.length === 1) {
await fillFields(exactMatches[0]);
return;
}
// No exact match (or ambiguous) — show picker modal with "Not Listed" escape hatch
hideStatus();
showPickerModal(items);
@@ -89,6 +91,18 @@
}
});
// ── AI fallback helper ───────────────────────────────────────────────────
async function runAiOrWarn() {
hideStatus();
if (typeof window._runInventoryAiLookup === 'function') {
showStatus('info', '<span class="spinner-border spinner-border-sm me-1"></span>Not in catalog — searching online with AI…');
await window._runInventoryAiLookup();
} else {
showStatus('warning', 'No match found in the catalog. Enter details manually or enable AI Lookup.');
}
}
// ── Fill fields from a catalog result ────────────────────────────────────
async function fillFields(item) {
@@ -368,6 +382,12 @@
<div class="modal-body p-0">
<div class="list-group list-group-flush">${rows}</div>
</div>
<div class="modal-footer py-2 justify-content-start">
<button type="button" class="btn btn-sm btn-outline-secondary" id="catalogPickerNotListed">
<i class="bi bi-search me-1"></i>Not listed — search online
</button>
<span class="text-muted small ms-2">Uses AI to look up the exact product</span>
</div>
</div>
</div>
</div>`;
@@ -383,6 +403,11 @@
});
});
document.getElementById('catalogPickerNotListed').addEventListener('click', function () {
bsModal.hide();
runAiOrWarn();
});
bsModal.show();
}