← back to Reno Visualizer
Handle new catalog fields: buyable, sample_only, price_display, product_url
915041e00fd56be69b1b329376dd308db203070d · 2026-07-16 08:48:25 -0700 · steve@designerwallcoverings.com
- server.js loadProducts(): pass through price_display (pre-formatted string),
buyable (bool), sample_only (bool), product_url (live DW product page);
derive price_display fallback if absent so no null ever reaches the client
- index.html pattern grid: show price via price_display (never fmtPrice/NaN);
sample-only badge uses sample_only field; append "/roll" only for buyable
- Step 3 selected-pattern info + Step 4 shop preview: same price_display fix;
product_url used for "View on Designer Wallcoverings" links throughout
No $null / $NaN possible: price_display is always a non-empty string from the
catalog (e.g. "Roll price on request" for the 83 sample-only products, "$94.42"
for the 167 buyable products).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Files touched
M public/index.htmlM server.js
Diff
commit 915041e00fd56be69b1b329376dd308db203070d
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date: Thu Jul 16 08:48:25 2026 -0700
Handle new catalog fields: buyable, sample_only, price_display, product_url
- server.js loadProducts(): pass through price_display (pre-formatted string),
buyable (bool), sample_only (bool), product_url (live DW product page);
derive price_display fallback if absent so no null ever reaches the client
- index.html pattern grid: show price via price_display (never fmtPrice/NaN);
sample-only badge uses sample_only field; append "/roll" only for buyable
- Step 3 selected-pattern info + Step 4 shop preview: same price_display fix;
product_url used for "View on Designer Wallcoverings" links throughout
No $null / $NaN possible: price_display is always a non-empty string from the
catalog (e.g. "Roll price on request" for the 83 sample-only products, "$94.42"
for the 167 buyable products).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
public/index.html | 13 +++++++------
server.js | 8 ++++++--
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/public/index.html b/public/index.html
index e179a8e..510902a 100644
--- a/public/index.html
+++ b/public/index.html
@@ -554,9 +554,9 @@ function renderPatterns(products) {
return `<div class="pattern-card${isSelected ? ' selected' : ''}" id="pc-${esc(p.sku)}" onclick="selectPattern(${JSON.stringify(JSON.stringify(p))})">
<img src="${esc(imgSrc)}" alt="${esc(p.title)}" loading="lazy" onerror="this.src='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22200%22 height=%22200%22><rect width=%22200%22 height=%22200%22 fill=%22%23e8e4dc%22/></svg>'">
<div class="pattern-meta">
- <div class="pattern-title">${esc(p.title)}${p.is_sample ? '<span class="pattern-sample-badge">Sample</span>' : ''}</div>
+ <div class="pattern-title">${esc(p.title)}${p.sample_only ? '<span class="pattern-sample-badge">Sample only</span>' : ''}</div>
<div class="pattern-sub">${esc(p.vendor || '')}</div>
- ${p.price ? `<div class="pattern-price">${fmtPrice(p.price)}/roll</div>` : ''}
+ ${p.price_display ? `<div class="pattern-price">${esc(p.price_display)}${p.buyable ? '/roll' : ''}</div>` : ''}
</div>
</div>`;
}).join('');
@@ -617,7 +617,7 @@ function updateSelectedPatternInfo() {
<div class="selected-pattern-meta">
<div class="title">${esc(p.title)}</div>
<div class="sub">${esc(p.vendor || '')} · ${esc(p.color || '')}</div>
- <div class="sub" style="margin-top:2px;font-weight:600;color:var(--brand)">${fmtPrice(p.price)}/roll</div>
+ <div class="sub" style="margin-top:2px;font-weight:600;color:var(--brand)">${esc(p.price_display || '')}${p.buyable ? '/roll' : ''}</div>
</div>`;
}
@@ -819,14 +819,15 @@ function initShopStep() {
if (!p) return;
const imgSrc = p.image_url || p.image || '';
const roomSrc = r?.custom ? r.dataUrl : (r?.src || '');
- const productUrl = p.shopify_url || (p.handle ? `https://www.designerwallcoverings.com/products/${p.handle}` : '');
+ const productUrl = p.product_url || p.shopify_url || (p.handle ? `https://www.designerwallcoverings.com/products/${p.handle}` : '');
+ const priceLabel = p.price_display ? (p.price_display + (p.buyable ? '/roll' : '')) : '';
$('#look-preview').innerHTML = `
<img class="look-room-thumb" src="${esc(roomSrc)}" alt="Room" onerror="this.style.display='none'">
<img class="look-pattern-thumb" src="${esc(imgSrc)}" alt="${esc(p.title)}" onerror="this.style.background='${esc(p.hex||'#e8e4dc')}'">
<div class="look-info">
<h3>${esc(p.title)}</h3>
<p>${esc(p.vendor || '')}${p.color ? ' · ' + esc(p.color) : ''}${p.collection ? ' · ' + esc(p.collection) : ''}</p>
- <p class="price">${fmtPrice(p.price)}/roll</p>
+ ${priceLabel ? `<p class="price">${esc(priceLabel)}</p>` : ''}
${productUrl ? `<p style="margin-top:6px;"><a href="${esc(productUrl)}" target="_blank" rel="noopener noreferrer" style="font-size:12px;color:var(--brand-light);text-decoration:underline;">View on Designer Wallcoverings →</a></p>` : ''}
</div>`;
}
@@ -839,7 +840,7 @@ async function submitLead(e) {
const p = state.selectedPattern;
const r = state.selectedRoom;
- const productUrl = p?.shopify_url || (p?.handle ? `https://www.designerwallcoverings.com/products/${p.handle}` : '');
+ const productUrl = p?.product_url || p?.shopify_url || (p?.handle ? `https://www.designerwallcoverings.com/products/${p.handle}` : '');
const body = {
email: $('#lead-email').value.trim(),
name: $('#lead-name').value.trim(),
diff --git a/server.js b/server.js
index 1edc643..2db7f6c 100644
--- a/server.js
+++ b/server.js
@@ -53,10 +53,14 @@ function loadProducts() {
type: p.type,
image: p.image,
image_url: p.image,
- price: p.price,
+ price: p.price, // numeric or null (null = sample_only)
+ price_display: p.price_display || (p.price != null ? '$' + Number(p.price).toFixed(2) : 'Roll price on request'),
+ buyable: !!p.buyable,
+ sample_only: !!p.sample_only,
+ product_url: p.product_url || `https://${checkoutDomain}/products/${p.handle}`,
handle: p.handle,
variant_id: p.variant_id,
- shopify_url: `https://${checkoutDomain}/products/${p.handle}`,
+ shopify_url: p.product_url || `https://${checkoutDomain}/products/${p.handle}`,
}));
} catch {
try { return JSON.parse(fs.readFileSync(path.join(DIR, 'data', 'products.json'), 'utf8')); }
← 258fcf0 shopify: refresh catalog — real roll prices, no $4.25 sample
·
back to Reno Visualizer
·
shopify: refresh catalog — keep sample-only as 'by request' 572601b →