[object Object]

← back to Permit Radar

cross-sell strip: use price_display + product_url, prefer buyable products

12a1e8e49bc1a568dbd64b5a4b648fb23685cce5 · 2026-07-16 08:48:31 -0700 · Steve Abrams

- /api/shopify-crosssell now returns price_display (pre-formatted string),
  sample_only, and product_url for each pick instead of raw numeric price
- Pool is filtered to buyable products first (167 available >> 6 needed),
  so "Roll price on request" cards are suppressed in normal operation
- Frontend uses esc(p.price_display) — no manual \$+toFixed() formatting,
  eliminates any \$null/\$NaN risk

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files touched

Diff

commit 12a1e8e49bc1a568dbd64b5a4b648fb23685cce5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 16 08:48:31 2026 -0700

    cross-sell strip: use price_display + product_url, prefer buyable products
    
    - /api/shopify-crosssell now returns price_display (pre-formatted string),
      sample_only, and product_url for each pick instead of raw numeric price
    - Pool is filtered to buyable products first (167 available >> 6 needed),
      so "Roll price on request" cards are suppressed in normal operation
    - Frontend uses esc(p.price_display) — no manual \$+toFixed() formatting,
      eliminates any \$null/\$NaN risk
    
    Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
 public/index.html |  2 +-
 server.js         | 13 +++++++++----
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/public/index.html b/public/index.html
index f20e74d..f9ddce2 100644
--- a/public/index.html
+++ b/public/index.html
@@ -629,7 +629,7 @@ async function submitLead(e) {
   <div class="xsell-info">
     <div class="xsell-product-title">${esc(p.title)}</div>
     <div class="xsell-vendor">${esc(p.vendor)}</div>
-    <div class="xsell-price">${p.price ? '$' + Number(p.price).toFixed(2) : ''}</div>
+    <div class="xsell-price">${esc(p.price_display || '')}</div>
   </div>
 </a>`).join('');
   } catch (err) {
diff --git a/server.js b/server.js
index 5e6ef75..6d5f282 100644
--- a/server.js
+++ b/server.js
@@ -198,9 +198,13 @@ const server = http.createServer(async (req, res) => {
   if (pathname === '/api/shopify-crosssell' && method === 'GET') {
     const catalog = loadShopifyCatalog();
     const prods = catalog.products || [];
-    const n = Math.min(6, prods.length);
+    // Prefer buyable products so most cards show a real price; fall back to all if needed
+    const pool = prods.filter(p => p.buyable).length >= 6
+      ? prods.filter(p => p.buyable)
+      : prods;
+    const n = Math.min(6, pool.length);
     // Fisher-Yates shuffle then take n
-    const shuffled = prods.slice();
+    const shuffled = pool.slice();
     for (let i = shuffled.length - 1; i > 0; i--) {
       const j = Math.floor(Math.random() * (i + 1));
       [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
@@ -209,10 +213,11 @@ const server = http.createServer(async (req, res) => {
       title: p.title,
       handle: p.handle,
       image: p.image,
-      price: p.price,
+      price_display: p.price_display || 'Roll price on request',
+      sample_only: p.sample_only || false,
       vendor: p.vendor,
       type: p.type,
-      url: 'https://' + catalog.checkout_domain + '/products/' + p.handle
+      url: p.product_url || ('https://' + catalog.checkout_domain + '/products/' + p.handle)
     }));
     res.writeHead(200, { 'Content-Type': 'application/json' });
     return res.end(JSON.stringify({ products: picks, checkout_domain: catalog.checkout_domain }));

← ed3fea7 shopify: refresh catalog — real roll prices, no $4.25 sample  ·  back to Permit Radar  ·  shopify: refresh catalog — keep sample-only as 'by request' fdc840b →