diff --git a/framework/commerce/api/index.ts b/framework/commerce/api/index.ts index 56ac58dad..77b2eeb7e 100644 --- a/framework/commerce/api/index.ts +++ b/framework/commerce/api/index.ts @@ -5,7 +5,6 @@ export interface CommerceAPIConfig { commerceUrl: string apiToken: string cartCookie: string - cartIdCookie: string cartCookieMaxAge: number customerCookie: string fetch( diff --git a/framework/reactioncommerce/.env.template b/framework/reactioncommerce/.env.template index e69de29bb..039387377 100644 --- a/framework/reactioncommerce/.env.template +++ b/framework/reactioncommerce/.env.template @@ -0,0 +1,2 @@ +REACTION_STORE_DOMAIN= +REACTION_SHOP_ID= diff --git a/framework/reactioncommerce/api/index.ts b/framework/reactioncommerce/api/index.ts index 46eab8a65..0ec59fe67 100644 --- a/framework/reactioncommerce/api/index.ts +++ b/framework/reactioncommerce/api/index.ts @@ -18,7 +18,14 @@ if (!API_URL) { import fetchGraphqlApi from './utils/fetch-graphql-api' -export interface ReactionCommerceConfig extends CommerceAPIConfig {} +export interface ReactionCommerceConfig + extends Omit { + shopId: string + cartIdCookie: string + dummyEmptyCartId?: string + anonymousCartTokenCookie?: string + anonymousCartTokenCookieMaxAge?: number +} export class Config { private config: ReactionCommerceConfig @@ -42,9 +49,11 @@ export class Config { const config = new Config({ locale: 'en-US', commerceUrl: API_URL, - anonymousCartTokenCookie: REACTION_ANONYMOUS_CART_TOKEN_COOKIE, + cartCookie: REACTION_ANONYMOUS_CART_TOKEN_COOKIE, cartIdCookie: REACTION_CART_ID_COOKIE, dummyEmptyCartId: REACTION_EMPTY_DUMMY_CART_ID, + cartCookieMaxAge: REACTION_COOKIE_EXPIRE, + anonymousCartTokenCookie: REACTION_ANONYMOUS_CART_TOKEN_COOKIE, anonymousCartTokenCookieMaxAge: REACTION_COOKIE_EXPIRE, fetch: fetchGraphqlApi, customerCookie: REACTION_CUSTOMER_TOKEN_COOKIE, diff --git a/framework/reactioncommerce/const.ts b/framework/reactioncommerce/const.ts index f401666dc..e21bf1d09 100644 --- a/framework/reactioncommerce/const.ts +++ b/framework/reactioncommerce/const.ts @@ -1,14 +1,16 @@ export const REACTION_ANONYMOUS_CART_TOKEN_COOKIE = 'reaction_anonymousCartToken' +export const REACTION_CUSTOMER_TOKEN_COOKIE = 'reaction_customerToken' + export const REACTION_CART_ID_COOKIE = 'reaction_cartId' export const REACTION_EMPTY_DUMMY_CART_ID = 'DUMMY_EMPTY_CART_ID' -export const REACTION_CUSTOMER_TOKEN_COOKIE = 'reaction_customerToken' +export const STORE_DOMAIN = process.env.NEXT_PUBLIC_REACTION_STORE_DOMAIN export const REACTION_COOKIE_EXPIRE = 30 -export const API_URL = `http://127.0.0.1:3000/graphql` +export const API_URL = `http://${process.env.REACTION_STORE_DOMAIN}/graphql` -export const SHOP_ID = 'cmVhY3Rpb24vc2hvcDpIZGIycnRYTWVpbVRKbzZrcg==' +export const SHOP_ID = process.env.REACTION_SHOP_ID ?? '' diff --git a/framework/reactioncommerce/index.tsx b/framework/reactioncommerce/index.tsx index e18c619e4..e90571c2d 100644 --- a/framework/reactioncommerce/index.tsx +++ b/framework/reactioncommerce/index.tsx @@ -8,18 +8,28 @@ import { } from '@commerce' import { reactionCommerceProvider, ReactionCommerceProvider } from './provider' -import { REACTION_ANONYMOUS_CART_TOKEN_COOKIE, SHOP_ID } from './const' +import { + REACTION_ANONYMOUS_CART_TOKEN_COOKIE, + SHOP_ID, + REACTION_CART_ID_COOKIE, +} from './const' export { reactionCommerceProvider } export type { ReactionCommerceProvider } -export const reactionCommerceConfig: CommerceConfig = { +type ReactionConfig = CommerceConfig & { + shopId: string + anonymousCartTokenCookie: string +} + +export const reactionCommerceConfig: ReactionConfig = { locale: 'en-us', anonymousCartTokenCookie: REACTION_ANONYMOUS_CART_TOKEN_COOKIE, + cartCookie: REACTION_CART_ID_COOKIE, shopId: SHOP_ID, } -export type ReactionCommerceConfig = Partial +export type ReactionCommerceConfig = Partial export type ReactionCommerceProps = { children?: ReactNode diff --git a/framework/reactioncommerce/product/get-all-products.ts b/framework/reactioncommerce/product/get-all-products.ts index f620ddda3..a2cbb1879 100644 --- a/framework/reactioncommerce/product/get-all-products.ts +++ b/framework/reactioncommerce/product/get-all-products.ts @@ -1,6 +1,6 @@ import { GraphQLFetcherResult } from '@commerce/api' import { getConfig, ReactionCommerceConfig } from '../api' -import { CatalogItemEdge } from '../schema' +import { CatalogItemEdge, CatalogItemProduct } from '../schema' import { catalogItemsQuery, normalizeProduct } from '../utils' import { Product } from '@commerce/types' @@ -10,7 +10,7 @@ type Variables = { } type ReturnType = { - products: CatalogItemConnection[] + products: Product[] } const getAllProducts = async (options: { @@ -29,8 +29,8 @@ const getAllProducts = async (options: { }) const catalogItems = - data.catalogItems?.edges?.map(({ node: p }: CatalogItemEdge) => - normalizeProduct(p) + data.catalogItems?.edges?.map(({ node: itemProduct }: CatalogItemEdge) => + normalizeProduct(itemProduct as CatalogItemProduct) ) ?? [] return { diff --git a/framework/reactioncommerce/utils/normalize.ts b/framework/reactioncommerce/utils/normalize.ts index 32c687d8d..2b015868f 100644 --- a/framework/reactioncommerce/utils/normalize.ts +++ b/framework/reactioncommerce/utils/normalize.ts @@ -1,27 +1,39 @@ -import { Customer } from '@commerce/types' +import { Product, Customer } from '@commerce/types' import { Account, - CatalogItem, Cart as ReactionCart, ProductPricingInfo, CatalogProductVariant, CartItemEdge, + CatalogItemProduct, + CatalogProduct, + ImageInfo, + Maybe, } from '../schema' import type { Cart, LineItem } from '../types' +type ProductOption = { + __typename?: string + id: string + displayName: string + values: any[] +} + const money = ({ displayPrice }: ProductPricingInfo) => { return { displayPrice, } } -const normalizeProductOption = ({ - id, - name: displayName, - values, -}: ProductOption) => { +const normalizeProductImages = (images: Maybe[], name: string) => + images.map((image) => ({ + url: image?.URLs?.original || image?.URLs?.medium || '', + alt: name, + })) + +const normalizeProductOption = ({ id, displayName, values }: ProductOption) => { return { __typename: 'MultipleChoiceOption', id, @@ -30,7 +42,7 @@ const normalizeProductOption = ({ let output: any = { label: value, } - if (displayName === 'Color') { + if (displayName.toLowerCase() === 'color') { output = { ...output, hexColors: [value], @@ -41,59 +53,39 @@ const normalizeProductOption = ({ } } -const normalizeProductVariants = (variants: [CatalogProductVariant]) => { - return variants?.map( - ({ - variantId, - attributeLabel, - optionTitle, - options, - sku, - title, - pricing, - }) => { - let variantPrice = pricing[0]?.price ?? pricing[0]?.minPrice +const normalizeProductVariants = (variants: Maybe[]) => { + return variants.map((variant) => { + const { _id, options, sku, title, pricing = [], variantId } = variant ?? {} + const variantPrice = pricing[0]?.price ?? pricing[0]?.minPrice ?? 0 - if (variantPrice === undefined) { - variantPrice = 0 - } - - return { - id: variantId, - name: title, - sku: sku ?? variantId, - price: variantPrice, - listPrice: pricing[0]?.compareAtPrice?.amount ?? variantPrice, - requiresShipping: true, - options: - options?.map( - ({ _id, attributeLabel, optionTitle }: CatalogProductVariant) => - normalizeProductOption({ - id: _id, - name: attributeLabel, - values: [optionTitle], - }) - ) ?? [], - // options: [ - // { - // __typename: 'MultipleChoiceOption', - // displayName: attributeLabel, - // values: [{ label: optionTitle }], - // }, - // ], - } + return { + id: _id ?? '', + name: title, + sku: sku ?? variantId, + price: variantPrice, + listPrice: pricing[0]?.compareAtPrice?.amount ?? variantPrice, + requiresShipping: true, + options: options?.length + ? options.map((option) => { + return normalizeProductOption({ + id: option?._id ?? '', + displayName: option?.attributeLabel ?? '', + values: [option?.optionTitle], + }) + }) + : [], } - ) + }) } export function groupProductOptionsByAttributeLabel( - options: [CatalogProductVariant] + options: Maybe[] ) { return options.reduce((groupedOptions, currentOption) => { const attributeLabelIndex = groupedOptions.findIndex((option) => { return ( option.displayName.toLowerCase() === - currentOption.attributeLabel.toLowerCase() + currentOption?.attributeLabel.toLowerCase() ) }) @@ -101,68 +93,61 @@ export function groupProductOptionsByAttributeLabel( groupedOptions[attributeLabelIndex].values = [ ...groupedOptions[attributeLabelIndex].values, { - label: currentOption.optionTitle, - hexColors: [currentOption.optionTitle], + label: currentOption?.optionTitle ?? '', + hexColors: [currentOption?.optionTitle] ?? '', }, ] } else { groupedOptions = [ ...groupedOptions, normalizeProductOption({ - id: currentOption.variantId, - name: currentOption.attributeLabel, - values: [currentOption.optionTitle], + id: currentOption?.variantId ?? '', + displayName: currentOption?.attributeLabel ?? '', + values: [currentOption?.optionTitle ?? ''], }), ] } return groupedOptions - }, []) + }, [] as ProductOption[]) } -export function normalizeProduct(productNode: CatalogItemEdge): CatalogItem { +export function normalizeProduct(productNode: CatalogItemProduct): Product { + const product = productNode.product as CatalogProduct + const { _id, - product: { - productId, - description, - title: name, - vendor, - pricing, - slug, - primaryImage, - variants, - }, - ...rest - } = productNode - - const product = { - id: productId ?? _id, - name, - vendor, + productId, + title, description, - path: `/${slug}`, - slug: slug?.replace(/^\/+|\/+$/g, ''), + slug, + sku, + media, + pricing, + vendor, + variants, + ...rest + } = product + + return { + id: productId ?? _id, + name: title ?? '', + description: description ?? '', + slug: slug?.replace(/^\/+|\/+$/g, '') ?? '', + path: slug ?? '', + sku: sku ?? '', + images: media?.length ? normalizeProductImages(media, title ?? '') : [], + vendor: product.vendor, price: { - value: pricing[0].minPrice, - currencyCode: pricing[0].currency.code, + value: pricing[0]?.price ?? 0, + currencyCode: pricing[0]?.currency.code, }, - variants: variants ? normalizeProductVariants(variants) : [], - options: variants ? groupProductOptionsByAttributeLabel(variants) : [], - images: [], + variants: variants?.length ? normalizeProductVariants(variants) : [], + options: variants?.length + ? groupProductOptionsByAttributeLabel(variants) + : [], ...rest, } - - if (productNode.product.primaryImage) { - product.images = [ - { - url: primaryImage?.URLs?.original, - alt: name, - }, - ] - } - - return product } export function normalizeCart(cart: ReactionCart): Cart { diff --git a/framework/reactioncommerce/utils/queries/catalog-items-query.ts b/framework/reactioncommerce/utils/queries/catalog-items-query.ts index 928d7452d..c4b68ad61 100644 --- a/framework/reactioncommerce/utils/queries/catalog-items-query.ts +++ b/framework/reactioncommerce/utils/queries/catalog-items-query.ts @@ -29,7 +29,7 @@ edges { minPrice maxPrice } - primaryImage { + media { URLs { thumbnail small