Start to replace Checkout with Cart API

This commit is contained in:
cond0r 2021-08-04 23:07:38 +03:00
parent 0e7e7b7d5f
commit c5e3d82202
32 changed files with 3992 additions and 957 deletions

View File

@ -19,7 +19,7 @@ const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({
try { try {
await config.fetch(associateCustomerWithCheckoutMutation, { await config.fetch(associateCustomerWithCheckoutMutation, {
variables: { variables: {
checkoutId: cookies[SHOPIFY_CHECKOUT_ID_COOKIE], cartId: cookies[SHOPIFY_CHECKOUT_ID_COOKIE],
customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE], customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE],
}, },
}) })

View File

@ -3,9 +3,9 @@ import type { OperationContext } from '@commerce/api/operations'
import type { LoginOperation } from '../../types/login' import type { LoginOperation } from '../../types/login'
import type { ShopifyConfig, Provider } from '..' import type { ShopifyConfig, Provider } from '..'
import { import {
customerAccessTokenCreateMutation,
setCustomerToken, setCustomerToken,
throwUserErrors, throwUserErrors,
customerAccessTokenCreateMutation,
} from '../../utils' } from '../../utils'
import { CustomerAccessTokenCreateMutation } from '../../schema' import { CustomerAccessTokenCreateMutation } from '../../schema'

View File

@ -4,19 +4,19 @@ import { CommerceError } from '@commerce/utils/errors'
import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item' import useAddItem, { UseAddItem } from '@commerce/cart/use-add-item'
import type { AddItemHook } from '../types/cart' import type { AddItemHook } from '../types/cart'
import useCart from './use-cart' import useCart from './use-cart'
import { import {
checkoutLineItemAddMutation, getCartId,
getCheckoutId, normalizeCart,
checkoutToCart, throwUserErrors,
cartLineItemAddMutation,
} from '../utils' } from '../utils'
import { Mutation, MutationCheckoutLineItemsAddArgs } from '../schema' import { CartLinesAddMutation, CartLinesAddMutationVariables } from '../schema'
export default useAddItem as UseAddItem<typeof handler> export default useAddItem as UseAddItem<typeof handler>
export const handler: MutationHook<AddItemHook> = { export const handler: MutationHook<AddItemHook> = {
fetchOptions: { fetchOptions: {
query: checkoutLineItemAddMutation, query: cartLineItemAddMutation,
}, },
async fetcher({ input: item, options, fetch }) { async fetcher({ input: item, options, fetch }) {
if ( if (
@ -28,13 +28,13 @@ export const handler: MutationHook<AddItemHook> = {
}) })
} }
const { checkoutLineItemsAdd } = await fetch< const { cartLinesAdd } = await fetch<
Mutation, CartLinesAddMutation,
MutationCheckoutLineItemsAddArgs CartLinesAddMutationVariables
>({ >({
...options, ...options,
variables: { variables: {
checkoutId: getCheckoutId(), checkoutId: getCartId(),
lineItems: [ lineItems: [
{ {
variantId: item.variantId, variantId: item.variantId,
@ -44,18 +44,26 @@ export const handler: MutationHook<AddItemHook> = {
}, },
}) })
return checkoutToCart(checkoutLineItemsAdd) throwUserErrors(cartLinesAdd?.userErrors)
},
useHook: ({ fetch }) => () => {
const { mutate } = useCart()
return useCallback( if (!cartLinesAdd?.cart) {
async function addItem(input) { throw new CommerceError({ message: 'Missing cart from response' })
const data = await fetch({ input }) }
await mutate(data, false)
return data return normalizeCart(cartLinesAdd?.cart)
},
[fetch, mutate]
)
}, },
useHook:
({ fetch }) =>
() => {
const { mutate } = useCart()
return useCallback(
async function addItem(input) {
const data = await fetch({ input })
await mutate(data, false)
return data
},
[fetch, mutate]
)
},
} }

View File

@ -2,56 +2,35 @@ import { useMemo } from 'react'
import useCommerceCart, { UseCart } from '@commerce/cart/use-cart' import useCommerceCart, { UseCart } from '@commerce/cart/use-cart'
import { SWRHook } from '@commerce/utils/types' import { SWRHook } from '@commerce/utils/types'
import { checkoutCreate, checkoutToCart } from '../utils' import getCartQuery from '../utils/queries/get-cart-query'
import getCheckoutQuery from '../utils/queries/get-checkout-query'
import { GetCartHook } from '../types/cart' import { GetCartHook } from '../types/cart'
import {
GetCheckoutQuery,
GetCheckoutQueryVariables,
CheckoutDetailsFragment,
} from '../schema'
export default useCommerceCart as UseCart<typeof handler> export default useCommerceCart as UseCart<typeof handler>
export const handler: SWRHook<GetCartHook> = { export const handler: SWRHook<GetCartHook> = {
fetchOptions: { fetchOptions: {
query: getCheckoutQuery, query: getCartQuery,
}, },
async fetcher({ input: { cartId: checkoutId }, options, fetch }) { async fetcher({ input: { cartId }, options, fetch }) {
let checkout return cartId ? await fetch(options) : null
},
if (checkoutId) { useHook:
const data = await fetch({ ({ useData }) =>
...options, (input) => {
variables: { const response = useData({
checkoutId: checkoutId, swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
},
}) })
checkout = data.node return useMemo(
} () =>
Object.create(response, {
if (checkout?.completedAt || !checkoutId) { isEmpty: {
checkout = await checkoutCreate(fetch) get() {
} return (response.data?.lineItems.length ?? 0) <= 0
},
return checkoutToCart({ checkout }) enumerable: true,
},
useHook: ({ useData }) => (input) => {
const response = useData({
swrOptions: { revalidateOnFocus: false, ...input?.swrOptions },
})
return useMemo(
() =>
Object.create(response, {
isEmpty: {
get() {
return (response.data?.lineItems.length ?? 0) <= 0
}, },
enumerable: true, }),
}, [response]
}), )
[response] },
)
},
} }

View File

@ -18,50 +18,54 @@ export type RemoveItemActionInput<T = any> = T extends LineItem
export default useRemoveItem as UseRemoveItem<typeof handler> export default useRemoveItem as UseRemoveItem<typeof handler>
import { import { getCartId, normalizeCart, throwUserErrors } from '../utils'
checkoutLineItemRemoveMutation, import cartLineItemRemoveMutation from '../utils/mutations/cart-line-item-remove'
getCheckoutId,
checkoutToCart,
} from '../utils'
import { Mutation, MutationCheckoutLineItemsRemoveArgs } from '../schema' import {
CartLinesRemoveMutation,
CartLinesRemoveMutationVariables,
} from '../schema'
export const handler = { export const handler = {
fetchOptions: { fetchOptions: {
query: checkoutLineItemRemoveMutation, query: cartLineItemRemoveMutation,
}, },
async fetcher({ async fetcher({
input: { itemId }, input: { itemId },
options, options,
fetch, fetch,
}: HookFetcherContext<RemoveItemHook>) { }: HookFetcherContext<RemoveItemHook>) {
const data = await fetch<Mutation, MutationCheckoutLineItemsRemoveArgs>({ const data = await fetch<
CartLinesRemoveMutation,
CartLinesRemoveMutationVariables
>({
...options, ...options,
variables: { checkoutId: getCheckoutId(), lineItemIds: [itemId] }, variables: { cartId: getCartId(), lineItemIds: [itemId] },
}) })
return checkoutToCart(data.checkoutLineItemsRemove)
},
useHook: ({ fetch }: MutationHookContext<RemoveItemHook>) => <
T extends LineItem | undefined = undefined
>(
ctx: { item?: T } = {}
) => {
const { item } = ctx
const { mutate } = useCart()
const removeItem: RemoveItemFn<LineItem> = async (input) => {
const itemId = input?.id ?? item?.id
if (!itemId) { throwUserErrors(data.cartLinesRemove?.userErrors)
throw new ValidationError({
message: 'Invalid input used for this operation', return normalizeCart(data.cartLinesRemove?.cart)
}) },
useHook:
({ fetch }: MutationHookContext<RemoveItemHook>) =>
<T extends LineItem | undefined = undefined>(ctx: { item?: T } = {}) => {
const { item } = ctx
const { mutate } = useCart()
const removeItem: RemoveItemFn<LineItem> = async (input) => {
const itemId = input?.id ?? item?.id
if (!itemId) {
throw new ValidationError({
message: 'Invalid input used for this operation',
})
}
const data = await fetch({ input: { itemId } })
await mutate(data, false)
return data
} }
const data = await fetch({ input: { itemId } }) return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
await mutate(data, false) },
return data
}
return useCallback(removeItem as RemoveItemFn<T>, [fetch, mutate])
},
} }

View File

@ -10,12 +10,14 @@ import useUpdateItem, { UseUpdateItem } from '@commerce/cart/use-update-item'
import useCart from './use-cart' import useCart from './use-cart'
import { handler as removeItemHandler } from './use-remove-item' import { handler as removeItemHandler } from './use-remove-item'
import type { UpdateItemHook, LineItem } from '../types/cart' import type { UpdateItemHook, LineItem } from '../types/cart'
import { getCartId, normalizeCart } from '../utils'
import { import {
getCheckoutId, CartLinesUpdateMutation,
checkoutLineItemUpdateMutation, CartLinesUpdateMutationVariables,
checkoutToCart, Mutation,
} from '../utils' MutationCheckoutLineItemsUpdateArgs,
import { Mutation, MutationCheckoutLineItemsUpdateArgs } from '../schema' } from '../schema'
import cartLineItemUpdateMutation from '../utils/mutations/cart-line-item-update'
export type UpdateItemActionInput<T = any> = T extends LineItem export type UpdateItemActionInput<T = any> = T extends LineItem
? Partial<UpdateItemHook['actionInput']> ? Partial<UpdateItemHook['actionInput']>
@ -25,7 +27,7 @@ export default useUpdateItem as UseUpdateItem<typeof handler>
export const handler = { export const handler = {
fetchOptions: { fetchOptions: {
query: checkoutLineItemUpdateMutation, query: cartLineItemUpdateMutation,
}, },
async fetcher({ async fetcher({
input: { itemId, item }, input: { itemId, item },
@ -46,13 +48,13 @@ export const handler = {
message: 'The item quantity has to be a valid integer', message: 'The item quantity has to be a valid integer',
}) })
} }
const { checkoutLineItemsUpdate } = await fetch< const { cartLinesUpdate } = await fetch<
Mutation, CartLinesUpdateMutation,
MutationCheckoutLineItemsUpdateArgs CartLinesUpdateMutationVariables
>({ >({
...options, ...options,
variables: { variables: {
checkoutId: getCheckoutId(), checkoutId: getCartId(),
lineItems: [ lineItems: [
{ {
id: itemId, id: itemId,
@ -62,44 +64,44 @@ export const handler = {
}, },
}) })
return checkoutToCart(checkoutLineItemsUpdate) return normalizeCart(cartLinesUpdate?.cart)
}, },
useHook: ({ fetch }: MutationHookContext<UpdateItemHook>) => < useHook:
T extends LineItem | undefined = undefined ({ fetch }: MutationHookContext<UpdateItemHook>) =>
>( <T extends LineItem | undefined = undefined>(
ctx: { ctx: {
item?: T item?: T
wait?: number wait?: number
} = {} } = {}
) => { ) => {
const { item } = ctx const { item } = ctx
const { mutate } = useCart() as any const { mutate } = useCart() as any
return useCallback( return useCallback(
debounce(async (input: UpdateItemActionInput<T>) => { debounce(async (input: UpdateItemActionInput<T>) => {
const itemId = input.id ?? item?.id const itemId = input.id ?? item?.id
const productId = input.productId ?? item?.productId const productId = input.productId ?? item?.productId
const variantId = input.productId ?? item?.variantId const variantId = input.productId ?? item?.variantId
if (!itemId || !productId || !variantId) { if (!itemId || !productId || !variantId) {
throw new ValidationError({ throw new ValidationError({
message: 'Invalid input used for this operation', message: 'Invalid input used for this operation',
}) })
} }
const data = await fetch({ const data = await fetch({
input: { input: {
item: { item: {
productId, productId,
variantId, variantId,
quantity: input.quantity, quantity: input.quantity,
},
itemId,
}, },
itemId, })
}, await mutate(data, false)
}) return data
await mutate(data, false) }, ctx.wait ?? 500),
return data [fetch, mutate]
}, ctx.wait ?? 500), )
[fetch, mutate] },
)
},
} }

View File

@ -1,6 +1,6 @@
{ {
"schema": { "schema": {
"https://${NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/api/2021-07/graphql.json": { "https://${NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN}/api/unstable/graphql.json": {
"headers": { "headers": {
"X-Shopify-Storefront-Access-Token": "${NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN}" "X-Shopify-Storefront-Access-Token": "${NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN}"
} }

View File

@ -8,6 +8,8 @@ export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN
export const SHOPIFY_COOKIE_EXPIRE = 30 export const SHOPIFY_COOKIE_EXPIRE = 30
export const API_URL = `https://${STORE_DOMAIN}/api/2021-01/graphql.json` export const API_URL = `https://${STORE_DOMAIN}/api/unstable/graphql.json`
export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN export const API_TOKEN = process.env.NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN
export const SHOPIFY_CART_ID_COOKIE = 'shopify_cartId'

View File

@ -9,14 +9,14 @@ import {
import { shopifyProvider } from './provider' import { shopifyProvider } from './provider'
import type { ShopifyProvider } from './provider' import type { ShopifyProvider } from './provider'
import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' import { SHOPIFY_CART_ID_COOKIE } from './const'
export { shopifyProvider } export { shopifyProvider }
export type { ShopifyProvider } export type { ShopifyProvider }
export const shopifyConfig: CommerceConfig = { export const shopifyConfig: CommerceConfig = {
locale: 'en-us', locale: 'en-us',
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, cartCookie: SHOPIFY_CART_ID_COOKIE,
} }
export type ShopifyConfig = Partial<CommerceConfig> export type ShopifyConfig = Partial<CommerceConfig>

View File

@ -1,4 +1,4 @@
import { SHOPIFY_CHECKOUT_ID_COOKIE } from './const' import { SHOPIFY_CART_ID_COOKIE } from './const'
import { handler as useCart } from './cart/use-cart' import { handler as useCart } from './cart/use-cart'
import { handler as useAddItem } from './cart/use-add-item' import { handler as useAddItem } from './cart/use-add-item'
@ -16,7 +16,7 @@ import fetcher from './fetcher'
export const shopifyProvider = { export const shopifyProvider = {
locale: 'en-us', locale: 'en-us',
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, cartCookie: SHOPIFY_CART_ID_COOKIE,
fetcher, fetcher,
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem }, cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
customer: { useCustomer }, customer: { useCustomer },

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
import Cookies from 'js-cookie'
import { SHOPIFY_CART_ID_COOKIE, SHOPIFY_COOKIE_EXPIRE } from '../const'
import cartCreateMutation from './mutations/cart-create'
import {
CartCreateMutation,
CartCreateMutationVariables,
CartDetailsFragment,
} from '../schema'
import { FetcherOptions } from '@commerce/utils/types'
import { FetcherError } from '@commerce/utils/errors'
export const cartCreate = async (
fetch: <T = any, B = Body>(options: FetcherOptions<B>) => Promise<T>
): Promise<{ node: CartDetailsFragment }> => {
const { cartCreate } = await fetch<
CartCreateMutation,
CartCreateMutationVariables
>({
query: cartCreateMutation,
})
const cart = cartCreate?.cart
if (!cart) {
throw new FetcherError({
status: 500,
errors: cartCreate?.userErrors?.map((e) => ({
message: e.message,
})) ?? [{ message: 'Could not create cart' }],
})
}
if (cart?.id) {
const options = {
expires: SHOPIFY_COOKIE_EXPIRE,
}
Cookies.set(SHOPIFY_CART_ID_COOKIE, cart.id, options)
}
return { node: cart }
}
export default cartCreate

View File

@ -1,42 +0,0 @@
import type { Cart } from '../types/cart'
import { CommerceError } from '@commerce/utils/errors'
import {
CheckoutLineItemsAddPayload,
CheckoutLineItemsRemovePayload,
CheckoutLineItemsUpdatePayload,
CheckoutCreatePayload,
CheckoutUserError,
Checkout,
Maybe,
} from '../schema'
import { normalizeCart } from './normalize'
import throwUserErrors from './throw-user-errors'
export type CheckoutQuery = {
checkout: Checkout
checkoutUserErrors?: Array<CheckoutUserError>
}
export type CheckoutPayload =
| CheckoutLineItemsAddPayload
| CheckoutLineItemsUpdatePayload
| CheckoutLineItemsRemovePayload
| CheckoutCreatePayload
| CheckoutQuery
const checkoutToCart = (checkoutPayload?: Maybe<CheckoutPayload>): Cart => {
const checkout = checkoutPayload?.checkout
throwUserErrors(checkoutPayload?.checkoutUserErrors)
if (!checkout) {
throw new CommerceError({
message: 'Missing checkout object from response',
})
}
return normalizeCart(checkout)
}
export default checkoutToCart

View File

@ -0,0 +1,8 @@
import Cookies from 'js-cookie'
import { SHOPIFY_CART_ID_COOKIE } from '../const'
const getCartId = (id?: string) => {
return id ?? Cookies.get(SHOPIFY_CART_ID_COOKIE)
}
export default getCartId

View File

@ -1,8 +0,0 @@
import Cookies from 'js-cookie'
import { SHOPIFY_CHECKOUT_ID_COOKIE } from '../const'
const getCheckoutId = (id?: string) => {
return id ?? Cookies.get(SHOPIFY_CHECKOUT_ID_COOKIE)
}
export default getCheckoutId

View File

@ -3,12 +3,12 @@ export { default as getSearchVariables } from './get-search-variables'
export { default as getSortVariables } from './get-sort-variables' export { default as getSortVariables } from './get-sort-variables'
export { default as getBrands } from './get-brands' export { default as getBrands } from './get-brands'
export { default as getCategories } from './get-categories' export { default as getCategories } from './get-categories'
export { default as getCheckoutId } from './get-checkout-id' export { default as getCartId } from './get-cart-id'
export { default as checkoutCreate } from './checkout-create' export { default as cartCreate } from './cart-create'
export { default as checkoutToCart } from './checkout-to-cart'
export { default as handleLogin, handleAutomaticLogin } from './handle-login' export { default as handleLogin, handleAutomaticLogin } from './handle-login'
export { default as handleAccountActivation } from './handle-account-activation' export { default as handleAccountActivation } from './handle-account-activation'
export { default as throwUserErrors } from './throw-user-errors' export { default as throwUserErrors } from './throw-user-errors'
export * from './queries' export * from './queries'
export * from './mutations' export * from './mutations'
export * from './normalize' export * from './normalize'

View File

@ -0,0 +1,18 @@
import { cartDetailsFragment } from '../queries/get-cart-query'
const cartCreateMutation = /* GraphQL */ `
mutation cartCreate {
cartCreate {
cart {
...cartDetails
}
userErrors {
code
field
message
}
}
}
${cartDetailsFragment}
`
export default cartCreateMutation

View File

@ -0,0 +1,18 @@
import { cartDetailsFragment } from '../queries/get-cart-query'
const cartLinesAddMutation = /* GraphQL */ `
mutation cartLinesAdd($lines: [CartLineInput!]!, $cartId: ID!) {
cartLinesAdd(lines: $lines, cartId: $cartId) {
cart {
...cartDetails
}
userErrors {
code
field
message
}
}
}
${cartDetailsFragment}
`
export default cartLinesAddMutation

View File

@ -0,0 +1,18 @@
import { cartDetailsFragment } from '../queries/get-cart-query'
const cartLinesAddMutation = /* GraphQL */ `
mutation cartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
cart {
...cartDetails
}
userErrors {
code
field
message
}
}
}
${cartDetailsFragment}
`
export default cartLinesAddMutation

View File

@ -0,0 +1,18 @@
import { cartDetailsFragment } from '../queries/get-cart-query'
const cartLinesAddMutation = /* GraphQL */ `
mutation cartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
cartLinesUpdate(cartId: $cartId, lines: $lines) {
cart {
...cartDetails
}
userErrors {
code
field
message
}
}
}
${cartDetailsFragment}
`
export default cartLinesAddMutation

View File

@ -1,19 +0,0 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutCreateMutation = /* GraphQL */ `
mutation checkoutCreate($input: CheckoutCreateInput = {}) {
checkoutCreate(input: $input) {
checkoutUserErrors {
code
field
message
}
checkout {
...checkoutDetails
}
}
}
${checkoutDetailsFragment}
`
export default checkoutCreateMutation

View File

@ -1,22 +0,0 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutLineItemAddMutation = /* GraphQL */ `
mutation checkoutLineItemAdd(
$checkoutId: ID!
$lineItems: [CheckoutLineItemInput!]!
) {
checkoutLineItemsAdd(checkoutId: $checkoutId, lineItems: $lineItems) {
checkoutUserErrors {
code
field
message
}
checkout {
...checkoutDetails
}
}
}
${checkoutDetailsFragment}
`
export default checkoutLineItemAddMutation

View File

@ -1,21 +0,0 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutLineItemRemoveMutation = /* GraphQL */ `
mutation checkoutLineItemRemove($checkoutId: ID!, $lineItemIds: [ID!]!) {
checkoutLineItemsRemove(
checkoutId: $checkoutId
lineItemIds: $lineItemIds
) {
checkoutUserErrors {
code
field
message
}
checkout {
...checkoutDetails
}
}
}
${checkoutDetailsFragment}
`
export default checkoutLineItemRemoveMutation

View File

@ -1,22 +0,0 @@
import { checkoutDetailsFragment } from '../queries/get-checkout-query'
const checkoutLineItemUpdateMutation = /* GraphQL */ `
mutation checkoutLineItemUpdate(
$checkoutId: ID!
$lineItems: [CheckoutLineItemUpdateInput!]!
) {
checkoutLineItemsUpdate(checkoutId: $checkoutId, lineItems: $lineItems) {
checkoutUserErrors {
code
field
message
}
checkout {
...checkoutDetails
}
}
}
${checkoutDetailsFragment}
`
export default checkoutLineItemUpdateMutation

View File

@ -1,8 +1,8 @@
export { default as customerCreateMutation } from './customer-create' export { default as customerCreateMutation } from './customer-create'
export { default as checkoutCreateMutation } from './checkout-create' export { default as cartCreateMutation } from './cart-create'
export { default as checkoutLineItemAddMutation } from './checkout-line-item-add' export { default as cartLineItemAddMutation } from './cart-line-item-add'
export { default as checkoutLineItemUpdateMutation } from './checkout-line-item-update' export { default as cartLineItemUpdateMutation } from './cart-line-item-update'
export { default as checkoutLineItemRemoveMutation } from './checkout-line-item-remove' export { default as cartLineItemRemoveMutation } from './cart-line-item-remove'
export { default as customerAccessTokenCreateMutation } from './customer-access-token-create' export { default as customerAccessTokenCreateMutation } from './customer-access-token-create'
export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete' export { default as customerAccessTokenDeleteMutation } from './customer-access-token-delete'
export { default as customerActivateMutation } from './customer-activate' export { default as customerActivateMutation } from './customer-activate'

View File

@ -5,18 +5,18 @@ import type { Category } from '../types/site'
import { import {
Product as ShopifyProduct, Product as ShopifyProduct,
Checkout,
CheckoutLineItemEdge,
SelectedOption, SelectedOption,
ImageConnection, ImageConnection,
ProductVariantConnection,
MoneyV2, MoneyV2,
ProductOption, ProductOption,
Page as ShopifyPage, Page as ShopifyPage,
PageEdge, PageEdge,
Collection, Collection,
CartDetailsFragment,
ProductVariantConnection,
} from '../schema' } from '../schema'
import { colorMap } from '@lib/colors' import { colorMap } from '@lib/colors'
import { CommerceError } from '@commerce/utils/errors'
const money = ({ amount, currencyCode }: MoneyV2) => { const money = ({ amount, currencyCode }: MoneyV2) => {
return { return {
@ -128,34 +128,41 @@ export function normalizeProduct({
} }
} }
export function normalizeCart(checkout: Checkout): Cart { export function normalizeCart(
cart: CartDetailsFragment | undefined | null
): Cart {
if (!cart) {
throw new CommerceError({ message: 'Missing cart details' })
}
return { return {
id: checkout.id, id: cart.id,
url: checkout.webUrl, customerId: cart.buyerIdentity?.customer?.id,
customerId: '', email: cart.buyerIdentity?.email ?? '',
email: '', createdAt: cart.createdAt,
createdAt: checkout.createdAt,
currency: { currency: {
code: checkout.totalPriceV2?.currencyCode, code: cart.estimatedCost?.totalAmount?.currencyCode,
}, },
taxesIncluded: checkout.taxesIncluded, taxesIncluded: !!cart.estimatedCost?.totalTaxAmount,
lineItems: checkout.lineItems?.edges.map(normalizeLineItem), lineItems: cart.lines?.edges?.map(normalizeLineItem) ?? [],
lineItemsSubtotalPrice: +checkout.subtotalPriceV2?.amount, lineItemsSubtotalPrice: +cart.estimatedCost?.totalAmount,
subtotalPrice: +checkout.subtotalPriceV2?.amount, subtotalPrice: +cart.estimatedCost?.subtotalAmount,
totalPrice: checkout.totalPriceV2?.amount, totalPrice: +cart.estimatedCost?.totalAmount,
discounts: [], discounts: [],
} }
} }
function normalizeLineItem({ function normalizeLineItem({
node: { id, title, variant, quantity }, node: { id, merchandise: variant, quantity },
}: CheckoutLineItemEdge): LineItem { }: {
node: any
}): LineItem {
return { return {
id, id,
variantId: String(variant?.id), variantId: String(variant?.id),
productId: String(variant?.id), productId: String(variant?.id),
name: `${title}`, name: `${variant?.title}`,
quantity, quantity: quantity ?? 0,
variant: { variant: {
id: String(variant?.id), id: String(variant?.id),
sku: variant?.sku ?? '', sku: variant?.sku ?? '',

View File

@ -0,0 +1,58 @@
export const cartDetailsFragment = /* GraphQL */ `
fragment cartDetails on Cart {
id
checkoutUrl
createdAt
updatedAt
lines(first: 10) {
edges {
node {
id
merchandise {
... on ProductVariant {
id
}
}
}
}
}
attributes {
key
value
}
buyerIdentity {
email
customer {
id
}
}
estimatedCost {
totalAmount {
amount
currencyCode
}
subtotalAmount {
amount
currencyCode
}
totalTaxAmount {
amount
currencyCode
}
totalDutyAmount {
amount
currencyCode
}
}
}
`
const getCartQuery = /* GraphQL */ `
query getCart($cartId: ID!) {
node(id: $cartId) {
...cartDetails
}
}
${cartDetailsFragment}
`
export default getCartQuery

View File

@ -1,70 +0,0 @@
export const checkoutDetailsFragment = /* GraphQL */ `
fragment checkoutDetails on Checkout {
id
webUrl
subtotalPriceV2 {
amount
currencyCode
}
totalTaxV2 {
amount
currencyCode
}
totalPriceV2 {
amount
currencyCode
}
completedAt
createdAt
taxesIncluded
lineItems(first: 250) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
id
title
variant {
id
sku
title
selectedOptions {
name
value
}
image {
originalSrc
altText
width
height
}
priceV2 {
amount
currencyCode
}
compareAtPriceV2 {
amount
currencyCode
}
product {
handle
}
}
quantity
}
}
}
}
`
const getCheckoutQuery = /* GraphQL */ `
query getCheckout($checkoutId: ID!) {
node(id: $checkoutId) {
...checkoutDetails
}
}
${checkoutDetailsFragment}
`
export default getCheckoutQuery

View File

@ -4,7 +4,7 @@ export { default as getAllProductsQuery } from './get-all-products-query'
export { default as getAllProductsPathtsQuery } from './get-all-products-paths-query' export { default as getAllProductsPathtsQuery } from './get-all-products-paths-query'
export { default as getAllProductVendors } from './get-all-product-vendors-query' export { default as getAllProductVendors } from './get-all-product-vendors-query'
export { default as getCollectionProductsQuery } from './get-collection-products-query' export { default as getCollectionProductsQuery } from './get-collection-products-query'
export { default as getCheckoutQuery } from './get-checkout-query' export { default as getCartQuery } from './get-cart-query'
export { default as getAllPagesQuery } from './get-all-pages-query' export { default as getAllPagesQuery } from './get-all-pages-query'
export { default as getPageQuery } from './get-page-query' export { default as getPageQuery } from './get-page-query'
export { default as getCustomerQuery } from './get-customer-query' export { default as getCustomerQuery } from './get-customer-query'

View File

@ -5,13 +5,18 @@ import {
CheckoutUserError, CheckoutUserError,
CustomerErrorCode, CustomerErrorCode,
CustomerUserError, CustomerUserError,
CartUserError,
CartErrorCode,
} from '../schema' } from '../schema'
export type UserErrors = Array<CheckoutUserError | CustomerUserError> export type UserErrors = Array<
CheckoutUserError | CustomerUserError | CartUserError
>
export type UserErrorCode = export type UserErrorCode =
| CustomerErrorCode | CustomerErrorCode
| CheckoutErrorCode | CheckoutErrorCode
| CartErrorCode
| null | null
| undefined | undefined

View File

@ -23,8 +23,8 @@
"@components/*": ["components/*"], "@components/*": ["components/*"],
"@commerce": ["framework/commerce"], "@commerce": ["framework/commerce"],
"@commerce/*": ["framework/commerce/*"], "@commerce/*": ["framework/commerce/*"],
"@framework": ["framework/local"], "@framework": ["framework/shopify"],
"@framework/*": ["framework/local/*"] "@framework/*": ["framework/shopify/*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],