diff --git a/commerce/types.ts b/commerce/types.ts index 1925879a2..3861ca6bb 100644 --- a/commerce/types.ts +++ b/commerce/types.ts @@ -379,6 +379,7 @@ export interface Product extends Omit { sku: string; /** A pointer to another product (or multiple products) for which this product is an accessory or spare part. */ isAccessoryOrSparePartFor?: ProductLeaf[]; + kitItems?: Product[]; } export interface ListItem extends Omit { diff --git a/vtex/loaders/intelligentSearch/productList.ts b/vtex/loaders/intelligentSearch/productList.ts index d5750c4ad..899ade8a3 100644 --- a/vtex/loaders/intelligentSearch/productList.ts +++ b/vtex/loaders/intelligentSearch/productList.ts @@ -1,4 +1,5 @@ import type { Product } from "../../../commerce/types.ts"; +import type { Product as VTEXProduct } from "../../utils/types.ts"; import { STALE } from "../../../utils/fetch.ts"; import { AppContext } from "../../mod.ts"; import { @@ -10,6 +11,7 @@ import { getSegment, withSegmentCookie } from "../../utils/segment.ts"; import { withIsSimilarTo } from "../../utils/similars.ts"; import { toProduct } from "../../utils/transform.ts"; import type { ProductID, Sort } from "../../utils/types.ts"; +import { withIsKitlookTo } from "../../utils/kitlook.ts"; export interface CollectionProps extends CommonProps { // TODO: pattern property isn't being handled by RJSF @@ -50,6 +52,11 @@ export interface CommonProps { * @description Do not return out of stock items */ hideUnavailableItems?: boolean; + /** + * @title Active Kit Items + * @description Send kit items product by product + */ + isKit?: boolean; /** * @description Include similar products */ @@ -127,17 +134,22 @@ const loader = async ( ...params, facets: toPath(facets), }, { ...STALE, headers: withSegmentCookie(segment) }) - .then((res) => res.json()); + .then((res: Response) => res.json()) as { products: VTEXProduct[] }; const options = { baseUrl: url, priceCurrency: "BRL", // config!.defaultPriceCurrency, // TOO }; + // if isKit is true, we need to fetch the kit items for each product + const currentProducts = props.isKit + ? await withIsKitlookTo({ vtexProducts, ctx, params, options }) + : vtexProducts; + // Transform VTEX product format into schema.org's compatible format // If a property is missing from the final `products` array you can add // it in here - const products = vtexProducts + const products = currentProducts .map((p) => toProduct(p, p.items[0], 0, options)); return Promise.all( diff --git a/vtex/utils/kitlook.ts b/vtex/utils/kitlook.ts new file mode 100644 index 000000000..a0b8f0adb --- /dev/null +++ b/vtex/utils/kitlook.ts @@ -0,0 +1,87 @@ +import type { Product } from "../../commerce/types.ts"; +import type { LegacyProduct, Product as VTEXProduct } from "../utils/types.ts"; +import type { AppContext } from "../mod.ts"; +import type { Sort } from "../utils/types.ts"; + +import { toProduct } from "../utils/transform.ts"; +import { getSegment, withSegmentCookie } from "../utils/segment.ts"; + +interface Params { + query: string; + page: number; + count: number; + sort: Sort; + fuzzy: string; + locale?: string; + hideUnavailableItems: boolean; +} + +interface WithKitLookToProps { + vtexProducts: VTEXProduct[]; + params: Params; + options: { + baseUrl: string; + priceCurrency: string; + }; + ctx: AppContext; +} + +interface GETProductKitLookItem { + product: LegacyProduct; + ctx: AppContext; + params: Params; +} + +/** Retrieves a KitLook items for a given product. */ +async function getKitlookItem({ product, ctx, params }: GETProductKitLookItem) { + const { vcsDeprecated } = ctx; + const { items } = product; + if (!items[0]?.isKit || !product.items[0]?.kitItems) return []; + + const kitItems = await vcsDeprecated + ["GET /api/catalog_system/pub/products/search/:term?"]({ + ...params, + fq: product.items[0].kitItems.map((item) => `skuId:${item.itemId}`), + }, { deco: { cache: "stale-while-revalidate" } }); + + return kitItems.json(); +} + +/** Retrieves VTEX products with Kitlook. */ +export async function withIsKitlookTo( + { vtexProducts, params, options, ctx }: WithKitLookToProps, +): Promise> { + const { vcsDeprecated } = ctx; + const segment = getSegment(ctx); + + const productIds = vtexProducts.map((p) => p.productId); + + const products: LegacyProduct[] = await vcsDeprecated + [ + `GET /api/catalog_system/pub/products/search/:term?` + ]( + { + ...params, + fq: productIds.map((productId) => `productId:${productId}`), + }, + { + deco: { cache: "stale-while-revalidate" }, + headers: withSegmentCookie(segment), + }, + ).then((res: Response) => res.json()); + + const formattedProducts = await Promise.all(products.map(async (product) => { + const kitItems: LegacyProduct[] = await getKitlookItem({ + product, + ctx, + params, + }); + + return { + ...product, + kitItems: kitItems.map((p) => toProduct(p, p.items[0], 0, options)), + }; + })); + + return formattedProducts; +} diff --git a/vtex/utils/transform.ts b/vtex/utils/transform.ts index 8c297a3eb..4b0f7c25f 100644 --- a/vtex/utils/transform.ts +++ b/vtex/utils/transform.ts @@ -279,7 +279,7 @@ export const aggregateOffers = ( }; export const toProduct =

( - product: P, + product: P & { kitItems?: Product[] }, sku: P["items"][number], level = 0, // prevent inifinte loop while self referencing the product options: ProductOptions, @@ -347,6 +347,7 @@ export const toProduct =

( return { "@type": "Product", category: categoriesString, + kitItems: product?.kitItems, productID: skuId, url: getProductURL(baseUrl, product, sku.itemId).href, name,