diff --git a/wake/actions/cart/partnerAssociate.ts b/wake/actions/cart/partnerAssociate.ts index 0aae919e1..409fd987c 100644 --- a/wake/actions/cart/partnerAssociate.ts +++ b/wake/actions/cart/partnerAssociate.ts @@ -8,6 +8,7 @@ import { CheckoutPartnerAssociateMutationVariables, } from "../../utils/graphql/storefront.graphql.gen.ts"; import { parseHeaders } from "../../utils/parseHeaders.ts"; +import { setPartnerCookie } from "../../utils/partner.ts"; export interface Props { partnerAccessToken: string; @@ -21,6 +22,7 @@ const action = async ( const { storefront } = ctx; const cartId = getCartCookie(req.headers); const headers = parseHeaders(req.headers); + const { partnerAccessToken } = props; if (!cartId) { throw new HttpError(400, "Missing cart cookie"); @@ -30,7 +32,7 @@ const action = async ( CheckoutPartnerAssociateMutation, CheckoutPartnerAssociateMutationVariables >({ - variables: { checkoutId: cartId, ...props }, + variables: { checkoutId: cartId, partnerAccessToken }, ...CheckoutPartnerAssociate, }, { headers }); @@ -40,6 +42,8 @@ const action = async ( setCartCookie(ctx.response.headers, checkoutId); } + setPartnerCookie(ctx.response.headers, partnerAccessToken); + return data.checkout ?? {}; }; diff --git a/wake/actions/cart/partnerDisassociate.ts b/wake/actions/cart/partnerDisassociate.ts index 1986d20a5..619417ef3 100644 --- a/wake/actions/cart/partnerDisassociate.ts +++ b/wake/actions/cart/partnerDisassociate.ts @@ -8,6 +8,7 @@ import { CheckoutPartnerDisassociateMutationVariables, } from "../../utils/graphql/storefront.graphql.gen.ts"; import { parseHeaders } from "../../utils/parseHeaders.ts"; +import { deletePartnerCookie } from "../../utils/partner.ts"; const action = async ( _props: unknown, @@ -36,6 +37,8 @@ const action = async ( setCartCookie(ctx.response.headers, checkoutId); } + deletePartnerCookie(ctx.response.headers); + return data.checkout ?? {}; }; diff --git a/wake/loaders/productDetailsPage.ts b/wake/loaders/productDetailsPage.ts index 560e93a15..f88e97e93 100644 --- a/wake/loaders/productDetailsPage.ts +++ b/wake/loaders/productDetailsPage.ts @@ -10,6 +10,7 @@ import { GetProductQueryVariables, } from "../utils/graphql/storefront.graphql.gen.ts"; import { parseHeaders } from "../utils/parseHeaders.ts"; +import { getPartnerCookie } from "../utils/partner.ts"; import { parseSlug, toBreadcrumbList, toProduct } from "../utils/transform.ts"; export interface Props { @@ -25,12 +26,14 @@ export interface Props { async function loader( props: Props, req: Request, - ctx: AppContext, + ctx: AppContext ): Promise { const url = new URL(req.url); const { slug, buyTogether, includeSameParent } = props; const { storefront } = ctx; + const partnerAccessToken = getPartnerCookie(req.headers); + const headers = parseHeaders(req.headers); if (!slug) return null; @@ -45,23 +48,24 @@ async function loader( const { buyList: wakeBuyList } = await storefront.query< BuyListQuery, BuyListQueryVariables - >({ - variables: { id: productId }, - ...GetBuyList, - }, { - headers, - }); + >( + { + variables: { id: productId, partnerAccessToken }, + ...GetBuyList, + }, + { + headers, + } + ); const buyListProducts = await Promise.all( - wakeBuyList?.buyListProducts?.map(async ( - buyListProduct, - ) => { + wakeBuyList?.buyListProducts?.map(async (buyListProduct) => { if (!buyListProduct) return; const { productId, includeSameParent, quantity } = buyListProduct; - const buyListProductPage = await ctx.invoke.wake.loaders - .productDetailsPage({ + const buyListProductPage = + await ctx.invoke.wake.loaders.productDetailsPage({ // 'slug' its just to fit the parse function of loader slug: `slug-${productId}`, includeSameParent, @@ -77,7 +81,7 @@ async function loader( }); return buyListProductPage.product; - }) ?? [], + }) ?? [] ).then((maybeProductList) => maybeProductList.filter((node): node is Product => Boolean(node)) ); @@ -85,12 +89,19 @@ async function loader( const { product: wakeProduct } = await storefront.query< GetProductQuery, GetProductQueryVariables - >({ - variables: { productId, includeParentIdVariants: includeSameParent }, - ...GetProduct, - }, { - headers, - }); + >( + { + variables: { + productId, + includeParentIdVariants: includeSameParent, + partnerAccessToken, + }, + ...GetProduct, + }, + { + headers, + } + ); const wakeProductOrBuyList = wakeProduct || wakeBuyList; @@ -98,51 +109,55 @@ async function loader( return null; } - const variantsItems = await ctx.invoke.wake.loaders.productList({ - first: MAXIMUM_REQUEST_QUANTITY, - sortDirection: "ASC", - sortKey: "RANDOM", - filters: { productId: [productId] }, - }) ?? []; + const variantsItems = + (await ctx.invoke.wake.loaders.productList({ + first: MAXIMUM_REQUEST_QUANTITY, + sortDirection: "ASC", + sortKey: "RANDOM", + filters: { productId: [productId] }, + })) ?? []; const buyTogetherItens = buyTogether && !!wakeProductOrBuyList.buyTogether?.length - ? await ctx.invoke.wake.loaders.productList({ - first: MAXIMUM_REQUEST_QUANTITY, - sortDirection: "ASC", - sortKey: "RANDOM", - filters: { - productId: wakeProductOrBuyList.buyTogether?.map((bt) => - bt!.productId - ), - mainVariant: true, - }, - getVariations: true, - }) ?? [] + ? (await ctx.invoke.wake.loaders.productList({ + first: MAXIMUM_REQUEST_QUANTITY, + sortDirection: "ASC", + sortKey: "RANDOM", + filters: { + productId: wakeProductOrBuyList.buyTogether?.map( + (bt) => bt!.productId + ), + mainVariant: true, + }, + getVariations: true, + })) ?? [] : []; const product = toProduct( wakeProductOrBuyList, { base: url }, variantsItems, - variantId, + variantId ); return { "@type": "ProductDetailsPage", - breadcrumbList: toBreadcrumbList(wakeProductOrBuyList.breadcrumbs ?? [], { - base: url, - }, product), + breadcrumbList: toBreadcrumbList( + wakeProductOrBuyList.breadcrumbs ?? [], + { + base: url, + }, + product + ), product: { ...product, isAccessoryOrSparePartFor: buyListProducts, - isRelatedTo: buyTogetherItens?.map( - (buyItem) => { + isRelatedTo: + buyTogetherItens?.map((buyItem) => { return { ...buyItem, additionalType: "BuyTogether", }; - }, - ) ?? [], + }) ?? [], }, seo: { canonical: product.isVariantOf?.url ?? "", diff --git a/wake/loaders/productList.ts b/wake/loaders/productList.ts index b529f10b6..5bcb2e802 100644 --- a/wake/loaders/productList.ts +++ b/wake/loaders/productList.ts @@ -8,6 +8,7 @@ import { ProductFragment, } from "../utils/graphql/storefront.graphql.gen.ts"; import { parseHeaders } from "../utils/parseHeaders.ts"; +import { getPartnerCookie } from "../utils/partner.ts"; import { toProduct } from "../utils/transform.ts"; export interface StockFilter { @@ -141,6 +142,7 @@ const productListLoader = async ( ): Promise => { const url = new URL(req.url); const { storefront } = ctx; + const partnerAccessToken = getPartnerCookie(req.headers); const headers = parseHeaders(req.headers); @@ -148,7 +150,7 @@ const productListLoader = async ( GetProductsQuery, GetProductsQueryVariables >({ - variables: props, + variables: { ...props, partnerAccessToken }, ...GetProducts, }, { headers, diff --git a/wake/loaders/productListingPage.ts b/wake/loaders/productListingPage.ts index 35c990067..2c9061f5c 100644 --- a/wake/loaders/productListingPage.ts +++ b/wake/loaders/productListingPage.ts @@ -27,6 +27,7 @@ import { SortDirection, } from "../utils/graphql/storefront.graphql.gen.ts"; import { parseHeaders } from "../utils/parseHeaders.ts"; +import { getPartnerCookie } from "../utils/partner.ts"; import { FILTER_PARAM, toBreadcrumbList, @@ -107,9 +108,17 @@ export interface Props { pageHref?: string; /** + * @title Partner Param * @description page param to partners page + * @deprecated */ slug?: RequestURLParam; + + /** + * @title Partner Param + * @description page param to partners page + */ + partnerAlias?: RequestURLParam; } const OUTSIDE_ATTRIBUTES_FILTERS = ["precoPor"]; @@ -138,54 +147,69 @@ const filtersFromParams = (searchParams: URLSearchParams) => { const searchLoader = async ( props: Props, req: Request, - ctx: AppContext, + ctx: AppContext ): Promise => { // get url from params - const url = new URL(req.url).pathname === "/live/invoke" - ? new URL(props.pageHref || req.headers.get("referer") || req.url) - : new URL(props.pageHref || req.url); + const url = + new URL(req.url).pathname === "/live/invoke" + ? new URL(props.pageHref || req.headers.get("referer") || req.url) + : new URL(props.pageHref || req.url); const { storefront } = ctx; + const partnerAlias = props.partnerAlias ?? props.slug; + + const partnerAccessTokenCookie = getPartnerCookie(req.headers); + const headers = parseHeaders(req.headers); const limit = Number(url.searchParams.get("tamanho") ?? props.limit ?? 12); const filters = filtersFromParams(url.searchParams) ?? props.filters; - const sort = (url.searchParams.get("sort") as SortValue | null) ?? + const sort = + (url.searchParams.get("sort") as SortValue | null) ?? (url.searchParams.get("ordenacao") as SortValue | null) ?? props.sort ?? "SALES:DESC"; - const page = props.page ?? Number(url.searchParams.get("page")) ?? - Number(url.searchParams.get("pagina")) ?? 0; + const page = + props.page ?? + Number(url.searchParams.get("page")) ?? + Number(url.searchParams.get("pagina")) ?? + 0; const query = props.query ?? url.searchParams.get("busca"); const operation = props.operation ?? "AND"; const [sortKey, sortDirection] = sort.split(":") as [ ProductSortKeys, - SortDirection, + SortDirection ]; const onlyMainVariant = props.onlyMainVariant ?? true; const [minimumPrice, maximumPrice] = - url.searchParams.getAll("filtro")?.find((i) => i.startsWith("precoPor")) - ?.split(":")[1]?.split(";").map(Number) ?? - url.searchParams.get("precoPor")?.split(";").map(Number) ?? []; + url.searchParams + .getAll("filtro") + ?.find((i) => i.startsWith("precoPor")) + ?.split(":")[1] + ?.split(";") + .map(Number) ?? + url.searchParams.get("precoPor")?.split(";").map(Number) ?? + []; const offset = page <= 1 ? 0 : (page - 1) * limit; - const partnerData = props.slug - ? await storefront.query< - GetPartnersQuery, - GetPartnersQueryVariables - >({ - variables: { first: 1, alias: [props.slug] }, - ...GetPartners, - }, { headers }) + const partnerData = partnerAlias + ? await storefront.query( + { + variables: { first: 1, alias: [partnerAlias] }, + ...GetPartners, + }, + { headers } + ) : null; - const partnerAccessToken = partnerData?.partners?.edges?.[0]?.node - ?.partnerAccessToken; + const partnerAccessToken = + partnerData?.partners?.edges?.[0]?.node?.partnerAccessToken ?? + partnerAccessTokenCookie; if (partnerAccessToken) { try { @@ -197,14 +221,17 @@ const searchLoader = async ( } } - const urlData = await storefront.query({ - variables: { - url: url.pathname, + const urlData = await storefront.query( + { + variables: { + url: url.pathname, + }, + ...GetURL, }, - ...GetURL, - }, { - headers, - }); + { + headers, + } + ); const isHotsite = urlData.uri?.kind === "HOTSITE"; @@ -224,20 +251,20 @@ const searchLoader = async ( const data = isHotsite ? await storefront.query({ - variables: { - ...commonParams, - url: url.pathname, - }, - ...Hotsite, - }) + variables: { + ...commonParams, + url: url.pathname, + }, + ...Hotsite, + }) : await storefront.query({ - variables: { - ...commonParams, - query, - operation, - }, - ...Search, - }); + variables: { + ...commonParams, + query, + operation, + }, + ...Search, + }); const products = data?.result?.productsByOffset?.items ?? []; @@ -245,9 +272,8 @@ const searchLoader = async ( const previousPage = new URLSearchParams(url.searchParams); const hasNextPage = Boolean( - (data?.result?.productsByOffset?.totalCount ?? 0) / - limit > - (data?.result?.productsByOffset?.page ?? 0), + (data?.result?.productsByOffset?.totalCount ?? 0) / limit > + (data?.result?.productsByOffset?.page ?? 0) ); const hasPreviousPage = page > 1; @@ -274,16 +300,17 @@ const searchLoader = async ( const title = isHotsite ? (data as HotsiteQuery)?.result?.seo?.find((i) => i?.type === "TITLE") - ?.content + ?.content : capitalize(query || ""); const description = isHotsite - ? (data as HotsiteQuery)?.result?.seo?.find((i) => - i?.name === "description" - )?.content + ? (data as HotsiteQuery)?.result?.seo?.find( + (i) => i?.name === "description" + )?.content : capitalize(query || ""); - const canonical = - new URL(isHotsite ? `/${(data as HotsiteQuery)?.result?.url}` : url, url) - .href; + const canonical = new URL( + isHotsite ? `/${(data as HotsiteQuery)?.result?.url}` : url, + url + ).href; return { "@type": "ProductListingPage", @@ -305,8 +332,8 @@ const searchLoader = async ( products: products ?.filter((p): p is ProductFragment => Boolean(p)) .map((variant) => { - const productVariations = variations?.filter((v) => - v.inProductGroupWithID === variant.productId + const productVariations = variations?.filter( + (v) => v.inProductGroupWithID === variant.productId ); return toProduct(variant, { base: url }, productVariations); diff --git a/wake/loaders/recommendations.ts b/wake/loaders/recommendations.ts index 2a7b37431..2d43d0978 100644 --- a/wake/loaders/recommendations.ts +++ b/wake/loaders/recommendations.ts @@ -10,6 +10,7 @@ import { import { parseSlug, toProduct } from "../utils/transform.ts"; import { parseHeaders } from "../utils/parseHeaders.ts"; import { getVariations } from "../utils/getVariations.ts"; +import { getPartnerCookie } from "../utils/partner.ts"; export interface Props { /** @@ -35,6 +36,7 @@ const productRecommendationsLoader = async ( const url = new URL(req.url); const { storefront } = ctx; const { slug, quantity, algorithm = "DEFAULT" } = props; + const partnerAccessToken = getPartnerCookie(req.headers); const headers = parseHeaders(req.headers); @@ -44,7 +46,7 @@ const productRecommendationsLoader = async ( ProductRecommendationsQuery, ProductRecommendationsQueryVariables >({ - variables: { quantity, productId, algorithm }, + variables: { quantity, productId, algorithm, partnerAccessToken }, ...ProductRecommendations, }, { headers }); diff --git a/wake/loaders/suggestion.ts b/wake/loaders/suggestion.ts index c580511e1..e059ab018 100644 --- a/wake/loaders/suggestion.ts +++ b/wake/loaders/suggestion.ts @@ -7,6 +7,7 @@ import { ProductFragment, } from "../utils/graphql/storefront.graphql.gen.ts"; import { parseHeaders } from "../utils/parseHeaders.ts"; +import { getPartnerCookie } from "../utils/partner.ts"; import { toProduct } from "../utils/transform.ts"; export interface Props { @@ -26,6 +27,7 @@ const loader = async ( const { storefront } = ctx; const { query, limit = 10 } = props; + const partnerAccessToken = getPartnerCookie(req.headers); const headers = parseHeaders(req.headers); if (!query) return null; @@ -34,7 +36,7 @@ const loader = async ( AutocompleteQuery, AutocompleteQueryVariables >({ - variables: { query, limit }, + variables: { query, limit, partnerAccessToken }, ...Autocomplete, }, { headers, diff --git a/wake/utils/graphql/queries.ts b/wake/utils/graphql/queries.ts index 262a04bb3..905085681 100644 --- a/wake/utils/graphql/queries.ts +++ b/wake/utils/graphql/queries.ts @@ -944,8 +944,8 @@ export const WishlistReducedProduct = gql` export const GetProduct = { fragments: [SingleProductPart, SingleProduct, ProductVariant], query: - gql`query GetProduct($productId: Long!, $includeParentIdVariants: Boolean) { - product(productId: $productId) { + gql`query GetProduct($productId: Long!, $includeParentIdVariants: Boolean, $partnerAccessToken: String) { + product(productId: $productId , partnerAccessToken: $partnerAccessToken) { ...SingleProduct } }`, @@ -966,7 +966,7 @@ export const CreateCart = { export const GetProducts = { fragments: [Product], query: - gql`query GetProducts($filters: ProductExplicitFiltersInput!, $first: Int!, $sortDirection: SortDirection!, $sortKey: ProductSortKeys, $after: String) { products(filters: $filters, first: $first, sortDirection: $sortDirection, sortKey: $sortKey, after: $after) { + gql`query GetProducts($filters: ProductExplicitFiltersInput!, $first: Int!, $sortDirection: SortDirection!, $sortKey: ProductSortKeys, $after: String, $partnerAccessToken: String) { products(filters: $filters, first: $first, sortDirection: $sortDirection, sortKey: $sortKey, after: $after, partnerAccessToken: $partnerAccessToken) { nodes { ...Product } totalCount pageInfo{ diff --git a/wake/utils/graphql/storefront.graphql.gen.ts b/wake/utils/graphql/storefront.graphql.gen.ts index aec4c3bb9..8db7b3e02 100644 --- a/wake/utils/graphql/storefront.graphql.gen.ts +++ b/wake/utils/graphql/storefront.graphql.gen.ts @@ -5012,6 +5012,7 @@ export type WishlistReducedProductFragment = { productId?: any | null, productNa export type GetProductQueryVariables = Exact<{ productId: Scalars['Long']['input']; includeParentIdVariants?: InputMaybe; + partnerAccessToken?: InputMaybe; }>; @@ -5035,6 +5036,7 @@ export type GetProductsQueryVariables = Exact<{ sortDirection: SortDirection; sortKey?: InputMaybe; after?: InputMaybe; + partnerAccessToken?: InputMaybe; }>; diff --git a/wake/utils/partner.ts b/wake/utils/partner.ts new file mode 100644 index 000000000..bb538e4e1 --- /dev/null +++ b/wake/utils/partner.ts @@ -0,0 +1,22 @@ +import { deleteCookie, getCookies, setCookie } from "std/http/cookie.ts"; + +const PARTNER_COOKIE = "partner-token"; + +const TEN_DAYS_MS = 10 * 24 * 3600 * 1_000; + +export const getPartnerCookie = (headers: Headers): string | undefined => { + const cookies = getCookies(headers); + + return cookies[PARTNER_COOKIE]; +}; + +export const setPartnerCookie = (headers: Headers, PartnerToken: string) => + setCookie(headers, { + name: PARTNER_COOKIE, + value: PartnerToken, + path: "/", + expires: new Date(Date.now() + TEN_DAYS_MS), + }); + +export const deletePartnerCookie = (headers: Headers) => + deleteCookie(headers, PARTNER_COOKIE);