mirror of
https://github.com/vercel/commerce.git
synced 2025-06-19 21:51:21 +00:00
Add Shopify related products
This commit is contained in:
parent
74dda1aa55
commit
396a708e23
@ -61,29 +61,31 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
|
||||
<ProductSidebar product={product} className={s.sidebar} />
|
||||
</div>
|
||||
<hr className="mt-7 border-accent-2" />
|
||||
<section className="py-12 px-6 mb-10">
|
||||
<Text variant="sectionHeading">Related Products</Text>
|
||||
<div className={s.relatedProductsGrid}>
|
||||
{relatedProducts.map((p) => (
|
||||
<div
|
||||
key={p.path}
|
||||
className="animated fadeIn bg-accent-0 border border-accent-2"
|
||||
>
|
||||
<ProductCard
|
||||
noNameTag
|
||||
product={p}
|
||||
{relatedProducts.length && (
|
||||
<section className="py-12 px-6 mb-10">
|
||||
<Text variant="sectionHeading">Related Products</Text>
|
||||
<div className={s.relatedProductsGrid}>
|
||||
{relatedProducts.map((p) => (
|
||||
<div
|
||||
key={p.path}
|
||||
variant="simple"
|
||||
className="animated fadeIn"
|
||||
imgProps={{
|
||||
width: 300,
|
||||
height: 300,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
className="animated fadeIn bg-accent-0 border border-accent-2"
|
||||
>
|
||||
<ProductCard
|
||||
noNameTag
|
||||
product={p}
|
||||
key={p.path}
|
||||
variant="simple"
|
||||
className="animated fadeIn"
|
||||
imgProps={{
|
||||
width: 300,
|
||||
height: 300,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</Container>
|
||||
<NextSeo
|
||||
title={product.name}
|
||||
|
@ -7,10 +7,11 @@ import type {
|
||||
GetAllProductPathsOperation,
|
||||
GetAllProductsOperation,
|
||||
GetProductOperation,
|
||||
GetRelatedProductsOperation,
|
||||
} from '../types/product'
|
||||
import type { APIProvider, CommerceAPI } from '.'
|
||||
|
||||
const noop = () => {
|
||||
const noop = (_props?: any) => {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
@ -22,6 +23,7 @@ export const OPERATIONS = [
|
||||
'getCustomerWishlist',
|
||||
'getAllProductPaths',
|
||||
'getAllProducts',
|
||||
'getRelatedProducts',
|
||||
'getProduct',
|
||||
] as const
|
||||
|
||||
@ -139,6 +141,22 @@ export type Operations<P extends APIProvider> = {
|
||||
): Promise<T['data']>
|
||||
}
|
||||
|
||||
getRelatedProducts: {
|
||||
<T extends GetRelatedProductsOperation>(opts: {
|
||||
variables: T['variables']
|
||||
config?: P['config']
|
||||
preview?: boolean
|
||||
}): Promise<T['data']>
|
||||
|
||||
<T extends GetAllProductsOperation>(
|
||||
opts: {
|
||||
variables: T['variables']
|
||||
config?: P['config']
|
||||
preview?: boolean
|
||||
} & OperationOptions
|
||||
): Promise<T['data']>
|
||||
}
|
||||
|
||||
getProduct: {
|
||||
<T extends GetProductOperation>(opts: {
|
||||
variables: T['variables']
|
||||
|
@ -93,6 +93,16 @@ export type GetAllProductsOperation<T extends ProductTypes = ProductTypes> = {
|
||||
}
|
||||
}
|
||||
|
||||
export type GetRelatedProductsOperation<
|
||||
T extends ProductTypes = ProductTypes
|
||||
> = {
|
||||
data: { products: T['product'][] }
|
||||
variables: {
|
||||
productId: string
|
||||
first?: number
|
||||
}
|
||||
}
|
||||
|
||||
export type GetProductOperation<T extends ProductTypes = ProductTypes> = {
|
||||
data: { product?: T['product'] }
|
||||
variables: { path: string; slug?: never } | { path?: never; slug: string }
|
||||
|
74
framework/shopify/api/operations/get-related-products.ts
Normal file
74
framework/shopify/api/operations/get-related-products.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import type {
|
||||
OperationContext,
|
||||
OperationOptions,
|
||||
} from '@commerce/api/operations'
|
||||
import { GetRelatedProductsOperation } from '../../types/product'
|
||||
import {
|
||||
GetRelatedProductsQuery,
|
||||
GetRelatedProductsQueryVariables,
|
||||
Product as ShopifyProduct,
|
||||
} from '../../schema'
|
||||
import type { ShopifyConfig, Provider } from '..'
|
||||
import getRelatedProductsQuery from '../../utils/queries/get-related-products-query'
|
||||
import { normalizeProduct } from '../../utils'
|
||||
|
||||
export default function getRelatedProductsOperation({
|
||||
commerce,
|
||||
}: OperationContext<Provider>) {
|
||||
async function getRelatedProductsOperation<
|
||||
T extends GetRelatedProductsOperation
|
||||
>(opts: {
|
||||
variables: T['variables']
|
||||
config?: Partial<ShopifyConfig>
|
||||
preview?: boolean
|
||||
}): Promise<T['data']>
|
||||
|
||||
async function getRelatedProductsOperation<
|
||||
T extends GetRelatedProductsOperation
|
||||
>(
|
||||
opts: {
|
||||
variables: T['variables']
|
||||
config?: Partial<ShopifyConfig>
|
||||
preview?: boolean
|
||||
} & OperationOptions
|
||||
): Promise<T['data']>
|
||||
|
||||
async function getRelatedProductsOperation<
|
||||
T extends GetRelatedProductsOperation
|
||||
>({
|
||||
query = getRelatedProductsQuery,
|
||||
variables,
|
||||
config,
|
||||
}: {
|
||||
query?: string
|
||||
variables: T['variables']
|
||||
config?: Partial<ShopifyConfig>
|
||||
preview?: boolean
|
||||
}): Promise<T['data']> {
|
||||
const { fetch, locale } = commerce.getConfig(config)
|
||||
|
||||
const { data } = await fetch<
|
||||
GetRelatedProductsQuery,
|
||||
GetRelatedProductsQueryVariables
|
||||
>(
|
||||
query,
|
||||
{ variables },
|
||||
{
|
||||
...(locale && {
|
||||
headers: {
|
||||
'Accept-Language': locale,
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
products:
|
||||
data.productRecommendations
|
||||
?.map((product) => normalizeProduct(product as ShopifyProduct))
|
||||
.splice(0, variables?.first || 4) ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
return getRelatedProductsOperation
|
||||
}
|
@ -2,6 +2,7 @@ export { default as getAllPages } from './get-all-pages'
|
||||
export { default as getPage } from './get-page'
|
||||
export { default as getAllProducts } from './get-all-products'
|
||||
export { default as getAllProductPaths } from './get-all-product-paths'
|
||||
export { default as getRelatedProducts } from './get-related-products'
|
||||
export { default as getProduct } from './get-product'
|
||||
export { default as getSiteInfo } from './get-site-info'
|
||||
export { default as login } from './login'
|
||||
|
221
framework/shopify/schema.d.ts
vendored
221
framework/shopify/schema.d.ts
vendored
@ -2635,7 +2635,7 @@ export type FulfillmentTrackingInfo = {
|
||||
|
||||
/** Represents information about the metafields associated to the specified resource. */
|
||||
export type HasMetafields = {
|
||||
/** The metafield associated with the resource. */
|
||||
/** Returns a metafield found by namespace and key. */
|
||||
metafield?: Maybe<Metafield>
|
||||
/** A paginated list of metafields associated with the resource. */
|
||||
metafields: MetafieldConnection
|
||||
@ -3908,7 +3908,7 @@ export type Product = Node &
|
||||
images: ImageConnection
|
||||
/** The media associated with the product. */
|
||||
media: MediaConnection
|
||||
/** The metafield associated with the resource. */
|
||||
/** Returns a metafield found by namespace and key. */
|
||||
metafield?: Maybe<Metafield>
|
||||
/** A paginated list of metafields associated with the resource. */
|
||||
metafields: MetafieldConnection
|
||||
@ -4235,7 +4235,7 @@ export type ProductVariant = Node &
|
||||
id: Scalars['ID']
|
||||
/** Image associated with the product variant. This field falls back to the product image if no image is available. */
|
||||
image?: Maybe<Image>
|
||||
/** The metafield associated with the resource. */
|
||||
/** Returns a metafield found by namespace and key. */
|
||||
metafield?: Maybe<Metafield>
|
||||
/** A paginated list of metafields associated with the resource. */
|
||||
metafields: MetafieldConnection
|
||||
@ -5265,6 +5265,32 @@ export type GetAllProductPathsQuery = { __typename?: 'QueryRoot' } & {
|
||||
}
|
||||
}
|
||||
|
||||
export type ListProductDetailsFragment = { __typename?: 'Product' } & Pick<
|
||||
Product,
|
||||
'id' | 'title' | 'vendor' | 'handle'
|
||||
> & {
|
||||
priceRange: { __typename?: 'ProductPriceRange' } & {
|
||||
minVariantPrice: { __typename?: 'MoneyV2' } & Pick<
|
||||
MoneyV2,
|
||||
'amount' | 'currencyCode'
|
||||
>
|
||||
}
|
||||
images: { __typename?: 'ImageConnection' } & {
|
||||
pageInfo: { __typename?: 'PageInfo' } & Pick<
|
||||
PageInfo,
|
||||
'hasNextPage' | 'hasPreviousPage'
|
||||
>
|
||||
edges: Array<
|
||||
{ __typename?: 'ImageEdge' } & {
|
||||
node: { __typename?: 'Image' } & Pick<
|
||||
Image,
|
||||
'originalSrc' | 'altText' | 'width' | 'height'
|
||||
>
|
||||
}
|
||||
>
|
||||
}
|
||||
}
|
||||
|
||||
export type ProductConnectionFragment = { __typename?: 'ProductConnection' } & {
|
||||
pageInfo: { __typename?: 'PageInfo' } & Pick<
|
||||
PageInfo,
|
||||
@ -5272,31 +5298,7 @@ export type ProductConnectionFragment = { __typename?: 'ProductConnection' } & {
|
||||
>
|
||||
edges: Array<
|
||||
{ __typename?: 'ProductEdge' } & {
|
||||
node: { __typename?: 'Product' } & Pick<
|
||||
Product,
|
||||
'id' | 'title' | 'vendor' | 'handle'
|
||||
> & {
|
||||
priceRange: { __typename?: 'ProductPriceRange' } & {
|
||||
minVariantPrice: { __typename?: 'MoneyV2' } & Pick<
|
||||
MoneyV2,
|
||||
'amount' | 'currencyCode'
|
||||
>
|
||||
}
|
||||
images: { __typename?: 'ImageConnection' } & {
|
||||
pageInfo: { __typename?: 'PageInfo' } & Pick<
|
||||
PageInfo,
|
||||
'hasNextPage' | 'hasPreviousPage'
|
||||
>
|
||||
edges: Array<
|
||||
{ __typename?: 'ImageEdge' } & {
|
||||
node: { __typename?: 'Image' } & Pick<
|
||||
Image,
|
||||
'originalSrc' | 'altText' | 'width' | 'height'
|
||||
>
|
||||
}
|
||||
>
|
||||
}
|
||||
}
|
||||
node: { __typename?: 'Product' } & ListProductDetailsFragment
|
||||
}
|
||||
>
|
||||
}
|
||||
@ -5344,6 +5346,12 @@ export type CheckoutDetailsFragment = { __typename?: 'Checkout' } & Pick<
|
||||
ProductVariant,
|
||||
'id' | 'sku' | 'title'
|
||||
> & {
|
||||
selectedOptions: Array<
|
||||
{ __typename?: 'SelectedOption' } & Pick<
|
||||
SelectedOption,
|
||||
'name' | 'value'
|
||||
>
|
||||
>
|
||||
image?: Maybe<
|
||||
{ __typename?: 'Image' } & Pick<
|
||||
Image,
|
||||
@ -5498,84 +5506,95 @@ export type GetPageQuery = { __typename?: 'QueryRoot' } & {
|
||||
>
|
||||
}
|
||||
|
||||
export type ProductDetailsFragment = { __typename?: 'Product' } & Pick<
|
||||
Product,
|
||||
| 'id'
|
||||
| 'handle'
|
||||
| 'availableForSale'
|
||||
| 'title'
|
||||
| 'productType'
|
||||
| 'vendor'
|
||||
| 'description'
|
||||
| 'descriptionHtml'
|
||||
> & {
|
||||
options: Array<
|
||||
{ __typename?: 'ProductOption' } & Pick<
|
||||
ProductOption,
|
||||
'id' | 'name' | 'values'
|
||||
>
|
||||
>
|
||||
priceRange: { __typename?: 'ProductPriceRange' } & {
|
||||
maxVariantPrice: { __typename?: 'MoneyV2' } & Pick<
|
||||
MoneyV2,
|
||||
'amount' | 'currencyCode'
|
||||
>
|
||||
minVariantPrice: { __typename?: 'MoneyV2' } & Pick<
|
||||
MoneyV2,
|
||||
'amount' | 'currencyCode'
|
||||
>
|
||||
}
|
||||
variants: { __typename?: 'ProductVariantConnection' } & {
|
||||
pageInfo: { __typename?: 'PageInfo' } & Pick<
|
||||
PageInfo,
|
||||
'hasNextPage' | 'hasPreviousPage'
|
||||
>
|
||||
edges: Array<
|
||||
{ __typename?: 'ProductVariantEdge' } & {
|
||||
node: { __typename?: 'ProductVariant' } & Pick<
|
||||
ProductVariant,
|
||||
'id' | 'title' | 'sku' | 'availableForSale' | 'requiresShipping'
|
||||
> & {
|
||||
selectedOptions: Array<
|
||||
{ __typename?: 'SelectedOption' } & Pick<
|
||||
SelectedOption,
|
||||
'name' | 'value'
|
||||
>
|
||||
>
|
||||
priceV2: { __typename?: 'MoneyV2' } & Pick<
|
||||
MoneyV2,
|
||||
'amount' | 'currencyCode'
|
||||
>
|
||||
compareAtPriceV2?: Maybe<
|
||||
{ __typename?: 'MoneyV2' } & Pick<
|
||||
MoneyV2,
|
||||
'amount' | 'currencyCode'
|
||||
>
|
||||
>
|
||||
}
|
||||
}
|
||||
>
|
||||
}
|
||||
images: { __typename?: 'ImageConnection' } & {
|
||||
pageInfo: { __typename?: 'PageInfo' } & Pick<
|
||||
PageInfo,
|
||||
'hasNextPage' | 'hasPreviousPage'
|
||||
>
|
||||
edges: Array<
|
||||
{ __typename?: 'ImageEdge' } & {
|
||||
node: { __typename?: 'Image' } & Pick<
|
||||
Image,
|
||||
'originalSrc' | 'altText' | 'width' | 'height'
|
||||
>
|
||||
}
|
||||
>
|
||||
}
|
||||
}
|
||||
|
||||
export type GetProductBySlugQueryVariables = Exact<{
|
||||
slug: Scalars['String']
|
||||
}>
|
||||
|
||||
export type GetProductBySlugQuery = { __typename?: 'QueryRoot' } & {
|
||||
productByHandle?: Maybe<
|
||||
{ __typename?: 'Product' } & Pick<
|
||||
Product,
|
||||
| 'id'
|
||||
| 'handle'
|
||||
| 'title'
|
||||
| 'productType'
|
||||
| 'vendor'
|
||||
| 'description'
|
||||
| 'descriptionHtml'
|
||||
> & {
|
||||
options: Array<
|
||||
{ __typename?: 'ProductOption' } & Pick<
|
||||
ProductOption,
|
||||
'id' | 'name' | 'values'
|
||||
>
|
||||
>
|
||||
priceRange: { __typename?: 'ProductPriceRange' } & {
|
||||
maxVariantPrice: { __typename?: 'MoneyV2' } & Pick<
|
||||
MoneyV2,
|
||||
'amount' | 'currencyCode'
|
||||
>
|
||||
minVariantPrice: { __typename?: 'MoneyV2' } & Pick<
|
||||
MoneyV2,
|
||||
'amount' | 'currencyCode'
|
||||
>
|
||||
}
|
||||
variants: { __typename?: 'ProductVariantConnection' } & {
|
||||
pageInfo: { __typename?: 'PageInfo' } & Pick<
|
||||
PageInfo,
|
||||
'hasNextPage' | 'hasPreviousPage'
|
||||
>
|
||||
edges: Array<
|
||||
{ __typename?: 'ProductVariantEdge' } & {
|
||||
node: { __typename?: 'ProductVariant' } & Pick<
|
||||
ProductVariant,
|
||||
'id' | 'title' | 'sku'
|
||||
> & {
|
||||
selectedOptions: Array<
|
||||
{ __typename?: 'SelectedOption' } & Pick<
|
||||
SelectedOption,
|
||||
'name' | 'value'
|
||||
>
|
||||
>
|
||||
priceV2: { __typename?: 'MoneyV2' } & Pick<
|
||||
MoneyV2,
|
||||
'amount' | 'currencyCode'
|
||||
>
|
||||
compareAtPriceV2?: Maybe<
|
||||
{ __typename?: 'MoneyV2' } & Pick<
|
||||
MoneyV2,
|
||||
'amount' | 'currencyCode'
|
||||
>
|
||||
>
|
||||
}
|
||||
}
|
||||
>
|
||||
}
|
||||
images: { __typename?: 'ImageConnection' } & {
|
||||
pageInfo: { __typename?: 'PageInfo' } & Pick<
|
||||
PageInfo,
|
||||
'hasNextPage' | 'hasPreviousPage'
|
||||
>
|
||||
edges: Array<
|
||||
{ __typename?: 'ImageEdge' } & {
|
||||
node: { __typename?: 'Image' } & Pick<
|
||||
Image,
|
||||
'originalSrc' | 'altText' | 'width' | 'height'
|
||||
>
|
||||
}
|
||||
>
|
||||
}
|
||||
}
|
||||
productByHandle?: Maybe<{ __typename?: 'Product' } & ProductDetailsFragment>
|
||||
}
|
||||
|
||||
export type GetRelatedProductsQueryVariables = Exact<{
|
||||
productId: Scalars['ID']
|
||||
}>
|
||||
|
||||
export type GetRelatedProductsQuery = { __typename?: 'QueryRoot' } & {
|
||||
productRecommendations?: Maybe<
|
||||
Array<{ __typename?: 'Product' } & ListProductDetailsFragment>
|
||||
>
|
||||
}
|
||||
|
||||
|
@ -13,16 +13,6 @@ directive @accessRestricted(
|
||||
reason: String = null
|
||||
) on FIELD_DEFINITION | OBJECT
|
||||
|
||||
"""
|
||||
Contextualize data.
|
||||
"""
|
||||
directive @inContext(
|
||||
"""
|
||||
The country code for context.
|
||||
"""
|
||||
country: CountryCode!
|
||||
) on QUERY | MUTATION
|
||||
|
||||
"""
|
||||
A version of the API.
|
||||
"""
|
||||
@ -829,7 +819,7 @@ input CheckoutAttributesUpdateInput {
|
||||
The required attributes are city, province, and country.
|
||||
Full validation of the addresses is still done at complete time.
|
||||
"""
|
||||
allowPartialAddresses: Boolean
|
||||
allowPartialAddresses: Boolean = false
|
||||
}
|
||||
|
||||
"""
|
||||
@ -872,7 +862,7 @@ input CheckoutAttributesUpdateV2Input {
|
||||
The required attributes are city, province, and country.
|
||||
Full validation of the addresses is still done at complete time.
|
||||
"""
|
||||
allowPartialAddresses: Boolean
|
||||
allowPartialAddresses: Boolean = false
|
||||
}
|
||||
|
||||
"""
|
||||
@ -3391,7 +3381,7 @@ input CreditCardPaymentInput {
|
||||
"""
|
||||
Executes the payment in test mode if possible. Defaults to `false`.
|
||||
"""
|
||||
test: Boolean
|
||||
test: Boolean = false
|
||||
}
|
||||
|
||||
"""
|
||||
@ -3422,7 +3412,7 @@ input CreditCardPaymentInputV2 {
|
||||
"""
|
||||
Executes the payment in test mode if possible. Defaults to `false`.
|
||||
"""
|
||||
test: Boolean
|
||||
test: Boolean = false
|
||||
}
|
||||
|
||||
"""
|
||||
@ -5325,7 +5315,7 @@ Represents information about the metafields associated to the specified resource
|
||||
"""
|
||||
interface HasMetafields {
|
||||
"""
|
||||
The metafield associated with the resource.
|
||||
Returns a metafield found by namespace and key.
|
||||
"""
|
||||
metafield(
|
||||
"""
|
||||
@ -7648,7 +7638,7 @@ type Product implements Node & HasMetafields {
|
||||
): MediaConnection!
|
||||
|
||||
"""
|
||||
The metafield associated with the resource.
|
||||
Returns a metafield found by namespace and key.
|
||||
"""
|
||||
metafield(
|
||||
"""
|
||||
@ -8150,7 +8140,7 @@ type ProductVariant implements Node & HasMetafields {
|
||||
): Image
|
||||
|
||||
"""
|
||||
The metafield associated with the resource.
|
||||
Returns a metafield found by namespace and key.
|
||||
"""
|
||||
metafield(
|
||||
"""
|
||||
@ -9298,7 +9288,7 @@ input TokenizedPaymentInput {
|
||||
"""
|
||||
Executes the payment in test mode if possible. Defaults to `false`.
|
||||
"""
|
||||
test: Boolean
|
||||
test: Boolean = false
|
||||
|
||||
"""
|
||||
Public Hash Key used for AndroidPay payments only.
|
||||
@ -9334,7 +9324,7 @@ input TokenizedPaymentInputV2 {
|
||||
"""
|
||||
Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`.
|
||||
"""
|
||||
test: Boolean
|
||||
test: Boolean = false
|
||||
|
||||
"""
|
||||
Public Hash Key used for AndroidPay payments only.
|
||||
@ -9375,7 +9365,7 @@ input TokenizedPaymentInputV3 {
|
||||
"""
|
||||
Whether to execute the payment in test mode, if possible. Test mode is not supported in production stores. Defaults to `false`.
|
||||
"""
|
||||
test: Boolean
|
||||
test: Boolean = false
|
||||
|
||||
"""
|
||||
Public Hash Key used for AndroidPay payments only.
|
||||
|
@ -1,3 +1,32 @@
|
||||
export const listProductDetailsFragment = /* GraphQL */ `
|
||||
fragment listProductDetails on Product {
|
||||
id
|
||||
title
|
||||
vendor
|
||||
handle
|
||||
priceRange {
|
||||
minVariantPrice {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
}
|
||||
images(first: 1) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
originalSrc
|
||||
altText
|
||||
width
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const productConnectionFragment = /* GraphQL */ `
|
||||
fragment productConnection on ProductConnection {
|
||||
pageInfo {
|
||||
@ -6,33 +35,11 @@ export const productConnectionFragment = /* GraphQL */ `
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
vendor
|
||||
handle
|
||||
priceRange {
|
||||
minVariantPrice {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
}
|
||||
images(first: 1) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
originalSrc
|
||||
altText
|
||||
width
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
...listProductDetails
|
||||
}
|
||||
}
|
||||
}
|
||||
${listProductDetailsFragment}
|
||||
`
|
||||
|
||||
const getAllProductsQuery = /* GraphQL */ `
|
||||
|
@ -1,72 +1,79 @@
|
||||
const getProductQuery = /* GraphQL */ `
|
||||
query getProductBySlug($slug: String!) {
|
||||
productByHandle(handle: $slug) {
|
||||
export const productDetailsFragment = /* GraphQL */ `
|
||||
fragment productDetails on Product {
|
||||
id
|
||||
handle
|
||||
availableForSale
|
||||
title
|
||||
productType
|
||||
vendor
|
||||
description
|
||||
descriptionHtml
|
||||
options {
|
||||
id
|
||||
handle
|
||||
availableForSale
|
||||
title
|
||||
productType
|
||||
vendor
|
||||
description
|
||||
descriptionHtml
|
||||
options {
|
||||
id
|
||||
name
|
||||
values
|
||||
name
|
||||
values
|
||||
}
|
||||
priceRange {
|
||||
maxVariantPrice {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
priceRange {
|
||||
maxVariantPrice {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
minVariantPrice {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
minVariantPrice {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
variants(first: 250) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
sku
|
||||
availableForSale
|
||||
requiresShipping
|
||||
selectedOptions {
|
||||
name
|
||||
value
|
||||
}
|
||||
priceV2 {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
compareAtPriceV2 {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
}
|
||||
variants(first: 250) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
title
|
||||
sku
|
||||
availableForSale
|
||||
requiresShipping
|
||||
selectedOptions {
|
||||
name
|
||||
value
|
||||
}
|
||||
priceV2 {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
compareAtPriceV2 {
|
||||
amount
|
||||
currencyCode
|
||||
}
|
||||
}
|
||||
}
|
||||
images(first: 250) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
originalSrc
|
||||
altText
|
||||
width
|
||||
height
|
||||
}
|
||||
}
|
||||
images(first: 250) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
originalSrc
|
||||
altText
|
||||
width
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const getProductQuery = /* GraphQL */ `
|
||||
query getProductBySlug($slug: String!) {
|
||||
productByHandle(handle: $slug) {
|
||||
...productDetails
|
||||
}
|
||||
}
|
||||
${productDetailsFragment}
|
||||
`
|
||||
|
||||
export default getProductQuery
|
||||
|
@ -0,0 +1,11 @@
|
||||
import { listProductDetailsFragment } from './get-all-products-query'
|
||||
|
||||
const getRelatedProductsQuery = /* GraphQL */ `
|
||||
query getRelatedProducts($productId: ID!) {
|
||||
productRecommendations(productId: $productId) {
|
||||
...listProductDetails
|
||||
}
|
||||
}
|
||||
${listProductDetailsFragment}
|
||||
`
|
||||
export default getRelatedProductsQuery
|
@ -28,15 +28,27 @@ export async function getStaticProps({
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
|
||||
const { pages } = await pagesPromise
|
||||
const { categories } = await siteInfoPromise
|
||||
const { product } = await productPromise
|
||||
const { products: relatedProducts } = await allProductsPromise
|
||||
|
||||
if (!product) {
|
||||
throw new Error(`Product with slug '${params!.slug}' not found`)
|
||||
}
|
||||
|
||||
const relatedProductsPromise = commerce.getRelatedProducts({
|
||||
variables: { productId: product.id, first: 4 },
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
|
||||
// Temporary conditional query
|
||||
const { products: relatedProducts } =
|
||||
process.env.COMMERCE_PROVIDER === 'shopify'
|
||||
? await relatedProductsPromise
|
||||
: await allProductsPromise
|
||||
|
||||
return {
|
||||
props: {
|
||||
pages,
|
||||
|
Loading…
x
Reference in New Issue
Block a user