[object Object]

← back to Japan Enrich

japan viewer: contrarian-gate fixes — nginx port 9934, no-split distributor fallback, queue queued_at chip (CLAUDE.md admin-card rule), promote settlement guard

955be29fc62251440039ffe647af50bae29b412a · 2026-07-06 14:44:48 -0700 · Steve

Files touched

Diff

commit 955be29fc62251440039ffe647af50bae29b412a
Author: Steve <steve@designerwallcoverings.com>
Date:   Mon Jul 6 14:44:48 2026 -0700

    japan viewer: contrarian-gate fixes — nginx port 9934, no-split distributor fallback, queue queued_at chip (CLAUDE.md admin-card rule), promote settlement guard
---
 db/promote-import-queue.js                         | 8 ++++++++
 deploy/japan-china.designerwallcoverings.com.nginx | 2 +-
 viewer-local/public/index.html                     | 5 ++++-
 viewer-local/server.js                             | 2 +-
 4 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/db/promote-import-queue.js b/db/promote-import-queue.js
index 5fff4eb..c144917 100644
--- a/db/promote-import-queue.js
+++ b/db/promote-import-queue.js
@@ -54,6 +54,14 @@ async function main() {
   if (!DSN) { console.error('COMMIT aborted: DATABASE_URL not set.'); process.exit(1); }
   let Client;
   try { ({ Client } = require('pg')); } catch { console.error('COMMIT needs `pg` — run: npm i pg'); process.exit(1); }
+  // SETTLEMENT GUARD: this script ONLY stages PostgreSQL-side with on_shopify=false.
+  // It NEVER publishes to Shopify. The Sangetsu mural line (~60% birds/tropical) must
+  // clear the `settlement` gate before ANY on_shopify flip — which is a separate,
+  // Steve-gated step this script deliberately does not perform.
+  if (byVendor.sangetsu && byVendor.sangetsu.length) {
+    console.log(`\n⚠️  SETTLEMENT: ${byVendor.sangetsu.length} Sangetsu SKUs will be STAGED (on_shopify=false) only.`);
+    console.log('   Do NOT flip on_shopify=true / publish to Shopify until the settlement gate passes per SKU.');
+  }
   const db = new Client({ connectionString: DSN });
   await db.connect();
   let n = 0;
diff --git a/deploy/japan-china.designerwallcoverings.com.nginx b/deploy/japan-china.designerwallcoverings.com.nginx
index b0583f7..1874616 100644
--- a/deploy/japan-china.designerwallcoverings.com.nginx
+++ b/deploy/japan-china.designerwallcoverings.com.nginx
@@ -6,7 +6,7 @@ server {
   server_name japan.designerwallcoverings.com china.designerwallcoverings.com;
   client_max_body_size 12m;              # matches the import-queue body cap
   location / {
-    proxy_pass http://127.0.0.1:9931;
+    proxy_pass http://127.0.0.1:9934;   # japan-viewer listens on 9934 (9931 on this box is a DIFFERENT app)
     proxy_set_header Host $host;          # REQUIRED — hostScope reads Host to pick japan vs china
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
diff --git a/viewer-local/public/index.html b/viewer-local/public/index.html
index 9eed1de..0e4b97c 100644
--- a/viewer-local/public/index.html
+++ b/viewer-local/public/index.html
@@ -113,6 +113,7 @@ body.imgonly .grid:not(.list) .card .b{display:none}
 .qi .qt{font-size:12.5px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
 .qi .qd{font-size:10px;color:var(--acc)}
 .qi .qd.direct{color:var(--mut)}
+.qi .qwhen{font-size:10px;color:var(--mut)}
 .qi .qx{background:transparent;border:1px solid var(--line);color:var(--mut);border-radius:6px;cursor:pointer;width:22px;height:22px;line-height:1;padding:0}
 .qi .qx:hover{border-color:#c0392b;color:#e57373}
 .qp-empty{padding:40px 12px;text-align:center;color:var(--mut);font-size:12.5px}
@@ -230,6 +231,7 @@ const FAMILY_DOT={'Red':'#c0392b','Orange/Brown':'#a8642a','Yellow':'#d4ac0d','G
  'Blue':'#2e6fb0','Purple':'#7d5ba6','Pink':'#d87aa5','Neutral/Beige':'#cdbd96','Black':'#1a1a1a','Grey':'#9a9aa2','White':'#f2f2f2'};
 const SRC_LABEL={lilycolor:'Lilycolor',sangetsu:'Sangetsu',greenland:'Greenland'};
 function esc(s){return String(s==null?'':s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));}
+function fmtWhen(iso){try{return new Date(iso).toLocaleString(undefined,{year:'numeric',month:'short',day:'numeric',hour:'numeric',minute:'2-digit'});}catch(e){return String(iso||'');}}
 // ── every data field is a left-panel table: collapsed on load, click value = filter ──
 // 'functions' is an array field (multi-valued) — counted/filtered element-wise.
 const FIELDS=[
@@ -364,9 +366,10 @@ function renderQueue(items){
   list.innerHTML=items.map(r=>{
     const img=r.image?`<img loading="lazy" src="${esc(r.image)}" onerror="this.style.visibility='hidden'">`:'<img>';
     const d=r.distributor?`<div class="qd${r.dist_direct?' direct':''}">${r.dist_direct?'':'🏬 '}${esc(r.distributor)}</div>`:'';
+    const when=r.queued_at?`<div class="qwhen" title="${esc(r.queued_at)}">🕓 ${esc(fmtWhen(r.queued_at))}</div>`:'';
     return `<div class="qi" data-sku="${esc(r.sku)}">${img}
       <div class="qm"><div class="qt" title="${esc(r.title||'')}">${esc(r.title||r.sku)}</div>
-      <div class="qs">${esc(r.sku)}</div>${d}</div>
+      <div class="qs">${esc(r.sku)}</div>${d}${when}</div>
       <button class="qx" title="Remove from queue" onclick="removeFromQueue('${esc(r.sku)}')">✕</button></div>`;
   }).join('');
 }
diff --git a/viewer-local/server.js b/viewer-local/server.js
index 53173c9..80af25c 100644
--- a/viewer-local/server.js
+++ b/viewer-local/server.js
@@ -137,7 +137,7 @@ function distInfo(source, url) {
   let host = '';
   try { host = new URL(url).hostname.replace(/^www\./, ''); } catch {}
   for (const [re, label] of DIST_BY_HOST) if (re.test(host)) return { distributor: label, dist_direct: false };
-  if (source === 'sangetsu') return { distributor: 'Goodrich', dist_direct: false };   // sangetsu w/o a recognized host is still via Goodrich
+  if (source === 'sangetsu') return { distributor: 'Goodrich (Thailand)', dist_direct: false };   // sangetsu w/o a recognized host: same label as .co.th so the facet never splits
   if (source === 'lilycolor') return { distributor: 'Lilycolor (direct)', dist_direct: true };
   if (source === 'greenland') return { distributor: 'Greenland (direct)', dist_direct: true };
   return { distributor: null, dist_direct: false };

← 8b2471a japan viewer: URL-param deep-link + sharable state for every  ·  back to Japan Enrich  ·  japan: Tier-1 canonical dw_unified stage runbook (approved; 5b7654c →