diff --git a/packages/opencommerce/.env.template b/packages/opencommerce/.env.template index e1808adc2..04cf1c87b 100644 --- a/packages/opencommerce/.env.template +++ b/packages/opencommerce/.env.template @@ -1,5 +1,4 @@ COMMERCE_PROVIDER=@vercel/commerce-opencommerce -OPENCOMMERCE_STOREFRONT_API_URL= -OPENCOMMERCE_PRIMARY_SHOP_ID= -OPENCOMMERCE_IMAGE_DOMAIN= +NEXT_PUBLIC_OPENCOMMERCE_API_URL= +NEXT_PUBLIC_OPENCOMMERCE_IMAGE_DOMAIN= \ No newline at end of file diff --git a/packages/opencommerce/codegen.json b/packages/opencommerce/codegen.json index 2fd5a6c36..621a8a5e8 100644 --- a/packages/opencommerce/codegen.json +++ b/packages/opencommerce/codegen.json @@ -1,6 +1,6 @@ { "overwrite": true, - "schema": "http://localhost:3000/graphql", + "schema": "http://api.open-commerce.io/graphql", "documents": [ { "./src/api/**/*.ts": { diff --git a/packages/opencommerce/package.json b/packages/opencommerce/package.json index ed2680a1b..9f342e3bf 100644 --- a/packages/opencommerce/package.json +++ b/packages/opencommerce/package.json @@ -48,32 +48,31 @@ } }, "dependencies": { - "@vercel/commerce": "^0.0.1", - "@vercel/fetch": "^6.1.1" + "@vercel/commerce": "workspace:*" }, "peerDependencies": { - "next": "^12", - "react": "^17", - "react-dom": "^17" + "next": "^13", + "react": "^18", + "react-dom": "^18" }, "devDependencies": { - "@graphql-codegen/cli": "2.6.2", + "@graphql-codegen/cli": "2.7.0", "@graphql-codegen/schema-ast": "^2.4.1", - "@graphql-codegen/typescript": "2.4.8", - "@graphql-codegen/typescript-operations": "2.3.5", + "@graphql-codegen/typescript": "^2.6.0", + "@graphql-codegen/typescript-operations": "^2.4.3", "@taskr/clear": "^1.1.0", "@taskr/esnext": "^1.1.0", "@taskr/watch": "^1.1.0", - "@types/node": "^17.0.8", - "@types/react": "^17.0.38", - "lint-staged": "^12.1.7", - "next": "^12.0.8", - "prettier": "^2.5.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "@types/node": "^18.0.3", + "@types/react": "^18.0.14", + "lint-staged": "^13.0.3", + "next": "^13.0.4", + "prettier": "^2.7.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", "taskr": "^1.1.0", - "taskr-swc": "^0.0.1", - "typescript": "^4.5.4" + "taskr-swc": "workspace:*", + "typescript": "^4.7.4" }, "lint-staged": { "**/*.{js,jsx,ts,tsx,json}": [ diff --git a/packages/opencommerce/schema.d.ts b/packages/opencommerce/schema.d.ts index 675b24424..895337e9f 100644 --- a/packages/opencommerce/schema.d.ts +++ b/packages/opencommerce/schema.d.ts @@ -16,27 +16,11 @@ export type Scalars = { Boolean: boolean Int: number Float: number - /** - * - * An opaque string that identifies a particular result within a connection, - * allowing you to request a subset of results before or after that result. - * - */ ConnectionCursor: any - /** - * - * An integer between 1 and 50, inclusive. Values less than 1 become 1 and - * values greater than 50 become 50. - * - */ ConnectionLimitInt: any - /** A date string, such as 2007-12-03, compliant with the `full-date` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */ Date: any - /** A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the `date-time` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar. */ DateTime: any - /** A string email address */ Email: any - /** An object with any fields */ JSONObject: any } @@ -685,6 +669,35 @@ export type AuthenticateParamsInput = { user?: InputMaybe } +/** Represents a single user partial account */ +export type BasicAccount = Node & { + __typename?: 'BasicAccount' + /** The account ID */ + _id: Scalars['ID'] + /** Flag to indicate if the account accepts marketing emails */ + acceptsMarketing?: Maybe + /** List of shop Ids */ + adminUIShopIds?: Maybe>> + /** The date and time at which this account was created */ + createdAt?: Maybe + /** Email record associated with the account */ + emails?: Maybe>> + /** List of group Ids to which the account belongs */ + groups?: Maybe>> + /** The full name of the person this account represents, if known */ + name?: Maybe + /** List of shipping/billing addresses */ + profile?: Maybe>> + /** ID of shop */ + shopId?: Maybe + /** Account creation state */ + state?: Maybe + /** The date and time at which this account was last updated */ + updatedAt?: Maybe + /** ID of user */ + userId?: Maybe +} + /** A single calculated tax for a cart, order group, cart item, or order item */ export type CalculatedTax = { __typename?: 'CalculatedTax' @@ -1459,6 +1472,8 @@ export type CreateAccountGroupPayload = { export type CreateAccountInput = { /** Bio to display on profile */ bio?: InputMaybe + /** An optional string identifying the mutation call, which will be returned in the response payload */ + clientMutationId?: InputMaybe /** Email record to create account with */ emails: Array> /** Name to display on profile */ @@ -1477,7 +1492,7 @@ export type CreateAccountInput = { export type CreateAccountPayload = { __typename?: 'CreateAccountPayload' /** The added account */ - account?: Maybe + account?: Maybe /** The same string you sent with the mutation params, for matching mutation calls with their responses */ clientMutationId?: Maybe } @@ -2315,12 +2330,16 @@ export type EmailJob = { __typename?: 'EmailJob' /** The ID of the e-mail job */ _id: Scalars['ID'] + /** The date and time of the creation of the e-mail job */ + createdAt: Scalars['DateTime'] /** The data of the e-mail */ data: EmailJobData /** The status of the e-mail job */ status: Scalars['String'] - /** The date and time of the last update to the e-mail job */ + /** To be deprecated. Use updatedAt instead. The date and time of the last update to the e-mail job. */ updated: Scalars['DateTime'] + /** The date and time of the last update to the e-mail job */ + updatedAt: Scalars['DateTime'] } /** @@ -4894,6 +4913,26 @@ export type ProductConnection = { totalCount: Scalars['Int'] } +/** Operators for filtering on a DateTime field */ +export type ProductDateOperators = { + /** The value must be greater than or equal to the given value */ + after?: InputMaybe + /** The value must be greater than the given value */ + before?: InputMaybe + /** The value must be between the given values */ + between?: InputMaybe + /** The value must be equal to the given value */ + eq?: InputMaybe +} + +/** Range operator for DateTime fields */ +export type ProductDateRange = { + /** The end of the date range */ + end: Scalars['DateTime'] + /** The start of the date range */ + start: Scalars['DateTime'] +} + /** A connection edge in which each node is a `Product` object */ export type ProductEdge = { __typename?: 'ProductEdge' @@ -5183,6 +5222,11 @@ export type ProductVariantPricesInput = { price?: InputMaybe } +export type Profile = { + __typename?: 'Profile' + addressBook?: Maybe>> +} + /** Input for the `publishNavigationChanges` mutation */ export type PublishNavigationChangesInput = { /** An optional string identifying the mutation call, which will be returned in the response payload */ @@ -5590,8 +5634,10 @@ export type QueryProductArgs = { export type QueryProductsArgs = { after?: InputMaybe before?: InputMaybe + createdAt?: InputMaybe first?: InputMaybe isArchived?: InputMaybe + isExactMatch?: InputMaybe isVisible?: InputMaybe last?: InputMaybe metafieldKey?: InputMaybe @@ -5605,6 +5651,7 @@ export type QueryProductsArgs = { sortBy?: InputMaybe sortOrder?: InputMaybe tagIds?: InputMaybe>> + updatedAt?: InputMaybe } /** Queries return all requested data, without any side effects */ @@ -5719,6 +5766,7 @@ export type QueryTagArgs = { export type QueryTagsArgs = { after?: InputMaybe before?: InputMaybe + createdAt?: InputMaybe excludedTagIds?: InputMaybe>> filter?: InputMaybe first?: InputMaybe @@ -5730,6 +5778,7 @@ export type QueryTagsArgs = { shouldIncludeInvisible?: InputMaybe sortBy?: InputMaybe sortOrder?: InputMaybe + updatedAt?: InputMaybe } /** Queries return all requested data, without any side effects */ @@ -6890,6 +6939,26 @@ export type TagConnection = { totalCount: Scalars['Int'] } +/** Operators for filtering on a DateTime field */ +export type TagDateOperators = { + /** The value must be greater than or equal to the given value */ + after?: InputMaybe + /** The value must be greater than the given value */ + before?: InputMaybe + /** The value must be between the given values */ + between?: InputMaybe + /** The value must be equal to the given value */ + eq?: InputMaybe +} + +/** Range operator for DateTime fields */ +export type TagDateRange = { + /** The end of the date range */ + end: Scalars['DateTime'] + /** The start of the date range */ + start: Scalars['DateTime'] +} + /** A connection edge in which each node is a `Tag` object */ export type TagEdge = NodeEdge & { __typename?: 'TagEdge' @@ -8001,127 +8070,6 @@ export type CatalogItemsQuery = { } | null } -export type PrimaryShopQueryVariables = Exact<{ - language?: Scalars['String'] -}> - -export type PrimaryShopQuery = { - __typename?: 'Query' - primaryShop?: { - __typename?: 'Shop' - _id: string - description?: string | null - name: string - currency: { __typename?: 'Currency'; code: string } - defaultNavigationTree?: { - __typename?: 'NavigationTree' - _id: string - shopId: string - name: string - items?: Array<{ - __typename?: 'NavigationTreeItem' - navigationItem: { - __typename?: 'NavigationItem' - data?: { - __typename?: 'NavigationItemData' - contentForLanguage?: string | null - classNames?: string | null - url?: string | null - isUrlRelative?: boolean | null - shouldOpenInNewWindow?: boolean | null - } | null - } - items?: Array<{ - __typename?: 'NavigationTreeItem' - navigationItem: { - __typename?: 'NavigationItem' - data?: { - __typename?: 'NavigationItemData' - contentForLanguage?: string | null - classNames?: string | null - url?: string | null - isUrlRelative?: boolean | null - shouldOpenInNewWindow?: boolean | null - } | null - } - items?: Array<{ - __typename?: 'NavigationTreeItem' - navigationItem: { - __typename?: 'NavigationItem' - data?: { - __typename?: 'NavigationItemData' - contentForLanguage?: string | null - classNames?: string | null - url?: string | null - isUrlRelative?: boolean | null - shouldOpenInNewWindow?: boolean | null - } | null - } - } | null> | null - } | null> | null - } | null> | null - } | null - } | null -} - -export type NavigationTreeFragmentFragment = { - __typename?: 'NavigationTree' - _id: string - shopId: string - name: string - items?: Array<{ - __typename?: 'NavigationTreeItem' - navigationItem: { - __typename?: 'NavigationItem' - data?: { - __typename?: 'NavigationItemData' - contentForLanguage?: string | null - classNames?: string | null - url?: string | null - isUrlRelative?: boolean | null - shouldOpenInNewWindow?: boolean | null - } | null - } - items?: Array<{ - __typename?: 'NavigationTreeItem' - navigationItem: { - __typename?: 'NavigationItem' - data?: { - __typename?: 'NavigationItemData' - contentForLanguage?: string | null - classNames?: string | null - url?: string | null - isUrlRelative?: boolean | null - shouldOpenInNewWindow?: boolean | null - } | null - } - items?: Array<{ - __typename?: 'NavigationTreeItem' - navigationItem: { - __typename?: 'NavigationItem' - data?: { - __typename?: 'NavigationItemData' - contentForLanguage?: string | null - classNames?: string | null - url?: string | null - isUrlRelative?: boolean | null - shouldOpenInNewWindow?: boolean | null - } | null - } - } | null> | null - } | null> | null - } | null> | null -} - -export type NavigationItemFieldsFragment = { - __typename?: 'NavigationItemData' - contentForLanguage?: string | null - classNames?: string | null - url?: string | null - isUrlRelative?: boolean | null - shouldOpenInNewWindow?: boolean | null -} - export type GetProductBySlugQueryVariables = Exact<{ slug: Scalars['String'] }> @@ -8130,6 +8078,7 @@ export type GetProductBySlugQuery = { __typename?: 'Query' catalogItemProduct?: { __typename?: 'CatalogItemProduct' + _id: string product?: { __typename?: 'CatalogProduct' _id: string @@ -8144,7 +8093,6 @@ export type GetProductBySlugQuery = { metafields?: Array<{ __typename?: 'Metafield' description?: string | null - key?: string | null namespace?: string | null scope?: string | null value?: string | null @@ -8324,6 +8272,133 @@ export type GetProductBySlugQuery = { } | null } +export type PrimaryShopQueryVariables = Exact<{ + language?: Scalars['String'] +}> + +export type PrimaryShopQuery = { + __typename?: 'Query' + primaryShop?: { + __typename?: 'Shop' + _id: string + description?: string | null + name: string + currency: { __typename?: 'Currency'; code: string } + defaultNavigationTree?: { + __typename?: 'NavigationTree' + _id: string + shopId: string + name: string + items?: Array<{ + __typename?: 'NavigationTreeItem' + navigationItem: { + __typename?: 'NavigationItem' + data?: { + __typename?: 'NavigationItemData' + contentForLanguage?: string | null + classNames?: string | null + url?: string | null + isUrlRelative?: boolean | null + shouldOpenInNewWindow?: boolean | null + } | null + } + items?: Array<{ + __typename?: 'NavigationTreeItem' + navigationItem: { + __typename?: 'NavigationItem' + data?: { + __typename?: 'NavigationItemData' + contentForLanguage?: string | null + classNames?: string | null + url?: string | null + isUrlRelative?: boolean | null + shouldOpenInNewWindow?: boolean | null + } | null + } + items?: Array<{ + __typename?: 'NavigationTreeItem' + navigationItem: { + __typename?: 'NavigationItem' + _id: string + shopId: string + createdAt: any + data?: { + __typename?: 'NavigationItemData' + contentForLanguage?: string | null + classNames?: string | null + url?: string | null + isUrlRelative?: boolean | null + shouldOpenInNewWindow?: boolean | null + } | null + } + } | null> | null + } | null> | null + } | null> | null + } | null + } | null +} + +export type NavigationTreeFragmentFragment = { + __typename?: 'NavigationTree' + _id: string + shopId: string + name: string + items?: Array<{ + __typename?: 'NavigationTreeItem' + navigationItem: { + __typename?: 'NavigationItem' + data?: { + __typename?: 'NavigationItemData' + contentForLanguage?: string | null + classNames?: string | null + url?: string | null + isUrlRelative?: boolean | null + shouldOpenInNewWindow?: boolean | null + } | null + } + items?: Array<{ + __typename?: 'NavigationTreeItem' + navigationItem: { + __typename?: 'NavigationItem' + data?: { + __typename?: 'NavigationItemData' + contentForLanguage?: string | null + classNames?: string | null + url?: string | null + isUrlRelative?: boolean | null + shouldOpenInNewWindow?: boolean | null + } | null + } + items?: Array<{ + __typename?: 'NavigationTreeItem' + navigationItem: { + __typename?: 'NavigationItem' + _id: string + shopId: string + createdAt: any + data?: { + __typename?: 'NavigationItemData' + contentForLanguage?: string | null + classNames?: string | null + url?: string | null + isUrlRelative?: boolean | null + shouldOpenInNewWindow?: boolean | null + } | null + } + } | null> | null + } | null> | null + } | null> | null +} + +export type NavigationItemFieldsFragment = { + __typename?: 'NavigationItemData' + contentForLanguage?: string | null + classNames?: string | null + url?: string | null + isUrlRelative?: boolean | null + shouldOpenInNewWindow?: boolean | null +} + export type GetShopCurrencyQueryVariables = Exact<{ id: Scalars['ID'] }> diff --git a/packages/opencommerce/schema.graphql b/packages/opencommerce/schema.graphql index eb85fafcb..50c9df360 100644 --- a/packages/opencommerce/schema.graphql +++ b/packages/opencommerce/schema.graphql @@ -1190,6 +1190,71 @@ input AuthenticateParamsInput { user: UserInput } +""" +Represents a single user partial account +""" +type BasicAccount implements Node { + """ + The account ID + """ + _id: ID! + + """ + Flag to indicate if the account accepts marketing emails + """ + acceptsMarketing: Boolean + + """ + List of shop Ids + """ + adminUIShopIds: [String] + + """ + The date and time at which this account was created + """ + createdAt: DateTime + + """ + Email record associated with the account + """ + emails: [EmailRecord] + + """ + List of group Ids to which the account belongs + """ + groups: [String] + + """ + The full name of the person this account represents, if known + """ + name: String + + """ + List of shipping/billing addresses + """ + profile: [Profile] + + """ + ID of shop + """ + shopId: String + + """ + Account creation state + """ + state: String + + """ + The date and time at which this account was last updated + """ + updatedAt: DateTime + + """ + ID of user + """ + userId: String +} + """ A single calculated tax for a cart, order group, cart item, or order item """ @@ -2641,6 +2706,11 @@ input CreateAccountInput { """ bio: String + """ + An optional string identifying the mutation call, which will be returned in the response payload + """ + clientMutationId: String + """ Email record to create account with """ @@ -2679,7 +2749,7 @@ type CreateAccountPayload { """ The added account """ - account: Account + account: BasicAccount """ The same string you sent with the mutation params, for matching mutation calls with their responses @@ -3172,6 +3242,8 @@ type CreateShopPayload { Input for the createStripePaymentIntent mutation """ input CreateStripePaymentIntentInput { + """ + """ cartId: String! """ @@ -3183,6 +3255,9 @@ input CreateStripePaymentIntentInput { An optional string identifying the mutation call, which will be returned in the response payload """ clientMutationId: String + + """ + """ shopId: String! } @@ -3194,6 +3269,9 @@ type CreateStripePaymentIntentPayload { The same string you sent with the mutation params, for matching mutation calls with their responses """ clientMutationId: String + + """ + """ paymentIntentClientSecret: String } @@ -4191,6 +4269,11 @@ type EmailJob { """ _id: ID! + """ + The date and time of the creation of the e-mail job + """ + createdAt: DateTime! + """ The data of the e-mail """ @@ -4202,9 +4285,14 @@ type EmailJob { status: String! """ - The date and time of the last update to the e-mail job + To be deprecated. Use updatedAt instead. The date and time of the last update to the e-mail job. """ updated: DateTime! + + """ + The date and time of the last update to the e-mail job + """ + updatedAt: DateTime! } """ @@ -4827,6 +4915,8 @@ input GlobalSettingsUpdates { doNotUse: String } +""" +""" input GrantOrRevokeAdminUIAccessInput { """ The account ID to update @@ -4844,6 +4934,8 @@ input GrantOrRevokeAdminUIAccessInput { shopId: String! } +""" +""" type GrantOrRevokeAdminUIAccessPayload { """ The up to date account object @@ -8474,6 +8566,46 @@ type ProductConnection { totalCount: Int! } +""" +Operators for filtering on a DateTime field +""" +input ProductDateOperators { + """ + The value must be greater than or equal to the given value + """ + after: DateTime + + """ + The value must be greater than the given value + """ + before: DateTime + + """ + The value must be between the given values + """ + between: ProductDateRange + + """ + The value must be equal to the given value + """ + eq: DateTime +} + +""" +Range operator for DateTime fields +""" +input ProductDateRange { + """ + The end of the date range + """ + end: DateTime! + + """ + The start of the date range + """ + start: DateTime! +} + """ A connection edge in which each node is a `Product` object """ @@ -9030,6 +9162,14 @@ input ProductVariantPricesInput { price: Float } +""" +""" +type Profile { + """ + """ + addressBook: [Address] +} + """ Input for the `publishNavigationChanges` mutation """ @@ -9961,6 +10101,11 @@ type Query { """ before: ConnectionCursor + """ + Filter by created timestamp + """ + createdAt: ProductDateOperators + """ Return at most this many results. This parameter may be used with either `after` or `offset` parameters. """ @@ -9971,6 +10116,11 @@ type Query { """ isArchived: Boolean + """ + Flag to set metafields search method. Default value false, performs a fuzzy search. Explicitly set it to true to perform exact match. + """ + isExactMatch: Boolean = false + """ Filter by visibility """ @@ -9982,12 +10132,12 @@ type Query { last: ConnectionLimitInt """ - Filter by metafield key + Filter by metafield key using `regex` match. To get specific searches send the argument as a regex pattern """ metafieldKey: String """ - Filter by metafield value + Filter by metafield value using `regex` match. To get specific searches send the argument as a regex pattern """ metafieldValue: String @@ -10035,6 +10185,11 @@ type Query { List of tag ids to filter by """ tagIds: [ID] + + """ + Filter by updated at timestamp + """ + updatedAt: ProductDateOperators ): ProductConnection """ @@ -10202,6 +10357,9 @@ type Query { """ shopId: ID! ): ShopSettings! + + """ + """ shops( """ Return only results that come after this cursor. Use this with `first` to specify the number of results to return. @@ -10379,6 +10537,11 @@ type Query { """ before: ConnectionCursor + """ + Filter by created timestamp + """ + createdAt: TagDateOperators + """ Tags to exclude from results """ @@ -10433,6 +10596,11 @@ type Query { Return results sorted in this order """ sortOrder: SortOrder = asc + + """ + Filter by updated at timestamp + """ + updatedAt: TagDateOperators ): TagConnection """ @@ -12631,6 +12799,46 @@ type TagConnection { totalCount: Int! } +""" +Operators for filtering on a DateTime field +""" +input TagDateOperators { + """ + The value must be greater than or equal to the given value + """ + after: DateTime + + """ + The value must be greater than the given value + """ + before: DateTime + + """ + The value must be between the given values + """ + between: ProductDateRange + + """ + The value must be equal to the given value + """ + eq: DateTime +} + +""" +Range operator for DateTime fields +""" +input TagDateRange { + """ + The end of the date range + """ + end: DateTime! + + """ + The start of the date range + """ + start: DateTime! +} + """ A connection edge in which each node is a `Tag` object """ @@ -13282,6 +13490,8 @@ type UpdateAddressValidationRulePayload { clientMutationId: String } +""" +""" input UpdateAdminUIAccessInput { """ The account IDs to update @@ -13299,6 +13509,8 @@ input UpdateAdminUIAccessInput { shopIds: [String]! } +""" +""" type UpdateAdminUIAccessPayload { """ The up to date account objects @@ -13581,6 +13793,8 @@ input UpdateGroupInput { slug: String } +""" +""" input UpdateGroupsForAccountsInput { """ The account IDs @@ -13598,6 +13812,8 @@ input UpdateGroupsForAccountsInput { groupIds: [ID]! } +""" +""" type UpdateGroupsForAccountsPayload { """ The accounts that were modified @@ -14401,6 +14617,8 @@ input UserInput { username: String } +""" +""" type Vendor { """ The name of the vendor diff --git a/packages/opencommerce/src/api/endpoints/cart/add-item.ts b/packages/opencommerce/src/api/endpoints/cart/add-item.ts index a292c35ea..bca8ef118 100644 --- a/packages/opencommerce/src/api/endpoints/cart/add-item.ts +++ b/packages/opencommerce/src/api/endpoints/cart/add-item.ts @@ -1,37 +1,69 @@ -import { normalizeCart } from '../../../utils/normalize' +import { normalizeCart, normalizeProduct } from '../../../utils/normalize' import getCartCookie from '../../utils/get-cart-cookie' import addCartItemsMutation from '../../mutations/add-cart-item' import createCartMutation from '../../mutations/create-cart' +import getProductQuery from '../../queries/get-catalog-product-item-query' +import getPrimaryShopQuery from '../../queries/get-primary-shop-query' import type { CartEndpoint } from '.' +import { PrimaryShopQuery } from '../../../../schema' const addItem: CartEndpoint['handlers']['addItem'] = async ({ - res, body: { cartId, item }, config, req: { cookies }, }) => { if (!item) { - return res.status(400).json({ + return { data: null, errors: [{ message: 'Missing item' }], - }) + } } + + const { + data: { primaryShop }, + } = await config.fetch(getPrimaryShopQuery) + + if (!primaryShop?._id) { + return { + data: null, + } + } + + const { + data: { catalogItemProduct }, + } = await config.fetch(getProductQuery, { + variables: { slug: item.productId! }, + }) + + const product = normalizeProduct(catalogItemProduct) + + const selectedVariant = product.variants.find( + ({ id }) => id === item.variantId + ) + + if (!selectedVariant) { + return { + data: null, + errors: [{ message: 'Invalid product or variant' }], + } + } + if (!item.quantity) item.quantity = 1 const variables = { input: { - shopId: config.shopId, + shopId: primaryShop._id, items: [ { productConfiguration: { - productId: item.productId, + productId: catalogItemProduct.product.productId, productVariantId: item.variantId, }, quantity: item.quantity, price: { - amount: item.variant?.price, - currencyCode: item.currencyCode, + amount: selectedVariant?.price?.value || 0, + currencyCode: product?.price.currencyCode, }, }, ], @@ -42,20 +74,23 @@ const addItem: CartEndpoint['handlers']['addItem'] = async ({ const { data: { createCart }, } = await config.fetch(createCartMutation, { variables }) - res.setHeader('Set-Cookie', [ - getCartCookie( - config.cartCookie, - createCart.cart._id, - config.cartCookieMaxAge - ), - getCartCookie( - config.anonymousCartTokenCookie, - createCart.token, - config.cartCookieMaxAge - ), - ]) - - return res.status(200).json({ data: normalizeCart(createCart.cart) }) + return { + data: normalizeCart(createCart.cart), + headers: { + 'Set-Cookie': [ + getCartCookie( + config.cartCookie, + createCart.cart._id, + config.cartCookieMaxAge + ), + getCartCookie( + config.anonymousCartTokenCookie, + createCart.token, + config.cartCookieMaxAge + ), + ], + }, + } } const { @@ -65,12 +100,12 @@ const addItem: CartEndpoint['handlers']['addItem'] = async ({ input: { items: variables.input.items, cartId, - cartToken: cookies[config.anonymousCartTokenCookie], + cartToken: cookies.get(config.anonymousCartTokenCookie)?.value, }, }, }) - return res.status(200).json({ data: normalizeCart(addCartItems.cart) }) + return { data: normalizeCart(addCartItems.cart) } } export default addItem diff --git a/packages/opencommerce/src/api/endpoints/cart/get-cart.ts b/packages/opencommerce/src/api/endpoints/cart/get-cart.ts index edae474bd..d10c903bf 100644 --- a/packages/opencommerce/src/api/endpoints/cart/get-cart.ts +++ b/packages/opencommerce/src/api/endpoints/cart/get-cart.ts @@ -4,29 +4,28 @@ import type { CartEndpoint } from '.' // Return current cart info const getCart: CartEndpoint['handlers']['getCart'] = async ({ - res, req: { cookies }, body: { cartId }, config, }) => { - if (cartId && cookies[config.anonymousCartTokenCookie]) { + if (cartId && cookies.get(config.anonymousCartTokenCookie)?.value) { const { data: { cart: rawAnonymousCart }, } = await config.fetch(getAnonymousCartQuery, { variables: { cartId, - cartToken: cookies[config.anonymousCartTokenCookie], + cartToken: cookies.get(config.anonymousCartTokenCookie)?.value, }, }) - return res.status(200).json({ + return { data: rawAnonymousCart ? normalizeCart(rawAnonymousCart) : null, - }) + } } - res.status(200).json({ + return { data: null, - }) + } } export default getCart diff --git a/packages/opencommerce/src/api/endpoints/cart/remove-item.ts b/packages/opencommerce/src/api/endpoints/cart/remove-item.ts index 72511589c..058e732e6 100644 --- a/packages/opencommerce/src/api/endpoints/cart/remove-item.ts +++ b/packages/opencommerce/src/api/endpoints/cart/remove-item.ts @@ -4,16 +4,15 @@ import removeCartItemsMutation from '../../mutations/remove-cart-item' import type { CartEndpoint } from '.' const removeItem: CartEndpoint['handlers']['removeItem'] = async ({ - res, body: { cartId, itemId }, config, req: { cookies }, }) => { if (!cartId || !itemId) { - return res.status(400).json({ + return { data: null, errors: [{ message: 'Invalid request' }], - }) + } } const { @@ -23,17 +22,21 @@ const removeItem: CartEndpoint['handlers']['removeItem'] = async ({ input: { cartId, cartItemIds: [itemId], - cartToken: cookies[config.anonymousCartTokenCookie], + cartToken: cookies.get(config.anonymousCartTokenCookie)?.value, }, }, }) - res.setHeader( - 'Set-Cookie', - getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge) - ) - - res.status(200).json({ data: normalizeCart(removeCartItems.cart) }) + return { + data: normalizeCart(removeCartItems.cart), + headers: { + 'Set-Cookie': getCartCookie( + config.cartCookie, + cartId, + config.cartCookieMaxAge + ), + }, + } } export default removeItem diff --git a/packages/opencommerce/src/api/endpoints/cart/update-item.ts b/packages/opencommerce/src/api/endpoints/cart/update-item.ts index 6645544f7..678338267 100644 --- a/packages/opencommerce/src/api/endpoints/cart/update-item.ts +++ b/packages/opencommerce/src/api/endpoints/cart/update-item.ts @@ -4,16 +4,15 @@ import updateCartItemsQuantityMutation from '../../mutations/update-cart-item-qu import type { CartEndpoint } from '.' const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ - res, body: { cartId, itemId, item }, config, req: { cookies }, }) => { if (!cartId || !itemId || !item) { - return res.status(400).json({ - data: null, + return { + data: undefined, errors: [{ message: 'Invalid request' }], - }) + } } const { @@ -22,18 +21,24 @@ const updateItem: CartEndpoint['handlers']['updateItem'] = async ({ variables: { updateCartItemsQuantityInput: { cartId, - cartToken: cookies[config.anonymousCartTokenCookie], + cartToken: cookies.get(config.anonymousCartTokenCookie)?.value, items: [{ cartItemId: itemId, quantity: item.quantity }], }, }, }) // Update the cart cookie - res.setHeader( - 'Set-Cookie', - getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge) - ) - res.status(200).json({ data: normalizeCart(updateCartItemsQuantity.cart) }) + + return { + data: normalizeCart(updateCartItemsQuantity.cart), + headers: { + 'Set-Cookie': getCartCookie( + config.cartCookie, + cartId, + config.cartCookieMaxAge + ), + }, + } } export default updateItem diff --git a/packages/opencommerce/src/api/endpoints/catalog/get-products.ts b/packages/opencommerce/src/api/endpoints/catalog/get-products.ts index 1c0126a42..121fad7c1 100644 --- a/packages/opencommerce/src/api/endpoints/catalog/get-products.ts +++ b/packages/opencommerce/src/api/endpoints/catalog/get-products.ts @@ -1,12 +1,14 @@ import type { ProductsEndpoint } from './products' import getSearchVariables from '../../utils/get-search-variables' import getSortVariables from '../../utils/get-sort-variables' -import { CatalogItemsQueryVariables } from '../../../../schema' -import getShopCurrencyQuery from '../../queries/get-shop-currency-query' +import { + CatalogItemsQueryVariables, + PrimaryShopQuery, +} from '../../../../schema' +import getPrimaryShopQuery from '../../queries/get-primary-shop-query' const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ body: { brandId, search, sort, categoryId }, - res, config, commerce, }) => { @@ -15,21 +17,21 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ sortParams = null } - let currency: string | null = null + const { + data: { primaryShop }, + } = await config.fetch(getPrimaryShopQuery) - if (sortParams?.sortBy === 'minPrice') { - const { + if (!primaryShop?._id) { + return { data: { - shop: { - currency: { code }, - }, + products: [], + found: false }, - } = await config.fetch(getShopCurrencyQuery, { - variables: { id: config.shopId }, - }) - currency = code + } } + let currency: string | null = primaryShop.currency.code + const { products } = await commerce.getAllProducts({ variables: { ...getSearchVariables({ brandId, search, categoryId }), @@ -45,12 +47,12 @@ const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ config, }) - res.status(200).json({ + return { data: { products, found: !!products.length, }, - }) + } } export default getProducts diff --git a/packages/opencommerce/src/api/endpoints/checkout/get-checkout.ts b/packages/opencommerce/src/api/endpoints/checkout/get-checkout.ts index 491bf0ac9..f6923f0e3 100644 --- a/packages/opencommerce/src/api/endpoints/checkout/get-checkout.ts +++ b/packages/opencommerce/src/api/endpoints/checkout/get-checkout.ts @@ -1 +1,3 @@ -export default function noopApi(...args: any[]): void {} +export default function getCheckout(..._args: any[]) { + return Promise.resolve({ data: null }) +} diff --git a/packages/opencommerce/src/api/endpoints/checkout/submit-checkout.ts b/packages/opencommerce/src/api/endpoints/checkout/submit-checkout.ts index 01504325d..d1d1f57a1 100644 --- a/packages/opencommerce/src/api/endpoints/checkout/submit-checkout.ts +++ b/packages/opencommerce/src/api/endpoints/checkout/submit-checkout.ts @@ -1,42 +1,67 @@ import { LineItem } from '../../../types/cart' import placeOrder from '../../mutations/place-order' import setEmailOnAnonymousCart from '../../mutations/set-email-on-anonymous-cart' +import getAnonymousCartQuery from '../../queries/get-anonymous-cart' import getCartCookie from '../../utils/get-cart-cookie' import type { CheckoutEndpoint } from '.' +import { normalizeCheckout, normalizeCart } from '../../../utils/normalize' +import { PrimaryShopQuery } from '../../../../schema' +import getPrimaryShopQuery from '../../queries/get-primary-shop-query' const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({ - res, - body: { item, cartId }, - config: { fetch, shopId, anonymousCartTokenCookie, cartCookie }, + body: { cartId }, + config: { fetch, anonymousCartTokenCookie, cartCookie }, req: { cookies }, }) => { + const { + data: { primaryShop }, + } = await fetch(getPrimaryShopQuery) + + if (!primaryShop?._id) { + return { + data: null, + } + } + await fetch(setEmailOnAnonymousCart, { variables: { input: { cartId, - cartToken: cookies[anonymousCartTokenCookie], + cartToken: cookies.get(anonymousCartTokenCookie)?.value, email: 'opencommerce@test.com', }, }, }) + const { + data: { cart: rawAnonymousCart }, + } = await fetch(getAnonymousCartQuery, { + variables: { + cartId, + cartToken: cookies.get(anonymousCartTokenCookie)?.value, + }, + }) + + const checkout = normalizeCheckout(rawAnonymousCart.checkout) + const cart = normalizeCart(rawAnonymousCart) + const { data } = await fetch(placeOrder, { variables: { input: { payments: { data: { fullName: 'Open Commerce Demo Site' }, - amount: item.checkout.cart.checkout.summary.total.amount, + amount: checkout.summary.total.amount, method: 'iou_example', }, order: { cartId, - currencyCode: item.checkout.cart.currency.code, + currencyCode: cart.currency.code, email: 'opencommerce@test.com', - shopId, + shopId: primaryShop._id, fulfillmentGroups: { - shopId, - data: item.checkout.cart.checkout.fulfillmentGroups[0].data, - items: item.checkout.cart.lineItems.map((item: LineItem) => ({ + shopId: primaryShop._id, + data: checkout.fulfillmentGroups[0].data, + items: cart.lineItems.map((item: LineItem) => ({ price: item.variant.price, quantity: item.quantity, productConfiguration: { @@ -44,22 +69,26 @@ const submitCheckout: CheckoutEndpoint['handlers']['submitCheckout'] = async ({ productVariantId: item.variantId, }, })), - type: item.checkout.cart.checkout.fulfillmentGroups[0].type, + type: checkout.fulfillmentGroups[0].type, selectedFulfillmentMethodId: - item.checkout.cart.checkout.fulfillmentGroups[0] - .selectedFulfillmentOption.fulfillmentMethod._id, + checkout.fulfillmentGroups[0].selectedFulfillmentOption + ?.fulfillmentMethod?._id, }, }, }, }, }) - res.setHeader('Set-Cookie', [ - getCartCookie(cartCookie), - getCartCookie(anonymousCartTokenCookie), - ]) - - res.status(200).json({ data: null, errors: [] }) + return { + data: null, + errors: [], + headers: { + 'Set-Cookie': [ + getCartCookie(cartCookie), + getCartCookie(anonymousCartTokenCookie), + ], + }, + } } export default submitCheckout diff --git a/packages/opencommerce/src/api/endpoints/customer/address/add-item.ts b/packages/opencommerce/src/api/endpoints/customer/address/add-item.ts index 229eed701..e7b31d87f 100644 --- a/packages/opencommerce/src/api/endpoints/customer/address/add-item.ts +++ b/packages/opencommerce/src/api/endpoints/customer/address/add-item.ts @@ -4,17 +4,16 @@ import updateFulfillmentOptions from '../../../mutations/update-fulfillment-opti import selectFulfillmentOptions from '../../../mutations/select-fulfillment-options' const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({ - res, body: { item, cartId }, config: { fetch, anonymousCartTokenCookie }, req: { cookies }, }) => { // Return an error if no cart is present if (!cartId) { - return res.status(400).json({ + return { data: null, errors: [{ message: 'Cookie not found' }], - }) + } } // Register address @@ -35,7 +34,6 @@ const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({ region: item.city || 'LA', }, cartId, - cartToken: cookies[anonymousCartTokenCookie], }, }, }) @@ -56,7 +54,7 @@ const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({ variables: { input: { cartId, - cartToken: cookies[anonymousCartTokenCookie], + cartToken: cookies.get(anonymousCartTokenCookie)?.value, fulfillmentGroupId: updateFulfillmentOptionsForGroup.cart.checkout.fulfillmentGroups[0] ._id, @@ -67,7 +65,7 @@ const addItem: CustomerAddressEndpoint['handlers']['addItem'] = async ({ }, }) - return res.status(200).json({ data: null, errors: [] }) + return { data: null, errors: [] } } export default addItem diff --git a/packages/opencommerce/src/api/endpoints/customer/address/get-addresses.ts b/packages/opencommerce/src/api/endpoints/customer/address/get-addresses.ts index 2e27591c0..cb0bfc4be 100644 --- a/packages/opencommerce/src/api/endpoints/customer/address/get-addresses.ts +++ b/packages/opencommerce/src/api/endpoints/customer/address/get-addresses.ts @@ -1,9 +1,8 @@ import type { CustomerAddressEndpoint } from '.' -const getCards: CustomerAddressEndpoint['handlers']['getAddresses'] = async ({ - res, -}) => { - return res.status(200).json({ data: null, errors: [] }) -} +const getCards: CustomerAddressEndpoint['handlers']['getAddresses'] = + async ({}) => { + return { data: null, errors: [] } + } export default getCards diff --git a/packages/opencommerce/src/api/endpoints/customer/address/index.ts b/packages/opencommerce/src/api/endpoints/customer/address/index.ts index e2f93ffde..d1a7f6fca 100644 --- a/packages/opencommerce/src/api/endpoints/customer/address/index.ts +++ b/packages/opencommerce/src/api/endpoints/customer/address/index.ts @@ -1,7 +1,4 @@ -import type { - CustomerAddressSchema, - CustomerAddressTypes, -} from '../../../../types/customer/address' +import type { CustomerAddressSchema } from '../../../../types/customer/address' import type { OpenCommerceAPI } from '../../..' import { GetAPISchema, createEndpoint } from '@vercel/commerce/api' @@ -14,7 +11,7 @@ import removeItem from './remove-item' export type CustomerAddressAPI = GetAPISchema< OpenCommerceAPI, - CustomerAddressSchema + CustomerAddressSchema > export type CustomerAddressEndpoint = CustomerAddressAPI['endpoint'] diff --git a/packages/opencommerce/src/api/endpoints/customer/address/remove-item.ts b/packages/opencommerce/src/api/endpoints/customer/address/remove-item.ts index fba4e1154..648b2fa33 100644 --- a/packages/opencommerce/src/api/endpoints/customer/address/remove-item.ts +++ b/packages/opencommerce/src/api/endpoints/customer/address/remove-item.ts @@ -1,9 +1,8 @@ import type { CustomerAddressEndpoint } from '.' -const removeItem: CustomerAddressEndpoint['handlers']['removeItem'] = async ({ - res, -}) => { - return res.status(200).json({ data: null, errors: [] }) -} +const removeItem: CustomerAddressEndpoint['handlers']['removeItem'] = + async ({}) => { + return { data: null, errors: [] } + } export default removeItem diff --git a/packages/opencommerce/src/api/endpoints/customer/address/update-item.ts b/packages/opencommerce/src/api/endpoints/customer/address/update-item.ts index f34088f0f..74e2c3bec 100644 --- a/packages/opencommerce/src/api/endpoints/customer/address/update-item.ts +++ b/packages/opencommerce/src/api/endpoints/customer/address/update-item.ts @@ -1,34 +1,7 @@ -import selectFulfillmentOptions from '../../../mutations/select-fulfillment-options' import type { CustomerAddressEndpoint } from '.' -const updateItem: CustomerAddressEndpoint['handlers']['updateItem'] = async ({ - res, - body: { item, cartId }, - config: { fetch, anonymousCartTokenCookie }, - req: { cookies }, -}) => { - // Return an error if no cart is present - if (!cartId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Cookie not found' }], - }) - } - - if (item.shippingMethodId) { - await fetch(selectFulfillmentOptions, { - variables: { - input: { - cartId, - cartToken: cookies[anonymousCartTokenCookie], - fulfillmentGroupId: item.fulfillmentGroupId, - fulfillmentMethodId: item.shippingMethodId, - }, - }, - }) - } - - return res.status(200).json({ data: null, errors: [] }) +const updateItem: CustomerAddressEndpoint['handlers']['updateItem'] = () => { + return Promise.resolve({ data: null }) } export default updateItem diff --git a/packages/opencommerce/src/api/endpoints/customer/card.ts b/packages/opencommerce/src/api/endpoints/customer/card.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/opencommerce/src/api/endpoints/customer/card.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/opencommerce/src/api/endpoints/customer/index.ts b/packages/opencommerce/src/api/endpoints/customer/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/opencommerce/src/api/endpoints/customer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/opencommerce/src/api/endpoints/index.ts b/packages/opencommerce/src/api/endpoints/index.ts new file mode 100644 index 000000000..543fdc4d5 --- /dev/null +++ b/packages/opencommerce/src/api/endpoints/index.ts @@ -0,0 +1,19 @@ +import { OpenCommerceAPI, Provider } from '../index' + +import createEndpoints from '@vercel/commerce/api/endpoints' + +import cart from './cart' +import checkout from './checkout' +import customerAddress from './customer/address' +import products from './catalog/products' + +const endpoints = { + cart, + checkout, + 'customer/address': customerAddress, + 'catalog/products': products, +} + +export default function opencommerceAPI(commerce: OpenCommerceAPI) { + return createEndpoints(commerce, endpoints) +} diff --git a/packages/opencommerce/src/api/endpoints/login/index.ts b/packages/opencommerce/src/api/endpoints/login/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/opencommerce/src/api/endpoints/login/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/opencommerce/src/api/endpoints/logout/index.ts b/packages/opencommerce/src/api/endpoints/logout/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/opencommerce/src/api/endpoints/logout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/opencommerce/src/api/endpoints/signup/index.ts b/packages/opencommerce/src/api/endpoints/signup/index.ts deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/opencommerce/src/api/endpoints/signup/index.ts +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/opencommerce/src/api/endpoints/wishlist/index.tsx b/packages/opencommerce/src/api/endpoints/wishlist/index.tsx deleted file mode 100644 index 491bf0ac9..000000000 --- a/packages/opencommerce/src/api/endpoints/wishlist/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export default function noopApi(...args: any[]): void {} diff --git a/packages/opencommerce/src/api/index.ts b/packages/opencommerce/src/api/index.ts index 9065b5421..62ae1e53d 100644 --- a/packages/opencommerce/src/api/index.ts +++ b/packages/opencommerce/src/api/index.ts @@ -5,10 +5,21 @@ import { } from '@vercel/commerce/api' import createFetchGraphqlApi from './utils/fetch-grapql-api' -import * as operations from './operations' +import getAllPages from './operations/get-all-pages' +import getPage from './operations/get-page' +import getSiteInfo from './operations/get-site-info' +import getAllProductPaths from './operations/get-all-product-paths' +import getAllProducts from './operations/get-all-products' +import getProduct from './operations/get-product' +import { + OPENCOMMERCE_ANONYMOUS_CART_TOKEN_COOKIE, + OPENCOMMERCE_CART_TOKEN, + API_URL, +} from '../const' -const API_URL = process.env.OPENCOMMERCE_STOREFRONT_API_URL -const SHOP_ID = process.env.OPENCOMMERCE_PRIMARY_SHOP_ID +export interface OpenCommerceConfig extends CommerceAPIConfig { + anonymousCartTokenCookie: string +} if (!API_URL) { throw new Error( @@ -16,24 +27,27 @@ if (!API_URL) { ) } -export interface OpenCommerceConfig extends CommerceAPIConfig { - shopId: string - anonymousCartTokenCookie: string -} - const ONE_DAY = 60 * 60 * 24 const config: OpenCommerceConfig = { commerceUrl: API_URL, apiToken: '', - shopId: SHOP_ID ?? '', customerCookie: 'opencommerce_customerToken', - cartCookie: 'opencommerce_cartId', + cartCookie: OPENCOMMERCE_CART_TOKEN, cartCookieMaxAge: ONE_DAY * 30, - anonymousCartTokenCookie: 'opencommerce_anonymousCartToken', + anonymousCartTokenCookie: OPENCOMMERCE_ANONYMOUS_CART_TOKEN_COOKIE, fetch: createFetchGraphqlApi(() => getCommerceApi().getConfig()), } +const operations = { + getAllPages, + getPage, + getSiteInfo, + getAllProductPaths, + getAllProducts, + getProduct, +} + export const provider = { config, operations } export type Provider = typeof provider diff --git a/packages/opencommerce/src/api/operations/get-all-product-paths.ts b/packages/opencommerce/src/api/operations/get-all-product-paths.ts index 05cb78212..9be6c150f 100644 --- a/packages/opencommerce/src/api/operations/get-all-product-paths.ts +++ b/packages/opencommerce/src/api/operations/get-all-product-paths.ts @@ -6,12 +6,14 @@ import type { CatalogItemsQuery, CatalogItemsQueryVariables, CatalogItemProduct, + PrimaryShopQuery, } from '../../../schema' import type { GetAllProductPathsOperation } from '../../types/product' import type { RecursivePartial, RecursiveRequired } from '../utils/types' import filterEdges from '../utils/filter-edges' import { OpenCommerceConfig, Provider } from '..' import getAllProductPathsQuery from '../queries/get-all-product-paths-query' +import getPrimaryShopQuery from '../queries/get-primary-shop-query' export default function getAllProductPathsOperation({ commerce, @@ -39,14 +41,25 @@ export default function getAllProductPathsOperation({ variables?: CatalogItemsQueryVariables config?: OpenCommerceConfig } = {}): Promise { - const { fetch, shopId } = commerce.getConfig(config) + const { fetch } = commerce.getConfig(config) + + const { + data: { primaryShop }, + } = await fetch(getPrimaryShopQuery) + + if (!primaryShop?._id) { + return { + products: [], + } + } + // RecursivePartial forces the method to check for every prop in the data, which is // required in case there's a custom `query` const { data } = await fetch< RecursivePartial, CatalogItemsQueryVariables >(query, { - variables: { ...variables, shopIds: [shopId] }, + variables: { ...variables, shopIds: [primaryShop._id] }, }) const products = data.catalogItems?.edges diff --git a/packages/opencommerce/src/api/operations/get-all-products.ts b/packages/opencommerce/src/api/operations/get-all-products.ts index 6aae7789d..3cc87b081 100644 --- a/packages/opencommerce/src/api/operations/get-all-products.ts +++ b/packages/opencommerce/src/api/operations/get-all-products.ts @@ -4,12 +4,14 @@ import { CatalogItemProduct, CatalogItemsQuery, CatalogItemsQueryVariables, + PrimaryShopQuery, } from '../../../schema' import catalogItemsQuery from '../queries/get-all-products-query' import type { OpenCommerceConfig, Provider } from '..' import { normalizeProduct } from '../../utils/normalize' import { RecursivePartial, RecursiveRequired } from '../utils/types' import filterEdges from '../utils/filter-edges' +import getPrimaryShopQuery from '../queries/get-primary-shop-query' export default function getAllProductsOperation({ commerce, @@ -30,14 +32,23 @@ export default function getAllProductsOperation({ config?: Partial preview?: boolean } = {}): Promise { - const { fetch, locale, shopId } = commerce.getConfig(config) + const { fetch, locale } = commerce.getConfig(config) + const { + data: { primaryShop }, + } = await fetch(getPrimaryShopQuery) + + if (!primaryShop?._id) { + return { + products: [] + } + } const { data } = await fetch< RecursivePartial, CatalogItemsQueryVariables >( query, - { variables: { ...variables, shopIds: [shopId] } }, + { variables: { ...variables, shopIds: [primaryShop._id] } }, { ...(locale && { headers: { diff --git a/packages/opencommerce/src/api/operations/get-product.ts b/packages/opencommerce/src/api/operations/get-product.ts index 95657f6db..c1103fbb1 100644 --- a/packages/opencommerce/src/api/operations/get-product.ts +++ b/packages/opencommerce/src/api/operations/get-product.ts @@ -10,7 +10,7 @@ import { GetProductBySlugQuery, GetProductBySlugQueryVariables, } from '../../../schema' -import getProductQuery from '../queries/get-product-query' +import getProductQuery from '../queries/get-catalog-product-item-query' export default function getProductOperation({ commerce, diff --git a/packages/opencommerce/src/api/operations/get-site-info.ts b/packages/opencommerce/src/api/operations/get-site-info.ts index 5921fba6e..eac76a041 100644 --- a/packages/opencommerce/src/api/operations/get-site-info.ts +++ b/packages/opencommerce/src/api/operations/get-site-info.ts @@ -7,9 +7,11 @@ import { GetAllProductVendorsQuery, GetTagsQueryVariables, GetAllProductVendorsQueryVariables, + PrimaryShopQuery, + NavigationTreeItem, } from '../../../schema' import getTagsQuery from '../queries/get-tags-query' -import { GetSiteInfoOperation, OCCategory, SiteTypes } from '../../types/site' +import { GetSiteInfoOperation, OCCategory } from '../../types/site' import { normalizeCategory, normalizeNavigation, @@ -42,19 +44,29 @@ export default function getSiteInfoOperation({ config?: Partial preview?: boolean } = {}): Promise { - const { fetch, shopId } = commerce.getConfig(cfg) + const { fetch } = commerce.getConfig(cfg) - const [categoriesResponse, vendorsResponse, primaryShopResponse] = - await Promise.all([ - await fetch(getTagsQuery, { - variables: { first: 250, shopId }, - }), - await fetch< - GetAllProductVendorsQuery, - GetAllProductVendorsQueryVariables - >(getAllProductVendors, { variables: { shopIds: [shopId] } }), - await fetch(getPrimaryShopQuery), - ]) + const { + data: { primaryShop }, + } = await fetch(getPrimaryShopQuery) + + if (!primaryShop?._id) { + return { + categories: [], + brands: [], + navigation: [], + } + } + + const [categoriesResponse, vendorsResponse] = await Promise.all([ + await fetch(getTagsQuery, { + variables: { first: 250, shopId: primaryShop._id }, + }), + await fetch< + GetAllProductVendorsQuery, + GetAllProductVendorsQueryVariables + >(getAllProductVendors, { variables: { shopIds: [primaryShop._id] } }), + ]) const categories = filterEdges(categoriesResponse.data.tags?.edges).map( (edge) => normalizeCategory(edge.node! as OCCategory) @@ -64,8 +76,9 @@ export default function getSiteInfoOperation({ ...new Set(filterEdges(vendorsResponse.data.vendors?.nodes)), ].map(normalizeVendors) - const navigationItems = - primaryShopResponse.data.primaryShop.defaultNavigationTree.items ?? [] + const navigationItems = filterEdges( + primaryShop?.defaultNavigationTree?.items + ) as NavigationTreeItem[] return { categories, diff --git a/packages/opencommerce/src/api/queries/get-catalog-product-item-query.ts b/packages/opencommerce/src/api/queries/get-catalog-product-item-query.ts new file mode 100644 index 000000000..ddfc3782a --- /dev/null +++ b/packages/opencommerce/src/api/queries/get-catalog-product-item-query.ts @@ -0,0 +1,181 @@ +const getProductQuery = /* GraphQL */ ` + query getProductBySlug($slug: String!) { + catalogItemProduct(slugOrId: $slug) { + _id + product { + _id + productId + title + slug + description + vendor + isLowQuantity + isSoldOut + isBackorder + metafields { + description + namespace + scope + value + valueType + } + pricing { + currency { + code + } + displayPrice + minPrice + maxPrice + } + shop { + currency { + code + } + } + primaryImage { + URLs { + large + medium + original + small + thumbnail + } + priority + productId + variantId + } + media { + priority + productId + variantId + URLs { + thumbnail + small + medium + large + original + } + } + tags { + nodes { + name + slug + position + } + } + variants { + _id + variantId + attributeLabel + title + optionTitle + index + pricing { + compareAtPrice { + displayAmount + } + price + currency { + code + } + displayPrice + } + canBackorder + inventoryAvailableToSell + isBackorder + isSoldOut + isLowQuantity + options { + _id + variantId + attributeLabel + title + index + pricing { + compareAtPrice { + displayAmount + } + price + currency { + code + } + displayPrice + } + optionTitle + canBackorder + inventoryAvailableToSell + isBackorder + isSoldOut + isLowQuantity + media { + priority + productId + variantId + URLs { + thumbnail + small + medium + large + original + } + } + metafields { + description + key + namespace + scope + value + valueType + } + primaryImage { + URLs { + large + medium + original + small + thumbnail + } + priority + productId + variantId + } + } + media { + priority + productId + variantId + URLs { + thumbnail + small + medium + large + original + } + } + metafields { + description + key + namespace + scope + value + valueType + } + primaryImage { + URLs { + large + medium + original + small + thumbnail + } + priority + productId + variantId + } + } + } + } + } +` + +export default getProductQuery diff --git a/packages/opencommerce/src/api/queries/get-primary-shop-query.ts b/packages/opencommerce/src/api/queries/get-primary-shop-query.ts index ac8df4924..fabfe9af8 100644 --- a/packages/opencommerce/src/api/queries/get-primary-shop-query.ts +++ b/packages/opencommerce/src/api/queries/get-primary-shop-query.ts @@ -30,6 +30,9 @@ const getPrimaryShopQuery = /* GraphQL */ ` } items { navigationItem { + _id + shopId + createdAt data { ...NavigationItemFields } diff --git a/packages/opencommerce/src/api/queries/get-product-query.ts b/packages/opencommerce/src/api/queries/get-product-query.ts index 6b72be616..e69de29bb 100644 --- a/packages/opencommerce/src/api/queries/get-product-query.ts +++ b/packages/opencommerce/src/api/queries/get-product-query.ts @@ -1,181 +0,0 @@ -const getProductQuery = /* GraphQL */ ` - query getProductBySlug($slug: String!) { - catalogItemProduct(slugOrId: $slug) { - product { - _id - productId - title - slug - description - vendor - isLowQuantity - isSoldOut - isBackorder - metafields { - description - key - namespace - scope - value - valueType - } - pricing { - currency { - code - } - displayPrice - minPrice - maxPrice - } - shop { - currency { - code - } - } - primaryImage { - URLs { - large - medium - original - small - thumbnail - } - priority - productId - variantId - } - media { - priority - productId - variantId - URLs { - thumbnail - small - medium - large - original - } - } - tags { - nodes { - name - slug - position - } - } - variants { - _id - variantId - attributeLabel - title - optionTitle - index - pricing { - compareAtPrice { - displayAmount - } - price - currency { - code - } - displayPrice - } - canBackorder - inventoryAvailableToSell - isBackorder - isSoldOut - isLowQuantity - options { - _id - variantId - attributeLabel - title - index - pricing { - compareAtPrice { - displayAmount - } - price - currency { - code - } - displayPrice - } - optionTitle - canBackorder - inventoryAvailableToSell - isBackorder - isSoldOut - isLowQuantity - media { - priority - productId - variantId - URLs { - thumbnail - small - medium - large - original - } - } - metafields { - description - key - namespace - scope - value - valueType - } - primaryImage { - URLs { - large - medium - original - small - thumbnail - } - priority - productId - variantId - } - } - media { - priority - productId - variantId - URLs { - thumbnail - small - medium - large - original - } - } - metafields { - description - key - namespace - scope - value - valueType - } - primaryImage { - URLs { - large - medium - original - small - thumbnail - } - priority - productId - variantId - } - } - } - } - } -` - -export default getProductQuery diff --git a/packages/opencommerce/src/api/queries/get-shop-currency-query.ts b/packages/opencommerce/src/api/queries/get-shop-currency-query.ts deleted file mode 100644 index 80b134685..000000000 --- a/packages/opencommerce/src/api/queries/get-shop-currency-query.ts +++ /dev/null @@ -1,11 +0,0 @@ -const getShopCurrencyQuery = /* GraphQL */ ` - query getShopCurrency($id: ID!) { - shop(id: $id) { - currency { - code - } - } - } -` - -export default getShopCurrencyQuery diff --git a/packages/opencommerce/src/api/utils/fetch-grapql-api.ts b/packages/opencommerce/src/api/utils/fetch-grapql-api.ts index d5f20ebaf..7b5c812b4 100644 --- a/packages/opencommerce/src/api/utils/fetch-grapql-api.ts +++ b/packages/opencommerce/src/api/utils/fetch-grapql-api.ts @@ -1,23 +1,27 @@ import { FetcherError } from '@vercel/commerce/utils/errors' -import type { GraphQLFetcher } from '@vercel/commerce/api' +import type { FetchOptions, GraphQLFetcher } from '@vercel/commerce/api' import type { OpenCommerceConfig } from '../index' -import fetch from './fetch' const fetchGraphqlApi: ( getConfig: () => OpenCommerceConfig ) => GraphQLFetcher = (getConfig) => - async (query: string, { variables, preview } = {}, fetchOptions) => { + async ( + query: string, + { variables } = {}, + options?: FetchOptions + ): Promise => { // log.warn(query) const config = getConfig() - const res = await fetch(config.commerceUrl + (preview ? '/preview' : ''), { - ...fetchOptions, + const res = await fetch(config.commerceUrl, { + ...options, method: 'POST', headers: { - ...fetchOptions?.headers, + ...options?.headers, 'Content-Type': 'application/json', }, body: JSON.stringify({ + ...options?.body, query, variables, }), diff --git a/packages/opencommerce/src/cart/use-add-item.tsx b/packages/opencommerce/src/cart/use-add-item.tsx index 5496963ff..c8e626fbe 100644 --- a/packages/opencommerce/src/cart/use-add-item.tsx +++ b/packages/opencommerce/src/cart/use-add-item.tsx @@ -3,14 +3,13 @@ import useAddItem, { UseAddItem } from '@vercel/commerce/cart/use-add-item' import type { AddItemHook } from '@vercel/commerce/types/cart' import { CommerceError } from '@vercel/commerce/utils/errors' import { MutationHook } from '@vercel/commerce/utils/types' -import { CartTypes } from '../types/cart' import useCart from './use-cart' export default useAddItem as UseAddItem -export const handler: MutationHook> = { +export const handler: MutationHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'POST', }, async fetcher({ input: item, options, fetch }) { @@ -22,7 +21,6 @@ export const handler: MutationHook> = { message: 'The item quantity has to be a valid integer greater than 0', }) } - const data = await fetch({ ...options, body: { item } }) return data }, diff --git a/packages/opencommerce/src/cart/use-cart.tsx b/packages/opencommerce/src/cart/use-cart.tsx index ff669dbb3..c7c08b570 100644 --- a/packages/opencommerce/src/cart/use-cart.tsx +++ b/packages/opencommerce/src/cart/use-cart.tsx @@ -2,13 +2,12 @@ import { useMemo } from 'react' import { SWRHook } from '@vercel/commerce/utils/types' import useCart, { UseCart } from '@vercel/commerce/cart/use-cart' import type { GetCartHook } from '@vercel/commerce/types/cart' -import { CartTypes } from '../types/cart' export default useCart as UseCart -export const handler: SWRHook> = { +export const handler: SWRHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'GET', }, useHook: diff --git a/packages/opencommerce/src/cart/use-remove-item.tsx b/packages/opencommerce/src/cart/use-remove-item.tsx index eb68d435b..5dde7db27 100644 --- a/packages/opencommerce/src/cart/use-remove-item.tsx +++ b/packages/opencommerce/src/cart/use-remove-item.tsx @@ -27,7 +27,7 @@ export default useRemoveItem as UseRemoveItem export const handler: MutationHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'DELETE', }, async fetcher({ input: { itemId }, options, fetch }) { diff --git a/packages/opencommerce/src/cart/use-update-item.tsx b/packages/opencommerce/src/cart/use-update-item.tsx index 8ede20bf3..c03068392 100644 --- a/packages/opencommerce/src/cart/use-update-item.tsx +++ b/packages/opencommerce/src/cart/use-update-item.tsx @@ -17,7 +17,7 @@ export default useUpdateItem as UseUpdateItem export const handler: MutationHook = { fetchOptions: { - url: '/api/cart', + url: '/api/commerce/cart', method: 'PUT', }, async fetcher({ input: { itemId, item }, options, fetch }) { diff --git a/packages/opencommerce/src/checkout/use-checkout.tsx b/packages/opencommerce/src/checkout/use-checkout.tsx index 721f2e72b..6cf1c8ed7 100644 --- a/packages/opencommerce/src/checkout/use-checkout.tsx +++ b/packages/opencommerce/src/checkout/use-checkout.tsx @@ -1,6 +1,5 @@ -import type { GetCheckoutHook } from '@vercel/commerce/types/checkout' - import { useMemo } from 'react' +import Cookies from 'js-cookie' import { SWRHook } from '@vercel/commerce/utils/types' import useCheckout, { UseCheckout, @@ -8,27 +7,64 @@ import useCheckout, { import useSubmitCheckout from './use-submit-checkout' import { useCheckoutContext } from '@components/checkout/context' import { AddressFields } from '../types/customer/address' -import { useCart } from '../cart' +import { GetCheckoutHook } from '../types/checkout' import { FulfillmentGroup } from '../types/cart' +import { API_URL, OPENCOMMERCE_ANONYMOUS_CART_TOKEN_COOKIE } from '../const' +import getAnonymousCartQuery from '../api/queries/get-anonymous-cart' +import { normalizeCheckout } from '../utils/normalize' export default useCheckout as UseCheckout export const handler: SWRHook = { fetchOptions: { - query: '', - method: '', + query: getAnonymousCartQuery, + url: API_URL, }, - useHook: () => - function useHook() { - const { data: cart } = useCart() - const shippingTypeMethod = - cart?.checkout?.fulfillmentGroups && - cart.checkout.fulfillmentGroups.find( - (group) => group?.type === 'shipping' - ) - const hasShippingMethods = - !!shippingTypeMethod?.availableFulfillmentOptions?.length + async fetcher({ options, fetch, input: { cartId } }) { + const cartToken = Cookies.get(OPENCOMMERCE_ANONYMOUS_CART_TOKEN_COOKIE) + if (cartId && cartToken) { + const { cart: rawAnonymousCart } = await fetch({ + ...options, + variables: { + cartId, + cartToken: cartToken, + }, + }) + + if (!rawAnonymousCart?.checkout) return null + + const checkout = normalizeCheckout(rawAnonymousCart.checkout) + + const shippingTypeFulfillment = checkout.fulfillmentGroups.find( + (group) => group.type === 'shipping' + ) + + const hasShippingMethods = + !!shippingTypeFulfillment?.availableFulfillmentOptions?.length + + const shippingGroup = checkout.fulfillmentGroups.find( + (group: FulfillmentGroup) => group?.type === 'shipping' + ) + + const totalDisplayAmount = + checkout.summary.fulfillmentTotal?.displayAmount || '0' + + return { + hasPayment: true, + hasShippingMethods, + shippingGroup, + hasShipping: false, + addressId: 'addressId', + totalDisplayAmount, + } + } + + return null + }, + useHook: ({ useData }) => + function useHook(input) { + const submit = useSubmitCheckout() const { addressFields } = useCheckoutContext() const { shippingMethodId, ...restAddressFields } = @@ -38,45 +74,38 @@ export const handler: SWRHook = { (fieldValue) => !!fieldValue ) - const totalAmount = - cart?.checkout?.summary.fulfillmentTotal?.displayAmount + const response = useData({ + swrOptions: { revalidateOnFocus: false, ...input?.swrOptions }, + }) - const shippingGroup = cart?.checkout?.fulfillmentGroups.find( - (group: FulfillmentGroup) => group?.type === 'shipping' - ) - - const response = useMemo( - () => ({ - data: { - // example payment plugin does not need payment info - hasPayment: true, - hasShipping: hasEnteredAddress, - hasShippingMethods, - hasSelectedShippingMethod: !!shippingMethodId, - totalAmount, - shippingGroup, - }, - }), - [ - hasEnteredAddress, - hasShippingMethods, - shippingMethodId, - totalAmount, - shippingGroup, - ] - ) - - return useMemo( - () => - Object.create(response, { - submit: { - get() { - return useSubmitCheckout - }, - enumerable: true, + return useMemo(() => { + if (response.data) { + return { + ...response, + data: { + ...response.data, + hasShipping: hasEnteredAddress, + hasSelectedShippingMethod: !!shippingMethodId, }, - }), - [response, useSubmitCheckout] - ) + submit: () => submit, + isEmpty: response.data.lineItems?.length ?? 0, + } + } + + return Object.create(response, { + isEmpty: { + get() { + return response.data?.lineItems?.length ?? 0 + }, + enumerable: true, + }, + submit: { + get() { + return submit + }, + enumerable: true, + }, + }) + }, [response, submit, hasEnteredAddress, shippingMethodId]) }, } diff --git a/packages/opencommerce/src/checkout/use-submit-checkout.ts b/packages/opencommerce/src/checkout/use-submit-checkout.ts index ea07b252f..7ac65d569 100644 --- a/packages/opencommerce/src/checkout/use-submit-checkout.ts +++ b/packages/opencommerce/src/checkout/use-submit-checkout.ts @@ -1,6 +1,7 @@ import { useCallback } from 'react' import type { SubmitCheckoutHook } from '@vercel/commerce/types/checkout' +import { useCheckoutContext } from '@components/checkout/context' import type { MutationHook } from '@vercel/commerce/utils/types' import useSubmitCheckout, { UseSubmitCheckout, @@ -10,7 +11,7 @@ export default useSubmitCheckout as UseSubmitCheckout export const handler: MutationHook = { fetchOptions: { - url: '/api/checkout', + url: '/api/commerce/checkout', method: 'POST', }, async fetcher({ input: item, options, fetch }) { @@ -22,9 +23,21 @@ export const handler: MutationHook = { }, useHook: ({ fetch }) => function useHook() { + const { addressFields } = useCheckoutContext() + return useCallback(async function onSubmitCheckout(input) { const data = await fetch({ - input, + // dummy data here since OC does not need card data for example payment method + input: { + ...input, + card: { + cardHolder: 'Open Commerce', + cardCvc: '123', + cardExpireDate: '03/30', + cardNumber: '123456789', + ...addressFields, + }, + }, }) return data }, []) diff --git a/packages/opencommerce/src/const.ts b/packages/opencommerce/src/const.ts new file mode 100644 index 000000000..2297976a4 --- /dev/null +++ b/packages/opencommerce/src/const.ts @@ -0,0 +1,4 @@ +export const OPENCOMMERCE_ANONYMOUS_CART_TOKEN_COOKIE = + 'opencommerce_anonymousCartToken' +export const OPENCOMMERCE_CART_TOKEN = 'opencommerce_cartId' +export const API_URL = process.env.NEXT_PUBLIC_OPENCOMMERCE_API_URL diff --git a/packages/opencommerce/src/customer/address/use-add-item.tsx b/packages/opencommerce/src/customer/address/use-add-item.tsx index 0d5f05093..878b1964b 100644 --- a/packages/opencommerce/src/customer/address/use-add-item.tsx +++ b/packages/opencommerce/src/customer/address/use-add-item.tsx @@ -5,13 +5,12 @@ import useAddItem, { UseAddItem, } from '@vercel/commerce/customer/address/use-add-item' import { useCheckoutContext } from '@components/checkout/context' -import { CustomerAddressTypes } from '../../types/customer/address' export default useAddItem as UseAddItem -export const handler: MutationHook> = { +export const handler: MutationHook = { fetchOptions: { - url: '/api/customer/address', + url: '/api/commerce/customer/address', method: 'POST', }, async fetcher({ input: item, options, fetch }) { @@ -29,7 +28,7 @@ export const handler: MutationHook> = { async function addItem(input) { await fetch({ input }) setAddressFields({ ...addressFields, ...input }) - return undefined + return null }, [setAddressFields] ) diff --git a/packages/opencommerce/src/customer/address/use-update-item.tsx b/packages/opencommerce/src/customer/address/use-update-item.tsx index bc59c6506..91ed494a3 100644 --- a/packages/opencommerce/src/customer/address/use-update-item.tsx +++ b/packages/opencommerce/src/customer/address/use-update-item.tsx @@ -1,38 +1,51 @@ -import type { UpdateItemHook } from '@vercel/commerce/types/customer/address' +import Cookies from 'js-cookie' import type { MutationHook } from '@vercel/commerce/utils/types' import { useCallback } from 'react' import useUpdateItem, { UseUpdateItem, } from '@vercel/commerce/customer/address/use-update-item' import { useCheckoutContext } from '@components/checkout/context' -import { CustomerAddressTypes } from '../../types/customer/address' -import { useCart } from '../../cart' +import selectFulfillmentOptions from '../../api/mutations/select-fulfillment-options' + +import { useCheckout } from '../../checkout' +import { UpdateAddressItemHook } from '../../types/customer/address' +import { OPENCOMMERCE_ANONYMOUS_CART_TOKEN_COOKIE } from '../../const' export default useUpdateItem as UseUpdateItem -export const handler: MutationHook> = { +export const handler: MutationHook = { fetchOptions: { - url: '/api/customer/address', - method: 'PUT', + query: selectFulfillmentOptions, }, - async fetcher({ input: { item, itemId }, options, fetch }) { + async fetcher({ input: { item, itemId: cartId }, options, fetch }) { + const cartToken = Cookies.get(OPENCOMMERCE_ANONYMOUS_CART_TOKEN_COOKIE) + + if (!cartId) return null + const data = await fetch({ ...options, - body: { item, itemId }, + variables: { + input: { + cartId, + cartToken, + fulfillmentGroupId: item.fulfillmentGroupId, + fulfillmentMethodId: item.shippingMethodId, + }, + }, }) return data }, useHook: ({ fetch }) => function useHook() { - const { data: cart } = useCart() + const { data: checkoutData } = useCheckout() const { setAddressFields, addressFields } = useCheckoutContext() return useCallback( async function updateItem(input) { const { id, ...rest } = input const fulfillmentGroupId = - cart?.checkout?.fulfillmentGroups[0]._id || 'groupId' + checkoutData?.shippingGroup?._id || 'groupId' await fetch({ input: { item: { ...rest, fulfillmentGroupId }, itemId: id }, }) @@ -41,7 +54,7 @@ export const handler: MutationHook> = { shippingMethodId: rest.shippingMethodId, fulfillmentGroupId, }) - return undefined + return null }, [setAddressFields] ) diff --git a/packages/opencommerce/src/customer/card/use-add-item.tsx b/packages/opencommerce/src/customer/card/use-add-item.tsx index a201a289a..810af1c06 100644 --- a/packages/opencommerce/src/customer/card/use-add-item.tsx +++ b/packages/opencommerce/src/customer/card/use-add-item.tsx @@ -19,7 +19,7 @@ export const handler: MutationHook = { return useCallback( async function addItem(input) { setCardFields(input) - return undefined + return null }, [setCardFields] ) diff --git a/packages/opencommerce/src/fetcher.ts b/packages/opencommerce/src/fetcher.ts index 61619dad4..203f67afe 100644 --- a/packages/opencommerce/src/fetcher.ts +++ b/packages/opencommerce/src/fetcher.ts @@ -1,5 +1,6 @@ import type { Fetcher } from '@vercel/commerce/utils/types' import { FetcherError } from '@vercel/commerce/utils/errors' +import { API_URL } from './const' async function getText(res: Response) { try { @@ -18,20 +19,24 @@ async function getError(res: Response) { } const fetcher: Fetcher = async ({ - url, - method = 'GET', + url = API_URL, + method = 'POST', variables, + query, body: bodyObj, }) => { const hasBody = Boolean(variables || bodyObj) const body = hasBody - ? JSON.stringify(variables ? { variables } : bodyObj) + ? JSON.stringify(variables ? { query, variables } : bodyObj) : undefined const headers = hasBody ? { 'Content-Type': 'application/json' } : undefined const res = await fetch(url!, { method, body, headers }) if (res.ok) { - const { data } = await res.json() + const { data, errors } = await res.json() + if (errors && errors.length) { + throw getError(res) + } return data } diff --git a/packages/opencommerce/src/next.config.cjs b/packages/opencommerce/src/next.config.cjs index 82113f616..c1f4c39ed 100644 --- a/packages/opencommerce/src/next.config.cjs +++ b/packages/opencommerce/src/next.config.cjs @@ -3,6 +3,6 @@ const commerce = require('./commerce.config.json') module.exports = { commerce, images: { - domains: [process.env.OPENCOMMERCE_IMAGE_DOMAIN], + domains: [process.env.NEXT_PUBLIC_OPENCOMMERCE_IMAGE_DOMAIN], }, } diff --git a/packages/opencommerce/src/product/use-search.tsx b/packages/opencommerce/src/product/use-search.tsx index c0232def2..7951bac4e 100644 --- a/packages/opencommerce/src/product/use-search.tsx +++ b/packages/opencommerce/src/product/use-search.tsx @@ -6,7 +6,7 @@ export default useSearch as UseSearch export const handler: SWRHook = { fetchOptions: { - url: '/api/catalog/products', + url: '/api/commerce/catalog/products', method: 'GET', }, async fetcher({ diff --git a/packages/opencommerce/src/types/cart.ts b/packages/opencommerce/src/types/cart.ts index 874d08805..cf8d434e1 100644 --- a/packages/opencommerce/src/types/cart.ts +++ b/packages/opencommerce/src/types/cart.ts @@ -1,10 +1,8 @@ -import * as Core from '@vercel/commerce/types/cart' import { Checkout as CheckoutSchema, FulfillmentOption as FulfillmentOptionSchema, FulfillmentGroup as FulfillmentGroupSchema, } from '../../schema' -import { ProductVariant } from './product' export * from '@vercel/commerce/types/cart' @@ -24,19 +22,3 @@ export type Checkout = { fulfillmentGroups: FulfillmentGroup[] summary: CheckoutSchema['summary'] } - -export type Cart = Core.Cart & { - checkout?: Checkout -} - -export type CartItemBody = Core.CartItemBody & { - currencyCode?: string - variant?: ProductVariant -} - -export type CartTypes = Core.CartTypes & { - itemBody: CartItemBody - cart?: Cart -} - -export type CartSchema = Core.CartSchema diff --git a/packages/opencommerce/src/types/checkout.ts b/packages/opencommerce/src/types/checkout.ts index d139db685..d95d8cce4 100644 --- a/packages/opencommerce/src/types/checkout.ts +++ b/packages/opencommerce/src/types/checkout.ts @@ -1 +1,18 @@ export * from '@vercel/commerce/types/checkout' + +import { + GetCheckoutHook as CoreGetCheckoutHook, + Checkout, +} from '@vercel/commerce/types/checkout' +import { FulfillmentGroup } from './cart' + +export type GetCheckoutHook = Omit & { + data: + | (Checkout & { + hasShippingMethods?: boolean + hasSelectedShippingMethod?: boolean + totalDisplayAmount?: string + shippingGroup?: FulfillmentGroup + }) + | null +} diff --git a/packages/opencommerce/src/types/customer/address.ts b/packages/opencommerce/src/types/customer/address.ts index 42407399f..7db1316a5 100644 --- a/packages/opencommerce/src/types/customer/address.ts +++ b/packages/opencommerce/src/types/customer/address.ts @@ -7,6 +7,18 @@ export type AddressFields = Core.AddressFields & { fulfillmentGroupId?: string } -export type CustomerAddressTypes = Core.CustomerAddressTypes & { - fields: AddressFields +export type UpdateAddressItemHook = Omit< + Core.UpdateItemHook, + 'input' | 'body' | 'fetcherInput' | 'actionInput' +> & { + input: { + item?: AddressFields + wait?: number + } + body: { + itemId: string + item: AddressFields + } + fetcherInput: { itemId: string; item: AddressFields } + actionInput: AddressFields & { id: string } } diff --git a/packages/opencommerce/src/types/page.ts b/packages/opencommerce/src/types/page.ts index ccbc29f7b..5b54dd90b 100644 --- a/packages/opencommerce/src/types/page.ts +++ b/packages/opencommerce/src/types/page.ts @@ -7,5 +7,5 @@ export type PageTypes = { page: Page } -export type GetAllPagesOperation = Core.GetAllPagesOperation -export type GetPageOperation = Core.GetPageOperation +export type GetAllPagesOperation = Core.GetAllPagesOperation +export type GetPageOperation = Core.GetPageOperation diff --git a/packages/opencommerce/src/types/product.ts b/packages/opencommerce/src/types/product.ts index e8dcbe2a6..aa298399f 100644 --- a/packages/opencommerce/src/types/product.ts +++ b/packages/opencommerce/src/types/product.ts @@ -1,6 +1,4 @@ import * as Core from '@vercel/commerce/types/product' export * from '@vercel/commerce/types/product' -export type ProductVariant = Core.ProductVariant & { - price?: number -} +export type ProductVariant = Core.ProductVariant diff --git a/packages/opencommerce/src/types/site.ts b/packages/opencommerce/src/types/site.ts index b0c7334cb..089323c4c 100644 --- a/packages/opencommerce/src/types/site.ts +++ b/packages/opencommerce/src/types/site.ts @@ -1,5 +1,5 @@ import { Vendor as QueryVender, TagEdge } from '../../schema' -import { SiteTypes as CoreSiteTypes } from '@vercel/commerce/types/site' +import { Category, Brand } from '@vercel/commerce/types/site' export * from '@vercel/commerce/types/site' @@ -23,14 +23,10 @@ export type Navigation = { items?: Navigation[] } -export type SiteTypes = CoreSiteTypes & { - navigation: Navigation -} - -export type GetSiteInfoOperation = { +export type GetSiteInfoOperation = { data: { - categories: T['category'][] - brands: T['brand'][] - navigation: T['navigation'][] + categories: Category[] + brands: Brand[] + navigation: Navigation[] } } diff --git a/packages/opencommerce/src/utils/normalize.ts b/packages/opencommerce/src/utils/normalize.ts index c4c456966..5de41b23c 100644 --- a/packages/opencommerce/src/utils/normalize.ts +++ b/packages/opencommerce/src/utils/normalize.ts @@ -7,7 +7,7 @@ import type { import { OCCategory, Category, - Vendor, + Brand, OCVendor, Navigation, } from '../types/site' @@ -49,18 +49,18 @@ export function normalizeProduct( } = product return { - id: productId ?? _id, + id: productNode._id || productId, name: title ?? '', description: description ?? '', slug: slug?.replace(/^\/+|\/+$/g, '') ?? '', - path: slug ?? '', + path: `/${slug}` ?? '/', sku: sku ?? '', images: media?.length ? normalizeProductImages(media, title ?? '') : [], ...(product.vendor ? { vendor: product.vendor } : {}), price: { - value: pricing[0]?.minPrice ?? 0, + value: pricing[0]?.price || pricing[0]?.minPrice || 0, currencyCode: pricing[0]?.currency.code, }, variants: !!variants @@ -167,7 +167,7 @@ const normalizeProductVariants = ( productVariants.push({ id: variantId ?? '', - price: variantPrice, + price: { value: variantPrice }, options: [normalizeProductOption(variant)], }) @@ -226,13 +226,12 @@ export function normalizeCategory(category: OCCategory): Category { } } -export function normalizeVendors({ name }: OCVendor): Vendor { +export function normalizeVendors({ name }: OCVendor): Brand { return { - node: { - entityId: name ?? '', - name: name ?? '', - path: `brands/${name}`, - }, + id: name ?? '', + name: name ?? '', + path: `/${name}`, + slug: `/brands/${name}`, } } @@ -246,7 +245,7 @@ export function normalizeCart(cart: OCCart): Cart { createdAt: cart.createdAt, currency: { - code: cart.checkout?.summary?.total?.currency.code ?? '', + code: cart.checkout?.summary?.total?.currency.code ?? 'USD', }, lineItems: cart.items?.edges?.map((cartItem) => @@ -257,7 +256,6 @@ export function normalizeCart(cart: OCCart): Cart { totalPrice: cart.checkout?.summary?.total?.amount ?? 0, discounts: [], taxesIncluded: !!cart.checkout?.summary?.taxTotal?.amount, - checkout: cart.checkout ? normalizeCheckout(cart.checkout) : undefined, } } @@ -267,7 +265,7 @@ function filterNullValue( return items?.filter((item: T | null | undefined): item is T => !!item) ?? [] } -function normalizeCheckout(checkout: OCCheckout): Checkout { +export function normalizeCheckout(checkout: OCCheckout): Checkout { const fulfillmentGroups = filterNullValue(checkout.fulfillmentGroups).map( (group) => ({ selectedFulfillmentOption: group.selectedFulfillmentOption, @@ -323,7 +321,7 @@ function normalizeLineItem(cartItemEdge: CartItemEdge): LineItem { price: priceWhenAdded?.amount, listPrice: compareAtPrice?.amount ?? 0, }, - path: productSlug ?? '', + path: `/${productSlug}` ?? '', discounts: [], options: [ { diff --git a/packages/opencommerce/tsconfig.json b/packages/opencommerce/tsconfig.json index 2f84a843f..8f98f047b 100644 --- a/packages/opencommerce/tsconfig.json +++ b/packages/opencommerce/tsconfig.json @@ -4,11 +4,7 @@ "module": "esnext", "outDir": "dist", "baseUrl": "src", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "declaration": true, "allowJs": true, "skipLibCheck": true, @@ -20,12 +16,6 @@ "incremental": true, "jsx": "react-jsx" }, - "include": [ - "src", - "global.d.ts" - ], - "exclude": [ - "node_modules", - "dist" - ] -} \ No newline at end of file + "include": ["src", "global.d.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3669499f1..ffe406bef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -257,6 +257,47 @@ importers: taskr-swc: link:../taskr-swc typescript: 4.8.3 + packages/opencommerce: + specifiers: + '@graphql-codegen/cli': 2.7.0 + '@graphql-codegen/schema-ast': ^2.4.1 + '@graphql-codegen/typescript': ^2.6.0 + '@graphql-codegen/typescript-operations': ^2.4.3 + '@taskr/clear': ^1.1.0 + '@taskr/esnext': ^1.1.0 + '@taskr/watch': ^1.1.0 + '@types/node': ^18.0.3 + '@types/react': ^18.0.14 + '@vercel/commerce': workspace:* + lint-staged: ^13.0.3 + next: ^13.0.4 + prettier: ^2.7.1 + react: ^18.2.0 + react-dom: ^18.2.0 + taskr: ^1.1.0 + taskr-swc: workspace:* + typescript: ^4.7.4 + dependencies: + '@vercel/commerce': link:../commerce + devDependencies: + '@graphql-codegen/cli': 2.7.0_bidgzm5cq2du6gnjtweqqjrrn4 + '@graphql-codegen/schema-ast': 2.5.1 + '@graphql-codegen/typescript': 2.7.3 + '@graphql-codegen/typescript-operations': 2.5.3 + '@taskr/clear': 1.1.0 + '@taskr/esnext': 1.1.0 + '@taskr/watch': 1.1.0 + '@types/node': 18.7.18 + '@types/react': 18.0.20 + lint-staged: 13.0.3 + next: 13.0.6_biqbaboplfbrettd7655fr4n2y + prettier: 2.7.1 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + taskr: 1.1.0 + taskr-swc: link:../taskr-swc + typescript: 4.8.3 + packages/ordercloud: specifiers: '@taskr/clear': ^1.1.0 @@ -615,6 +656,7 @@ importers: '@vercel/commerce-commercejs': workspace:* '@vercel/commerce-kibocommerce': workspace:* '@vercel/commerce-local': workspace:* + '@vercel/commerce-opencommerce': workspace:* '@vercel/commerce-ordercloud': workspace:* '@vercel/commerce-saleor': workspace:* '@vercel/commerce-sfcc': workspace:* @@ -657,6 +699,7 @@ importers: '@vercel/commerce-commercejs': link:../packages/commercejs '@vercel/commerce-kibocommerce': link:../packages/kibocommerce '@vercel/commerce-local': link:../packages/local + '@vercel/commerce-opencommerce': link:../packages/opencommerce '@vercel/commerce-ordercloud': link:../packages/ordercloud '@vercel/commerce-saleor': link:../packages/saleor '@vercel/commerce-sfcc': link:../packages/sfcc @@ -711,6 +754,34 @@ packages: '@jridgewell/trace-mapping': 0.3.15 dev: true + /@ardatan/relay-compiler/12.0.0: + resolution: {integrity: sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q==} + hasBin: true + peerDependencies: + graphql: '*' + dependencies: + '@babel/core': 7.20.5 + '@babel/generator': 7.20.5 + '@babel/parser': 7.20.5 + '@babel/runtime': 7.19.0 + '@babel/traverse': 7.20.5 + '@babel/types': 7.20.5 + babel-preset-fbjs: 3.4.0_@babel+core@7.20.5 + chalk: 4.1.2 + fb-watchman: 2.0.1 + fbjs: 3.0.4 + glob: 7.2.3 + immutable: 3.7.6 + invariant: 2.2.4 + nullthrows: 1.1.1 + relay-runtime: 12.0.0 + signedsource: 1.0.0 + yargs: 15.4.1 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@ardatan/relay-compiler/12.0.0_graphql@16.6.0: resolution: {integrity: sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q==} hasBin: true @@ -1616,6 +1687,59 @@ packages: - utf-8-validate dev: true + /@graphql-codegen/cli/2.7.0_bidgzm5cq2du6gnjtweqqjrrn4: + resolution: {integrity: sha512-qlBcS6jGfZ/xWXwqiyRLHGRuLC9gUdF8AwGHN7LdAYEP5MjL7pIXb02W5JuvMn47rrvr2Q22H9ECppZX65oSAg==} + hasBin: true + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/core': 2.5.1 + '@graphql-codegen/plugin-helpers': 2.7.0 + '@graphql-tools/apollo-engine-loader': 7.3.13 + '@graphql-tools/code-file-loader': 7.3.6 + '@graphql-tools/git-loader': 7.2.6 + '@graphql-tools/github-loader': 7.3.13 + '@graphql-tools/graphql-file-loader': 7.5.5 + '@graphql-tools/json-file-loader': 7.4.6 + '@graphql-tools/load': 7.7.7 + '@graphql-tools/prisma-loader': 7.2.22_@types+node@18.7.18 + '@graphql-tools/url-loader': 7.16.2_@types+node@18.7.18 + '@graphql-tools/utils': 8.12.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + change-case-all: 1.0.14 + chokidar: 3.5.3 + common-tags: 1.8.2 + cosmiconfig: 7.0.1 + debounce: 1.2.1 + detect-indent: 6.1.0 + graphql-config: 4.3.5_bidgzm5cq2du6gnjtweqqjrrn4 + inquirer: 8.2.4 + is-glob: 4.0.3 + json-to-pretty-yaml: 1.2.2 + latest-version: 5.1.0 + listr: 0.14.3 + listr-update-renderer: 0.5.0_listr@0.14.3 + log-symbols: 4.1.0 + mkdirp: 1.0.4 + string-env-interpolation: 1.0.1 + ts-log: 2.2.4 + wrap-ansi: 7.0.0 + yaml: 1.10.2 + yargs: 17.5.1 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - bufferutil + - encoding + - supports-color + - typescript + - utf-8-validate + - zen-observable + - zenObservable + dev: true + /@graphql-codegen/cli/2.7.0_fte77dov2vin5jxmf6euzzc57i: resolution: {integrity: sha512-qlBcS6jGfZ/xWXwqiyRLHGRuLC9gUdF8AwGHN7LdAYEP5MjL7pIXb02W5JuvMn47rrvr2Q22H9ECppZX65oSAg==} hasBin: true @@ -1724,6 +1848,17 @@ packages: - zenObservable dev: true + /@graphql-codegen/core/2.5.1: + resolution: {integrity: sha512-alctBVl2hMnBXDLwkgmnFPrZVIiBDsWJSmxJcM4GKg1PB23+xuov35GE47YAyAhQItE1B1fbYnbb1PtGiDZ4LA==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 2.7.0 + '@graphql-tools/schema': 8.5.1 + '@graphql-tools/utils': 8.12.0 + tslib: 2.3.1 + dev: true + /@graphql-codegen/core/2.5.1_graphql@16.6.0: resolution: {integrity: sha512-alctBVl2hMnBXDLwkgmnFPrZVIiBDsWJSmxJcM4GKg1PB23+xuov35GE47YAyAhQItE1B1fbYnbb1PtGiDZ4LA==} peerDependencies: @@ -1748,6 +1883,19 @@ packages: tslib: 2.4.0 dev: true + /@graphql-codegen/plugin-helpers/2.7.0: + resolution: {integrity: sha512-+a2VP/4Ob0fwP8YLrQ/hhYlAA9UZUdDFNqwS543DmyiGFUkNIsa7TnTsE/mBDKJSMsCVWLw78949fCpzjyw/9Q==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-tools/utils': 8.12.0 + change-case-all: 1.0.14 + common-tags: 1.8.2 + import-from: 4.0.0 + lodash: 4.17.21 + tslib: 2.4.0 + dev: true + /@graphql-codegen/plugin-helpers/2.7.0_graphql@16.6.0: resolution: {integrity: sha512-+a2VP/4Ob0fwP8YLrQ/hhYlAA9UZUdDFNqwS543DmyiGFUkNIsa7TnTsE/mBDKJSMsCVWLw78949fCpzjyw/9Q==} peerDependencies: @@ -1762,6 +1910,16 @@ packages: tslib: 2.4.0 dev: true + /@graphql-codegen/schema-ast/2.5.1: + resolution: {integrity: sha512-tewa5DEKbglWn7kYyVBkh3J8YQ5ALqAMVmZwiVFIGOao5u66nd+e4HuFqp0u+Jpz4SJGGi0ap/oFrEvlqLjd2A==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 2.7.0 + '@graphql-tools/utils': 8.12.0 + tslib: 2.4.0 + dev: true + /@graphql-codegen/schema-ast/2.5.1_graphql@16.6.0: resolution: {integrity: sha512-tewa5DEKbglWn7kYyVBkh3J8YQ5ALqAMVmZwiVFIGOao5u66nd+e4HuFqp0u+Jpz4SJGGi0ap/oFrEvlqLjd2A==} peerDependencies: @@ -1773,6 +1931,21 @@ packages: tslib: 2.4.0 dev: true + /@graphql-codegen/typescript-operations/2.5.3: + resolution: {integrity: sha512-s+pA+Erm0HeBb/D5cNrflwRM5KWhkiA5cbz4uA99l3fzFPveoQBPfRCBu0XAlJLP/kBDy64+o4B8Nfc7wdRtmA==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 2.7.0 + '@graphql-codegen/typescript': 2.7.3 + '@graphql-codegen/visitor-plugin-common': 2.12.1 + auto-bind: 4.0.0 + tslib: 2.4.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@graphql-codegen/typescript-operations/2.5.3_graphql@16.6.0: resolution: {integrity: sha512-s+pA+Erm0HeBb/D5cNrflwRM5KWhkiA5cbz4uA99l3fzFPveoQBPfRCBu0XAlJLP/kBDy64+o4B8Nfc7wdRtmA==} peerDependencies: @@ -1789,6 +1962,21 @@ packages: - supports-color dev: true + /@graphql-codegen/typescript/2.7.3: + resolution: {integrity: sha512-EzX/acijXtbG/AwPzho2ZZWaNo00+xAbsRDP+vnT2PwQV3AYq3/5bFvjq1XfAGWbTntdmlYlIwC9hf5bI85WVA==} + peerDependencies: + graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 2.7.0 + '@graphql-codegen/schema-ast': 2.5.1 + '@graphql-codegen/visitor-plugin-common': 2.12.1 + auto-bind: 4.0.0 + tslib: 2.4.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@graphql-codegen/typescript/2.7.3_graphql@16.6.0: resolution: {integrity: sha512-EzX/acijXtbG/AwPzho2ZZWaNo00+xAbsRDP+vnT2PwQV3AYq3/5bFvjq1XfAGWbTntdmlYlIwC9hf5bI85WVA==} peerDependencies: @@ -1805,6 +1993,26 @@ packages: - supports-color dev: true + /@graphql-codegen/visitor-plugin-common/2.12.1: + resolution: {integrity: sha512-dIUrX4+i/uazyPQqXyQ8cqykgNFe1lknjnfDWFo0gnk2W8+ruuL2JpSrj/7efzFHxbYGMQrCABDCUTVLi3DcVA==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-codegen/plugin-helpers': 2.7.0 + '@graphql-tools/optimize': 1.3.1 + '@graphql-tools/relay-operation-optimizer': 6.5.6 + '@graphql-tools/utils': 8.12.0 + auto-bind: 4.0.0 + change-case-all: 1.0.14 + dependency-graph: 0.11.0 + graphql-tag: 2.12.6 + parse-filepath: 1.0.2 + tslib: 2.4.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@graphql-codegen/visitor-plugin-common/2.12.1_graphql@16.6.0: resolution: {integrity: sha512-dIUrX4+i/uazyPQqXyQ8cqykgNFe1lknjnfDWFo0gnk2W8+ruuL2JpSrj/7efzFHxbYGMQrCABDCUTVLi3DcVA==} peerDependencies: @@ -1826,6 +2034,19 @@ packages: - supports-color dev: true + /@graphql-tools/apollo-engine-loader/7.3.13: + resolution: {integrity: sha512-fr2TcA9fM+H81ymdtyDaocZ/Ua4Vhhf1IvpQoPpuEUwLorREd86N8VORUEIBvEdJ1b7Bz7NqwL3RnM5m9KXftA==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/utils': 8.12.0 + '@whatwg-node/fetch': 0.4.3 + tslib: 2.4.0 + transitivePeerDependencies: + - encoding + dev: true + /@graphql-tools/apollo-engine-loader/7.3.13_graphql@16.6.0: resolution: {integrity: sha512-fr2TcA9fM+H81ymdtyDaocZ/Ua4Vhhf1IvpQoPpuEUwLorREd86N8VORUEIBvEdJ1b7Bz7NqwL3RnM5m9KXftA==} peerDependencies: @@ -1840,6 +2061,17 @@ packages: - encoding dev: true + /@graphql-tools/batch-execute/8.5.6: + resolution: {integrity: sha512-33vMvVDLBKsNJVNhcySVXF+zkcRL/GRs1Lt+MxygrYCypcAPpFm+amE2y9vOCFufuaKExIX7Lonnmxu19vPzaQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 8.12.0 + dataloader: 2.1.0 + tslib: 2.4.0 + value-or-promise: 1.0.11 + dev: true + /@graphql-tools/batch-execute/8.5.6_graphql@16.6.0: resolution: {integrity: sha512-33vMvVDLBKsNJVNhcySVXF+zkcRL/GRs1Lt+MxygrYCypcAPpFm+amE2y9vOCFufuaKExIX7Lonnmxu19vPzaQ==} peerDependencies: @@ -1852,6 +2084,20 @@ packages: value-or-promise: 1.0.11 dev: true + /@graphql-tools/code-file-loader/7.3.6: + resolution: {integrity: sha512-PNWWSwSuQAqANerDwS0zdQ5FPipirv75TjjzBHnY+6AF/WvKq5sQiUQheA2P7B+MZc/KdQ7h/JAGMQOhKNVA+Q==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/graphql-tag-pluck': 7.3.6 + '@graphql-tools/utils': 8.12.0 + globby: 11.1.0 + tslib: 2.4.0 + unixify: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /@graphql-tools/code-file-loader/7.3.6_graphql@16.6.0: resolution: {integrity: sha512-PNWWSwSuQAqANerDwS0zdQ5FPipirv75TjjzBHnY+6AF/WvKq5sQiUQheA2P7B+MZc/KdQ7h/JAGMQOhKNVA+Q==} peerDependencies: @@ -1867,6 +2113,19 @@ packages: - supports-color dev: true + /@graphql-tools/delegate/9.0.6: + resolution: {integrity: sha512-HMA7rcJLQA3dJwWRG2271mRCdh0SLaK5+FPg+F7JIa3aF5fRdN4pVHNDaAjQeyKOQ2afjgjO5FvOyJwv/ve7Bg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/batch-execute': 8.5.6 + '@graphql-tools/schema': 9.0.4 + '@graphql-tools/utils': 8.12.0 + dataloader: 2.1.0 + tslib: 2.4.0 + value-or-promise: 1.0.11 + dev: true + /@graphql-tools/delegate/9.0.6_graphql@16.6.0: resolution: {integrity: sha512-HMA7rcJLQA3dJwWRG2271mRCdh0SLaK5+FPg+F7JIa3aF5fRdN4pVHNDaAjQeyKOQ2afjgjO5FvOyJwv/ve7Bg==} peerDependencies: @@ -1881,6 +2140,21 @@ packages: value-or-promise: 1.0.11 dev: true + /@graphql-tools/git-loader/7.2.6: + resolution: {integrity: sha512-QA94Gjp70xcdIYUbZDIm8fnuDN0IvoIIVVU+lXQemoV+vDeJKIjrP9tfOTjVDPIDXQnCYswvu9HLe8BlEApQYw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/graphql-tag-pluck': 7.3.6 + '@graphql-tools/utils': 8.12.0 + is-glob: 4.0.3 + micromatch: 4.0.5 + tslib: 2.4.0 + unixify: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /@graphql-tools/git-loader/7.2.6_graphql@16.6.0: resolution: {integrity: sha512-QA94Gjp70xcdIYUbZDIm8fnuDN0IvoIIVVU+lXQemoV+vDeJKIjrP9tfOTjVDPIDXQnCYswvu9HLe8BlEApQYw==} peerDependencies: @@ -1897,6 +2171,21 @@ packages: - supports-color dev: true + /@graphql-tools/github-loader/7.3.13: + resolution: {integrity: sha512-4RTjdtdtQC+n9LJMKpBThQGD3LnpeLVjU2A7BoVuKR+NQPJtcUzzuD6dXeYm5RiOMOQUsPGxQWKhJenW20aLUg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/graphql-tag-pluck': 7.3.6 + '@graphql-tools/utils': 8.12.0 + '@whatwg-node/fetch': 0.4.3 + tslib: 2.4.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@graphql-tools/github-loader/7.3.13_graphql@16.6.0: resolution: {integrity: sha512-4RTjdtdtQC+n9LJMKpBThQGD3LnpeLVjU2A7BoVuKR+NQPJtcUzzuD6dXeYm5RiOMOQUsPGxQWKhJenW20aLUg==} peerDependencies: @@ -1913,6 +2202,18 @@ packages: - supports-color dev: true + /@graphql-tools/graphql-file-loader/7.5.5: + resolution: {integrity: sha512-OL+7qO1S66TpMK7OGz8Ag2WL08HlxKxrObVSDlxzWbSubWuXM5v959XscYAKRf6daYcVpkfNvO37QjflL9mjhg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/import': 6.7.6 + '@graphql-tools/utils': 8.12.0 + globby: 11.1.0 + tslib: 2.4.0 + unixify: 1.0.0 + dev: true + /@graphql-tools/graphql-file-loader/7.5.5_graphql@16.6.0: resolution: {integrity: sha512-OL+7qO1S66TpMK7OGz8Ag2WL08HlxKxrObVSDlxzWbSubWuXM5v959XscYAKRf6daYcVpkfNvO37QjflL9mjhg==} peerDependencies: @@ -1926,6 +2227,20 @@ packages: unixify: 1.0.0 dev: true + /@graphql-tools/graphql-tag-pluck/7.3.6: + resolution: {integrity: sha512-qULgqsOGKY1/PBqmP7fJZqbCg/TzPHKB9Wl51HGA9QjGymrzmrH5EjvsC8RtgdubF8yuTTVVFTz1lmSQ7RPssQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@babel/parser': 7.20.5 + '@babel/traverse': 7.20.5 + '@babel/types': 7.20.5 + '@graphql-tools/utils': 8.12.0 + tslib: 2.4.0 + transitivePeerDependencies: + - supports-color + dev: true + /@graphql-tools/graphql-tag-pluck/7.3.6_graphql@16.6.0: resolution: {integrity: sha512-qULgqsOGKY1/PBqmP7fJZqbCg/TzPHKB9Wl51HGA9QjGymrzmrH5EjvsC8RtgdubF8yuTTVVFTz1lmSQ7RPssQ==} peerDependencies: @@ -1941,6 +2256,16 @@ packages: - supports-color dev: true + /@graphql-tools/import/6.7.6: + resolution: {integrity: sha512-WtUyiO2qCaK/H4u81zAw/NbBvCOzwKl4N+Vl+FqrFCzYobscwL6x6roePyoXM1O3+JJIIn3CETv4kg4kwxaBVw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 8.12.0 + resolve-from: 5.0.0 + tslib: 2.4.0 + dev: true + /@graphql-tools/import/6.7.6_graphql@16.6.0: resolution: {integrity: sha512-WtUyiO2qCaK/H4u81zAw/NbBvCOzwKl4N+Vl+FqrFCzYobscwL6x6roePyoXM1O3+JJIIn3CETv4kg4kwxaBVw==} peerDependencies: @@ -1952,6 +2277,17 @@ packages: tslib: 2.4.0 dev: true + /@graphql-tools/json-file-loader/7.4.6: + resolution: {integrity: sha512-34AfjCitO4NtJ5AcXYLcFF3GDsMVTycrljSaBA2t1d7B4bMPtREDphKXLMc/Uf2zW6IW1i1sZZyrcmArPy1Z8A==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 8.12.0 + globby: 11.1.0 + tslib: 2.4.0 + unixify: 1.0.0 + dev: true + /@graphql-tools/json-file-loader/7.4.6_graphql@16.6.0: resolution: {integrity: sha512-34AfjCitO4NtJ5AcXYLcFF3GDsMVTycrljSaBA2t1d7B4bMPtREDphKXLMc/Uf2zW6IW1i1sZZyrcmArPy1Z8A==} peerDependencies: @@ -1964,6 +2300,17 @@ packages: unixify: 1.0.0 dev: true + /@graphql-tools/load/7.7.7: + resolution: {integrity: sha512-IpI2672zcoAX4FLjcH5kvHc7eqjPyLP1svrIcZKQenv0GRS6dW0HI9E5UCBs0y/yy8yW6s+SvpmNsfIlkMj3Kw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/schema': 9.0.4 + '@graphql-tools/utils': 8.12.0 + p-limit: 3.1.0 + tslib: 2.4.0 + dev: true + /@graphql-tools/load/7.7.7_graphql@16.6.0: resolution: {integrity: sha512-IpI2672zcoAX4FLjcH5kvHc7eqjPyLP1svrIcZKQenv0GRS6dW0HI9E5UCBs0y/yy8yW6s+SvpmNsfIlkMj3Kw==} peerDependencies: @@ -1976,6 +2323,15 @@ packages: tslib: 2.4.0 dev: true + /@graphql-tools/merge/8.3.1: + resolution: {integrity: sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 8.9.0 + tslib: 2.4.0 + dev: true + /@graphql-tools/merge/8.3.1_graphql@16.6.0: resolution: {integrity: sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg==} peerDependencies: @@ -1986,6 +2342,15 @@ packages: tslib: 2.4.0 dev: true + /@graphql-tools/merge/8.3.6: + resolution: {integrity: sha512-uUBokxXi89bj08P+iCvQk3Vew4vcfL5ZM6NTylWi8PIpoq4r5nJ625bRuN8h2uubEdRiH8ntN9M4xkd/j7AybQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/utils': 8.12.0 + tslib: 2.4.0 + dev: true + /@graphql-tools/merge/8.3.6_graphql@16.6.0: resolution: {integrity: sha512-uUBokxXi89bj08P+iCvQk3Vew4vcfL5ZM6NTylWi8PIpoq4r5nJ625bRuN8h2uubEdRiH8ntN9M4xkd/j7AybQ==} peerDependencies: @@ -1996,6 +2361,14 @@ packages: tslib: 2.4.0 dev: true + /@graphql-tools/optimize/1.3.1: + resolution: {integrity: sha512-5j5CZSRGWVobt4bgRRg7zhjPiSimk+/zIuColih8E8DxuFOaJ+t0qu7eZS5KXWBkjcd4BPNuhUPpNlEmHPqVRQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + tslib: 2.4.0 + dev: true + /@graphql-tools/optimize/1.3.1_graphql@16.6.0: resolution: {integrity: sha512-5j5CZSRGWVobt4bgRRg7zhjPiSimk+/zIuColih8E8DxuFOaJ+t0qu7eZS5KXWBkjcd4BPNuhUPpNlEmHPqVRQ==} peerDependencies: @@ -2005,6 +2378,38 @@ packages: tslib: 2.4.0 dev: true + /@graphql-tools/prisma-loader/7.2.22_@types+node@18.7.18: + resolution: {integrity: sha512-QafvScyyJ9Nvi1r4dmYUBzk1pe5MDwhMQUlJQLIphIPHYP8so8aRHKttoycuMZgQB43uOP+9RpdK0BIPa84/dw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/url-loader': 7.16.2_@types+node@18.7.18 + '@graphql-tools/utils': 8.12.0 + '@types/js-yaml': 4.0.5 + '@types/json-stable-stringify': 1.0.34 + '@types/jsonwebtoken': 8.5.9 + chalk: 4.1.2 + debug: 4.3.4 + dotenv: 16.0.2 + graphql-request: 5.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + isomorphic-fetch: 3.0.0 + js-yaml: 4.1.0 + json-stable-stringify: 1.0.1 + jsonwebtoken: 8.5.1 + lodash: 4.17.21 + scuid: 1.1.0 + tslib: 2.4.0 + yaml-ast-parser: 0.0.43 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: true + /@graphql-tools/prisma-loader/7.2.22_c3mutv243l2gduwl4hptzcimie: resolution: {integrity: sha512-QafvScyyJ9Nvi1r4dmYUBzk1pe5MDwhMQUlJQLIphIPHYP8so8aRHKttoycuMZgQB43uOP+9RpdK0BIPa84/dw==} peerDependencies: @@ -2071,6 +2476,19 @@ packages: - utf-8-validate dev: true + /@graphql-tools/relay-operation-optimizer/6.5.6: + resolution: {integrity: sha512-2KjaWYxD/NC6KtckbDEAbN46QO+74d1SBaZQ26qQjWhyoAjon12xlMW4HWxHEN0d0xuz0cnOVUVc+t4wVXePUg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@ardatan/relay-compiler': 12.0.0 + '@graphql-tools/utils': 8.12.0 + tslib: 2.4.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /@graphql-tools/relay-operation-optimizer/6.5.6_graphql@16.6.0: resolution: {integrity: sha512-2KjaWYxD/NC6KtckbDEAbN46QO+74d1SBaZQ26qQjWhyoAjon12xlMW4HWxHEN0d0xuz0cnOVUVc+t4wVXePUg==} peerDependencies: @@ -2085,6 +2503,17 @@ packages: - supports-color dev: true + /@graphql-tools/schema/8.5.1: + resolution: {integrity: sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/merge': 8.3.1 + '@graphql-tools/utils': 8.9.0 + tslib: 2.4.0 + value-or-promise: 1.0.11 + dev: true + /@graphql-tools/schema/8.5.1_graphql@16.6.0: resolution: {integrity: sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg==} peerDependencies: @@ -2097,6 +2526,17 @@ packages: value-or-promise: 1.0.11 dev: true + /@graphql-tools/schema/9.0.4: + resolution: {integrity: sha512-B/b8ukjs18fq+/s7p97P8L1VMrwapYc3N2KvdG/uNThSazRRn8GsBK0Nr+FH+mVKiUfb4Dno79e3SumZVoHuOQ==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/merge': 8.3.6 + '@graphql-tools/utils': 8.12.0 + tslib: 2.4.0 + value-or-promise: 1.0.11 + dev: true + /@graphql-tools/schema/9.0.4_graphql@16.6.0: resolution: {integrity: sha512-B/b8ukjs18fq+/s7p97P8L1VMrwapYc3N2KvdG/uNThSazRRn8GsBK0Nr+FH+mVKiUfb4Dno79e3SumZVoHuOQ==} peerDependencies: @@ -2109,6 +2549,32 @@ packages: value-or-promise: 1.0.11 dev: true + /@graphql-tools/url-loader/7.16.2_@types+node@18.7.18: + resolution: {integrity: sha512-ZVG3kDEJ88zLfqYtVmI36RUzaP/0bPBcJfBH8whMYL620tE6kizEQsON8iKsxcU1bWB5D7m9ZVFqW4eZ5EqVWw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@ardatan/sync-fetch': 0.0.1 + '@graphql-tools/delegate': 9.0.6 + '@graphql-tools/utils': 8.12.0 + '@graphql-tools/wrap': 9.2.1 + '@types/ws': 8.5.3 + '@whatwg-node/fetch': 0.4.3 + dset: 3.1.2 + extract-files: 11.0.0 + graphql-ws: 5.10.2 + isomorphic-ws: 5.0.0_ws@8.8.1 + meros: 1.2.0_@types+node@18.7.18 + tslib: 2.4.0 + value-or-promise: 1.0.11 + ws: 8.8.1 + transitivePeerDependencies: + - '@types/node' + - bufferutil + - encoding + - utf-8-validate + dev: true + /@graphql-tools/url-loader/7.16.2_c3mutv243l2gduwl4hptzcimie: resolution: {integrity: sha512-ZVG3kDEJ88zLfqYtVmI36RUzaP/0bPBcJfBH8whMYL620tE6kizEQsON8iKsxcU1bWB5D7m9ZVFqW4eZ5EqVWw==} peerDependencies: @@ -2163,6 +2629,14 @@ packages: - utf-8-validate dev: true + /@graphql-tools/utils/8.12.0: + resolution: {integrity: sha512-TeO+MJWGXjUTS52qfK4R8HiPoF/R7X+qmgtOYd8DTH0l6b+5Y/tlg5aGeUJefqImRq7nvi93Ms40k/Uz4D5CWw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + tslib: 2.4.0 + dev: true + /@graphql-tools/utils/8.12.0_graphql@16.6.0: resolution: {integrity: sha512-TeO+MJWGXjUTS52qfK4R8HiPoF/R7X+qmgtOYd8DTH0l6b+5Y/tlg5aGeUJefqImRq7nvi93Ms40k/Uz4D5CWw==} peerDependencies: @@ -2172,6 +2646,14 @@ packages: tslib: 2.4.0 dev: true + /@graphql-tools/utils/8.9.0: + resolution: {integrity: sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + tslib: 2.4.0 + dev: true + /@graphql-tools/utils/8.9.0_graphql@16.6.0: resolution: {integrity: sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==} peerDependencies: @@ -2181,6 +2663,18 @@ packages: tslib: 2.4.0 dev: true + /@graphql-tools/wrap/9.2.1: + resolution: {integrity: sha512-W8bzJijTZDNi8e1oM2AMG89CtvfTYaJ9lCe0dYMN+a+OPMhRfgR9+eO7ALcUa9y4MTu+YEDVjUq0ZboaSvesyA==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + dependencies: + '@graphql-tools/delegate': 9.0.6 + '@graphql-tools/schema': 9.0.4 + '@graphql-tools/utils': 8.12.0 + tslib: 2.4.0 + value-or-promise: 1.0.11 + dev: true + /@graphql-tools/wrap/9.2.1_graphql@16.6.0: resolution: {integrity: sha512-W8bzJijTZDNi8e1oM2AMG89CtvfTYaJ9lCe0dYMN+a+OPMhRfgR9+eO7ALcUa9y4MTu+YEDVjUq0ZboaSvesyA==} peerDependencies: @@ -2194,6 +2688,12 @@ packages: value-or-promise: 1.0.11 dev: true + /@graphql-typed-document-node/core/3.1.1: + resolution: {integrity: sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dev: true + /@graphql-typed-document-node/core/3.1.1_graphql@16.6.0: resolution: {integrity: sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==} peerDependencies: @@ -5005,7 +5505,7 @@ packages: eslint: 8.23.1 eslint-import-resolver-node: 0.3.6 eslint-import-resolver-typescript: 2.7.1_hdzsmr7kawaomymueo2tso6fjq - eslint-plugin-import: 2.26.0_eslint@8.23.1 + eslint-plugin-import: 2.26.0_6r44hdgvdiqbnekattsi42eeia eslint-plugin-jsx-a11y: 6.6.1_eslint@8.23.1 eslint-plugin-react: 7.31.8_eslint@8.23.1 eslint-plugin-react-hooks: 4.6.0_eslint@8.23.1 @@ -5042,7 +5542,7 @@ packages: dependencies: debug: 4.3.4 eslint: 8.23.1 - eslint-plugin-import: 2.26.0_eslint@8.23.1 + eslint-plugin-import: 2.26.0_6r44hdgvdiqbnekattsi42eeia glob: 7.2.3 is-glob: 4.0.3 resolve: 1.22.1 @@ -5051,7 +5551,7 @@ packages: - supports-color dev: true - /eslint-module-utils/2.7.4_oxfrjumrtiktpkw7r2zaom7f74: + /eslint-module-utils/2.7.4_ssuof5somrshypivkj4dxrg5tm: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -5072,14 +5572,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: + '@typescript-eslint/parser': 5.37.0_4brgkhw6cq4me3drk3kxrpb2mm debug: 3.2.7 eslint: 8.23.1 eslint-import-resolver-node: 0.3.6 + eslint-import-resolver-typescript: 2.7.1_hdzsmr7kawaomymueo2tso6fjq transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import/2.26.0_eslint@8.23.1: + /eslint-plugin-import/2.26.0_6r44hdgvdiqbnekattsi42eeia: resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} engines: {node: '>=4'} peerDependencies: @@ -5089,13 +5591,14 @@ packages: '@typescript-eslint/parser': optional: true dependencies: + '@typescript-eslint/parser': 5.37.0_4brgkhw6cq4me3drk3kxrpb2mm array-includes: 3.1.5 array.prototype.flat: 1.3.0 debug: 2.6.9 doctrine: 2.1.0 eslint: 8.23.1 eslint-import-resolver-node: 0.3.6 - eslint-module-utils: 2.7.4_oxfrjumrtiktpkw7r2zaom7f74 + eslint-module-utils: 2.7.4_ssuof5somrshypivkj4dxrg5tm has: 1.0.3 is-core-module: 2.10.0 is-glob: 4.0.3 @@ -5797,6 +6300,35 @@ packages: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} dev: true + /graphql-config/4.3.5_bidgzm5cq2du6gnjtweqqjrrn4: + resolution: {integrity: sha512-B4jXhHL7j3llCem+ACeo48wvVYhtJxRyt5SfSnvywbRlVYyUzt5ibZV6WJU2Yii2/rcVRIGi7BHDgcAPWdWdJg==} + engines: {node: '>= 10.0.0'} + peerDependencies: + graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + '@graphql-tools/graphql-file-loader': 7.5.5 + '@graphql-tools/json-file-loader': 7.4.6 + '@graphql-tools/load': 7.7.7 + '@graphql-tools/merge': 8.3.6 + '@graphql-tools/url-loader': 7.16.2_@types+node@18.7.18 + '@graphql-tools/utils': 8.12.0 + cosmiconfig: 7.0.1 + cosmiconfig-toml-loader: 1.0.0 + cosmiconfig-typescript-loader: 4.0.0_7erwhgajjbydxgvliy6verrdiu + minimatch: 4.2.1 + string-env-interpolation: 1.0.1 + ts-node: 10.9.1_bidgzm5cq2du6gnjtweqqjrrn4 + tslib: 2.4.0 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - bufferutil + - encoding + - typescript + - utf-8-validate + dev: true + /graphql-config/4.3.5_fte77dov2vin5jxmf6euzzc57i: resolution: {integrity: sha512-B4jXhHL7j3llCem+ACeo48wvVYhtJxRyt5SfSnvywbRlVYyUzt5ibZV6WJU2Yii2/rcVRIGi7BHDgcAPWdWdJg==} engines: {node: '>= 10.0.0'} @@ -5857,6 +6389,19 @@ packages: - utf-8-validate dev: true + /graphql-request/5.0.0: + resolution: {integrity: sha512-SpVEnIo2J5k2+Zf76cUkdvIRaq5FMZvGQYnA4lUWYbc99m+fHh4CZYRRO/Ff4tCLQ613fzCm3SiDT64ubW5Gyw==} + peerDependencies: + graphql: 14 - 16 + dependencies: + '@graphql-typed-document-node/core': 3.1.1 + cross-fetch: 3.1.5 + extract-files: 9.0.0 + form-data: 3.0.1 + transitivePeerDependencies: + - encoding + dev: true + /graphql-request/5.0.0_graphql@16.6.0: resolution: {integrity: sha512-SpVEnIo2J5k2+Zf76cUkdvIRaq5FMZvGQYnA4lUWYbc99m+fHh4CZYRRO/Ff4tCLQ613fzCm3SiDT64ubW5Gyw==} peerDependencies: @@ -5871,6 +6416,15 @@ packages: - encoding dev: true + /graphql-tag/2.12.6: + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + dependencies: + tslib: 2.4.0 + dev: true + /graphql-tag/2.12.6_graphql@16.6.0: resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} engines: {node: '>=10'} @@ -5881,6 +6435,13 @@ packages: tslib: 2.4.0 dev: true + /graphql-ws/5.10.2: + resolution: {integrity: sha512-QnLz0hbpTkmp/ATKAA3Tsg9LFZjWtgrLMgxc3W9G+3+rxUtzcYLwQh4YbXELYcpCqo8zdQxmERAtIQSgq75LTw==} + engines: {node: '>=10'} + peerDependencies: + graphql: '>=0.11 <=16' + dev: true + /graphql-ws/5.10.2_graphql@16.6.0: resolution: {integrity: sha512-QnLz0hbpTkmp/ATKAA3Tsg9LFZjWtgrLMgxc3W9G+3+rxUtzcYLwQh4YbXELYcpCqo8zdQxmERAtIQSgq75LTw==} engines: {node: '>=10'} @@ -6598,7 +7159,7 @@ packages: dev: true /json-buffer/3.0.0: - resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==} + resolution: {integrity: sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=} dev: true /json-buffer/3.0.1: @@ -7375,7 +7936,7 @@ packages: dependencies: '@next/env': 12.3.0 '@swc/helpers': 0.4.11 - caniuse-lite: 1.0.30001399 + caniuse-lite: 1.0.30001422 postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 diff --git a/site/.env.template b/site/.env.template index 5eab31d0a..3aac476d2 100644 --- a/site/.env.template +++ b/site/.env.template @@ -50,9 +50,8 @@ KIBO_API_HOST= NEXT_PUBLIC_COMMERCEJS_PUBLIC_KEY= NEXT_PUBLIC_COMMERCEJS_DEPLOYMENT_URL= -OPENCOMMERCE_STOREFRONT_API_URL= -OPENCOMMERCE_PRIMARY_SHOP_ID= -OPENCOMMERCE_IMAGE_DOMAIN= +NEXT_PUBLIC_OPENCOMMERCE_API_URL= +NEXT_PUBLIC_OPENCOMMERCE_IMAGE_DOMAIN= SFCC_CLIENT_ID= SFCC_CLIENT_SECRET= diff --git a/site/components/cart/CartItem/CartItem.tsx b/site/components/cart/CartItem/CartItem.tsx index 666b1a4ca..42025c7f3 100644 --- a/site/components/cart/CartItem/CartItem.tsx +++ b/site/components/cart/CartItem/CartItem.tsx @@ -89,7 +89,7 @@ const CartItem = ({ >
- + closeSidebarIfPresent()} className={s.productImage} diff --git a/site/components/checkout/CheckoutSidebarView/CheckoutSidebarView.tsx b/site/components/checkout/CheckoutSidebarView/CheckoutSidebarView.tsx index e03c2db00..a15d104e3 100644 --- a/site/components/checkout/CheckoutSidebarView/CheckoutSidebarView.tsx +++ b/site/components/checkout/CheckoutSidebarView/CheckoutSidebarView.tsx @@ -24,7 +24,6 @@ const CheckoutSidebarView: FC = () => { const { clearCheckoutFields, cardFields, addressFields } = useCheckoutContext() - async function handleSubmit(event: React.ChangeEvent) { try { setLoadingSubmit(true) @@ -32,7 +31,6 @@ const CheckoutSidebarView: FC = () => { await onCheckout({ card: cardFields, address: addressFields, - checkout: { cart: cartData }, }) clearCheckoutFields() setLoadingSubmit(false) @@ -111,7 +109,7 @@ const CheckoutSidebarView: FC = () => { {checkoutData?.hasSelectedShippingMethod ? (
  • Shipping - {checkoutData?.totalAmount} + {checkoutData?.totalDisplayAmount}
  • ) : (
  • diff --git a/site/components/checkout/ShippingMethodView/ShippingMethodView.tsx b/site/components/checkout/ShippingMethodView/ShippingMethodView.tsx index 0d2a4a3b3..af455479b 100644 --- a/site/components/checkout/ShippingMethodView/ShippingMethodView.tsx +++ b/site/components/checkout/ShippingMethodView/ShippingMethodView.tsx @@ -37,7 +37,7 @@ const ShippingMethod = () => { setSidebarView('CHECKOUT_VIEW') } - return checkoutData.shippingGroup ? ( + return checkoutData?.shippingGroup ? (
    setSidebarView('CHECKOUT_VIEW')}>
    @@ -58,7 +58,7 @@ const ShippingMethod = () => { type="radio" value={option?.fulfillmentMethod?._id} defaultChecked={ - checkoutData.shippingGroup.selectedFulfillmentOption + checkoutData.shippingGroup?.selectedFulfillmentOption ?.fulfillmentMethod?._id === option?.fulfillmentMethod?._id } diff --git a/site/components/common/Layout/Layout.tsx b/site/components/common/Layout/Layout.tsx index 5d1170281..8e5749c14 100644 --- a/site/components/common/Layout/Layout.tsx +++ b/site/components/common/Layout/Layout.tsx @@ -16,7 +16,8 @@ import ShippingMethodView from '@components/checkout/ShippingMethodView' import { CheckoutProvider } from '@components/checkout/context' import { MenuSidebarView } from '@components/common/UserNav' import type { Page } from '@commerce/types/page' -import type { Category, Navigation } from '@commerce/types/site' +import type { Category } from '@commerce/types/site' +import type { Navigation } from '@framework/types/site' import type { Link as LinkProps } from '../UserNav/MenuSidebarView' const Loading = () => ( diff --git a/site/components/common/Navbar/CustomNavbar.tsx b/site/components/common/Navbar/CustomNavbar.tsx index e69a0a956..197d2c1fd 100644 --- a/site/components/common/Navbar/CustomNavbar.tsx +++ b/site/components/common/Navbar/CustomNavbar.tsx @@ -20,14 +20,14 @@ const SubItem = ({ subItem, level = 0 }: SubItemProps) => { return ( <> {subItem.isUrlRelative ? ( - - - {subItem.label} - + + {subItem.label} ) : ( { {links.map((item) => (
    {item.isUrlRelative ? ( - - 0 && s.customLink - )} - > - {item.label} - + 0 && s.customLink + )} + > + {item.label} ) : ( = ({ links, customNavigation }) => ( ) : ( <> - All + All {links?.map((l) => ( - {l.label} + {l.label} ))} diff --git a/site/components/product/ProductSidebar/ProductSidebar.tsx b/site/components/product/ProductSidebar/ProductSidebar.tsx index 338adac15..806bc7bac 100644 --- a/site/components/product/ProductSidebar/ProductSidebar.tsx +++ b/site/components/product/ProductSidebar/ProductSidebar.tsx @@ -36,11 +36,6 @@ const ProductSidebar: FC = ({ product, className }) => { await addItem({ productId: String(product.id), variantId: String(variant ? variant.id : product.variants[0]?.id), - // Open Commerce provider only - ...({ - variant: variant ?? product.variants[0], - currencyCode: String(product.price.currencyCode), - } as any), }) setSidebarView('CART_VIEW') openSidebar() diff --git a/site/tsconfig.json b/site/tsconfig.json index 7c91afd6f..7aaf090c8 100644 --- a/site/tsconfig.json +++ b/site/tsconfig.json @@ -23,8 +23,8 @@ "@components/*": ["components/*"], "@commerce": ["../packages/commerce/src"], "@commerce/*": ["../packages/commerce/src/*"], - "@framework": ["../packages/local/src"], - "@framework/*": ["../packages/local/src/*"] + "@framework": ["../packages/opencommerce/src"], + "@framework/*": ["../packages/opencommerce/src/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],