Files
apps-registry/workloads/ecommerce/lib/headless/providers/shopify.ts
T
devopsandClaude Sonnet 5 8742ce714c
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (pull_request) Successful in 35s
Build and Push Frontend / Construir y Subir Imagen (pull_request) Skipped
fix(ecommerce): nav y tiles data-driven contra la jerarquia real de categorias
Sub-fase 2 de Etapa 2 (ADR 0005 seccion 6). Navbar y CategoryTiles usaban
arrays hardcodeados (Figuras/Peluches/Mochilas/Marvel) que ya no coinciden
con los nombres reales de categoria en Medusa desde que se elimino
legacyProductMeta (PR #29) -- todos esos links de navegacion quedaron
rotos silenciosamente (0 productos al filtrar).

- lib/headless: nuevo tipo Category + metodo getCategories() en
  CatalogProvider (medusa via /store/product-categories, mock deriva de
  su catalogo plano, shopify sin implementar como el resto de sus metodos).
- Navbar: arbol real de categorias (6 raices/21 nodos), dropdown de
  hijos por raiz en desktop, lista anidada en el sheet movil. Colecciones
  del sheet ahora se derivan de los productos reales en vez de un array
  hardcodeado con 2 nombres obsoletos (Marvel/Frozen vs. los reales
  Universo Marvel/Reino de Frozen).
- CategoryTiles: tiles de home por categoria raiz real; imagen solo donde
  ya existe asset, gradiente honesto donde no (no hay fotografia para las
  6 raices todavia, deuda explicita del ADR).
- catalog-client/catalog page: cuando el query param `category` es una
  raiz con hijos, se expande a los nombres reales de sus hijos para que
  el filtro (igualdad exacta de producto.category) siga funcionando --
  sin esto, tiles/nav apuntando a una raiz mostrarian 0 productos porque
  ningun producto tiene la raiz asignada directamente.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013dunTcReWMKhWgCPkvAVHD
2026-08-24 21:21:18 -05:00

49 lines
1.6 KiB
TypeScript

import type { CatalogProvider, Product } from "@/lib/headless/types";
interface ShopifyGraphQLResponse<T> {
data?: T;
errors?: Array<{ message: string }>;
}
async function shopifyFetch<T>(query: string, variables: Record<string, unknown> = {}): Promise<T> {
const url = process.env.SHOPIFY_STOREFRONT_URL;
const token = process.env.SHOPIFY_STOREFRONT_TOKEN;
if (!url || !token) throw new Error("Shopify Storefront API no está configurada.");
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Shopify-Storefront-Access-Token": token,
},
body: JSON.stringify({ query, variables }),
next: { revalidate: 300 },
});
if (!response.ok) throw new Error(`Shopify respondió HTTP ${response.status}`);
const payload = (await response.json()) as ShopifyGraphQLResponse<T>;
if (payload.errors?.length) throw new Error(payload.errors.map((error) => error.message).join("; "));
if (!payload.data) throw new Error("Shopify no devolvió datos.");
return payload.data;
}
function notImplemented(): never {
throw new Error("Completa el mapeo Shopify → Product en lib/headless/providers/shopify.ts.");
}
export const shopifyCatalogProvider: CatalogProvider = {
async getProducts() {
await shopifyFetch<{ products: unknown }>(`query Products { products(first: 24) { nodes { id title handle } } }`);
return notImplemented();
},
async getProductBySlug(): Promise<Product | null> {
return notImplemented();
},
async searchProducts(): Promise<Product[]> {
return notImplemented();
},
async getCategories() {
return notImplemented();
},
};