← back to Dw Vendor Microsites

vendors/gracie/export.mjs

70 lines

#!/usr/bin/env node
/**
 * Gracie catalog → staging/gracie.jsonl (LOCAL dw_unified, READ-ONLY).
 * Normalizes gracie_catalog to the canonical microsite JSONL shape (same fields
 * the template viewer expects). QUOTE-ONLY: no prices emitted. Product URLs are
 * DROPPED (customer-facing microsite links into the DW store, never graciestudio.com).
 * Cost: $0 (local).
 */
import { Client } from 'pg';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const OUT = path.join(__dirname, '..', '..', 'staging', 'gracie.jsonl');

const sanitize = (s) => typeof s === 'string'
  ? s.replace(/\bwallpapers\b/gi, 'wallcoverings').replace(/\bwallpaper\b/gi, 'wallcovering')
  : s;

async function main() {
  const c = new Client({ connectionString: process.env.PG || 'postgresql://localhost/dw_unified' });
  await c.connect();
  const { rows } = await c.query(`
    SELECT mfr_sku, pattern_name, color_name, collection, width, roll_length, pattern_repeat,
           image_url, all_images, product_type, material, composition, specs,
           ai_description, description, ai_colors, ai_background_color, ai_styles, ai_tags,
           color_hex, dominant_color_hex, crawled_at
    FROM gracie_catalog WHERE image_url IS NOT NULL ORDER BY collection, pattern_name`);
  await c.end();

  const out = rows.map((r, i) => {
    let colors = []; let styles = []; let tags = [];
    try { colors = Array.isArray(r.ai_colors) ? r.ai_colors : JSON.parse(r.ai_colors || '[]'); } catch {}
    try { styles = Array.isArray(r.ai_styles) ? r.ai_styles : JSON.parse(r.ai_styles || '[]'); } catch {}
    try { tags = Array.isArray(r.ai_tags) ? r.ai_tags : JSON.parse(r.ai_tags || '[]'); } catch {}
    const gallery = (r.all_images || '').split('|').filter(Boolean);
    return {
      id: i + 1,
      mfr_sku: r.mfr_sku,
      pattern_name: sanitize(r.pattern_name),
      color_name: r.color_name || (colors[0] && colors[0].name) || '',
      collection: sanitize(r.collection),
      width: r.width || null,
      roll_length: r.roll_length || null,
      pattern_repeat: r.pattern_repeat || null,
      image_url: r.image_url,
      all_images: gallery,
      product_type: r.product_type || 'wallcovering',
      material: r.material || 'Handpainted',
      composition: r.composition || null,
      // QUOTE-ONLY — no price fields
      quote_only: true,
      specs: r.specs || {},
      ai_description: sanitize(r.ai_description || r.description || ''),
      ai_colors: colors,
      ai_background_color: r.ai_background_color || null,
      ai_styles: styles,
      ai_tags: tags,
      color_hex: r.color_hex || r.dominant_color_hex || null,
      // product_url intentionally DROPPED (no link to graciestudio.com)
    };
  });

  fs.mkdirSync(path.dirname(OUT), { recursive: true });
  fs.writeFileSync(OUT, out.map((o) => JSON.stringify(o)).join('\n') + '\n');
  console.log(`[gracie-export] wrote ${out.length} rows → ${OUT}. Cost: $0 (local)`);
}
main().catch((e) => { console.error('[gracie-export] FATAL', e); process.exit(1); });