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