← 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); });