mirror of
https://github.com/vercel/commerce.git
synced 2025-05-17 15:06:59 +00:00
Update queries & structure
This commit is contained in:
parent
0b41b4e618
commit
d4858910e4
@ -6,7 +6,16 @@ import { GetSiteInfoQueryVariables } from '../../../schema'
|
|||||||
import type { ShopifyConfig, Provider } from '..'
|
import type { ShopifyConfig, Provider } from '..'
|
||||||
import { GetSiteInfoOperation } from '../../types/site'
|
import { GetSiteInfoOperation } from '../../types/site'
|
||||||
|
|
||||||
import { getCategories, getBrands, getSiteInfoQuery } from '../../utils'
|
import { getBrands } from '../utils/get-brands'
|
||||||
|
import { getCategories } from '../utils/get-categories'
|
||||||
|
|
||||||
|
export const getSiteInfoQuery = /* GraphQL */ `
|
||||||
|
query getSiteInfo {
|
||||||
|
shop {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export default function getSiteInfoOperation({
|
export default function getSiteInfoOperation({
|
||||||
commerce,
|
commerce,
|
||||||
@ -37,20 +46,6 @@ export default function getSiteInfoOperation({
|
|||||||
|
|
||||||
const categoriesPromise = getCategories(cfg)
|
const categoriesPromise = getCategories(cfg)
|
||||||
const brandsPromise = getBrands(cfg)
|
const brandsPromise = getBrands(cfg)
|
||||||
/*
|
|
||||||
const { fetch, locale } = cfg
|
|
||||||
const { data } = await fetch<GetSiteInfoQuery, GetSiteInfoQueryVariables>(
|
|
||||||
query,
|
|
||||||
{ variables },
|
|
||||||
{
|
|
||||||
...(locale && {
|
|
||||||
headers: {
|
|
||||||
'Accept-Language': locale,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
categories: await categoriesPromise,
|
categories: await categoriesPromise,
|
||||||
|
@ -3,6 +3,7 @@ import fetch from './fetch'
|
|||||||
|
|
||||||
import { API_URL, API_TOKEN } from '../../const'
|
import { API_URL, API_TOKEN } from '../../const'
|
||||||
import { getError } from '../../utils/handle-fetch-response'
|
import { getError } from '../../utils/handle-fetch-response'
|
||||||
|
import { CommerceError } from '@vercel/commerce/utils/errors'
|
||||||
|
|
||||||
const fetchGraphqlApi: GraphQLFetcher = async (
|
const fetchGraphqlApi: GraphQLFetcher = async (
|
||||||
query: string,
|
query: string,
|
||||||
@ -32,14 +33,20 @@ const fetchGraphqlApi: GraphQLFetcher = async (
|
|||||||
|
|
||||||
return { data, res }
|
return { data, res }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw getError(
|
// if the error is a CommerceError, we can use it as-is
|
||||||
[
|
if (err instanceof CommerceError) {
|
||||||
{
|
throw err
|
||||||
message: `${err} \n Most likely related to an unexpected output. e.g the store might be protected with password or not available.`,
|
} else {
|
||||||
},
|
// otherwise, we'll wrap unknown errors in a CommerceError
|
||||||
],
|
throw getError(
|
||||||
500
|
[
|
||||||
)
|
{
|
||||||
|
message: `${err} \n Most likely related to an unexpected output. e.g the store might be protected with password or not available.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
500
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default fetchGraphqlApi
|
export default fetchGraphqlApi
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { ShopifyConfig } from '../api'
|
import { ShopifyConfig } from '..'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GetAllProductVendorsQuery,
|
GetAllProductVendorsQuery,
|
||||||
GetAllProductVendorsQueryVariables,
|
GetAllProductVendorsQueryVariables,
|
||||||
} from '../../schema'
|
} from '../../../schema'
|
||||||
|
|
||||||
import { getAllProductVendors } from './queries'
|
import { getAllProductVendors } from '../../utils/queries'
|
||||||
|
|
||||||
export type Brand = {
|
export type Brand = {
|
||||||
entityId: string
|
entityId: string
|
@ -1,8 +1,7 @@
|
|||||||
import type { Category } from '../types/site'
|
import { ShopifyConfig } from '..'
|
||||||
import { ShopifyConfig } from '../api'
|
import type { Category } from '../../types/site'
|
||||||
import { CollectionEdge } from '../../schema'
|
import { CollectionEdge } from '../../../schema'
|
||||||
import { normalizeCategory } from './normalize'
|
import { normalizeCategory, getSiteCollectionsQuery } from '../../utils'
|
||||||
import { getSiteCollectionsQuery } from './queries/get-all-collections-query'
|
|
||||||
|
|
||||||
export const getCategories = async ({
|
export const getCategories = async ({
|
||||||
fetch,
|
fetch,
|
@ -1,15 +1,13 @@
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import useCommerceCart, { UseCart } from '@vercel/commerce/cart/use-cart'
|
import useCommerceCart, { UseCart } from '@vercel/commerce/cart/use-cart'
|
||||||
|
|
||||||
import { SWRHook } from '@vercel/commerce/utils/types'
|
import { SWRHook } from '@vercel/commerce/utils/types'
|
||||||
import { getCartQuery, normalizeCart } from '../utils'
|
|
||||||
import { GetCartHook } from '../types/cart'
|
|
||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
|
|
||||||
import { GetCartQueryVariables, QueryRoot } from '../../schema'
|
import { getCartQuery, normalizeCart, setCartUrlCookie } from '../utils'
|
||||||
|
import { GetCartHook } from '../types/cart'
|
||||||
|
|
||||||
|
import { GetCartQueryVariables, QueryRoot } from '../../schema'
|
||||||
import { SHOPIFY_CART_ID_COOKIE } from '../const'
|
import { SHOPIFY_CART_ID_COOKIE } from '../const'
|
||||||
import { setCartUrlCookie } from '../utils/set-cart-url-cookie'
|
|
||||||
|
|
||||||
export default useCommerceCart as UseCart<typeof handler>
|
export default useCommerceCart as UseCart<typeof handler>
|
||||||
export const handler: SWRHook<GetCartHook> = {
|
export const handler: SWRHook<GetCartHook> = {
|
||||||
|
@ -10,6 +10,18 @@ import useRemoveItem, {
|
|||||||
import type { Cart, LineItem, RemoveItemHook } from '../types/cart'
|
import type { Cart, LineItem, RemoveItemHook } from '../types/cart'
|
||||||
import useCart from './use-cart'
|
import useCart from './use-cart'
|
||||||
|
|
||||||
|
import {
|
||||||
|
getCartId,
|
||||||
|
normalizeCart,
|
||||||
|
throwUserErrors,
|
||||||
|
cartLinesRemoveMutation,
|
||||||
|
} from '../utils'
|
||||||
|
|
||||||
|
import {
|
||||||
|
CartLinesRemoveMutation,
|
||||||
|
CartLinesRemoveMutationVariables,
|
||||||
|
} from '../../schema'
|
||||||
|
|
||||||
export type RemoveItemFn<T = any> = T extends LineItem
|
export type RemoveItemFn<T = any> = T extends LineItem
|
||||||
? (input?: RemoveItemActionInput<T>) => Promise<Cart | null | undefined>
|
? (input?: RemoveItemActionInput<T>) => Promise<Cart | null | undefined>
|
||||||
: (input: RemoveItemActionInput<T>) => Promise<Cart | null>
|
: (input: RemoveItemActionInput<T>) => Promise<Cart | null>
|
||||||
@ -20,17 +32,6 @@ export type RemoveItemActionInput<T = any> = T extends LineItem
|
|||||||
|
|
||||||
export default useRemoveItem as UseRemoveItem<typeof handler>
|
export default useRemoveItem as UseRemoveItem<typeof handler>
|
||||||
|
|
||||||
import {
|
|
||||||
cartLinesRemoveMutation,
|
|
||||||
getCartId,
|
|
||||||
normalizeCart,
|
|
||||||
throwUserErrors,
|
|
||||||
} from '../utils'
|
|
||||||
import {
|
|
||||||
CartLinesRemoveMutation,
|
|
||||||
CartLinesRemoveMutationVariables,
|
|
||||||
} from '../../schema'
|
|
||||||
|
|
||||||
export const handler = {
|
export const handler = {
|
||||||
fetchOptions: {
|
fetchOptions: {
|
||||||
query: cartLinesRemoveMutation,
|
query: cartLinesRemoveMutation,
|
||||||
|
@ -11,9 +11,12 @@ import useUpdateItem, {
|
|||||||
|
|
||||||
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 { getCartId, normalizeCart, cartLinesUpdateMutation } from '../utils'
|
||||||
|
|
||||||
import type { UpdateItemHook, LineItem } from '../types/cart'
|
import type { UpdateItemHook, LineItem } from '../types/cart'
|
||||||
import { getCartId, cartLinesUpdateMutation, normalizeCart } from '../utils'
|
|
||||||
import {
|
import type {
|
||||||
CartLinesUpdateMutation,
|
CartLinesUpdateMutation,
|
||||||
CartLinesUpdateMutationVariables,
|
CartLinesUpdateMutationVariables,
|
||||||
} from '../../schema'
|
} from '../../schema'
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
|
export const API_VERSION = '2022-04'
|
||||||
|
|
||||||
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_CART_URL_COOKIE = 'shopify_cartUrl'
|
||||||
|
|
||||||
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?.replace(/(^\w+:|^)\/\//, '')
|
||||||
|
|
||||||
export const SHOPIFY_COOKIE_EXPIRE = 30
|
export const SHOPIFY_COOKIE_EXPIRE = 30
|
||||||
|
|
||||||
export const API_URL = `https://${STORE_DOMAIN}/api/2022-04/graphql.json`
|
export const API_URL = `https://${STORE_DOMAIN}/api/${API_VERSION}/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
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
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-mutations'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CartCreateMutation,
|
CartCreateMutation,
|
||||||
@ -9,9 +9,9 @@ import {
|
|||||||
CartLineInput,
|
CartLineInput,
|
||||||
} from '../../schema'
|
} from '../../schema'
|
||||||
|
|
||||||
|
import { setCartUrlCookie } from './helpers'
|
||||||
import { throwUserErrors } from './throw-user-errors'
|
import { throwUserErrors } from './throw-user-errors'
|
||||||
import { setCartUrlCookie } from './set-cart-url-cookie'
|
import type { FetcherOptions } from '@vercel/commerce/utils/types'
|
||||||
import { FetcherOptions } from '@vercel/commerce/utils/types'
|
|
||||||
|
|
||||||
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>,
|
||||||
@ -44,5 +44,3 @@ export const cartCreate = async (
|
|||||||
|
|
||||||
return cart
|
return cart
|
||||||
}
|
}
|
||||||
|
|
||||||
export default cartCreate
|
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import Cookies, { CookieAttributes } from 'js-cookie'
|
|
||||||
import { SHOPIFY_COOKIE_EXPIRE, SHOPIFY_CUSTOMER_TOKEN_COOKIE } from '../const'
|
|
||||||
|
|
||||||
export const getCustomerToken = () => Cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE)
|
|
||||||
|
|
||||||
export const setCustomerToken = (
|
|
||||||
token: string | null,
|
|
||||||
options?: CookieAttributes
|
|
||||||
) => {
|
|
||||||
if (!token) {
|
|
||||||
Cookies.remove(SHOPIFY_CUSTOMER_TOKEN_COOKIE)
|
|
||||||
} else {
|
|
||||||
Cookies.set(
|
|
||||||
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
|
||||||
token,
|
|
||||||
options ?? {
|
|
||||||
expires: SHOPIFY_COOKIE_EXPIRE,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,82 @@
|
|||||||
|
export const cartDetailsFragment = /* GraphQL */ `
|
||||||
|
fragment cartDetails on Cart {
|
||||||
|
id
|
||||||
|
checkoutUrl
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
lines(first: 10) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
merchandise {
|
||||||
|
... on ProductVariant {
|
||||||
|
id
|
||||||
|
id
|
||||||
|
sku
|
||||||
|
title
|
||||||
|
selectedOptions {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
image {
|
||||||
|
url
|
||||||
|
altText
|
||||||
|
width
|
||||||
|
height
|
||||||
|
}
|
||||||
|
priceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
compareAtPriceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
title
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributes {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
buyerIdentity {
|
||||||
|
email
|
||||||
|
customer {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
estimatedCost {
|
||||||
|
totalAmount {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
subtotalAmount {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
totalTaxAmount {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
totalDutyAmount {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const userErrorsFragment = /* GraphQL */ `
|
||||||
|
fragment userErrors on UserError {
|
||||||
|
code
|
||||||
|
field
|
||||||
|
message
|
||||||
|
}
|
||||||
|
`
|
15
packages/shopify/src/utils/fragments/customer-fragments.ts
Normal file
15
packages/shopify/src/utils/fragments/customer-fragments.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export const customerUserErrorsFragment = /* GraphQL */ `
|
||||||
|
fragment customerUserErrors on CustomerUserErrors {
|
||||||
|
code
|
||||||
|
field
|
||||||
|
message
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const customerAccessTokenFragment = /* GraphQL */ `
|
||||||
|
fragment customerAccessToken on CustomerAccessToken {
|
||||||
|
code
|
||||||
|
field
|
||||||
|
message
|
||||||
|
}
|
||||||
|
`
|
36
packages/shopify/src/utils/fragments/media-fragment.ts
Normal file
36
packages/shopify/src/utils/fragments/media-fragment.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export const mediaFragment = /* GraphQL */ `
|
||||||
|
fragment Media on Media {
|
||||||
|
mediaContentType
|
||||||
|
alt
|
||||||
|
previewImage {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
... on MediaImage {
|
||||||
|
id
|
||||||
|
image {
|
||||||
|
url
|
||||||
|
width
|
||||||
|
height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# ... on Video {
|
||||||
|
# id
|
||||||
|
# sources {
|
||||||
|
# mimeType
|
||||||
|
# url
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# ... on Model3d {
|
||||||
|
# id
|
||||||
|
# sources {
|
||||||
|
# mimeType
|
||||||
|
# url
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# ... on ExternalVideo {
|
||||||
|
# id
|
||||||
|
# embedUrl
|
||||||
|
# host
|
||||||
|
# }
|
||||||
|
}
|
||||||
|
`
|
@ -0,0 +1,36 @@
|
|||||||
|
export const productCardFragment = /* GraphQL */ `
|
||||||
|
fragment productCard on Product {
|
||||||
|
id
|
||||||
|
handle
|
||||||
|
availableForSale
|
||||||
|
title
|
||||||
|
productType
|
||||||
|
vendor
|
||||||
|
variants(first: 1) {
|
||||||
|
nodes {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
requiresShipping
|
||||||
|
availableForSale
|
||||||
|
selectedOptions {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
image {
|
||||||
|
url
|
||||||
|
altText
|
||||||
|
width
|
||||||
|
height
|
||||||
|
}
|
||||||
|
priceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
compareAtPriceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
@ -1,6 +0,0 @@
|
|||||||
import Cookies from 'js-cookie'
|
|
||||||
import { SHOPIFY_CART_ID_COOKIE } from '../const'
|
|
||||||
|
|
||||||
export const getCartId = (id?: string) => {
|
|
||||||
return id || Cookies.get(SHOPIFY_CART_ID_COOKIE)
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
import { getSortVariables } from './get-sort-variables'
|
|
||||||
import { SearchProductsBody } from '../types/product'
|
|
||||||
|
|
||||||
export const getSearchVariables = ({
|
|
||||||
brandId,
|
|
||||||
search,
|
|
||||||
categoryId,
|
|
||||||
sort,
|
|
||||||
locale,
|
|
||||||
}: SearchProductsBody) => {
|
|
||||||
let query = ''
|
|
||||||
|
|
||||||
if (search) {
|
|
||||||
query += `product_type:${search} OR title:${search} OR tag:${search} `
|
|
||||||
}
|
|
||||||
|
|
||||||
if (brandId) {
|
|
||||||
query += `${search ? 'AND ' : ''}vendor:${brandId}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
categoryId,
|
|
||||||
query,
|
|
||||||
...getSortVariables(sort, !!categoryId),
|
|
||||||
...(locale && {
|
|
||||||
locale,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
export const getSortVariables = (
|
|
||||||
sort?: string,
|
|
||||||
isCategory: boolean = false
|
|
||||||
) => {
|
|
||||||
let output = {}
|
|
||||||
switch (sort) {
|
|
||||||
case 'price-asc':
|
|
||||||
output = {
|
|
||||||
sortKey: 'PRICE',
|
|
||||||
reverse: false,
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'price-desc':
|
|
||||||
output = {
|
|
||||||
sortKey: 'PRICE',
|
|
||||||
reverse: true,
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'trending-desc':
|
|
||||||
output = {
|
|
||||||
sortKey: 'BEST_SELLING',
|
|
||||||
reverse: false,
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'latest-desc':
|
|
||||||
output = {
|
|
||||||
sortKey: isCategory ? 'CREATED' : 'CREATED_AT',
|
|
||||||
reverse: true,
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
@ -1,12 +1,17 @@
|
|||||||
import { FetcherError } from '@vercel/commerce/utils/errors'
|
import { FetcherError } from '@vercel/commerce/utils/errors'
|
||||||
|
|
||||||
export function getError(errors: any[] | null, status: number) {
|
export function getError(err: any[] | string | null, status: number) {
|
||||||
errors = errors ?? [{ message: 'Failed to fetch Shopify API' }]
|
console.log(JSON.stringify(err, null, 2))
|
||||||
|
|
||||||
|
const errors = Array.isArray(err)
|
||||||
|
? err
|
||||||
|
: [{ message: err || 'Failed to fetch Shopify API' }]
|
||||||
return new FetcherError({ errors, status })
|
return new FetcherError({ errors, status })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAsyncError(res: Response) {
|
export async function getAsyncError(res: Response) {
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
|
|
||||||
return getError(data.errors, res.status)
|
return getError(data.errors, res.status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FetcherOptions } from '@vercel/commerce/utils/types'
|
import { FetcherOptions } from '@vercel/commerce/utils/types'
|
||||||
import { CustomerAccessTokenCreateInput } from '../../schema'
|
import { CustomerAccessTokenCreateInput } from '../../schema'
|
||||||
import { setCustomerToken } from './customer-token'
|
import { setCustomerToken } from './helpers'
|
||||||
import { customerAccessTokenCreateMutation } from './mutations'
|
import { customerAccessTokenCreateMutation } from './mutations'
|
||||||
import { throwUserErrors } from './throw-user-errors'
|
import { throwUserErrors } from './throw-user-errors'
|
||||||
|
|
||||||
|
104
packages/shopify/src/utils/helpers.ts
Normal file
104
packages/shopify/src/utils/helpers.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import Cookies, { CookieAttributes } from 'js-cookie'
|
||||||
|
import { SearchProductsBody } from '../types/product'
|
||||||
|
|
||||||
|
import {
|
||||||
|
SHOPIFY_CART_URL_COOKIE,
|
||||||
|
SHOPIFY_COOKIE_EXPIRE,
|
||||||
|
SHOPIFY_CART_ID_COOKIE,
|
||||||
|
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
||||||
|
} from '../const'
|
||||||
|
|
||||||
|
export const setCartUrlCookie = (cartUrl: string) => {
|
||||||
|
if (cartUrl) {
|
||||||
|
const oldCookie = Cookies.get(SHOPIFY_CART_URL_COOKIE)
|
||||||
|
if (oldCookie !== cartUrl) {
|
||||||
|
Cookies.set(SHOPIFY_CART_URL_COOKIE, cartUrl, {
|
||||||
|
expires: SHOPIFY_COOKIE_EXPIRE,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCartId = (id?: string) => {
|
||||||
|
return id || Cookies.get(SHOPIFY_CART_ID_COOKIE)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSortVariables = (
|
||||||
|
sort?: string,
|
||||||
|
isCategory: boolean = false
|
||||||
|
) => {
|
||||||
|
let output = {}
|
||||||
|
switch (sort) {
|
||||||
|
case 'price-asc':
|
||||||
|
output = {
|
||||||
|
sortKey: 'PRICE',
|
||||||
|
reverse: false,
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'price-desc':
|
||||||
|
output = {
|
||||||
|
sortKey: 'PRICE',
|
||||||
|
reverse: true,
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'trending-desc':
|
||||||
|
output = {
|
||||||
|
sortKey: 'BEST_SELLING',
|
||||||
|
reverse: false,
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'latest-desc':
|
||||||
|
output = {
|
||||||
|
sortKey: isCategory ? 'CREATED' : 'CREATED_AT',
|
||||||
|
reverse: true,
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSearchVariables = ({
|
||||||
|
brandId,
|
||||||
|
search,
|
||||||
|
categoryId,
|
||||||
|
sort,
|
||||||
|
locale,
|
||||||
|
}: SearchProductsBody) => {
|
||||||
|
let query = ''
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
query += `product_type:${search} OR title:${search} OR tag:${search} `
|
||||||
|
}
|
||||||
|
|
||||||
|
if (brandId) {
|
||||||
|
query += `${search ? 'AND ' : ''}vendor:${brandId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
categoryId,
|
||||||
|
query,
|
||||||
|
...getSortVariables(sort, !!categoryId),
|
||||||
|
...(locale && {
|
||||||
|
locale,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCustomerToken = () => Cookies.get(SHOPIFY_CUSTOMER_TOKEN_COOKIE)
|
||||||
|
|
||||||
|
export const setCustomerToken = (
|
||||||
|
token: string | null,
|
||||||
|
options?: CookieAttributes
|
||||||
|
) => {
|
||||||
|
if (!token) {
|
||||||
|
Cookies.remove(SHOPIFY_CUSTOMER_TOKEN_COOKIE)
|
||||||
|
} else {
|
||||||
|
Cookies.set(
|
||||||
|
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
|
||||||
|
token,
|
||||||
|
options ?? {
|
||||||
|
expires: SHOPIFY_COOKIE_EXPIRE,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,10 @@
|
|||||||
|
export { throwUserErrors } from './throw-user-errors'
|
||||||
export { handleFetchResponse } from './handle-fetch-response'
|
export { handleFetchResponse } from './handle-fetch-response'
|
||||||
export { getSearchVariables } from './get-search-variables'
|
|
||||||
export { getSortVariables } from './get-sort-variables'
|
|
||||||
export { getBrands } from './get-brands'
|
|
||||||
export { getCategories } from './get-categories'
|
|
||||||
export { getCartId } from './get-cart-id'
|
|
||||||
export { cartCreate } from './cart-create'
|
export { cartCreate } from './cart-create'
|
||||||
export { handleLogin, handleAutomaticLogin } from './handle-login'
|
export { handleLogin, handleAutomaticLogin } from './handle-login'
|
||||||
export { handleAccountActivation } from './handle-account-activation'
|
export { handleAccountActivation } from './handle-account-activation'
|
||||||
export { throwUserErrors } from './throw-user-errors'
|
|
||||||
|
|
||||||
|
export * from './helpers'
|
||||||
export * from './queries'
|
export * from './queries'
|
||||||
export * from './mutations'
|
export * from './mutations'
|
||||||
export * from './normalize'
|
export * from './normalize'
|
||||||
export * from './customer-token'
|
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
export const associateCustomerWithCheckoutMutation = /* GraphQl */ `
|
|
||||||
mutation associateCustomerWithCheckout($checkoutId: ID!, $customerAccessToken: String!) {
|
|
||||||
checkoutCustomerAssociateV2(checkoutId: $checkoutId, customerAccessToken: $customerAccessToken) {
|
|
||||||
checkout {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
checkoutUserErrors {
|
|
||||||
code
|
|
||||||
field
|
|
||||||
message
|
|
||||||
}
|
|
||||||
customer {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
@ -1,17 +0,0 @@
|
|||||||
import { cartDetailsFragment } from '../queries/get-cart-query'
|
|
||||||
|
|
||||||
export const cartCreateMutation = /* GraphQL */ `
|
|
||||||
mutation cartCreate($input: CartInput = {}) {
|
|
||||||
cartCreate(input: $input) {
|
|
||||||
cart {
|
|
||||||
...cartDetails
|
|
||||||
}
|
|
||||||
userErrors {
|
|
||||||
code
|
|
||||||
field
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${cartDetailsFragment}
|
|
||||||
`
|
|
@ -1,17 +0,0 @@
|
|||||||
import { cartDetailsFragment } from '../queries/get-cart-query'
|
|
||||||
|
|
||||||
export const cartLinesAddMutation = /* GraphQL */ `
|
|
||||||
mutation cartLinesAdd($lines: [CartLineInput!]!, $cartId: ID!) {
|
|
||||||
cartLinesAdd(lines: $lines, cartId: $cartId) {
|
|
||||||
cart {
|
|
||||||
...cartDetails
|
|
||||||
}
|
|
||||||
userErrors {
|
|
||||||
code
|
|
||||||
field
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${cartDetailsFragment}
|
|
||||||
`
|
|
@ -1,17 +0,0 @@
|
|||||||
import { cartDetailsFragment } from '../queries/get-cart-query'
|
|
||||||
|
|
||||||
export const cartLinesRemoveMutation = /* GraphQL */ `
|
|
||||||
mutation cartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
|
|
||||||
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
|
|
||||||
cart {
|
|
||||||
...cartDetails
|
|
||||||
}
|
|
||||||
userErrors {
|
|
||||||
code
|
|
||||||
field
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${cartDetailsFragment}
|
|
||||||
`
|
|
@ -1,17 +0,0 @@
|
|||||||
import { cartDetailsFragment } from '../queries/get-cart-query'
|
|
||||||
|
|
||||||
export const cartLinesUpdateMutation = /* GraphQL */ `
|
|
||||||
mutation cartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
|
|
||||||
cartLinesUpdate(cartId: $cartId, lines: $lines) {
|
|
||||||
cart {
|
|
||||||
...cartDetails
|
|
||||||
}
|
|
||||||
userErrors {
|
|
||||||
code
|
|
||||||
field
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${cartDetailsFragment}
|
|
||||||
`
|
|
64
packages/shopify/src/utils/mutations/cart-mutations.ts
Normal file
64
packages/shopify/src/utils/mutations/cart-mutations.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import {
|
||||||
|
cartDetailsFragment,
|
||||||
|
userErrorsFragment,
|
||||||
|
} from '../fragments/cart-details-fragment'
|
||||||
|
|
||||||
|
export const cartCreateMutation = /* GraphQL */ `
|
||||||
|
mutation cartCreate($input: CartInput = {}) {
|
||||||
|
cartCreate(input: $input) {
|
||||||
|
cart {
|
||||||
|
...cartDetails
|
||||||
|
}
|
||||||
|
userErrors {
|
||||||
|
...userErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${cartDetailsFragment}
|
||||||
|
${userErrorsFragment}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const cartLinesAddMutation = /* GraphQL */ `
|
||||||
|
mutation cartLinesAdd($lines: [CartLineInput!]!, $cartId: ID!) {
|
||||||
|
cartLinesAdd(lines: $lines, cartId: $cartId) {
|
||||||
|
cart {
|
||||||
|
...cartDetails
|
||||||
|
}
|
||||||
|
userErrors {
|
||||||
|
...userErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${cartDetailsFragment}
|
||||||
|
${userErrorsFragment}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const cartLinesRemoveMutation = /* GraphQL */ `
|
||||||
|
mutation cartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
|
||||||
|
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
|
||||||
|
cart {
|
||||||
|
...cartDetails
|
||||||
|
}
|
||||||
|
userErrors {
|
||||||
|
...userErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${cartDetailsFragment}
|
||||||
|
${userErrorsFragment}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const cartLinesUpdateMutation = /* GraphQL */ `
|
||||||
|
mutation cartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
|
||||||
|
cartLinesUpdate(cartId: $cartId, lines: $lines) {
|
||||||
|
cart {
|
||||||
|
...cartDetails
|
||||||
|
}
|
||||||
|
userErrors {
|
||||||
|
...userErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${cartDetailsFragment}
|
||||||
|
${userErrorsFragment}
|
||||||
|
`
|
@ -1,15 +0,0 @@
|
|||||||
export const customerAccessTokenCreateMutation = /* GraphQL */ `
|
|
||||||
mutation customerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) {
|
|
||||||
customerAccessTokenCreate(input: $input) {
|
|
||||||
customerAccessToken {
|
|
||||||
accessToken
|
|
||||||
expiresAt
|
|
||||||
}
|
|
||||||
customerUserErrors {
|
|
||||||
code
|
|
||||||
field
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
@ -1,12 +0,0 @@
|
|||||||
export const customerAccessTokenDeleteMutation = /* GraphQL */ `
|
|
||||||
mutation customerAccessTokenDelete($customerAccessToken: String!) {
|
|
||||||
customerAccessTokenDelete(customerAccessToken: $customerAccessToken) {
|
|
||||||
deletedAccessToken
|
|
||||||
deletedCustomerAccessTokenId
|
|
||||||
userErrors {
|
|
||||||
field
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
@ -1,18 +0,0 @@
|
|||||||
export const customerActivateByUrlMutation = /* GraphQL */ `
|
|
||||||
mutation customerActivateByUrl($activationUrl: URL!, $password: String!) {
|
|
||||||
customerActivateByUrl(activationUrl: $activationUrl, password: $password) {
|
|
||||||
customer {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
customerAccessToken {
|
|
||||||
accessToken
|
|
||||||
expiresAt
|
|
||||||
}
|
|
||||||
customerUserErrors {
|
|
||||||
code
|
|
||||||
field
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
@ -1,18 +0,0 @@
|
|||||||
export const customerActivateMutation = /* GraphQL */ `
|
|
||||||
mutation customerActivate($id: ID!, $input: CustomerActivateInput!) {
|
|
||||||
customerActivate(id: $id, input: $input) {
|
|
||||||
customer {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
customerAccessToken {
|
|
||||||
accessToken
|
|
||||||
expiresAt
|
|
||||||
}
|
|
||||||
customerUserErrors {
|
|
||||||
code
|
|
||||||
field
|
|
||||||
message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
@ -1,14 +0,0 @@
|
|||||||
export const customerCreateMutation = /* GraphQL */ `
|
|
||||||
mutation customerCreate($input: CustomerCreateInput!) {
|
|
||||||
customerCreate(input: $input) {
|
|
||||||
customerUserErrors {
|
|
||||||
code
|
|
||||||
field
|
|
||||||
message
|
|
||||||
}
|
|
||||||
customer {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
65
packages/shopify/src/utils/mutations/customer-mutations.ts
Normal file
65
packages/shopify/src/utils/mutations/customer-mutations.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import {
|
||||||
|
customerUserErrorsFragment,
|
||||||
|
customerAccessTokenFragment,
|
||||||
|
} from '../fragments/customer-fragments'
|
||||||
|
|
||||||
|
export const customerActivateMutation = /* GraphQL */ `
|
||||||
|
mutation customerActivate($id: ID!, $input: CustomerActivateInput!) {
|
||||||
|
customerActivate(id: $id, input: $input) {
|
||||||
|
customer {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
...customerAccessToken
|
||||||
|
...customerUserErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${customerUserErrorsFragment}
|
||||||
|
${customerAccessTokenFragment}
|
||||||
|
`
|
||||||
|
export const customerActivateByUrlMutation = /* GraphQL */ `
|
||||||
|
mutation customerActivateByUrl($activationUrl: URL!, $password: String!) {
|
||||||
|
customerActivateByUrl(activationUrl: $activationUrl, password: $password) {
|
||||||
|
customer {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
...customerAccessToken
|
||||||
|
...customerUserErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${customerUserErrorsFragment}
|
||||||
|
${customerAccessTokenFragment}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const customerAccessTokenCreateMutation = /* GraphQL */ `
|
||||||
|
mutation customerAccessTokenCreate($input: CustomerAccessTokenCreateInput!) {
|
||||||
|
customerAccessTokenCreate(input: $input) {
|
||||||
|
...customerAccessToken
|
||||||
|
...customerUserErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${customerUserErrorsFragment}
|
||||||
|
${customerAccessTokenFragment}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const customerAccessTokenDeleteMutation = /* GraphQL */ `
|
||||||
|
mutation customerAccessTokenDelete($customerAccessToken: String!) {
|
||||||
|
customerAccessTokenDelete(customerAccessToken: $customerAccessToken) {
|
||||||
|
deletedAccessToken
|
||||||
|
deletedCustomerAccessTokenId
|
||||||
|
...customerUserErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${customerUserErrorsFragment}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const customerCreateMutation = /* GraphQL */ `
|
||||||
|
mutation customerCreate($input: CustomerCreateInput!) {
|
||||||
|
customerCreate(input: $input) {
|
||||||
|
...customerUserErrors
|
||||||
|
customer {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${customerUserErrorsFragment}
|
||||||
|
`
|
@ -1,9 +1,2 @@
|
|||||||
export { customerCreateMutation } from './customer-create'
|
export * from './cart-mutations'
|
||||||
export { cartCreateMutation } from './cart-create'
|
export * from './customer-mutations'
|
||||||
export { cartLinesAddMutation } from './cart-lines-item-add'
|
|
||||||
export { cartLinesUpdateMutation } from './cart-lines-item-update'
|
|
||||||
export { cartLinesRemoveMutation } from './cart-lines-item-remove'
|
|
||||||
export { customerAccessTokenCreateMutation } from './customer-access-token-create'
|
|
||||||
export { customerAccessTokenDeleteMutation } from './customer-access-token-delete'
|
|
||||||
export { customerActivateMutation } from './customer-activate'
|
|
||||||
export { customerActivateByUrlMutation } from './customer-activate-by-url'
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { Page } from '../types/page'
|
import type { Page } from '../types/page'
|
||||||
import type { Product } from '../types/product'
|
import type { Product, ProductPrice } from '../types/product'
|
||||||
import type { Cart, LineItem } from '../types/cart'
|
import type { Cart, LineItem } from '../types/cart'
|
||||||
import type { Category } from '../types/site'
|
import type { Category } from '../types/site'
|
||||||
|
|
||||||
@ -18,10 +18,14 @@ import {
|
|||||||
import { colorMap } from './colors'
|
import { colorMap } from './colors'
|
||||||
import { CommerceError } from '@vercel/commerce/utils/errors'
|
import { CommerceError } from '@vercel/commerce/utils/errors'
|
||||||
|
|
||||||
const money = ({ amount, currencyCode }: MoneyV2) => {
|
type MoneyProps = MoneyV2 & { retailPrice?: string }
|
||||||
|
|
||||||
|
const money = (money: MoneyProps): ProductPrice => {
|
||||||
|
const { amount, currencyCode, retailPrice } = money || { currencyCode: 'USD' }
|
||||||
return {
|
return {
|
||||||
value: +amount,
|
value: +amount,
|
||||||
currencyCode,
|
currencyCode,
|
||||||
|
...(retailPrice && { retailPrice: +retailPrice }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,46 +56,47 @@ const normalizeProductOption = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizeProductImages = ({ edges }: ImageConnection) =>
|
const normalizeProductImages = (images: ImageConnection) =>
|
||||||
edges?.map(({ node: { altText: alt, url, ...rest } }) => ({
|
images?.nodes?.map(({ altText: alt, url, ...rest }) => ({
|
||||||
...rest,
|
...rest,
|
||||||
url,
|
url,
|
||||||
alt,
|
alt,
|
||||||
}))
|
})) ?? []
|
||||||
|
|
||||||
const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
|
const normalizeProductVariants = (variants: ProductVariantConnection) => {
|
||||||
return edges?.map(
|
return (
|
||||||
({
|
variants?.nodes?.map(
|
||||||
node: {
|
({
|
||||||
id,
|
id,
|
||||||
selectedOptions,
|
selectedOptions,
|
||||||
sku,
|
sku,
|
||||||
title,
|
title,
|
||||||
priceV2,
|
priceV2,
|
||||||
|
image,
|
||||||
compareAtPriceV2,
|
compareAtPriceV2,
|
||||||
requiresShipping,
|
requiresShipping,
|
||||||
availableForSale,
|
availableForSale,
|
||||||
},
|
}) => {
|
||||||
}) => {
|
return {
|
||||||
return {
|
id,
|
||||||
id,
|
name: title,
|
||||||
name: title,
|
sku: sku ?? id,
|
||||||
sku: sku ?? id,
|
image,
|
||||||
price: +priceV2.amount,
|
price: money({ ...priceV2, retailPrice: compareAtPriceV2?.amount }),
|
||||||
listPrice: +compareAtPriceV2?.amount,
|
requiresShipping,
|
||||||
requiresShipping,
|
availableForSale,
|
||||||
availableForSale,
|
options: selectedOptions.map(({ name, value }: SelectedOption) => {
|
||||||
options: selectedOptions.map(({ name, value }: SelectedOption) => {
|
const options = normalizeProductOption({
|
||||||
const options = normalizeProductOption({
|
id,
|
||||||
id,
|
name,
|
||||||
name,
|
values: [value],
|
||||||
values: [value],
|
})
|
||||||
})
|
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}),
|
}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
) ?? []
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,20 +109,24 @@ export function normalizeProduct({
|
|||||||
description,
|
description,
|
||||||
descriptionHtml,
|
descriptionHtml,
|
||||||
handle,
|
handle,
|
||||||
priceRange,
|
|
||||||
options,
|
options,
|
||||||
metafields,
|
metafields,
|
||||||
...rest
|
...rest
|
||||||
}: ShopifyProduct): Product {
|
}: ShopifyProduct): Product {
|
||||||
|
const variant = variants?.nodes?.[0]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
vendor,
|
vendor,
|
||||||
path: `/${handle}`,
|
path: `/${handle}`,
|
||||||
slug: handle?.replace(/^\/+|\/+$/g, ''),
|
slug: handle?.replace(/^\/+|\/+$/g, ''),
|
||||||
price: money(priceRange?.minVariantPrice),
|
price: money({
|
||||||
images: normalizeProductImages(images),
|
...variant?.priceV2,
|
||||||
variants: variants ? normalizeProductVariants(variants) : [],
|
retailPrice: variant?.compareAtPriceV2?.amount,
|
||||||
|
}),
|
||||||
|
images: images ? normalizeProductImages(images) : [variant?.image],
|
||||||
|
variants: normalizeProductVariants(variants),
|
||||||
options: options
|
options: options
|
||||||
? options
|
? options
|
||||||
.filter((o) => o.name !== 'Title') // By default Shopify adds a 'Title' name when there's only one option. We don't need it. https://community.shopify.com/c/Shopify-APIs-SDKs/Adding-new-product-variant-is-automatically-adding-quot-Default/td-p/358095
|
.filter((o) => o.name !== 'Title') // By default Shopify adds a 'Title' name when there's only one option. We don't need it. https://community.shopify.com/c/Shopify-APIs-SDKs/Adding-new-product-variant-is-automatically-adding-quot-Default/td-p/358095
|
||||||
@ -148,7 +157,7 @@ function normalizeLineItem({
|
|||||||
},
|
},
|
||||||
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: variant?.product?.handle,
|
path: variant?.product?.handle,
|
||||||
discounts: [],
|
discounts: [],
|
||||||
|
@ -1,39 +1,4 @@
|
|||||||
export const productConnectionFragment = /* GraphQL */ `
|
import { productCardFragment } from '../fragments/product-card-fragment'
|
||||||
fragment productConnection on ProductConnection {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
hasPreviousPage
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
title
|
|
||||||
vendor
|
|
||||||
handle
|
|
||||||
priceRange {
|
|
||||||
minVariantPrice {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
images(first: 1) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
hasPreviousPage
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
url
|
|
||||||
altText
|
|
||||||
width
|
|
||||||
height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export const getAllProductsQuery = /* GraphQL */ `
|
export const getAllProductsQuery = /* GraphQL */ `
|
||||||
query getAllProducts(
|
query getAllProducts(
|
||||||
@ -48,9 +13,16 @@ export const getAllProductsQuery = /* GraphQL */ `
|
|||||||
reverse: $reverse
|
reverse: $reverse
|
||||||
query: $query
|
query: $query
|
||||||
) {
|
) {
|
||||||
...productConnection
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
hasPreviousPage
|
||||||
|
}
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
...productCard
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${productCardFragment}
|
||||||
${productConnectionFragment}
|
|
||||||
`
|
`
|
||||||
|
@ -1,77 +1,4 @@
|
|||||||
export const cartDetailsFragment = /* GraphQL */ `
|
import { cartDetailsFragment } from '../fragments/cart-details-fragment'
|
||||||
fragment cartDetails on Cart {
|
|
||||||
id
|
|
||||||
checkoutUrl
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
lines(first: 10) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
quantity
|
|
||||||
merchandise {
|
|
||||||
... on ProductVariant {
|
|
||||||
id
|
|
||||||
id
|
|
||||||
sku
|
|
||||||
title
|
|
||||||
selectedOptions {
|
|
||||||
name
|
|
||||||
value
|
|
||||||
}
|
|
||||||
image {
|
|
||||||
url
|
|
||||||
altText
|
|
||||||
width
|
|
||||||
height
|
|
||||||
}
|
|
||||||
priceV2 {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
compareAtPriceV2 {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
product {
|
|
||||||
title
|
|
||||||
handle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attributes {
|
|
||||||
key
|
|
||||||
value
|
|
||||||
}
|
|
||||||
buyerIdentity {
|
|
||||||
email
|
|
||||||
customer {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
estimatedCost {
|
|
||||||
totalAmount {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
subtotalAmount {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
totalTaxAmount {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
totalDutyAmount {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export const getCartQuery = /* GraphQL */ `
|
export const getCartQuery = /* GraphQL */ `
|
||||||
query getCart($cartId: ID!) {
|
query getCart($cartId: ID!) {
|
||||||
|
@ -1,69 +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 {
|
|
||||||
url
|
|
||||||
altText
|
|
||||||
width
|
|
||||||
height
|
|
||||||
}
|
|
||||||
priceV2 {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
compareAtPriceV2 {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
product {
|
|
||||||
handle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
quantity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export const getCheckoutQuery = /* GraphQL */ `
|
|
||||||
query getCheckout($checkoutId: ID!) {
|
|
||||||
node(id: $checkoutId) {
|
|
||||||
...checkoutDetails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
${checkoutDetailsFragment}
|
|
||||||
`
|
|
@ -1,4 +1,4 @@
|
|||||||
import { productConnectionFragment } from './get-all-products-query'
|
import { productCardFragment } from '../fragments/product-card-fragment'
|
||||||
|
|
||||||
export const getCollectionProductsQuery = /* GraphQL */ `
|
export const getCollectionProductsQuery = /* GraphQL */ `
|
||||||
query getProductsFromCollection(
|
query getProductsFromCollection(
|
||||||
@ -11,10 +11,20 @@ export const getCollectionProductsQuery = /* GraphQL */ `
|
|||||||
id
|
id
|
||||||
... on Collection {
|
... on Collection {
|
||||||
products(first: $first, sortKey: $sortKey, reverse: $reverse) {
|
products(first: $first, sortKey: $sortKey, reverse: $reverse) {
|
||||||
...productConnection
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
hasPreviousPage
|
||||||
|
startCursor
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
...productCard
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${productConnectionFragment}
|
${productCardFragment}
|
||||||
`
|
`
|
||||||
|
@ -14,57 +14,56 @@ export const getProductQuery = /* GraphQL */ `
|
|||||||
name
|
name
|
||||||
values
|
values
|
||||||
}
|
}
|
||||||
priceRange {
|
seo {
|
||||||
maxVariantPrice {
|
description
|
||||||
amount
|
title
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
minVariantPrice {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
variants(first: 250) {
|
variants(first: 100) {
|
||||||
pageInfo {
|
nodes {
|
||||||
hasNextPage
|
id
|
||||||
hasPreviousPage
|
title
|
||||||
}
|
sku
|
||||||
edges {
|
availableForSale
|
||||||
node {
|
requiresShipping
|
||||||
|
image {
|
||||||
id
|
id
|
||||||
title
|
|
||||||
sku
|
|
||||||
availableForSale
|
|
||||||
requiresShipping
|
|
||||||
selectedOptions {
|
|
||||||
name
|
|
||||||
value
|
|
||||||
}
|
|
||||||
priceV2 {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
compareAtPriceV2 {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
images(first: 250) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
hasPreviousPage
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
url
|
|
||||||
altText
|
altText
|
||||||
|
url
|
||||||
width
|
width
|
||||||
height
|
height
|
||||||
}
|
}
|
||||||
|
selectedOptions {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
priceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
|
compareAtPriceV2 {
|
||||||
|
amount
|
||||||
|
currencyCode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
images(first: 15) {
|
||||||
|
nodes {
|
||||||
|
url
|
||||||
|
altText
|
||||||
|
width
|
||||||
|
height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shop {
|
||||||
|
shippingPolicy {
|
||||||
|
body
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
refundPolicy {
|
||||||
|
body
|
||||||
|
handle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
export const getSiteInfoQuery = /* GraphQL */ `
|
|
||||||
query getSiteInfo {
|
|
||||||
shop {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
@ -7,5 +7,4 @@ export { getCollectionProductsQuery } from './get-collection-products-query'
|
|||||||
export { getAllPagesQuery } from './get-all-pages-query'
|
export { getAllPagesQuery } from './get-all-pages-query'
|
||||||
export { getPageQuery } from './get-page-query'
|
export { getPageQuery } from './get-page-query'
|
||||||
export { getCustomerQuery } from './get-customer-query'
|
export { getCustomerQuery } from './get-customer-query'
|
||||||
export { getSiteInfoQuery } from './get-site-info-query'
|
|
||||||
export { getCartQuery } from './get-cart-query'
|
export { getCartQuery } from './get-cart-query'
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
import Cookies from 'js-cookie'
|
|
||||||
|
|
||||||
import { SHOPIFY_CART_URL_COOKIE, SHOPIFY_COOKIE_EXPIRE } from '../const'
|
|
||||||
|
|
||||||
export const setCartUrlCookie = (cartUrl: string) => {
|
|
||||||
if (cartUrl) {
|
|
||||||
const oldCookie = Cookies.get(SHOPIFY_CART_URL_COOKIE)
|
|
||||||
if (oldCookie !== cartUrl) {
|
|
||||||
Cookies.set(SHOPIFY_CART_URL_COOKIE, cartUrl, {
|
|
||||||
expires: SHOPIFY_COOKIE_EXPIRE,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user