[object Object]

← back to Mfr Review Viewer Corruption

viewer: prominent 'Suggested real mfr → Approve into field' banner (from own-SKU record) + grey/disable foreign records so they can't be picked or deleted

ac960498c99411f1c7db35fe9b658813ba50e605 · 2026-08-27 11:14:51 -0700 · Steve Abrams

Files touched

Diff

commit ac960498c99411f1c7db35fe9b658813ba50e605
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Aug 27 11:14:51 2026 -0700

    viewer: prominent 'Suggested real mfr → Approve into field' banner (from own-SKU record) + grey/disable foreign records so they can't be picked or deleted
---
 public/index.html | 43 +++++++++++++++++++++++++++++++++++--------
 1 file changed, 35 insertions(+), 8 deletions(-)

diff --git a/public/index.html b/public/index.html
index 8e35304..2a14a0c 100644
--- a/public/index.html
+++ b/public/index.html
@@ -135,6 +135,16 @@
   .remove-sug-btn { font-size:10px; padding:2px 8px; border-radius:6px; cursor:pointer; font-weight:700;
     background:#3a1a1f; color:#ff9aa6; border:1px solid #6b2b33; margin-right:6px; }
   .remove-sug-btn:hover { background:#5a2028; }
+  .mfr-suggest { margin:4px 0 6px; padding:6px 9px; background:rgba(34,197,94,.10); border:1px solid var(--green);
+    border-radius:7px; font-size:12px; display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
+  .mfr-suggest-code { color:var(--green); font-weight:700; font-family:ui-monospace,monospace; font-size:13px; }
+  .approve-mfr-btn { margin-left:auto; font-size:11px; padding:3px 10px; border-radius:6px; cursor:pointer;
+    background:var(--green); color:#04120f; border:none; font-weight:700; }
+  .approve-mfr-btn:hover { filter:brightness(1.1); }
+  .cand-row.foreign { opacity:.5; background:#141414; cursor:default; }
+  .cand-row.foreign:hover { border-color:var(--line); }
+  .foreign-badge { font-size:10px; padding:1px 6px; border-radius:6px; background:#2a2320; color:#d9b48a; font-weight:600; }
+  .del-wrap.del-disabled { color:var(--muted); opacity:.4; align-self:center; }
   .cand-row .del-wrap { display:flex; align-items:center; gap:3px; margin-left:auto; }
   .cand-row .del-chk { transform:scale(1.05); cursor:pointer; accent-color:var(--red); }
   .cand-row .del-lbl { font-size:9px; color:var(--red); text-transform:uppercase; letter-spacing:.03em; }
@@ -490,6 +500,13 @@ function renderCandidates(sku, panel){
     h += `<button class="keep-del-btn" data-keepdel="${esc(sku)}" ${keepRid?'':'disabled'} title="Mark every OTHER FmPro record of this SKU for deletion (keeps the one you chose)">🗑 Keep selected · delete the other ${otherCount}</button>`;
   }
   h += `</div>`;
+  const sug = j.suggestion||{};
+  if(sug.realMfr){
+    h += `<div class="mfr-suggest" title="The correct real mfr per FileMaker's OWN record for this SKU (not a foreign record that merely shares the number)">`+
+      `✅ Suggested real mfr: <span class="mfr-suggest-code">${esc(sug.realMfr)}</span>`+
+      (sug.keepRid?` <span class="cand-meta">(from FmPro #${esc(sug.keepRid)})</span>`:'')+
+      ` <button class="approve-mfr-btn" data-approvemfr="${esc(sku)}" data-mfr="${esc(sug.realMfr)}">Approve into field ↑</button></div>`;
+  }
   if(!fm.length){
     h += `<div class="cand-meta">no FileMaker master found`+(j.fmError?` — <span class="cand-err">${esc(j.fmError)}</span>`:'')+`</div>`;
   } else {
@@ -502,13 +519,15 @@ function renderCandidates(sku, panel){
       const rid = String(c.recordId);
       const isKeep = keepRid && rid===String(keepRid);
       const isDel = delSet.has(rid);
+      const foreign = !c.skuMatch;   // belongs to a DIFFERENT SKU (shares a number) — view-only
       const picked = pick!=null && pick===real && real!=='';
-      h += `<label class="cand-row${picked?' picked':''}${isDel?' marked-del':''}">
-        <input type="radio" name="cand-${esc(sku)}" ${picked?'checked':''} data-real="${esc(real)}" data-rid="${esc(rid)}" data-keepmfr="${esc(real)}" data-keepnote="${esc(c.mfrNote||'')}">
+      h += `<label class="cand-row${picked?' picked':''}${isDel?' marked-del':''}${foreign?' foreign':''}">
+        <input type="radio" name="cand-${esc(sku)}" ${picked?'checked':''} ${foreign?'disabled':''} data-real="${esc(real)}" data-rid="${esc(rid)}" data-keepmfr="${esc(real)}" data-keepnote="${esc(c.mfrNote||'')}">
         <span class="cand-body">
           <span class="cand-line"><span class="cand-rid">#${esc(c.recordId)}</span>`+
           (c.series||c.jsPattern?`<span class="cand-meta">${esc(c.series)}|${esc(c.jsPattern)}</span>`:'')+
           (c.mfrPattern?`<span class="cand-struct${isDw?' isdw':''}" title="Mfr Pattern (structured)">${esc(c.mfrPattern)}</span>`:'')+
+          (foreign?`<span class="foreign-badge" title="This record belongs to a DIFFERENT SKU (${esc(c.series)}|${esc(c.jsPattern)}) that merely shares a number — NOT this SKU. Cannot be picked or deleted here.">⚠ other SKU</span>`:'')+
           (isDel?`<span class="del-tag">will be staged for deletion</span>`:'')+
           `</span>`+
           (c.mfrNote && c.mfrNote!==c.mfrPattern?`<span class="cand-line"><span class="cand-note" title="mfr note (real code lives here)">${esc(c.mfrNote)}</span></span>`:'')+
@@ -516,18 +535,20 @@ function renderCandidates(sku, panel){
           `<span class="cand-line cand-meta">`+
             (c.name?`${esc(c.name)} · `:'')+(c.color?`${esc(c.color)} · `:'')+(c.vid?`vid ${esc(c.vid)}`:'')+
           `</span>`+
-          `<span class="cand-line">`+
+          (foreign?'':`<span class="cand-line">`+
             (c.sampleOrdered
               ? `<span class="samp-badge samp-yes" title="a client sample WAS ordered against this record — KEEP">🧾 sampled${c.sampleDate?' '+esc(c.sampleDate):''}</span>`
               : `<span class="samp-badge samp-no" title="no client sample ever ordered — a 'master' template (deletable when its mfr is just the DW# placeholder)">⛔ never sampled</span>`)+
             (c.deleteSuggested?`<span class="samp-badge samp-sug" title="auto-suggested for deletion by the rule (no sample + placeholder mfr, a keeper survives)">auto-delete</span>`:'')+
-          `</span>`+
-          (real?`<span class="cand-line">→ real mfr: <span class="cand-realmfr">${esc(real)}</span></span>`:'')+
-        `</span>
-        <span class="del-wrap" title="Mark THIS master for deletion (keep the rest)">
+          `</span>`)+
+          (real&&!foreign?`<span class="cand-line">→ real mfr: <span class="cand-realmfr">${esc(real)}</span></span>`:'')+
+        `</span>`+
+        (foreign
+          ? `<span class="del-wrap del-disabled" title="Foreign record — belongs to another SKU, cannot be deleted from here">—</span>`
+          : `<span class="del-wrap" title="Mark THIS master for deletion (keep the rest)">
           <input type="checkbox" class="del-chk" data-delsku="${esc(sku)}" data-delrid="${esc(rid)}" ${isDel?'checked':''}>
           <span class="del-lbl">🗑 delete<br>(keep rest)</span>
-        </span></label>`;
+        </span>`)+`</label>`;
     }
     // per-SKU running summary
     h += `<div class="prune-summary" data-prune="${esc(sku)}">${pruneSummaryText(sku)}</div>`;
@@ -588,6 +609,12 @@ function renderCandidates(sku, panel){
     };
   }
 
+  // wire the "Approve into field ↑" button (writes the suggested real mfr into the confirmed-mfr input)
+  const amBtn = panel.querySelector('.approve-mfr-btn');
+  if(amBtn){
+    amBtn.onclick = (ev)=>{ ev.preventDefault(); chooseMfr(sku, amBtn.dataset.mfr||''); amBtn.textContent='✓ approved'; };
+  }
+
   // wire per-record "🗑 delete (keep rest)" checkboxes
   for(const cb of panel.querySelectorAll('.del-chk')){
     cb.onchange = ()=>{

← 2326b02 candidates: CRITICAL — scope keep/delete/mfr-suggest to own-  ·  back to Mfr Review Viewer Corruption  ·  executor: add belongs-to-SKU (skuMatch) guard to execute-tim e24c287 →