mirror of
https://github.com/vercel/commerce.git
synced 2025-07-04 04:01:21 +00:00
Implement Cart API for cart & wishlish
This commit is contained in:
parent
1d79007171
commit
bc15597be9
@ -37,6 +37,7 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
|||||||
setLoading(false)
|
setLoading(false)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
console.error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,8 +31,8 @@ const WishlistButton: FC<Props> = ({
|
|||||||
const itemInWishlist = data?.items?.find(
|
const itemInWishlist = data?.items?.find(
|
||||||
// @ts-ignore Wishlist is not always enabled
|
// @ts-ignore Wishlist is not always enabled
|
||||||
(item) =>
|
(item) =>
|
||||||
item.product_id === Number(productId) &&
|
String(item.product_id) === String(productId) &&
|
||||||
(item.variant_id as any) === Number(variant.id)
|
String(item.variant_id) === String(variant?.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleWishlistChange = async (e: any) => {
|
const handleWishlistChange = async (e: any) => {
|
||||||
@ -41,7 +41,7 @@ const WishlistButton: FC<Props> = ({
|
|||||||
if (loading) return
|
if (loading) return
|
||||||
|
|
||||||
// A login is required before adding an item to the wishlist
|
// A login is required before adding an item to the wishlist
|
||||||
if (!customer) {
|
if (!customer && process.env.COMMERCE_PROVIDER !== 'shopify') {
|
||||||
setModalView('LOGIN_VIEW')
|
setModalView('LOGIN_VIEW')
|
||||||
return openModal()
|
return openModal()
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ const WishlistCard: FC<Props> = ({ product }) => {
|
|||||||
setLoading(false)
|
setLoading(false)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
console.error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,9 @@ export const handler: MutationHook<AddItemHook> = {
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
useHook: ({ fetch }) => () => {
|
useHook:
|
||||||
|
({ fetch }) =>
|
||||||
|
() => {
|
||||||
const { mutate } = useCart()
|
const { mutate } = useCart()
|
||||||
|
|
||||||
return useCallback(
|
return useCallback(
|
||||||
|
@ -1,35 +1,15 @@
|
|||||||
import {
|
import { SHOPIFY_CART_URL_COOKIE } from '../../../const'
|
||||||
SHOPIFY_CHECKOUT_ID_COOKIE,
|
|
||||||
SHOPIFY_CHECKOUT_URL_COOKIE,
|
|
||||||
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
|
||||||
} from '../../../const'
|
|
||||||
import associateCustomerWithCheckoutMutation from '../../../utils/mutations/associate-customer-with-checkout'
|
|
||||||
import type { CheckoutEndpoint } from '.'
|
import type { CheckoutEndpoint } from '.'
|
||||||
|
|
||||||
const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({
|
const checkout: CheckoutEndpoint['handlers']['checkout'] = async ({
|
||||||
req,
|
req,
|
||||||
res,
|
res,
|
||||||
config,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { cookies } = req
|
const { cookies } = req
|
||||||
const checkoutUrl = cookies[SHOPIFY_CHECKOUT_URL_COOKIE]
|
const cartUrl = cookies[SHOPIFY_CART_URL_COOKIE]
|
||||||
const customerCookie = cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE]
|
|
||||||
|
|
||||||
if (customerCookie) {
|
if (cartUrl) {
|
||||||
try {
|
res.redirect(cartUrl)
|
||||||
await config.fetch(associateCustomerWithCheckoutMutation, {
|
|
||||||
variables: {
|
|
||||||
cartId: cookies[SHOPIFY_CHECKOUT_ID_COOKIE],
|
|
||||||
customerAccessToken: cookies[SHOPIFY_CUSTOMER_TOKEN_COOKIE],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkoutUrl) {
|
|
||||||
res.redirect(checkoutUrl)
|
|
||||||
} else {
|
} else {
|
||||||
res.redirect('/cart')
|
res.redirect('/cart')
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
API_URL,
|
API_URL,
|
||||||
API_TOKEN,
|
API_TOKEN,
|
||||||
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
||||||
SHOPIFY_CHECKOUT_ID_COOKIE,
|
SHOPIFY_CART_ID_COOKIE,
|
||||||
} from '../const'
|
} from '../const'
|
||||||
|
|
||||||
import fetchGraphqlApi from './utils/fetch-graphql-api'
|
import fetchGraphqlApi from './utils/fetch-graphql-api'
|
||||||
@ -34,7 +34,7 @@ const config: ShopifyConfig = {
|
|||||||
commerceUrl: API_URL,
|
commerceUrl: API_URL,
|
||||||
apiToken: API_TOKEN,
|
apiToken: API_TOKEN,
|
||||||
customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
customerCookie: SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
||||||
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,
|
cartCookie: SHOPIFY_CART_ID_COOKIE,
|
||||||
cartCookieMaxAge: ONE_DAY * 30,
|
cartCookieMaxAge: ONE_DAY * 30,
|
||||||
fetch: fetchGraphqlApi,
|
fetch: fetchGraphqlApi,
|
||||||
}
|
}
|
||||||
|
@ -29,13 +29,19 @@ export const handler: MutationHook<AddItemHook> = {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lines = [
|
||||||
|
{
|
||||||
|
merchandiseId: item.variantId,
|
||||||
|
quantity: item.quantity ?? 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
let cartId = getCartId()
|
let cartId = getCartId()
|
||||||
|
|
||||||
if (!cartId) {
|
if (!cartId) {
|
||||||
const { id } = await cartCreate(fetch)
|
const cart = await cartCreate(fetch, lines)
|
||||||
cartId = id
|
return normalizeCart(cart)
|
||||||
}
|
} else {
|
||||||
|
|
||||||
const { cartLinesAdd } = await fetch<
|
const { cartLinesAdd } = await fetch<
|
||||||
CartLinesAddMutation,
|
CartLinesAddMutation,
|
||||||
CartLinesAddMutationVariables
|
CartLinesAddMutationVariables
|
||||||
@ -43,18 +49,14 @@ export const handler: MutationHook<AddItemHook> = {
|
|||||||
...options,
|
...options,
|
||||||
variables: {
|
variables: {
|
||||||
cartId,
|
cartId,
|
||||||
lineItems: [
|
lines,
|
||||||
{
|
|
||||||
variantId: item.variantId,
|
|
||||||
quantity: item.quantity ?? 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
throwUserErrors(cartLinesAdd?.userErrors)
|
throwUserErrors(cartLinesAdd?.userErrors)
|
||||||
|
|
||||||
return normalizeCart(cartLinesAdd?.cart)
|
return normalizeCart(cartLinesAdd?.cart)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
useHook:
|
useHook:
|
||||||
({ fetch }) =>
|
({ fetch }) =>
|
||||||
|
@ -4,6 +4,13 @@ import useCommerceCart, { UseCart } from '@commerce/cart/use-cart'
|
|||||||
import { SWRHook } from '@commerce/utils/types'
|
import { SWRHook } from '@commerce/utils/types'
|
||||||
import getCartQuery from '../utils/queries/get-cart-query'
|
import getCartQuery from '../utils/queries/get-cart-query'
|
||||||
import { GetCartHook } from '../types/cart'
|
import { GetCartHook } from '../types/cart'
|
||||||
|
import {
|
||||||
|
GetCartQuery,
|
||||||
|
GetCartQueryVariables,
|
||||||
|
QueryRoot,
|
||||||
|
} from '@framework/schema'
|
||||||
|
import { normalizeCart } from '@framework/utils'
|
||||||
|
import setCheckoutUrlCookie from '@framework/utils/set-checkout-url-cookie'
|
||||||
|
|
||||||
export default useCommerceCart as UseCart<typeof handler>
|
export default useCommerceCart as UseCart<typeof handler>
|
||||||
|
|
||||||
@ -12,7 +19,15 @@ export const handler: SWRHook<GetCartHook> = {
|
|||||||
query: getCartQuery,
|
query: getCartQuery,
|
||||||
},
|
},
|
||||||
async fetcher({ input: { cartId }, options, fetch }) {
|
async fetcher({ input: { cartId }, options, fetch }) {
|
||||||
return cartId ? await fetch(options) : null
|
if (cartId) {
|
||||||
|
const { cart } = await fetch<QueryRoot, GetCartQueryVariables>({
|
||||||
|
...options,
|
||||||
|
variables: { cartId },
|
||||||
|
})
|
||||||
|
setCheckoutUrlCookie(cart?.checkoutUrl)
|
||||||
|
return normalizeCart(cart)
|
||||||
|
}
|
||||||
|
return null
|
||||||
},
|
},
|
||||||
useHook:
|
useHook:
|
||||||
({ useData }) =>
|
({ useData }) =>
|
||||||
@ -25,7 +40,7 @@ export const handler: SWRHook<GetCartHook> = {
|
|||||||
Object.create(response, {
|
Object.create(response, {
|
||||||
isEmpty: {
|
isEmpty: {
|
||||||
get() {
|
get() {
|
||||||
return (response.data?.lineItems.length ?? 0) <= 0
|
return (response.data?.lineItems?.length ?? 0) <= 0
|
||||||
},
|
},
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
},
|
},
|
||||||
|
@ -40,7 +40,7 @@ export const handler = {
|
|||||||
CartLinesRemoveMutationVariables
|
CartLinesRemoveMutationVariables
|
||||||
>({
|
>({
|
||||||
...options,
|
...options,
|
||||||
variables: { cartId: getCartId(), lineItemIds: [itemId] },
|
variables: { cartId: getCartId(), lineIds: [itemId] },
|
||||||
})
|
})
|
||||||
|
|
||||||
throwUserErrors(data.cartLinesRemove?.userErrors)
|
throwUserErrors(data.cartLinesRemove?.userErrors)
|
||||||
|
@ -54,7 +54,7 @@ export const handler = {
|
|||||||
>({
|
>({
|
||||||
...options,
|
...options,
|
||||||
variables: {
|
variables: {
|
||||||
cartItems: getCartId(),
|
cartId: getCartId(),
|
||||||
lines: [
|
lines: [
|
||||||
{
|
{
|
||||||
id: itemId,
|
id: itemId,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"provider": "shopify",
|
"provider": "shopify",
|
||||||
"features": {
|
"features": {
|
||||||
"wishlist": false
|
"wishlist": true,
|
||||||
|
"customerAuth": true,
|
||||||
|
"search": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
export const SHOPIFY_CHECKOUT_ID_COOKIE = 'shopify_checkoutId'
|
|
||||||
|
|
||||||
export const SHOPIFY_CHECKOUT_URL_COOKIE = 'shopify_checkoutUrl'
|
|
||||||
|
|
||||||
export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken'
|
export const SHOPIFY_CUSTOMER_TOKEN_COOKIE = 'shopify_customerToken'
|
||||||
|
|
||||||
export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN
|
export const STORE_DOMAIN = process.env.NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN
|
||||||
@ -13,3 +9,7 @@ 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'
|
export const SHOPIFY_CART_ID_COOKIE = 'shopify_cartId'
|
||||||
|
|
||||||
|
export const SHOPIFY_CART_URL_COOKIE = 'shopify_cartUrl'
|
||||||
|
|
||||||
|
export const SHOPIFY_WHISLIST_ID_COOKIE = 'shopify_wishlistId'
|
||||||
|
@ -5,6 +5,10 @@ import { handler as useAddItem } from './cart/use-add-item'
|
|||||||
import { handler as useUpdateItem } from './cart/use-update-item'
|
import { handler as useUpdateItem } from './cart/use-update-item'
|
||||||
import { handler as useRemoveItem } from './cart/use-remove-item'
|
import { handler as useRemoveItem } from './cart/use-remove-item'
|
||||||
|
|
||||||
|
import { handler as useWishlist } from './wishlist/use-wishlist'
|
||||||
|
import { handler as useWishlistAddItem } from './wishlist/use-add-item'
|
||||||
|
import { handler as useWishlistRemoveItem } from './wishlist/use-remove-item'
|
||||||
|
|
||||||
import { handler as useCustomer } from './customer/use-customer'
|
import { handler as useCustomer } from './customer/use-customer'
|
||||||
import { handler as useSearch } from './product/use-search'
|
import { handler as useSearch } from './product/use-search'
|
||||||
|
|
||||||
@ -19,6 +23,11 @@ export const shopifyProvider = {
|
|||||||
cartCookie: SHOPIFY_CART_ID_COOKIE,
|
cartCookie: SHOPIFY_CART_ID_COOKIE,
|
||||||
fetcher,
|
fetcher,
|
||||||
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
|
cart: { useCart, useAddItem, useUpdateItem, useRemoveItem },
|
||||||
|
wishlist: {
|
||||||
|
useWishlist,
|
||||||
|
useAddItem: useWishlistAddItem,
|
||||||
|
useRemoveItem: useWishlistRemoveItem,
|
||||||
|
},
|
||||||
customer: { useCustomer },
|
customer: { useCustomer },
|
||||||
products: { useSearch },
|
products: { useSearch },
|
||||||
auth: { useLogin, useLogout, useSignup },
|
auth: { useLogin, useLogout, useSignup },
|
||||||
|
171
framework/shopify/schema.d.ts
vendored
171
framework/shopify/schema.d.ts
vendored
@ -974,7 +974,7 @@ export type CheckoutCreatePayload = {
|
|||||||
checkout?: Maybe<Checkout>
|
checkout?: Maybe<Checkout>
|
||||||
/** The list of errors that occurred from executing the mutation. */
|
/** The list of errors that occurred from executing the mutation. */
|
||||||
checkoutUserErrors: Array<CheckoutUserError>
|
checkoutUserErrors: Array<CheckoutUserError>
|
||||||
/** The checkout queue token. */
|
/** The checkout queue token. Available only to selected stores. */
|
||||||
queueToken?: Maybe<Scalars['String']>
|
queueToken?: Maybe<Scalars['String']>
|
||||||
/**
|
/**
|
||||||
* The list of errors that occurred from executing the mutation.
|
* The list of errors that occurred from executing the mutation.
|
||||||
@ -6036,7 +6036,9 @@ export type AssociateCustomerWithCheckoutMutation = {
|
|||||||
>
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CartCreateMutationVariables = Exact<{ [key: string]: never }>
|
export type CartCreateMutationVariables = Exact<{
|
||||||
|
input?: Maybe<CartInput>
|
||||||
|
}>
|
||||||
|
|
||||||
export type CartCreateMutation = { __typename?: 'Mutation' } & {
|
export type CartCreateMutation = { __typename?: 'Mutation' } & {
|
||||||
cartCreate?: Maybe<
|
cartCreate?: Maybe<
|
||||||
@ -6217,6 +6219,62 @@ export type CustomerCreateMutation = { __typename?: 'Mutation' } & {
|
|||||||
>
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type WishlistCreateMutationVariables = Exact<{
|
||||||
|
input?: Maybe<CartInput>
|
||||||
|
}>
|
||||||
|
|
||||||
|
export type WishlistCreateMutation = { __typename?: 'Mutation' } & {
|
||||||
|
cartCreate?: Maybe<
|
||||||
|
{ __typename?: 'CartCreatePayload' } & {
|
||||||
|
cart?: Maybe<{ __typename?: 'Cart' } & WishlistDetailsFragment>
|
||||||
|
userErrors: Array<
|
||||||
|
{ __typename?: 'CartUserError' } & Pick<
|
||||||
|
CartUserError,
|
||||||
|
'code' | 'field' | 'message'
|
||||||
|
>
|
||||||
|
>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WishlistLinesAddMutationVariables = Exact<{
|
||||||
|
lines: Array<CartLineInput> | CartLineInput
|
||||||
|
cartId: Scalars['ID']
|
||||||
|
}>
|
||||||
|
|
||||||
|
export type WishlistLinesAddMutation = { __typename?: 'Mutation' } & {
|
||||||
|
cartLinesAdd?: Maybe<
|
||||||
|
{ __typename?: 'CartLinesAddPayload' } & {
|
||||||
|
cart?: Maybe<{ __typename?: 'Cart' } & CartDetailsFragment>
|
||||||
|
userErrors: Array<
|
||||||
|
{ __typename?: 'CartUserError' } & Pick<
|
||||||
|
CartUserError,
|
||||||
|
'code' | 'field' | 'message'
|
||||||
|
>
|
||||||
|
>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WishlistLinesRemoveMutationVariables = Exact<{
|
||||||
|
cartId: Scalars['ID']
|
||||||
|
lineIds: Array<Scalars['ID']> | Scalars['ID']
|
||||||
|
}>
|
||||||
|
|
||||||
|
export type WishlistLinesRemoveMutation = { __typename?: 'Mutation' } & {
|
||||||
|
cartLinesRemove?: Maybe<
|
||||||
|
{ __typename?: 'CartLinesRemovePayload' } & {
|
||||||
|
cart?: Maybe<{ __typename?: 'Cart' } & CartDetailsFragment>
|
||||||
|
userErrors: Array<
|
||||||
|
{ __typename?: 'CartUserError' } & Pick<
|
||||||
|
CartUserError,
|
||||||
|
'code' | 'field' | 'message'
|
||||||
|
>
|
||||||
|
>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
}
|
||||||
|
|
||||||
export type GetSiteCollectionsQueryVariables = Exact<{
|
export type GetSiteCollectionsQueryVariables = Exact<{
|
||||||
first: Scalars['Int']
|
first: Scalars['Int']
|
||||||
}>
|
}>
|
||||||
@ -6335,16 +6393,46 @@ export type GetAllProductsQuery = { __typename?: 'QueryRoot' } & {
|
|||||||
|
|
||||||
export type CartDetailsFragment = { __typename?: 'Cart' } & Pick<
|
export type CartDetailsFragment = { __typename?: 'Cart' } & Pick<
|
||||||
Cart,
|
Cart,
|
||||||
'id' | 'createdAt' | 'updatedAt'
|
'id' | 'checkoutUrl' | 'createdAt' | 'updatedAt'
|
||||||
> & {
|
> & {
|
||||||
lines: { __typename?: 'CartLineConnection' } & {
|
lines: { __typename?: 'CartLineConnection' } & {
|
||||||
edges: Array<
|
edges: Array<
|
||||||
{ __typename?: 'CartLineEdge' } & {
|
{ __typename?: 'CartLineEdge' } & {
|
||||||
node: { __typename?: 'CartLine' } & Pick<CartLine, 'id'> & {
|
node: { __typename?: 'CartLine' } & Pick<
|
||||||
|
CartLine,
|
||||||
|
'id' | 'quantity'
|
||||||
|
> & {
|
||||||
merchandise: { __typename?: 'ProductVariant' } & Pick<
|
merchandise: { __typename?: 'ProductVariant' } & Pick<
|
||||||
ProductVariant,
|
ProductVariant,
|
||||||
'id'
|
'id' | 'sku' | 'title'
|
||||||
|
> & {
|
||||||
|
selectedOptions: Array<
|
||||||
|
{ __typename?: 'SelectedOption' } & Pick<
|
||||||
|
SelectedOption,
|
||||||
|
'name' | 'value'
|
||||||
>
|
>
|
||||||
|
>
|
||||||
|
image?: Maybe<
|
||||||
|
{ __typename?: 'Image' } & Pick<
|
||||||
|
Image,
|
||||||
|
'originalSrc' | 'altText' | 'width' | 'height'
|
||||||
|
>
|
||||||
|
>
|
||||||
|
priceV2: { __typename?: 'MoneyV2' } & Pick<
|
||||||
|
MoneyV2,
|
||||||
|
'amount' | 'currencyCode'
|
||||||
|
>
|
||||||
|
compareAtPriceV2?: Maybe<
|
||||||
|
{ __typename?: 'MoneyV2' } & Pick<
|
||||||
|
MoneyV2,
|
||||||
|
'amount' | 'currencyCode'
|
||||||
|
>
|
||||||
|
>
|
||||||
|
product: { __typename?: 'Product' } & Pick<
|
||||||
|
Product,
|
||||||
|
'title' | 'handle'
|
||||||
|
>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -6379,31 +6467,7 @@ export type GetCartQueryVariables = Exact<{
|
|||||||
}>
|
}>
|
||||||
|
|
||||||
export type GetCartQuery = { __typename?: 'QueryRoot' } & {
|
export type GetCartQuery = { __typename?: 'QueryRoot' } & {
|
||||||
node?: Maybe<
|
cart?: Maybe<{ __typename?: 'Cart' } & CartDetailsFragment>
|
||||||
| { __typename?: 'AppliedGiftCard' }
|
|
||||||
| { __typename?: 'Article' }
|
|
||||||
| { __typename?: 'Blog' }
|
|
||||||
| ({ __typename?: 'Cart' } & CartDetailsFragment)
|
|
||||||
| { __typename?: 'CartLine' }
|
|
||||||
| { __typename?: 'Checkout' }
|
|
||||||
| { __typename?: 'CheckoutLineItem' }
|
|
||||||
| { __typename?: 'Collection' }
|
|
||||||
| { __typename?: 'Comment' }
|
|
||||||
| { __typename?: 'ExternalVideo' }
|
|
||||||
| { __typename?: 'Location' }
|
|
||||||
| { __typename?: 'MailingAddress' }
|
|
||||||
| { __typename?: 'MediaImage' }
|
|
||||||
| { __typename?: 'Metafield' }
|
|
||||||
| { __typename?: 'Model3d' }
|
|
||||||
| { __typename?: 'Order' }
|
|
||||||
| { __typename?: 'Page' }
|
|
||||||
| { __typename?: 'Payment' }
|
|
||||||
| { __typename?: 'Product' }
|
|
||||||
| { __typename?: 'ProductOption' }
|
|
||||||
| { __typename?: 'ProductVariant' }
|
|
||||||
| { __typename?: 'ShopPolicy' }
|
|
||||||
| { __typename?: 'Video' }
|
|
||||||
>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetProductsFromCollectionQueryVariables = Exact<{
|
export type GetProductsFromCollectionQueryVariables = Exact<{
|
||||||
@ -6596,3 +6660,50 @@ export type GetSiteInfoQueryVariables = Exact<{ [key: string]: never }>
|
|||||||
export type GetSiteInfoQuery = { __typename?: 'QueryRoot' } & {
|
export type GetSiteInfoQuery = { __typename?: 'QueryRoot' } & {
|
||||||
shop: { __typename?: 'Shop' } & Pick<Shop, 'name'>
|
shop: { __typename?: 'Shop' } & Pick<Shop, 'name'>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type WishlistDetailsFragment = { __typename?: 'Cart' } & Pick<
|
||||||
|
Cart,
|
||||||
|
'id' | 'createdAt' | 'updatedAt'
|
||||||
|
> & {
|
||||||
|
lines: { __typename?: 'CartLineConnection' } & {
|
||||||
|
edges: Array<
|
||||||
|
{ __typename?: 'CartLineEdge' } & {
|
||||||
|
node: { __typename?: 'CartLine' } & Pick<
|
||||||
|
CartLine,
|
||||||
|
'id' | 'quantity'
|
||||||
|
> & {
|
||||||
|
merchandise: { __typename?: 'ProductVariant' } & Pick<
|
||||||
|
ProductVariant,
|
||||||
|
'id' | 'sku' | 'title'
|
||||||
|
> & {
|
||||||
|
image?: Maybe<
|
||||||
|
{ __typename?: 'Image' } & Pick<
|
||||||
|
Image,
|
||||||
|
'originalSrc' | 'altText' | 'width' | 'height'
|
||||||
|
>
|
||||||
|
>
|
||||||
|
product: { __typename?: 'Product' } & Pick<
|
||||||
|
Product,
|
||||||
|
'title' | 'handle'
|
||||||
|
>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
}
|
||||||
|
attributes: Array<
|
||||||
|
{ __typename?: 'Attribute' } & Pick<Attribute, 'key' | 'value'>
|
||||||
|
>
|
||||||
|
buyerIdentity: { __typename?: 'CartBuyerIdentity' } & Pick<
|
||||||
|
CartBuyerIdentity,
|
||||||
|
'email'
|
||||||
|
> & { customer?: Maybe<{ __typename?: 'Customer' } & Pick<Customer, 'id'>> }
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GetWishlistQueryVariables = Exact<{
|
||||||
|
cartId: Scalars['ID']
|
||||||
|
}>
|
||||||
|
|
||||||
|
export type GetWishlistQuery = { __typename?: 'QueryRoot' } & {
|
||||||
|
cart?: Maybe<{ __typename?: 'Cart' } & WishlistDetailsFragment>
|
||||||
|
}
|
||||||
|
@ -1806,7 +1806,7 @@ type CheckoutCreatePayload {
|
|||||||
checkoutUserErrors: [CheckoutUserError!]!
|
checkoutUserErrors: [CheckoutUserError!]!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The checkout queue token.
|
The checkout queue token. Available only to selected stores.
|
||||||
"""
|
"""
|
||||||
queueToken: String
|
queueToken: String
|
||||||
|
|
||||||
@ -7496,7 +7496,7 @@ type Mutation {
|
|||||||
input: CheckoutCreateInput!
|
input: CheckoutCreateInput!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The checkout queue token.
|
The checkout queue token. Available only to selected stores.
|
||||||
"""
|
"""
|
||||||
queueToken: String
|
queueToken: String
|
||||||
): CheckoutCreatePayload
|
): CheckoutCreatePayload
|
||||||
|
@ -1,42 +1,47 @@
|
|||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
|
|
||||||
import { SHOPIFY_CART_ID_COOKIE, SHOPIFY_COOKIE_EXPIRE } from '../const'
|
import { SHOPIFY_CART_ID_COOKIE, SHOPIFY_COOKIE_EXPIRE } from '../const'
|
||||||
|
|
||||||
import cartCreateMutation from './mutations/cart-create'
|
import cartCreateMutation from './mutations/cart-create'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CartCreateMutation,
|
CartCreateMutation,
|
||||||
CartCreateMutationVariables,
|
CartCreateMutationVariables,
|
||||||
CartDetailsFragment,
|
CartDetailsFragment,
|
||||||
|
CartLineInput,
|
||||||
} from '../schema'
|
} from '../schema'
|
||||||
|
|
||||||
import { FetcherOptions } from '@commerce/utils/types'
|
import { FetcherOptions } from '@commerce/utils/types'
|
||||||
import { CommerceError } from '@commerce/utils/errors'
|
import throwUserErrors from './throw-user-errors'
|
||||||
|
import setCheckoutUrlCookie from './set-checkout-url-cookie'
|
||||||
|
|
||||||
export const cartCreate = async (
|
export const cartCreate = async (
|
||||||
fetch: <T = any, B = Body>(options: FetcherOptions<B>) => Promise<T>
|
fetch: <T = any, B = Body>(options: FetcherOptions<B>) => Promise<T>,
|
||||||
): Promise<CartDetailsFragment> => {
|
lines?: Array<CartLineInput> | CartLineInput
|
||||||
|
): Promise<CartDetailsFragment | null | undefined> => {
|
||||||
const { cartCreate } = await fetch<
|
const { cartCreate } = await fetch<
|
||||||
CartCreateMutation,
|
CartCreateMutation,
|
||||||
CartCreateMutationVariables
|
CartCreateMutationVariables
|
||||||
>({
|
>({
|
||||||
query: cartCreateMutation,
|
query: cartCreateMutation,
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
lines,
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const cart = cartCreate?.cart
|
const cart = cartCreate?.cart
|
||||||
|
|
||||||
|
throwUserErrors(cartCreate?.userErrors)
|
||||||
|
|
||||||
if (cart?.id) {
|
if (cart?.id) {
|
||||||
const options = {
|
const options = {
|
||||||
expires: SHOPIFY_COOKIE_EXPIRE,
|
expires: SHOPIFY_COOKIE_EXPIRE,
|
||||||
}
|
}
|
||||||
Cookies.set(SHOPIFY_CART_ID_COOKIE, cart.id, options)
|
Cookies.set(SHOPIFY_CART_ID_COOKIE, cart.id, options)
|
||||||
} else {
|
|
||||||
throw new CommerceError({
|
|
||||||
errors: cartCreate?.userErrors?.map((e) => ({
|
|
||||||
message: e.message,
|
|
||||||
})) ?? [{ message: 'Could not create cart' }],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCheckoutUrlCookie(cart?.checkoutUrl)
|
||||||
|
|
||||||
return cart
|
return cart
|
||||||
}
|
}
|
||||||
|
|
||||||
|
8
framework/shopify/utils/get-wisthlist-id.ts
Normal file
8
framework/shopify/utils/get-wisthlist-id.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import Cookies from 'js-cookie'
|
||||||
|
import { SHOPIFY_WHISLIST_ID_COOKIE } from '../const'
|
||||||
|
|
||||||
|
const getWishlistId = (id?: string) => {
|
||||||
|
return id || Cookies.get(SHOPIFY_WHISLIST_ID_COOKIE)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getWishlistId
|
@ -7,6 +7,9 @@ export function getError(errors: any[] | null, status: number) {
|
|||||||
|
|
||||||
export async function getAsyncError(res: Response) {
|
export async function getAsyncError(res: Response) {
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
return getError(data.errors, res.status)
|
return getError(data.errors, res.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,9 +5,11 @@ export { default as getBrands } from './get-brands'
|
|||||||
export { default as getCategories } from './get-categories'
|
export { default as getCategories } from './get-categories'
|
||||||
export { default as getCartId } from './get-cart-id'
|
export { default as getCartId } from './get-cart-id'
|
||||||
export { default as cartCreate } from './cart-create'
|
export { default as cartCreate } from './cart-create'
|
||||||
|
export { default as wishlistCreate } from './wishlist-create'
|
||||||
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 { default as getWishlistId } from './get-wisthlist-id'
|
||||||
|
|
||||||
export * from './queries'
|
export * from './queries'
|
||||||
export * from './mutations'
|
export * from './mutations'
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { cartDetailsFragment } from '../queries/get-cart-query'
|
import { wishlistDetailsFragment } from '../queries/get-wishlist-query'
|
||||||
|
|
||||||
const cartCreateMutation = /* GraphQL */ `
|
const cartCreateMutation = /* GraphQL */ `
|
||||||
mutation cartCreate {
|
mutation cartCreate($input: CartInput = {}) {
|
||||||
cartCreate {
|
cartCreate(input: $input) {
|
||||||
cart {
|
cart {
|
||||||
id
|
...wishlistDetails
|
||||||
}
|
}
|
||||||
userErrors {
|
userErrors {
|
||||||
code
|
code
|
||||||
@ -13,6 +13,6 @@ const cartCreateMutation = /* GraphQL */ `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${cartDetailsFragment}
|
${wishlistDetailsFragment}
|
||||||
`
|
`
|
||||||
export default cartCreateMutation
|
export default cartCreateMutation
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { cartDetailsFragment } from '../queries/get-cart-query'
|
import { cartDetailsFragment } from '../queries/get-cart-query'
|
||||||
|
|
||||||
const cartLinesAddMutation = /* GraphQL */ `
|
const cartLinesRemoveMutation = /* GraphQL */ `
|
||||||
mutation cartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
|
mutation cartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
|
||||||
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
|
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
|
||||||
cart {
|
cart {
|
||||||
@ -15,4 +15,4 @@ const cartLinesAddMutation = /* GraphQL */ `
|
|||||||
}
|
}
|
||||||
${cartDetailsFragment}
|
${cartDetailsFragment}
|
||||||
`
|
`
|
||||||
export default cartLinesAddMutation
|
export default cartLinesRemoveMutation
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
export { default as customerCreateMutation } from './customer-create'
|
export { default as customerCreateMutation } from './customer-create'
|
||||||
export { default as cartCreateMutation } from './cart-create'
|
export { default as cartCreateMutation } from './cart-create'
|
||||||
|
export { default as wishlistCreateMutation } from './wishlist-create'
|
||||||
export { default as cartLineItemAddMutation } from './cart-line-item-add'
|
export { default as cartLineItemAddMutation } from './cart-line-item-add'
|
||||||
export { default as cartLineItemUpdateMutation } from './cart-line-item-update'
|
export { default as cartLineItemUpdateMutation } from './cart-line-item-update'
|
||||||
export { default as cartLineItemRemoveMutation } from './cart-line-item-remove'
|
export { default as cartLineItemRemoveMutation } from './cart-line-item-remove'
|
||||||
|
export { default as wishlistLineItemAddMutation } from './wishlist-line-item-add'
|
||||||
|
export { default as wishlistLineItemRemoveMutation } from './wishlist-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'
|
||||||
|
18
framework/shopify/utils/mutations/wishlist-create.ts
Normal file
18
framework/shopify/utils/mutations/wishlist-create.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { wishlistDetailsFragment } from '../queries/get-wishlist-query'
|
||||||
|
|
||||||
|
const wishlistCreateMutation = /* GraphQL */ `
|
||||||
|
mutation wishlistCreate($input: CartInput = {}) {
|
||||||
|
cartCreate(input: $input) {
|
||||||
|
cart {
|
||||||
|
...wishlistDetails
|
||||||
|
}
|
||||||
|
userErrors {
|
||||||
|
code
|
||||||
|
field
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${wishlistDetailsFragment}
|
||||||
|
`
|
||||||
|
export default wishlistCreateMutation
|
18
framework/shopify/utils/mutations/wishlist-line-item-add.ts
Normal file
18
framework/shopify/utils/mutations/wishlist-line-item-add.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { wishlistDetailsFragment } from '../queries/get-wishlist-query'
|
||||||
|
|
||||||
|
const wishlistLinesAddMutation = /* GraphQL */ `
|
||||||
|
mutation wishlistLinesAdd($lines: [CartLineInput!]!, $cartId: ID!) {
|
||||||
|
cartLinesAdd(lines: $lines, cartId: $cartId) {
|
||||||
|
cart {
|
||||||
|
...wishlistDetails
|
||||||
|
}
|
||||||
|
userErrors {
|
||||||
|
code
|
||||||
|
field
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${wishlistDetailsFragment}
|
||||||
|
`
|
||||||
|
export default wishlistLinesAddMutation
|
@ -0,0 +1,18 @@
|
|||||||
|
import { wishlistDetailsFragment } from '../queries/get-wishlist-query'
|
||||||
|
|
||||||
|
const wishlistLinesRemoveMutation = /* GraphQL */ `
|
||||||
|
mutation wishlistLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
|
||||||
|
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
|
||||||
|
cart {
|
||||||
|
...wishlistDetails
|
||||||
|
}
|
||||||
|
userErrors {
|
||||||
|
code
|
||||||
|
field
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${wishlistDetailsFragment}
|
||||||
|
`
|
||||||
|
export default wishlistLinesRemoveMutation
|
@ -15,8 +15,11 @@ import {
|
|||||||
CartDetailsFragment,
|
CartDetailsFragment,
|
||||||
ProductVariantConnection,
|
ProductVariantConnection,
|
||||||
} from '../schema'
|
} from '../schema'
|
||||||
|
|
||||||
import { colorMap } from '@lib/colors'
|
import { colorMap } from '@lib/colors'
|
||||||
|
|
||||||
import { CommerceError } from '@commerce/utils/errors'
|
import { CommerceError } from '@commerce/utils/errors'
|
||||||
|
import type { Wishlist } from '@commerce/types/wishlist'
|
||||||
|
|
||||||
const money = ({ amount, currencyCode }: MoneyV2) => {
|
const money = ({ amount, currencyCode }: MoneyV2) => {
|
||||||
return {
|
return {
|
||||||
@ -134,7 +137,6 @@ export function normalizeCart(
|
|||||||
if (!cart) {
|
if (!cart) {
|
||||||
throw new CommerceError({ message: 'Missing cart details' })
|
throw new CommerceError({ message: 'Missing cart details' })
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: cart.id,
|
id: cart.id,
|
||||||
customerId: cart.buyerIdentity?.customer?.id,
|
customerId: cart.buyerIdentity?.customer?.id,
|
||||||
@ -143,11 +145,11 @@ export function normalizeCart(
|
|||||||
currency: {
|
currency: {
|
||||||
code: cart.estimatedCost?.totalAmount?.currencyCode,
|
code: cart.estimatedCost?.totalAmount?.currencyCode,
|
||||||
},
|
},
|
||||||
taxesIncluded: !!cart.estimatedCost?.totalTaxAmount,
|
taxesIncluded: !!cart.estimatedCost?.totalTaxAmount?.amount,
|
||||||
lineItems: cart.lines?.edges?.map(normalizeLineItem) ?? [],
|
lineItems: cart.lines?.edges?.map(normalizeLineItem) ?? [],
|
||||||
lineItemsSubtotalPrice: +cart.estimatedCost?.totalAmount,
|
lineItemsSubtotalPrice: +cart.estimatedCost?.subtotalAmount?.amount,
|
||||||
subtotalPrice: +cart.estimatedCost?.subtotalAmount,
|
subtotalPrice: +cart.estimatedCost?.subtotalAmount?.amount,
|
||||||
totalPrice: +cart.estimatedCost?.totalAmount,
|
totalPrice: +cart.estimatedCost?.totalAmount?.amount,
|
||||||
discounts: [],
|
discounts: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,27 +161,58 @@ function normalizeLineItem({
|
|||||||
}): LineItem {
|
}): LineItem {
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
variantId: String(variant?.id),
|
variantId: variant?.id,
|
||||||
productId: String(variant?.id),
|
productId: variant?.id,
|
||||||
name: `${variant?.title}`,
|
name: variant?.product?.title || variant?.title,
|
||||||
quantity: quantity ?? 0,
|
quantity: quantity ?? 0,
|
||||||
variant: {
|
variant: {
|
||||||
id: String(variant?.id),
|
id: variant?.id,
|
||||||
sku: variant?.sku ?? '',
|
sku: variant?.sku ?? '',
|
||||||
name: variant?.title!,
|
name: variant?.title!,
|
||||||
image: {
|
image: {
|
||||||
url: variant?.image?.originalSrc || '/product-img-placeholder.svg',
|
url: variant?.image?.originalSrc || '/product-img-placeholder.svg',
|
||||||
},
|
},
|
||||||
requiresShipping: variant?.requiresShipping ?? false,
|
requiresShipping: variant?.requiresShipping ?? false,
|
||||||
price: variant?.priceV2?.amount,
|
price: +variant?.priceV2?.amount,
|
||||||
listPrice: variant?.compareAtPriceV2?.amount,
|
listPrice: variant?.compareAtPriceV2?.amount,
|
||||||
},
|
},
|
||||||
path: String(variant?.product?.handle),
|
path: variant?.product?.handle,
|
||||||
discounts: [],
|
discounts: [],
|
||||||
options: variant?.title == 'Default Title' ? [] : variant?.selectedOptions,
|
options: variant?.title == 'Default Title' ? [] : variant?.selectedOptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeWishlist(
|
||||||
|
cart: CartDetailsFragment | undefined | null
|
||||||
|
): Wishlist {
|
||||||
|
if (!cart) {
|
||||||
|
throw new CommerceError({ message: 'Missing cart details' })
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
items:
|
||||||
|
cart.lines?.edges?.map(({ node: { id, merchandise: variant } }: any) => ({
|
||||||
|
id,
|
||||||
|
product_id: variant?.product?.id,
|
||||||
|
variant_id: variant?.id,
|
||||||
|
product: {
|
||||||
|
name: variant?.product?.title,
|
||||||
|
path: '/' + variant?.product?.handle,
|
||||||
|
description: variant?.product?.description,
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url:
|
||||||
|
variant?.image?.originalSrc || '/product-img-placeholder.svg',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
variants: variant?.id ? [{ id: variant?.id }] : [],
|
||||||
|
amount: +variant?.priceV2?.amount,
|
||||||
|
baseAmount: +variant?.compareAtPriceV2?.amount,
|
||||||
|
currencyCode: variant?.priceV2?.currencyCode,
|
||||||
|
},
|
||||||
|
})) ?? [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const normalizePage = (
|
export const normalizePage = (
|
||||||
{ title: name, handle, ...page }: ShopifyPage,
|
{ title: name, handle, ...page }: ShopifyPage,
|
||||||
locale: string = 'en-US'
|
locale: string = 'en-US'
|
||||||
|
@ -16,11 +16,30 @@ export const productConnectionFragment = /* GraphQL */ `
|
|||||||
currencyCode
|
currencyCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
images(first: 1) {
|
variants(first: 1) {
|
||||||
pageInfo {
|
edges {
|
||||||
hasNextPage
|
node {
|
||||||
hasPreviousPage
|
id
|
||||||
|
title
|
||||||
|
sku
|
||||||
|
availableForSale
|
||||||
|
requiresShipping
|
||||||
|
selectedOptions {
|
||||||
|
name
|
||||||
|
value
|
||||||
}
|
}
|
||||||
|
priceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
compareAtPriceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
images(first: 1) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
originalSrc
|
originalSrc
|
||||||
|
@ -8,9 +8,35 @@ export const cartDetailsFragment = /* GraphQL */ `
|
|||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
|
quantity
|
||||||
merchandise {
|
merchandise {
|
||||||
... on ProductVariant {
|
... on ProductVariant {
|
||||||
id
|
id
|
||||||
|
id
|
||||||
|
sku
|
||||||
|
title
|
||||||
|
selectedOptions {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
image {
|
||||||
|
originalSrc
|
||||||
|
altText
|
||||||
|
width
|
||||||
|
height
|
||||||
|
}
|
||||||
|
priceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
compareAtPriceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
title
|
||||||
|
handle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,7 +75,7 @@ export const cartDetailsFragment = /* GraphQL */ `
|
|||||||
|
|
||||||
const getCartQuery = /* GraphQL */ `
|
const getCartQuery = /* GraphQL */ `
|
||||||
query getCart($cartId: ID!) {
|
query getCart($cartId: ID!) {
|
||||||
node(id: $cartId) {
|
cart(id: $cartId) {
|
||||||
...cartDetails
|
...cartDetails
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
57
framework/shopify/utils/queries/get-wishlist-query.ts
Normal file
57
framework/shopify/utils/queries/get-wishlist-query.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
export const wishlistDetailsFragment = /* GraphQL */ `
|
||||||
|
fragment wishlistDetails on Cart {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
lines(first: 10) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
merchandise {
|
||||||
|
... on ProductVariant {
|
||||||
|
id
|
||||||
|
id
|
||||||
|
sku
|
||||||
|
title
|
||||||
|
image {
|
||||||
|
originalSrc
|
||||||
|
altText
|
||||||
|
width
|
||||||
|
height
|
||||||
|
}
|
||||||
|
priceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
compareAtPriceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
description
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributes {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const getWishlistQuery = /* GraphQL */ `
|
||||||
|
query getWishlist($cartId: ID!) {
|
||||||
|
cart(id: $cartId) {
|
||||||
|
...wishlistDetails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${wishlistDetailsFragment}
|
||||||
|
`
|
||||||
|
export default getWishlistQuery
|
@ -5,6 +5,7 @@ export { default as getAllProductsPathtsQuery } from './get-all-products-paths-q
|
|||||||
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 getCartQuery } from './get-cart-query'
|
export { default as getCartQuery } from './get-cart-query'
|
||||||
|
export { default as getWishlistQuery } from './get-wishlist-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'
|
||||||
|
16
framework/shopify/utils/set-checkout-url-cookie.ts
Normal file
16
framework/shopify/utils/set-checkout-url-cookie.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import Cookies from 'js-cookie'
|
||||||
|
|
||||||
|
import { SHOPIFY_CART_URL_COOKIE, SHOPIFY_COOKIE_EXPIRE } from '../const'
|
||||||
|
|
||||||
|
export const setCheckoutUrlCookie = (checkoutUrl: string) => {
|
||||||
|
if (checkoutUrl) {
|
||||||
|
const oldCookie = Cookies.get(SHOPIFY_CART_URL_COOKIE)
|
||||||
|
if (oldCookie !== checkoutUrl) {
|
||||||
|
Cookies.set(SHOPIFY_CART_URL_COOKIE, checkoutUrl, {
|
||||||
|
expires: SHOPIFY_COOKIE_EXPIRE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default setCheckoutUrlCookie
|
45
framework/shopify/utils/wishlist-create.ts
Normal file
45
framework/shopify/utils/wishlist-create.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import Cookies from 'js-cookie'
|
||||||
|
import { SHOPIFY_COOKIE_EXPIRE, SHOPIFY_WHISLIST_ID_COOKIE } from '../const'
|
||||||
|
import wishlistCreateMutation from './mutations/wishlist-create'
|
||||||
|
import { FetcherOptions } from '@commerce/utils/types'
|
||||||
|
|
||||||
|
import {
|
||||||
|
CartCreateMutation,
|
||||||
|
CartCreateMutationVariables,
|
||||||
|
CartDetailsFragment,
|
||||||
|
CartLineInput,
|
||||||
|
} from '../schema'
|
||||||
|
|
||||||
|
import throwUserErrors from './throw-user-errors'
|
||||||
|
|
||||||
|
export const wishlistCreate = async (
|
||||||
|
fetch: <T = any, B = Body>(options: FetcherOptions<B>) => Promise<T>,
|
||||||
|
lines?: Array<CartLineInput> | CartLineInput
|
||||||
|
): Promise<CartDetailsFragment | null | undefined> => {
|
||||||
|
const { cartCreate } = await fetch<
|
||||||
|
CartCreateMutation,
|
||||||
|
CartCreateMutationVariables
|
||||||
|
>({
|
||||||
|
query: wishlistCreateMutation,
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
lines,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const wishlist = cartCreate?.cart
|
||||||
|
|
||||||
|
throwUserErrors(cartCreate?.userErrors)
|
||||||
|
|
||||||
|
if (wishlist?.id) {
|
||||||
|
const options = {
|
||||||
|
expires: SHOPIFY_COOKIE_EXPIRE,
|
||||||
|
}
|
||||||
|
Cookies.set(SHOPIFY_WHISLIST_ID_COOKIE, wishlist.id, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return wishlist
|
||||||
|
}
|
||||||
|
|
||||||
|
export default wishlistCreate
|
@ -1,13 +1,69 @@
|
|||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
|
import type { MutationHook } from '@commerce/utils/types'
|
||||||
|
import useAddItem, { UseAddItem } from '@commerce/wishlist/use-add-item'
|
||||||
|
|
||||||
export function emptyHook() {
|
import type { AddItemHook } from '../types/wishlist'
|
||||||
const useEmptyHook = async (options = {}) => {
|
|
||||||
return useCallback(async function () {
|
import {
|
||||||
return Promise.resolve()
|
getWishlistId,
|
||||||
}, [])
|
normalizeCart,
|
||||||
|
normalizeWishlist,
|
||||||
|
throwUserErrors,
|
||||||
|
wishlistCreate,
|
||||||
|
wishlistLineItemAddMutation,
|
||||||
|
} from '../utils'
|
||||||
|
|
||||||
|
import { CartLinesAddMutation, CartLinesAddMutationVariables } from '../schema'
|
||||||
|
|
||||||
|
import useWishlist from './use-wishlist'
|
||||||
|
export default useAddItem as UseAddItem<typeof handler>
|
||||||
|
|
||||||
|
export const handler: MutationHook<AddItemHook> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: wishlistLineItemAddMutation,
|
||||||
|
},
|
||||||
|
async fetcher({ input, options, fetch }) {
|
||||||
|
const lines = [
|
||||||
|
{
|
||||||
|
merchandiseId: String(input.item.variantId || input.item.productId),
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
let wishlistId = getWishlistId()
|
||||||
|
|
||||||
|
if (!wishlistId) {
|
||||||
|
const cart = await wishlistCreate(fetch, lines)
|
||||||
|
return normalizeCart(cart)
|
||||||
|
} else {
|
||||||
|
const { cartLinesAdd } = await fetch<
|
||||||
|
CartLinesAddMutation,
|
||||||
|
CartLinesAddMutationVariables
|
||||||
|
>({
|
||||||
|
...options,
|
||||||
|
variables: {
|
||||||
|
cartId: wishlistId,
|
||||||
|
lines,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
throwUserErrors(cartLinesAdd?.userErrors)
|
||||||
|
|
||||||
|
return normalizeWishlist(cartLinesAdd?.cart)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
useHook:
|
||||||
|
({ fetch }) =>
|
||||||
|
() => {
|
||||||
|
const { mutate } = useWishlist()
|
||||||
|
|
||||||
return useEmptyHook
|
return useCallback(
|
||||||
|
async function addItem(item) {
|
||||||
|
const data = await fetch({ input: { item } })
|
||||||
|
await mutate(data, false)
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
[fetch, mutate]
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default emptyHook
|
|
||||||
|
@ -1,17 +1,62 @@
|
|||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
|
import type { HookFetcherContext, MutationHook } from '@commerce/utils/types'
|
||||||
|
|
||||||
type Options = {
|
import useRemoveItem, {
|
||||||
includeProducts?: boolean
|
UseRemoveItem,
|
||||||
|
} from '@commerce/wishlist/use-remove-item'
|
||||||
|
import type { RemoveItemHook } from '../types/wishlist'
|
||||||
|
|
||||||
|
export default useRemoveItem as UseRemoveItem<typeof handler>
|
||||||
|
|
||||||
|
import {
|
||||||
|
getCartId,
|
||||||
|
getWishlistId,
|
||||||
|
normalizeWishlist,
|
||||||
|
throwUserErrors,
|
||||||
|
} from '../utils'
|
||||||
|
|
||||||
|
import {
|
||||||
|
CartLinesRemoveMutation,
|
||||||
|
CartLinesRemoveMutationVariables,
|
||||||
|
} from '../schema'
|
||||||
|
|
||||||
|
import useWishlist from './use-wishlist'
|
||||||
|
import wishlistLinesRemoveMutation from '../utils/mutations/wishlist-line-item-remove'
|
||||||
|
|
||||||
|
export const handler: MutationHook<RemoveItemHook> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: wishlistLinesRemoveMutation,
|
||||||
|
},
|
||||||
|
async fetcher({
|
||||||
|
input: { itemId },
|
||||||
|
options,
|
||||||
|
fetch,
|
||||||
|
}: HookFetcherContext<RemoveItemHook>) {
|
||||||
|
const { cartLinesRemove } = await fetch<
|
||||||
|
CartLinesRemoveMutation,
|
||||||
|
CartLinesRemoveMutationVariables
|
||||||
|
>({
|
||||||
|
...options,
|
||||||
|
variables: { cartId: getWishlistId(), lineIds: [itemId] },
|
||||||
|
})
|
||||||
|
|
||||||
|
throwUserErrors(cartLinesRemove?.userErrors)
|
||||||
|
|
||||||
|
return normalizeWishlist(cartLinesRemove?.cart)
|
||||||
|
},
|
||||||
|
|
||||||
|
useHook:
|
||||||
|
({ fetch }) =>
|
||||||
|
({ wishlist } = {}) => {
|
||||||
|
const { revalidate } = useWishlist(wishlist)
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
async function removeItem(input) {
|
||||||
|
const data = await fetch({ input: { itemId: String(input.id) } })
|
||||||
|
await revalidate()
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
[fetch, revalidate]
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export function emptyHook(options?: Options) {
|
|
||||||
const useEmptyHook = async ({ id }: { id: string | number }) => {
|
|
||||||
return useCallback(async function () {
|
|
||||||
return Promise.resolve()
|
|
||||||
}, [])
|
|
||||||
}
|
|
||||||
|
|
||||||
return useEmptyHook
|
|
||||||
}
|
|
||||||
|
|
||||||
export default emptyHook
|
|
||||||
|
@ -1,46 +1,52 @@
|
|||||||
// TODO: replace this hook and other wishlist hooks with a handler, or remove them if
|
import { useMemo } from 'react'
|
||||||
// Shopify doesn't have a wishlist
|
import { SWRHook } from '@commerce/utils/types'
|
||||||
|
import useWishlist, { UseWishlist } from '@commerce/wishlist/use-wishlist'
|
||||||
|
import type { GetWishlistHook } from '../types/wishlist'
|
||||||
|
import { getWishlistId, normalizeWishlist, getWishlistQuery } from '../utils'
|
||||||
|
import { GetCartQueryVariables, QueryRoot } from '../schema'
|
||||||
|
|
||||||
import { HookFetcher } from '@commerce/utils/types'
|
export default useWishlist as UseWishlist<typeof handler>
|
||||||
import { Product } from '../schema'
|
|
||||||
|
|
||||||
const defaultOpts = {}
|
export const handler: SWRHook<GetWishlistHook> = {
|
||||||
|
fetchOptions: {
|
||||||
|
query: getWishlistQuery,
|
||||||
|
},
|
||||||
|
async fetcher({ input: _input, options, fetch }) {
|
||||||
|
const wishListId = getWishlistId()
|
||||||
|
|
||||||
export type Wishlist = {
|
if (wishListId) {
|
||||||
items: [
|
const { cart } = await fetch<QueryRoot, GetCartQueryVariables>({
|
||||||
{
|
...options,
|
||||||
product_id: number
|
variables: { cartId: wishListId },
|
||||||
variant_id: number
|
})
|
||||||
id: number
|
|
||||||
product: Product
|
return normalizeWishlist(cart)
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UseWishlistOptions {
|
|
||||||
includeProducts?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UseWishlistInput extends UseWishlistOptions {
|
|
||||||
customerId?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetcher: HookFetcher<Wishlist | null, UseWishlistInput> = () => {
|
|
||||||
return null
|
return null
|
||||||
|
},
|
||||||
|
useHook:
|
||||||
|
({ useData }) =>
|
||||||
|
(input) => {
|
||||||
|
const response = useData({
|
||||||
|
input: [],
|
||||||
|
swrOptions: {
|
||||||
|
revalidateOnFocus: false,
|
||||||
|
...input?.swrOptions,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() =>
|
||||||
|
Object.create(response, {
|
||||||
|
isEmpty: {
|
||||||
|
get() {
|
||||||
|
return (response.data?.items?.length || 0) <= 0
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[response]
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extendHook(
|
|
||||||
customFetcher: typeof fetcher,
|
|
||||||
// swrOptions?: SwrOptions<Wishlist | null, UseWishlistInput>
|
|
||||||
swrOptions?: any
|
|
||||||
) {
|
|
||||||
const useWishlist = ({ includeProducts }: UseWishlistOptions = {}) => {
|
|
||||||
return { data: null }
|
|
||||||
}
|
|
||||||
|
|
||||||
useWishlist.extend = extendHook
|
|
||||||
|
|
||||||
return useWishlist
|
|
||||||
}
|
|
||||||
|
|
||||||
export default extendHook(fetcher)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user