← back to Dwjs Consolidation 2026 04 23

phase4_dwqw_collections.js

269 lines

#!/usr/bin/env node
/**
 * PHASE 4 — DWQW (Malibu Walls / WallQuest) SHOPIFY COLLECTIONS.
 *
 * Creates a family of smart collections scoped to active DWQW/Malibu products.
 * Uses `productCollectionCreate`-like pattern via GraphQL `collectionCreate`.
 * Rules are based on existing product tags (safe, no product mutations).
 *
 * Each collection:
 *   - title + handle
 *   - seo.title + seo.description (DW service voice — free samples, trade service)
 *   - smart collection rules (no manual product add)
 *
 * Safe: only creates collections; does not touch products.
 * Idempotent-ish: fails if a handle already exists; logs and skips.
 */
const https = require('https');
const fs = require('fs');
const path = require('path');

const STORE='designer-laboratory-sandbox.myshopify.com';
const TOKEN=(process.env.SHOPIFY_ADMIN_TOKEN || '');
const API='/admin/api/2024-10/graphql.json';
const OUT=__dirname;
const LOG = path.join(OUT, 'phase4_collections.log');

function ts(){ return new Date().toISOString().replace('T',' ').slice(0,19); }
function log(m){ const l=`[${ts()}] ${m}\n`; process.stdout.write(l); fs.appendFileSync(LOG,l); }

function gql(body, retry=0) {
  return new Promise((resolve, reject) => {
    const data = JSON.stringify(body);
    const req = https.request({ hostname:STORE, path:API, method:'POST',
      headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json','Content-Length':Buffer.byteLength(data)} },
      res => { let c=''; res.on('data',d=>c+=d); res.on('end', async ()=>{
        try { resolve(JSON.parse(c)); }
        catch (e) { if (retry<3) setTimeout(()=>resolve(gql(body,retry+1)), 2000); else resolve({error:c.slice(0,500)}); }
      }); }
    );
    req.on('error', err => { if (retry<3) setTimeout(()=>resolve(gql(body,retry+1)), 2000); else reject(err); });
    req.setTimeout(45000, ()=>{ req.destroy(); if (retry<3) resolve(gql(body,retry+1)); else reject(new Error('t')); });
    req.write(data); req.end();
  });
}

// Collection definitions. Each uses AND-rule: tag = "Malibu Wallcovering" AND tag = <theme>
// (The Malibu Wallcovering tag is on 1,051 products — restricts to DWQW umbrella.)
// Child rules set to AND so we only pull Malibu products that also match the theme.
const MALIBU_TAG = 'Malibu Wallcovering';
const SEO_SUFFIX = 'Free samples & trade service. Hand-held designer support. 1-888-373-4564';

const collections = [
  {
    title: 'Malibu Walls',
    handle: 'malibu-walls',
    descriptionHtml: '<p>The complete Malibu Walls wallcovering line — coastal-inspired, architectural-grade wallpapers by Designer Wallcoverings. Free samples, trade service, and hand-held designer support on every pattern.</p>',
    seoTitle: 'Malibu Walls Wallcoverings — Coastal Designer Wallpaper',
    seoDesc: `Shop the full Malibu Walls collection — textured, botanical, and coastal wallpapers. ${SEO_SUFFIX}`,
    rules: [{ column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG }],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Coastal',
    handle: 'malibu-walls-coastal',
    descriptionHtml: '<p>Sea-side textures, driftwood palettes, and beach-house botanicals. The Coastal selection from Malibu Walls.</p>',
    seoTitle: 'Coastal Wallcoverings — Malibu Walls Designer Wallpaper',
    seoDesc: `Coastal wallcoverings for beach homes, hotels, and resorts. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Coastal' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Contemporary',
    handle: 'malibu-walls-contemporary',
    descriptionHtml: '<p>Modern lines, clean palettes, and a light touch. The Contemporary edit from Malibu Walls.</p>',
    seoTitle: 'Contemporary Wallcoverings — Malibu Walls',
    seoDesc: `Contemporary wallpaper designs with a modern coastal edge. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Contemporary' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Traditional',
    handle: 'malibu-walls-traditional',
    descriptionHtml: '<p>Damasks, florals, and timeless prints. Traditional wallpapers in the Malibu Walls line.</p>',
    seoTitle: 'Traditional Wallcoverings — Malibu Walls',
    seoDesc: `Classic traditional wallpapers — damask, floral, and timeless motifs. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Traditional' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Transitional',
    handle: 'malibu-walls-transitional',
    descriptionHtml: '<p>The bridge between modern and classic. Transitional designs from Malibu Walls.</p>',
    seoTitle: 'Transitional Wallcoverings — Malibu Walls',
    seoDesc: `Transitional wallpaper blending traditional motifs with modern palettes. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Transitional' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Scandinavian',
    handle: 'malibu-walls-scandinavian',
    descriptionHtml: '<p>Soft neutrals, organic textures, and calm geometry — Scandinavian wallpapers from Malibu Walls.</p>',
    seoTitle: 'Scandinavian Wallcoverings — Malibu Walls',
    seoDesc: `Scandinavian-style wallpapers — soft neutrals and organic textures. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Scandinavian' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Minimalist',
    handle: 'malibu-walls-minimalist',
    descriptionHtml: '<p>Understated patterns and restrained palettes. Minimalist wallpapers from Malibu Walls.</p>',
    seoTitle: 'Minimalist Wallcoverings — Malibu Walls',
    seoDesc: `Minimalist wallpapers — subtle textures and restrained palettes. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Minimalist' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Abstract',
    handle: 'malibu-walls-abstract',
    descriptionHtml: '<p>Painterly textures, organic forms, and unexpected palettes. Abstract wallpapers from Malibu Walls.</p>',
    seoTitle: 'Abstract Wallcoverings — Malibu Walls',
    seoDesc: `Abstract wallpapers — painterly textures and bold patterns. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Abstract' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Grasscloth & Woven',
    handle: 'malibu-walls-grasscloth',
    descriptionHtml: '<p>Real and faux grasscloth weaves. Textured wallcoverings from Malibu Walls.</p>',
    seoTitle: 'Grasscloth Wallcoverings — Malibu Walls',
    seoDesc: `Grasscloth and woven-texture wallpapers. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Grasscloth' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Floral & Botanical',
    handle: 'malibu-walls-floral',
    descriptionHtml: '<p>Gardens, leaves, and soft florals. Botanical wallpapers from Malibu Walls.</p>',
    seoTitle: 'Floral & Botanical Wallcoverings — Malibu Walls',
    seoDesc: `Botanical wallpapers — florals, leaves, and garden prints. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Botanical' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Geometric',
    handle: 'malibu-walls-geometric',
    descriptionHtml: '<p>Clean lines, grids, and architectural geometry. Geometric wallpapers from Malibu Walls.</p>',
    seoTitle: 'Geometric Wallcoverings — Malibu Walls',
    seoDesc: `Geometric wallpapers — grids, chevrons, and architectural patterns. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Geometric' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Stripes',
    handle: 'malibu-walls-stripes',
    descriptionHtml: '<p>Horizontal bands, pinstripes, and broad stripes. Striped wallpapers from Malibu Walls.</p>',
    seoTitle: 'Striped Wallcoverings — Malibu Walls',
    seoDesc: `Striped wallpapers — pinstripes, broad stripes, horizontal bands. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Stripe' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Bedroom',
    handle: 'malibu-walls-bedroom',
    descriptionHtml: '<p>Calming, restful patterns for bedroom walls. Selected from the Malibu Walls line.</p>',
    seoTitle: 'Bedroom Wallcoverings — Malibu Walls',
    seoDesc: `Bedroom wallpapers — calming patterns and restful palettes. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Bedroom' },
    ],
    appliedDisjunctively: false,
  },
  {
    title: 'Malibu Walls — Living Room',
    handle: 'malibu-walls-living-room',
    descriptionHtml: '<p>Statement and supporting patterns for living-room walls. From the Malibu Walls line.</p>',
    seoTitle: 'Living Room Wallcoverings — Malibu Walls',
    seoDesc: `Living-room wallpapers — statement prints and quiet textures. ${SEO_SUFFIX}`,
    rules: [
      { column: 'TAG', relation: 'EQUALS', condition: MALIBU_TAG },
      { column: 'TAG', relation: 'EQUALS', condition: 'Living Room' },
    ],
    appliedDisjunctively: false,
  },
];

