← back to Novasuede Min Fix

reorder-default.mjs

55 lines

import fs from 'fs';
const env=fs.readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8');
const get=k=>(env.match(new RegExp('^'+k+'=(.*)$','m'))||[])[1];
const TOKEN=get('SHOPIFY_ADMIN_TOKEN');
const API='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json';
async function gql(q,v={}){const r=await fetch(API,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v})});return r.json();}

const APPLY = process.argv.includes('--apply');

// products to fix: Sample is at pos1, must move Per Yard to pos1, Wallcovering pos2, Sample pos3
const targets = [
  {handle:'novasuede™-sand', id:'gid://shopify/Product/7923064930355'},
  {handle:'novasuede™-fabric-wallcovering-gentle-pine', id:'gid://shopify/Product/7924270497843'},
];

const FETCH=`query($id:ID!){ product(id:$id){ id title variants(first:20){ nodes{ id title position } } } }`;
const REORDER=`mutation($id:ID!,$moves:[ProductVariantPositionInput!]!){
  productVariantsBulkReorder(productId:$id, positions:$moves){
    product{ id }
    userErrors{ field message }
  }
}`;

function classify(t){
  if(/per yard/i.test(t) && !/wallcover/i.test(t)) return 'peryard';
  if(/wallcover/i.test(t)) return 'wallcovering';
  if(/sample/i.test(t)) return 'sample';
  return 'other';
}
const DESIRED = {peryard:1, wallcovering:2, sample:3, other:99};

const restoreAdditions = [];
for(const t of targets){
  const f=await gql(FETCH,{id:t.id});
  const p=f.data.product;
  const vs=[...p.variants.nodes].sort((a,b)=>a.position-b.position);
  const oldOrder = vs.map(v=>({id:v.id, title:v.title, position:v.position}));
  // build desired positions
  const moves = vs.map(v=>({id:v.id, position:DESIRED[classify(v.title)]}))
                  .sort((a,b)=>a.position-b.position);
  console.log('\n'+p.title);
  console.log('  OLD:', oldOrder.map(v=>v.position+':'+v.title).join('  '));
  console.log('  NEW:', moves.map(m=>m.position+':'+(oldOrder.find(o=>o.id===m.id).title)).join('  '));
  restoreAdditions.push({ type:'variant_reorder', productId:t.id, title:p.title, handle:t.handle, oldOrder });
  if(APPLY){
    const r=await gql(REORDER,{id:t.id, moves});
    const ue=r?.data?.productVariantsBulkReorder?.userErrors||[];
    if(ue.length||r.errors){ console.log('  ERROR:', JSON.stringify(ue), JSON.stringify(r.errors)); }
    else console.log('  APPLIED ok');
  }
}
// write/extend restore map (separate file so we don't clobber the qty restore-map.json)
fs.writeFileSync('restore-map-reorder.json', JSON.stringify(restoreAdditions,null,2));
console.log('\nrestore-map-reorder.json written ('+restoreAdditions.length+' products).', APPLY?'APPLIED':'DRY-RUN (pass --apply to execute)');