← back to Dig Concept Finish

identify.mjs

139 lines

#!/usr/bin/env node
// READ-ONLY. Identify the remaining DIG- CONCEPT-SAMPLE variants in pockets (A) and (B).
// (A) the 4 quarantine sample variants on product 7664622272563 (already $12, SKU repaired to Dig-25-25-*-sample,
//     but Size option value still "Concept Sample").
// (B) the single-variant $4.25 CONCEPT-SAMPLE on handle dig_74754_..._wallpapers (vid 43321302351923).
// Also store-wide scan: any DIG- variant whose Size/sample option is still "Concept Sample"/"Concept Samples".
// NO WRITES.
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dir = path.dirname(fileURLToPath(import.meta.url));
const DOMAIN = 'designer-laboratory-sandbox.myshopify.com';
const API = '2024-10';
const URL = `https://${DOMAIN}/admin/api/${API}/graphql.json`;
const env = fs.readFileSync('/Users/macstudio3/Projects/Designer-Wallcoverings/.env', 'utf8');
const TOK = (env.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m) || [])[1]?.trim();
if (!TOK) { console.error('No SHOPIFY_ADMIN_TOKEN'); process.exit(1); }

async function gql(query, variables) {
  const r = await fetch(URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-Shopify-Access-Token': TOK },
    body: JSON.stringify({ query, variables }),
  });
  const j = await r.json();
  if (j.errors) { console.error(JSON.stringify(j.errors, null, 2)); throw new Error('gql err'); }
  return j.data;
}

const CONCEPT = new Set(['Concept Sample', 'Concept Samples']);

// --- Pocket A: product 7664622272563, dump all variants ---
async function dumpProduct(pid) {
  const q = `query($id:ID!){ product(id:$id){ id title handle status options{name values}
    variants(first:50){ nodes{ id title price sku selectedOptions{name value} } } } }`;
  const d = await gql(q, { id: `gid://shopify/Product/${pid}` });
  return d.product;
}

// --- Pocket B: find single-variant ..._wallpapers sample by handle ---
async function byHandle(handle) {
  const q = `query($h:String!){ products(first:5, query:$h){ nodes{ id title handle status
    options{name values}
    variants(first:50){ nodes{ id title price sku selectedOptions{name value} } } } } }`;
  const d = await gql(q, { h: `handle:${handle}` });
  return d.products.nodes;
}

// --- Store-wide: count remaining DIG- variants still labeled Concept Sample (ACTIVE/DRAFT) ---
async function storeWideConceptCount() {
  let cursor = null, hasNext = true;
  const hits = [];
  while (hasNext) {
    const q = `query($cursor:String){ products(first:100, query:"sku:DIG-* OR sku:Dig-* OR sku:dig-*", after:$cursor){
      pageInfo{hasNextPage endCursor}
      nodes{ id title handle status
        variants(first:30){ nodes{ id title price sku selectedOptions{name value} } } } } }`;
    const d = await gql(q, { cursor });
    for (const p of d.products.nodes) {
      if (p.status === 'ARCHIVED') continue; // active+draft only
      for (const v of p.variants.nodes) {
        for (const o of v.selectedOptions) {
          if (CONCEPT.has(o.value)) {
            hits.push({ product: p.id, handle: p.handle, status: p.status, vid: v.id, vtitle: v.title, price: v.price, sku: v.sku, optName: o.name, optValue: o.value });
          }
        }
      }
    }
    hasNext = d.products.pageInfo.hasNextPage;
    cursor = d.products.pageInfo.endCursor;
    process.stderr.write('.');
  }
  process.stderr.write('\n');
  return hits;
}

const QUAR_IDS = new Set([
  'gid://shopify/ProductVariant/44019704660019',
  'gid://shopify/ProductVariant/44019704692787',
  'gid://shopify/ProductVariant/44035167813683',
  'gid://shopify/ProductVariant/44035167846451',
]);
const B_VID = 'gid://shopify/ProductVariant/43321302351923';

const redactSku = (s) => (s && s.includes('@gpt')) ? '@gpt(REDACTED)' : s;

(async () => {
  // A
  const pA = await dumpProduct('7664622272563');
  const aVariants = pA.variants.nodes.filter(v => QUAR_IDS.has(v.id)).map(v => ({
    vid: v.id, title: v.title, price: v.price, sku: redactSku(v.sku),
    size: (v.selectedOptions.find(o => o.name === 'Size') || {}).value,
    selectedOptions: v.selectedOptions.map(o => ({ name: o.name, value: o.value })),
  }));

  // B
  const bNodes = await byHandle('dig_74754_vintage_wallpaper_designer_wallcoverings');
  // also try the explicit _wallpapers handle pattern
  let bProduct = null, bVar = null;
  for (const p of bNodes) {
    const hit = p.variants.nodes.find(v => v.id === B_VID);
    if (hit) { bProduct = p; bVar = hit; break; }
  }
  // fallback: direct variant lookup
  if (!bVar) {
    const d = await gql(`query($id:ID!){ productVariant(id:$id){ id title price sku
      selectedOptions{name value} product{ id title handle status options{name values} } } }`, { id: B_VID });
    if (d.productVariant) { bVar = d.productVariant; bProduct = d.productVariant.product; }
  }
  const bOut = bVar ? {
    product: bProduct.id, handle: bProduct.handle, title: bProduct.title, status: bProduct.status,
    vid: bVar.id, vtitle: bVar.title, price: bVar.price, sku: redactSku(bVar.sku),
    options: bProduct.options,
    size: (bVar.selectedOptions.find(o => o.name === 'Size') || {}).value,
    selectedOptions: bVar.selectedOptions.map(o => ({ name: o.name, value: o.value })),
  } : null;

  // Store-wide
  const allHits = await storeWideConceptCount();
  const sanitized = allHits.map(h => ({ ...h, sku: redactSku(h.sku) }));

  const out = {
    pocketA: { product: pA.id, handle: pA.handle, title: pA.title, status: pA.status, options: pA.options, variants: aVariants },
    pocketB: bOut,
    storeWideConceptRemaining: { count: sanitized.length, hits: sanitized },
  };
  fs.writeFileSync(path.join(__dir, 'identify_result.json'), JSON.stringify(out, null, 2));

  console.log('=== POCKET A (product 7664622272563) ===');
  console.log('status:', pA.status, '| handle:', pA.handle);
  for (const v of aVariants) console.log(`  ${v.vid.split('/').pop()} | "${v.title}" | $${v.price} | Size="${v.size}" | sku=${v.sku}`);
  console.log('\n=== POCKET B ===');
  if (bOut) console.log(`  ${bOut.vid.split('/').pop()} | handle=${bOut.handle} | status=${bOut.status} | "${bOut.vtitle}" | $${bOut.price} | Size="${bOut.size}" | sku=${bOut.sku}\n  options=${JSON.stringify(bOut.options)}`);
  else console.log('  B variant NOT FOUND');
  console.log('\n=== STORE-WIDE remaining "Concept Sample" (active+draft DIG) ===');
  console.log('  count:', sanitized.length);
  for (const h of sanitized) console.log(`  ${h.vid.split('/').pop()} | ${h.status} | ${h.handle} | "${h.vtitle}" | $${h.price} | ${h.optName}="${h.optValue}" | sku=${h.sku}`);
})();