async function createCollection(c) {
  const m = `mutation CollectionCreate($input: CollectionInput!) {
    collectionCreate(input: $input) {
      collection { id handle title }
      userErrors { field message }
    }
  }`;
  const input = {
    title: c.title,
    handle: c.handle,
    descriptionHtml: c.descriptionHtml,
    seo: { title: c.seoTitle, description: c.seoDesc },
    ruleSet: {
      appliedDisjunctively: c.appliedDisjunctively,
      rules: c.rules,
    },
  };
  const r = await gql({ query: m, variables: { input } });
  const errs = r?.data?.collectionCreate?.userErrors || [];
  const topErrs = r?.errors || [];
  const col = r?.data?.collectionCreate?.collection;
  if (errs.length || topErrs.length || !col) {
    return { ok:false, errs:[...errs, ...topErrs.map(e=>({message:e.message}))] };
  }
  return { ok:true, collection: col };
}

(async () => {
  log(`=== PHASE 4 DWQW COLLECTIONS — ${collections.length} to create ===`);
  let ok = 0, fail = 0, skipped = 0;
  for (const c of collections) {
    // Check if handle already exists
    const check = await gql({ query:`{ collectionByHandle(handle:"${c.handle}") { id } }` });
    if (check?.data?.collectionByHandle?.id) {
      log(`SKIP existing: ${c.handle}`);
      skipped++;
      continue;
    }
    const r = await createCollection(c);
    if (r.ok) {
      log(`OK  ${c.handle}  →  ${r.collection.id}`);
      ok++;
    } else {
      log(`FAIL ${c.handle}  ${JSON.stringify(r.errs).slice(0,200)}`);
      fail++;
    }
  }
  log(`=== DONE — ok=${ok} fail=${fail} skipped=${skipped} ===`);
})().catch(e => { log('FATAL ' + (e.stack||e.message||e)); process.exit(1); });