← back to Designerwallcoverings
scripts/activate-1838/activate-1838-showroom.mjs
41 lines
// Activate the 1838 $4.25 sample-only drafts as SHOWROOM LINES (Steve-authorized 2026-06-16).
// Per [[showroom-lines-category]]: tag 'Showroom Line' + metafield custom.showroom_line=true(boolean)
// → smart collection 'showroom-lines' auto-includes (vendor!=Phillip Jeffries). status ACTIVE +
// inventory=2026 on ALL variants. EXCLUDES paste-tub accessories (wrong class + placeholder price).
import fs from 'fs'; import os from 'os';
const env={}; for(const l of fs.readFileSync(os.homedir()+'/Projects/secrets-manager/.env','utf8').split('\n')){const m=l.match(/^([A-Z0-9_]+)=(.*)$/);if(m)env[m[1]]=m[2].replace(/^["']|["']$/g,'');}
const API=`https://${env.SHOPIFY_STORE}/admin/api/2024-10/graphql.json`;
const LOCATION='gid://shopify/Location/5795643504';
const APPLY=process.argv.includes('--apply');
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
async function gql(q,v){for(let t=0;t<5;t++){const r=await fetch(API,{method:'POST',headers:{'X-Shopify-Access-Token':env.SHOPIFY_ADMIN_TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v||{}})});if(r.status===429){await sleep(2000);continue;}const j=await r.json();if(j.errors&&JSON.stringify(j.errors).includes('THROTTLED')){await sleep(2000);continue;}return j;}throw new Error('throttled out');}
const sixteen=JSON.parse(fs.readFileSync('/tmp/sl16.json','utf8'));
const isPaste=p=>/paste tub|paste\b.*accessory|accessory.*paste/i.test(p.title);
const wallcoverings=sixteen.filter(p=>!isPaste(p));
const held=sixteen.filter(isPaste);
console.log(`16 sample-only: ${wallcoverings.length} wallcoverings → Showroom Lines | ${held.length} paste-tub accessories HELD`);
held.forEach(p=>console.log(` HOLD: ${p.title.slice(0,44)}`));
console.log(`\n${APPLY?'APPLYING':'DRY-RUN'} on ${wallcoverings.length} wallcoverings\n`);
if(!APPLY){wallcoverings.forEach(p=>console.log(` would SL: ${p.title.slice(0,44)} | ${p.variants.nodes.length}var`));process.exit(0);}
const M_TAG=`mutation($id:ID!,$tags:[String!]!){tagsAdd(id:$id,tags:$tags){userErrors{message}}}`;
const M_MF=`mutation($mf:[MetafieldsSetInput!]!){metafieldsSet(metafields:$mf){userErrors{field message}}}`;
const M_STATUS=`mutation($id:ID!){productUpdate(input:{id:$id,status:ACTIVE}){product{status} userErrors{message}}}`;
const M_INV=`mutation($input:InventorySetQuantitiesInput!){inventorySetQuantities(input:$input){userErrors{message}}}`;
let ok=0,fail=0;const errs=[];
for(const p of wallcoverings){
try{
let r=await gql(M_TAG,{id:p.id,tags:['Showroom Line']}); if(r.data?.tagsAdd?.userErrors?.length)throw new Error('tag:'+JSON.stringify(r.data.tagsAdd.userErrors));
r=await gql(M_MF,{mf:[{ownerId:p.id,namespace:'custom',key:'showroom_line',value:'true',type:'boolean'}]}); if(r.data?.metafieldsSet?.userErrors?.length)throw new Error('mf:'+JSON.stringify(r.data.metafieldsSet.userErrors));
r=await gql(M_STATUS,{id:p.id}); if(r.data?.productUpdate?.userErrors?.length)throw new Error('status:'+JSON.stringify(r.data.productUpdate.userErrors));
const quantities=p.variants.nodes.map(v=>({inventoryItemId:v.inventoryItem.id,locationId:LOCATION,quantity:2026}));
r=await gql(M_INV,{input:{reason:'correction',name:'available',ignoreCompareQuantity:true,quantities}}); if(r.data?.inventorySetQuantities?.userErrors?.length)throw new Error('inv:'+JSON.stringify(r.data.inventorySetQuantities.userErrors));
ok++; console.log(` ✓ ${p.title.slice(0,42)} | Showroom Line + ACTIVE + inv2026 x${quantities.length}`);
}catch(e){fail++;errs.push(`${p.handle}: ${e.message}`);console.log(` ✗ ${p.title.slice(0,42)} — ${e.message.slice(0,90)}`);}
await sleep(350);
}
console.log(`\nDONE: ${ok} showroom-lined, ${fail} failed`);
if(errs.length)console.log(errs.join('\n'));
fs.writeFileSync('scripts/activate-1838/showroom-run.json',JSON.stringify({ts:new Date().toISOString(),ok,fail,held:held.map(h=>h.title),handles:wallcoverings.map(w=>w.handle),errs},null,2));