[object Object]

← back to Dw Pitch Followup

auto-save: 2026-07-13T11:24:39 (2 files) — public/index.html scripts/build-lists.js

a22a58af1d80cb4109deca1f6cf6f7410abb5e21 · 2026-07-13 11:24:41 -0700 · Steve Abrams

Files touched

Diff

commit a22a58af1d80cb4109deca1f6cf6f7410abb5e21
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Jul 13 11:24:41 2026 -0700

    auto-save: 2026-07-13T11:24:39 (2 files) — public/index.html scripts/build-lists.js
---
 public/index.html      | 30 +++++++++++++++++++++++++++---
 scripts/build-lists.js |  6 +++++-
 2 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/public/index.html b/public/index.html
index 4085cd9..7bd0101 100644
--- a/public/index.html
+++ b/public/index.html
@@ -69,6 +69,12 @@
   .proj{font-size:calc(11px*var(--scale));color:var(--gold)}
   .postflag{background:#3a2a12;color:var(--amber);border:1px solid var(--amber);border-radius:6px;padding:4px 9px;font-size:calc(11px*var(--scale));font-weight:600;margin:3px 0;line-height:1.4}
   .postflag a{color:var(--amber)}
+  /* Discontinued-sample warning: sampled item(s) carry a FileMaker Date Discontinued → dead stock. */
+  .disco-ban{background:#3a1512;color:var(--red);border:1px solid var(--red);border-radius:6px;padding:5px 9px;font-size:calc(11px*var(--scale));font-weight:700;margin:4px 0;line-height:1.4}
+  .disco-ban .sub{display:block;font-weight:600;color:#e79a8d;font-size:calc(10px*var(--scale));margin-top:2px}
+  .disco-ban.all{animation:discoblink 1.05s steps(1,end) infinite}
+  @keyframes discoblink{50%{background:var(--red);color:#2a0d0a;border-color:#fff}}
+  .dtag{display:inline-block;background:#3a1512;color:var(--red);border:1px solid var(--red);border-radius:4px;padding:0 5px;margin-left:6px;font-size:calc(9.5px*var(--scale));font-weight:700;letter-spacing:.03em;vertical-align:middle}
   .acts{display:flex;gap:7px;margin-top:2px;flex-wrap:wrap}
 
   /* RIGHT PANEL */
@@ -178,6 +184,23 @@ const dateChip=r=>r.when_iso?`<span class="chip" title="${esc(r.when_iso)}">🕓
 const money=n=>'$'+(Number(n)||0).toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2});
 // Nothing shipped yet: samples are pending but ZERO have been sent → block the letter, prompt a reorder.
 const needsReorder=r=>Array.isArray(r.samples_pending)&&r.samples_pending.length>0&&Array.isArray(r.samples_sent)&&r.samples_sent.length===0;
+// Samples were sent, but the item carries a FileMaker Date Discontinued → it's really just
+// discontinued. `all` = EVERY shipped sample is dead stock → the blanket "how did the samples go /
+// ready to order?" pitch is inappropriate; card + panel blink a red DO-NOT-SEND banner. (Steve 2026-07-13)
+function discoState(r){
+  const sent=Array.isArray(r.samples_sent)?r.samples_sent:[];
+  const dead=sent.filter(s=>s&&s.disco);
+  if(!sent.length||!dead.length)return null;
+  return {count:dead.length,total:sent.length,all:dead.length===sent.length,items:dead};
+}
+function discoBanner(r){
+  const d=discoState(r); if(!d)return'';
+  const dates=[...new Set(d.items.map(s=>fmtDay(s.disco)).filter(Boolean))].slice(0,3).join(', ');
+  const msg=d.all
+    ? `⛔ DO NOT send a blanket follow-up — all ${d.total} sampled item(s) are DISCONTINUED.`
+    : `⚠️ ${d.count} of ${d.total} sampled item(s) DISCONTINUED — don't blanket-pitch “how did the samples go”.`;
+  return `<div class="disco-ban ${d.all?'all':''}">${msg}<span class="sub">Discontinued ${esc(dates||'—')} — offer alternatives, don't chase the sale on dead stock.</span></div>`;
+}
 // Compact card flag: any invoice placed since we sent samples (possible conversion).
 function postFlag(r){
   const p=Array.isArray(r.post_sample_invoices)?r.post_sample_invoices:[]; if(!p.length) return '';
@@ -238,7 +261,7 @@ function cardHTML(list,r){
   }
   return `<div class="card ${sel?'sel':''}" data-k="${esc(k)}" data-list="${list}">
     <div class="top"><input type="checkbox" ${sel?'checked':''} data-sel/>${head}</div>
-    ${facts}${projLine}${postFlag(r)}
+    ${facts}${projLine}${postFlag(r)}${discoBanner(r)}
     <div class="acts">${needsReorder(r)
       ? `<button class="btn reorder" data-view>Reorder Samples??</button>`
       : `<button class="btn gold" data-gen>Generate letter</button><button class="btn" data-view>Details ▸</button>`}</div>
@@ -272,7 +295,8 @@ function samplesBlock(r){
     const w=(withDate&&s&&s.date)?` · ${esc(fmtDay(s.date))}`:'';
     const img=(s&&s.image)?`<img src="${esc(s.image)}" style="width:22px;height:22px;object-fit:cover;border-radius:4px;vertical-align:middle;margin-right:6px;border:1px solid var(--line)">`:`<span class="dot ${cls}"></span>`;
     const name=(s&&s.url)?`<a href="${esc(s.url)}" target="_blank">${esc(nm)}</a>`:esc(nm);
-    return `<li>${img}${name}${w}</li>`;
+    const dead=(s&&s.disco)?`<span class="dtag" title="Discontinued ${esc(s.disco)}">DISCONTINUED · ${esc(fmtDay(s.disco))}</span>`:'';
+    return `<li>${img}${name}${w}${dead}</li>`;
   }).join('');
   let out='';
   if(sent.length)out+=`<div class="drow"><b>Samples sent (${sent.length})</b><ul class="slist">${li(sent,'ok',true)}</ul></div>`;
@@ -290,7 +314,7 @@ function merchBlock(r){
   return out;
 }
 function panelContextHTML(list,r){
-  let html=postSampleBlock(r);
+  let html=discoBanner(r)+postSampleBlock(r);
   if(r.project) html+=`<div class="drow">🏠 <b>Current project:</b> ${esc(lbl(r.project))}</div>`;
   if(list!=='list3') html+=merchBlock(r);
   html+=samplesBlock(r);
diff --git a/scripts/build-lists.js b/scripts/build-lists.js
index 81a1b08..fa91853 100644
--- a/scripts/build-lists.js
+++ b/scripts/build-lists.js
@@ -236,8 +236,12 @@ async function enrichSamples(accounts) {
     const sku = clean(fd['combo sku'] || '');
     const label = sku || clean(fd['Mfr Pattern'] || '') || 'sample';
     const sent = fmToISO(fd['Date WP Sample Sent']);
+    // `Date Discontinued` is a WALLPAPER product-level field: if present, this sampled item is
+    // DEAD STOCK — a "how did the samples go / ready to order?" follow-up can't convert on it.
+    // Carry it through so the viewer can red-flag "don't send a blanket pitch." (Steve 2026-07-13)
+    const disco = fmToISO(fd['Date Discontinued']);
     const g = map.get(acct) || { sent: [], pending: [] };
-    if (sent) g.sent.push({ label, sku, date: sent }); else g.pending.push({ label, sku });
+    if (sent) g.sent.push({ label, sku, date: sent, disco }); else g.pending.push({ label, sku, disco });
     map.set(acct, g);
   }
   for (const g of map.values()) {

← 93dde31 chore: v0.3.0 (session close — public tunnel + auth hardenin  ·  back to Dw Pitch Followup  ·  auto-save: 2026-07-13T11:54:48 (2 files) — public/index.html 0b34230 →