← back to Silverleafwallpaper

scripts/build-products.js

84 lines

#!/usr/bin/env node
/**
 * build-products.js — regenerate data/products.json for silverleafwallpaper.com
 *
 * Like its gold sibling, this micro-storefront must show ONLY the Renaissance
 * Metal Leaf line (Phillipe Romano, dw_sku prefix "RML-") — the gold & silver
 * leaf wallcoverings. It previously carried 464 loosely-"metallic" products
 * from unrelated vendors (Hollywood, LA Walls, generic DW), which was wrong.
 *
 * Source of truth: the local dw_unified mirror (host=/tmp socket). ACTIVE RML-
 * rows only (ARCHIVED excluded). Output is shaped for BOTH consumers:
 *   • migrate-microsite-products.js (upserts into dw_unified.microsite_products,
 *     the live read path) — needs sku, title, vendor, product_type, handle,
 *     image_url, tags[] (array), max_price, product_url.
 *   • the static data/products.json fallback the server reads when PG is down.
 *
 * Titles are derived from real catalog tags ("Gold Leaf — Brass", etc.) because
 * every RML- product ships the identical DW title.
 *
 * Usage:  node scripts/build-products.js   (then run migrate-microsite-products.js)
 */
const { execFileSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const OUT = path.join(__dirname, '..', 'data', 'products.json');

const SQL = `
  select coalesce(json_agg(row_to_json(t) order by t.dw_sku), '[]'::json)
  from (
    select dw_sku, mfr_sku, title, vendor, handle, product_type, image_url, tags
    from shopify_products
    where dw_sku ilike 'RML-%' and status = 'ACTIVE'
  ) t;
`;

const raw = execFileSync('psql', ['-h', '/tmp', 'dw_unified', '-tAc', SQL], {
  encoding: 'utf8', maxBuffer: 64 * 1024 * 1024,
});
const rows = JSON.parse(raw.trim());

function parseTags(tagStr) {
  if (!tagStr) return [];
  return String(tagStr)
    .replace(/^\{|\}$/g, '')
    .split(',')
    .map((s) => s.trim().replace(/^"|"$/g, ''))
    .filter(Boolean);
}

const FINISH_ORDER = ['Silver', 'Gold', 'Bronze'];
function deriveTitle(tags) {
  const finish = FINISH_ORDER.find((f) => tags.includes(f));
  const colorTag = tags.find((t) => /^color:/i.test(t));
  const colorway = colorTag ? colorTag.replace(/^color:/i, '').trim() : '';
  const finishLabel = finish ? `${finish} Leaf` : 'Renaissance Metal Leaf';
  // Separator is a middot, NOT an em-dash: the fleet's api-vendor-redact
  // scrubTitle() splits titles on | – — and drops trailing segments (it strips
  // "Vendor — Pattern" leakage), which would eat the colorway. Middot survives.
  return colorway ? `${finishLabel} · ${colorway}` : finishLabel;
}

const products = rows.map((r) => {
  const tags = parseTags(r.tags);
  return {
    sku: r.dw_sku,
    handle: r.handle,
    title: deriveTitle(tags),
    vendor: r.vendor,
    product_type: r.product_type || 'Wallcovering',
    image_url: r.image_url || '',
    tags, // array — migrate + niche/color logic expect an array here
    max_price: null, // quote-only metal-leaf line
    product_url: `https://designerwallcoverings.com/products/${r.handle}#sample`,
    mfr_sku: r.mfr_sku || '',
    material: 'Metal Leaf',
  };
});

fs.writeFileSync(OUT, JSON.stringify(products));
const n = (needle) => products.filter((p) => p.title.startsWith(needle)).length;
console.log(`Wrote ${products.length} RML- products → ${path.relative(process.cwd(), OUT)}`);
console.log(`  Gold Leaf: ${n('Gold Leaf')}  Silver Leaf: ${n('Silver Leaf')}  Other: ${products.length - n('Gold Leaf') - n('Silver Leaf')}`);