diff --git a/linx/loaders/auction/apiList.ts b/linx/loaders/auction/apiList.ts new file mode 100644 index 000000000..3a61fd539 --- /dev/null +++ b/linx/loaders/auction/apiList.ts @@ -0,0 +1,43 @@ +import { Auction } from "../../utils/types/auctionAPI.ts"; +import type { AppContext } from "../../../linx/mod.ts"; + +let cachedPromise: Promise | null = null; +let lastUpdate: number = Date.now(); +/** + * @title Linx Integration + * @description Search Wishlist loader + */ +const loader = ( + _props: unknown, + _req: Request, + ctx: AppContext, +): Promise => { + const { layer } = ctx; + + if (cachedPromise && Date.now() - lastUpdate < (60 * 1000)) { + return cachedPromise; + } + + lastUpdate = Date.now(); + const responsePromise = layer + ["POST /v1/Catalog/API.svc/web/SearchProductAuctions"]( + {}, + // @ts-ignore body is required + { body: {} }, + ).then(async (response) => { + return await response.json(); + }); + + cachedPromise = responsePromise; + return responsePromise; +}; + +export const cache = "stale-while-revalidate"; + +export const cacheKey = (_props: unknown, req: Request, _ctx: AppContext) => { + const url = new URL(req.url); + url.pathname = "/v1/Catalog/API.svc/web/SearchProductAuctions"; + return url.href; +}; + +export default loader; diff --git a/linx/loaders/auction/detailsPage.ts b/linx/loaders/auction/detailsPage.ts index e1b089315..aa0972bff 100644 --- a/linx/loaders/auction/detailsPage.ts +++ b/linx/loaders/auction/detailsPage.ts @@ -1,5 +1,6 @@ import type { AppContext } from "../../../linx/mod.ts"; import { nullOnNotFound } from "../../../utils/http.ts"; +import { removeCFHeaders } from "../../../website/handlers/proxy.ts"; import { isAuctionDetailModel } from "../../utils/paths.ts"; import { toAuctionDetail } from "../../utils/transform.ts"; import { Model as AuctionDetail } from "../../utils/types/auctionDetailJSON.ts"; @@ -17,10 +18,15 @@ const loader = async ( const upstream = new URL(req.url); const splat = upstream.pathname.slice(1); + const headers = new Headers(req.headers); + removeCFHeaders(headers); + const response = await api["GET /*splat"]({ splat, }, { - headers: req.headers, + headers: { + ...headers, + }, }).catch(nullOnNotFound); if (response === null) { diff --git a/linx/loaders/product/list.ts b/linx/loaders/product/list.ts index e207abace..7d720e16b 100644 --- a/linx/loaders/product/list.ts +++ b/linx/loaders/product/list.ts @@ -3,7 +3,7 @@ import { STALE } from "../../../utils/fetch.ts"; import { nullOnNotFound } from "../../../utils/http.ts"; import type { AppContext } from "../../mod.ts"; import { isGridProductsModel } from "../../utils/paths.ts"; -import { toProduct } from "../../utils/transform.ts"; +import { addAuctions, toProduct } from "../../utils/transform.ts"; export interface Props { /** @description e.g.: /listas/vitrine-destaque */ @@ -39,12 +39,17 @@ const loader = async ( const products = response?.Model?.Grid?.Products ?? []; - return products.map((product) => - toProduct(product, product.ProductSelection?.SkuID, { - cdn, - url, - currency: "BRL", - }) + return await Promise.all( + products.map(async (product) => + await addAuctions( + toProduct(product, product.ProductSelection?.SkuID, { + cdn, + url, + currency: "BRL", + }), + ctx, + ) + ), ); }; diff --git a/linx/loaders/product/listingPage.ts b/linx/loaders/product/listingPage.ts index f98fa62a1..416f1a16e 100644 --- a/linx/loaders/product/listingPage.ts +++ b/linx/loaders/product/listingPage.ts @@ -2,6 +2,7 @@ import type { ProductListingPage } from "../../../commerce/types.ts"; import { AppContext } from "../../mod.ts"; import { isGridProductsModel } from "../../utils/paths.ts"; import { + addAuctions, toBreadcrumbList, toFilters, toProduct, @@ -77,12 +78,17 @@ const loader = async ( } = forProducts; const { Model: { Grid: { Facets } } } = forProducts; - const products = Products.map((product) => - toProduct(product, product.ProductSelection?.SkuID, { - cdn, - currency: "BRL", - url, - }) + const products = await Promise.all( + Products.map(async (product) => + await addAuctions( + toProduct(product, product.ProductSelection?.SkuID, { + cdn, + currency: "BRL", + url, + }), + ctx, + ) + ), ); return { diff --git a/linx/manifest.gen.ts b/linx/manifest.gen.ts index 51bdd033e..69bf836ff 100644 --- a/linx/manifest.gen.ts +++ b/linx/manifest.gen.ts @@ -20,6 +20,7 @@ import * as $$$$0 from "./handlers/sitemap.ts"; import * as $$$1 from "./loaders/auction/detailsPage.ts"; import * as $$$2 from "./loaders/auction/list.ts"; import * as $$$0 from "./loaders/auction/ListBids.ts"; +import * as $$$100000 from "./loaders/auction/apiList.ts"; import * as $$$3 from "./loaders/cart.ts"; import * as $$$4 from "./loaders/page.ts"; import * as $$$5 from "./loaders/pages.ts"; @@ -40,6 +41,7 @@ const manifest = { "linx/loaders/auction/detailsPage.ts": $$$1, "linx/loaders/auction/list.ts": $$$2, "linx/loaders/auction/ListBids.ts": $$$0, + "linx/loaders/auction/apiList.ts": $$$100000, "linx/loaders/cart.ts": $$$3, "linx/loaders/page.ts": $$$4, "linx/loaders/pages.ts": $$$5, diff --git a/linx/utils/layer.ts b/linx/utils/layer.ts index 2d1f8c9e7..b8a5280ea 100644 --- a/linx/utils/layer.ts +++ b/linx/utils/layer.ts @@ -7,6 +7,8 @@ import { ShareWishlistResponse, } from "./types/wishlistJSON.ts"; +import { Auction } from "./types/auctionAPI.ts"; + export interface LayerAPI { "POST /v1/Profile/API.svc/web/SearchWishlist": { response: SearchWishlistResponse; @@ -21,6 +23,10 @@ export interface LayerAPI { }; }; + "POST /v1/Catalog/API.svc/web/SearchProductAuctions": { + response: Auction[]; + }; + "POST /v1/Profile/API.svg/web/ShareWishlist": { response: ShareWishlistResponse; body: { diff --git a/linx/utils/transform.ts b/linx/utils/transform.ts index 61f4f13fd..0ada86749 100644 --- a/linx/utils/transform.ts +++ b/linx/utils/transform.ts @@ -31,6 +31,7 @@ import { ProductAuction } from "./types/auctionJSON.ts"; import { Model as ProductAuctionDetail } from "./types/auctionDetailJSON.ts"; import { Product as LinxProductGetByIdJSON } from "./types/productByIdJSON.ts"; import { Associations } from "./types/associationsJSON.ts"; +import type { AppContext } from "../mod.ts"; type LinxProductGroup = | LinxProductGroupList @@ -58,6 +59,45 @@ const pickVariant = (variants: LinxProduct[], variantId: number | null) => { return variants[0]; }; +export const addAuctions = async (product: Product, ctx: AppContext) => { + const leiloes = await ctx.invoke.linx.loaders.auction.apiList(); + + const auctionPropertyIndex = product.additionalProperty?.findIndex( + (prop) => prop.name === "id_leilao", + ); + + if (auctionPropertyIndex !== undefined && auctionPropertyIndex !== -1) { + product.additionalProperty?.splice(auctionPropertyIndex, 1); + } + + const now = new Date().getTime() - (3 * 60 * 60 * 1000); + + const parseDate = (dateString: string) => { + const match = dateString.match(/\/Date\((\d+)([-+]\d{4})?\)\//); + return match ? parseInt(match[1], 10) : null; + }; + + leiloes?.find((leilao) => { + const visibleFrom = parseDate(leilao.VisibleFrom); + const visibleTo = parseDate(leilao.VisibleTo); + + let isVisible = (visibleFrom !== null && now >= visibleFrom) || + (visibleFrom === null); + isVisible &&= (visibleTo !== null && now <= visibleTo) || + (visibleTo === null); + isVisible &&= visibleTo !== null && visibleFrom !== null; + if (`${leilao.SkuID}` === product.productID && isVisible) { + product.additionalProperty = product.additionalProperty || []; + product.additionalProperty.push({ + "@type": "PropertyValue", + name: "id_leilao", + value: `${leilao.ProductAuctionID}`, + }); + } + }); + return product; +}; + const toOffer = (variant: LinxProduct, product: LinxProductGroup): Offer => { const item = variant.Price ? variant : product; const priceSpecification: UnitPriceSpecification[] = [ @@ -205,12 +245,20 @@ export const toProduct = ( additionalType: "categoryItem", })); + const displayPrice = { + "@type": "PropertyValue" as const, + name: product.DisplayPrice, + value: product.DisplayPrice, + additionalType: "displayPrice", + }; + const additionalProperty = [ ...skuOptions, ...metadatas, ...descriptions, ...prodOptions, ...categoryItems, + displayPrice, ]; const hasVariant = level < 1 diff --git a/linx/utils/types/auctionAPI.ts b/linx/utils/types/auctionAPI.ts new file mode 100644 index 000000000..dfbba28bf --- /dev/null +++ b/linx/utils/types/auctionAPI.ts @@ -0,0 +1,18 @@ +export type Auction = { + BidCount: number; + ExecutionFrom: string; + ExecutionTo: string; + IsVisible: boolean; + LastBidAmount: number; + LastBidCustomerID: number; + LastBidCustomerName: string; + LastBidCustomerState: string; + Name: string; + ProductAuctionID: number; + ProductID: number; + SkuID: number; + Status: number; + StatusInternal: string; + VisibleFrom: string; + VisibleTo: string; +}; diff --git a/linx/utils/types/auctionJSON.ts b/linx/utils/types/auctionJSON.ts index e2bfd1529..c8fe9360c 100644 --- a/linx/utils/types/auctionJSON.ts +++ b/linx/utils/types/auctionJSON.ts @@ -181,7 +181,6 @@ export type Alias = | "WarrantyDescription" | "LongDescription" | "Specifications" - | "id_leilao" | "SelecionarAro" | "Pedra" | "rank_products_mostseen"