mirror of
https://github.com/vercel/commerce.git
synced 2025-06-20 06:01: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} />
|
<ProductSidebar product={product} className={s.sidebar} />
|
||||||
</div>
|
</div>
|
||||||
<hr className="mt-7 border-accent-2" />
|
<hr className="mt-7 border-accent-2" />
|
||||||
<section className="py-12 px-6 mb-10">
|
{relatedProducts.length && (
|
||||||
<Text variant="sectionHeading">Related Products</Text>
|
<section className="py-12 px-6 mb-10">
|
||||||
<div className={s.relatedProductsGrid}>
|
<Text variant="sectionHeading">Related Products</Text>
|
||||||
{relatedProducts.map((p) => (
|
<div className={s.relatedProductsGrid}>
|
||||||
<div
|
{relatedProducts.map((p) => (
|
||||||
key={p.path}
|
<div
|
||||||
className="animated fadeIn bg-accent-0 border border-accent-2"
|
|
||||||
>
|
|
||||||
<ProductCard
|
|
||||||
noNameTag
|
|
||||||
product={p}
|
|
||||||
key={p.path}
|
key={p.path}
|
||||||
variant="simple"
|
className="animated fadeIn bg-accent-0 border border-accent-2"
|
||||||
className="animated fadeIn"
|
>
|
||||||
imgProps={{
|
<ProductCard
|
||||||
width: 300,
|
noNameTag
|
||||||
height: 300,
|
product={p}
|
||||||
}}
|
key={p.path}
|
||||||
/>
|
variant="simple"
|
||||||
</div>
|
className="animated fadeIn"
|
||||||
))}
|
imgProps={{
|
||||||
</div>
|
width: 300,
|
||||||
</section>
|
height: 300,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
<NextSeo
|
<NextSeo
|
||||||
title={product.name}
|
title={product.name}
|
||||||
|
@ -7,10 +7,11 @@ import type {
|
|||||||
GetAllProductPathsOperation,
|
GetAllProductPathsOperation,
|
||||||
GetAllProductsOperation,
|
GetAllProductsOperation,
|
||||||
GetProductOperation,
|
GetProductOperation,
|
||||||
|
GetRelatedProductsOperation,
|
||||||
} from '../types/product'
|
} from '../types/product'
|
||||||
import type { APIProvider, CommerceAPI } from '.'
|
import type { APIProvider, CommerceAPI } from '.'
|
||||||
|
|
||||||
const noop = () => {
|
const noop = (_props?: any) => {
|
||||||
throw new Error('Not implemented')
|
throw new Error('Not implemented')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ export const OPERATIONS = [
|
|||||||
'getCustomerWishlist',
|
'getCustomerWishlist',
|
||||||
'getAllProductPaths',
|
'getAllProductPaths',
|
||||||
'getAllProducts',
|
'getAllProducts',
|
||||||
|
'getRelatedProducts',
|
||||||
'getProduct',
|
'getProduct',
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
@ -139,6 +141,22 @@ export type Operations<P extends APIProvider> = {
|
|||||||
): Promise<T['data']>
|
): 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: {
|
getProduct: {
|
||||||
<T extends GetProductOperation>(opts: {
|
<T extends GetProductOperation>(opts: {
|
||||||
variables: T['variables']
|
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> = {
|
export type GetProductOperation<T extends ProductTypes = ProductTypes> = {
|
||||||
data: { product?: T['product'] }
|
data: { product?: T['product'] }
|
||||||
variables: { path: string; slug?: never } | { path?: never; slug: string }
|
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 getPage } from './get-page'
|
||||||
export { default as getAllProducts } from './get-all-products'
|
export { default as getAllProducts } from './get-all-products'
|
||||||
export { default as getAllProductPaths } from './get-all-product-paths'
|
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 getProduct } from './get-product'
|
||||||
export { default as getSiteInfo } from './get-site-info'
|
export { default as getSiteInfo } from './get-site-info'
|
||||||
export { default as login } from './login'
|
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. */
|
/** Represents information about the metafields associated to the specified resource. */
|
||||||
export type HasMetafields = {
|
export type HasMetafields = {
|
||||||
/** The metafield associated with the resource. */
|
/** Returns a metafield found by namespace and key. */
|
||||||
metafield?: Maybe<Metafield>
|
metafield?: Maybe<Metafield>
|
||||||
/** A paginated list of metafields associated with the resource. */
|
/** A paginated list of metafields associated with the resource. */
|
||||||
metafields: MetafieldConnection
|
metafields: MetafieldConnection
|
||||||
@ -3908,7 +3908,7 @@ export type Product = Node &
|
|||||||
images: ImageConnection
|
images: ImageConnection
|
||||||
/** The media associated with the product. */
|
/** The media associated with the product. */
|
||||||
media: MediaConnection
|
media: MediaConnection
|
||||||
/** The metafield associated with the resource. */
|
/** Returns a metafield found by namespace and key. */
|
||||||
metafield?: Maybe<Metafield>
|
metafield?: Maybe<Metafield>
|
||||||
/** A paginated list of metafields associated with the resource. */
|
/** A paginated list of metafields associated with the resource. */
|
||||||
metafields: MetafieldConnection
|
metafields: MetafieldConnection
|
||||||
@ -4235,7 +4235,7 @@ export type ProductVariant = Node &
|
|||||||
id: Scalars['ID']
|
id: Scalars['ID']
|
||||||
/** Image associated with the product variant. This field falls back to the product image if no image is available. */
|
/** Image associated with the product variant. This field falls back to the product image if no image is available. */
|
||||||
image?: Maybe<Image>
|
image?: Maybe<Image>
|
||||||
/** The metafield associated with the resource. */
|
/** Returns a metafield found by namespace and key. */
|
||||||
metafield?: Maybe<Metafield>
|
metafield?: Maybe<Metafield>
|
||||||
/** A paginated list of metafields associated with the resource. */
|
/** A paginated list of metafields associated with the resource. */
|
||||||
metafields: MetafieldConnection
|
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' } & {
|
export type ProductConnectionFragment = { __typename?: 'ProductConnection' } & {
|
||||||
pageInfo: { __typename?: 'PageInfo' } & Pick<
|
pageInfo: { __typename?: 'PageInfo' } & Pick<
|
||||||
PageInfo,
|
PageInfo,
|
||||||
@ -5272,31 +5298,7 @@ export type ProductConnectionFragment = { __typename?: 'ProductConnection' } & {
|
|||||||
>
|
>
|
||||||
edges: Array<
|
edges: Array<
|
||||||
{ __typename?: 'ProductEdge' } & {
|
{ __typename?: 'ProductEdge' } & {
|
||||||
node: { __typename?: 'Product' } & Pick<
|
node: { __typename?: 'Product' } & ListProductDetailsFragment
|
||||||
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'
|
|
||||||
>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
}
|
}
|
||||||
@ -5344,6 +5346,12 @@ export type CheckoutDetailsFragment = { __typename?: 'Checkout' } & Pick<
|
|||||||
ProductVariant,
|
ProductVariant,
|
||||||
'id' | 'sku' | 'title'
|
'id' | 'sku' | 'title'
|
||||||
> & {
|
> & {
|
||||||
|
selectedOptions: Array<
|
||||||
|
{ __typename?: 'SelectedOption' } & Pick<
|
||||||
|
SelectedOption,
|
||||||
|
'name' | 'value'
|
||||||
|
>
|
||||||
|
>
|
||||||
image?: Maybe<
|
image?: Maybe<
|
||||||
{ __typename?: 'Image' } & Pick<
|
{ __typename?: 'Image' } & Pick<
|
||||||
Image,
|
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<{
|
export type GetProductBySlugQueryVariables = Exact<{
|
||||||
slug: Scalars['String']
|
slug: Scalars['String']
|
||||||
}>
|
}>
|
||||||
|
|
||||||
export type GetProductBySlugQuery = { __typename?: 'QueryRoot' } & {
|
export type GetProductBySlugQuery = { __typename?: 'QueryRoot' } & {
|
||||||
productByHandle?: Maybe<
|
productByHandle?: Maybe<{ __typename?: 'Product' } & ProductDetailsFragment>
|
||||||
{ __typename?: 'Product' } & Pick<
|
}
|
||||||
Product,
|
|
||||||
| 'id'
|
export type GetRelatedProductsQueryVariables = Exact<{
|
||||||
| 'handle'
|
productId: Scalars['ID']
|
||||||
| 'title'
|
}>
|
||||||
| 'productType'
|
|
||||||
| 'vendor'
|
export type GetRelatedProductsQuery = { __typename?: 'QueryRoot' } & {
|
||||||
| 'description'
|
productRecommendations?: Maybe<
|
||||||
| 'descriptionHtml'
|
Array<{ __typename?: 'Product' } & ListProductDetailsFragment>
|
||||||
> & {
|
|
||||||
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'
|
|
||||||
>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,16 +13,6 @@ directive @accessRestricted(
|
|||||||
reason: String = null
|
reason: String = null
|
||||||
) on FIELD_DEFINITION | OBJECT
|
) on FIELD_DEFINITION | OBJECT
|
||||||
|
|
||||||
"""
|
|
||||||
Contextualize data.
|
|
||||||
"""
|
|
||||||
directive @inContext(
|
|
||||||
"""
|
|
||||||
The country code for context.
|
|
||||||
"""
|
|
||||||
country: CountryCode!
|
|
||||||
) on QUERY | MUTATION
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
A version of the API.
|
A version of the API.
|
||||||
"""
|
"""
|
||||||
@ -829,7 +819,7 @@ input CheckoutAttributesUpdateInput {
|
|||||||
The required attributes are city, province, and country.
|
The required attributes are city, province, and country.
|
||||||
Full validation of the addresses is still done at complete time.
|
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.
|
The required attributes are city, province, and country.
|
||||||
Full validation of the addresses is still done at complete time.
|
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`.
|
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`.
|
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 {
|
interface HasMetafields {
|
||||||
"""
|
"""
|
||||||
The metafield associated with the resource.
|
Returns a metafield found by namespace and key.
|
||||||
"""
|
"""
|
||||||
metafield(
|
metafield(
|
||||||
"""
|
"""
|
||||||
@ -7648,7 +7638,7 @@ type Product implements Node & HasMetafields {
|
|||||||
): MediaConnection!
|
): MediaConnection!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The metafield associated with the resource.
|
Returns a metafield found by namespace and key.
|
||||||
"""
|
"""
|
||||||
metafield(
|
metafield(
|
||||||
"""
|
"""
|
||||||
@ -8150,7 +8140,7 @@ type ProductVariant implements Node & HasMetafields {
|
|||||||
): Image
|
): Image
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The metafield associated with the resource.
|
Returns a metafield found by namespace and key.
|
||||||
"""
|
"""
|
||||||
metafield(
|
metafield(
|
||||||
"""
|
"""
|
||||||
@ -9298,7 +9288,7 @@ input TokenizedPaymentInput {
|
|||||||
"""
|
"""
|
||||||
Executes the payment in test mode if possible. Defaults to `false`.
|
Executes the payment in test mode if possible. Defaults to `false`.
|
||||||
"""
|
"""
|
||||||
test: Boolean
|
test: Boolean = false
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Public Hash Key used for AndroidPay payments only.
|
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`.
|
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.
|
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`.
|
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.
|
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 */ `
|
export const productConnectionFragment = /* GraphQL */ `
|
||||||
fragment productConnection on ProductConnection {
|
fragment productConnection on ProductConnection {
|
||||||
pageInfo {
|
pageInfo {
|
||||||
@ -6,33 +35,11 @@ export const productConnectionFragment = /* GraphQL */ `
|
|||||||
}
|
}
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
...listProductDetails
|
||||||
title
|
|
||||||
vendor
|
|
||||||
handle
|
|
||||||
priceRange {
|
|
||||||
minVariantPrice {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
images(first: 1) {
|
|
||||||
pageInfo {
|
|
||||||
hasNextPage
|
|
||||||
hasPreviousPage
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
originalSrc
|
|
||||||
altText
|
|
||||||
width
|
|
||||||
height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${listProductDetailsFragment}
|
||||||
`
|
`
|
||||||
|
|
||||||
const getAllProductsQuery = /* GraphQL */ `
|
const getAllProductsQuery = /* GraphQL */ `
|
||||||
|
@ -1,72 +1,79 @@
|
|||||||
const getProductQuery = /* GraphQL */ `
|
export const productDetailsFragment = /* GraphQL */ `
|
||||||
query getProductBySlug($slug: String!) {
|
fragment productDetails on Product {
|
||||||
productByHandle(handle: $slug) {
|
id
|
||||||
|
handle
|
||||||
|
availableForSale
|
||||||
|
title
|
||||||
|
productType
|
||||||
|
vendor
|
||||||
|
description
|
||||||
|
descriptionHtml
|
||||||
|
options {
|
||||||
id
|
id
|
||||||
handle
|
name
|
||||||
availableForSale
|
values
|
||||||
title
|
}
|
||||||
productType
|
priceRange {
|
||||||
vendor
|
maxVariantPrice {
|
||||||
description
|
amount
|
||||||
descriptionHtml
|
currencyCode
|
||||||
options {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
values
|
|
||||||
}
|
}
|
||||||
priceRange {
|
minVariantPrice {
|
||||||
maxVariantPrice {
|
amount
|
||||||
amount
|
currencyCode
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
minVariantPrice {
|
|
||||||
amount
|
|
||||||
currencyCode
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
variants(first: 250) {
|
}
|
||||||
pageInfo {
|
variants(first: 250) {
|
||||||
hasNextPage
|
pageInfo {
|
||||||
hasPreviousPage
|
hasNextPage
|
||||||
}
|
hasPreviousPage
|
||||||
edges {
|
}
|
||||||
node {
|
edges {
|
||||||
id
|
node {
|
||||||
title
|
id
|
||||||
sku
|
title
|
||||||
availableForSale
|
sku
|
||||||
requiresShipping
|
availableForSale
|
||||||
selectedOptions {
|
requiresShipping
|
||||||
name
|
selectedOptions {
|
||||||
value
|
name
|
||||||
}
|
value
|
||||||
priceV2 {
|
}
|
||||||
amount
|
priceV2 {
|
||||||
currencyCode
|
amount
|
||||||
}
|
currencyCode
|
||||||
compareAtPriceV2 {
|
}
|
||||||
amount
|
compareAtPriceV2 {
|
||||||
currencyCode
|
amount
|
||||||
}
|
currencyCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
images(first: 250) {
|
}
|
||||||
pageInfo {
|
images(first: 250) {
|
||||||
hasNextPage
|
pageInfo {
|
||||||
hasPreviousPage
|
hasNextPage
|
||||||
}
|
hasPreviousPage
|
||||||
edges {
|
}
|
||||||
node {
|
edges {
|
||||||
originalSrc
|
node {
|
||||||
altText
|
originalSrc
|
||||||
width
|
altText
|
||||||
height
|
width
|
||||||
}
|
height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const getProductQuery = /* GraphQL */ `
|
||||||
|
query getProductBySlug($slug: String!) {
|
||||||
|
productByHandle(handle: $slug) {
|
||||||
|
...productDetails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${productDetailsFragment}
|
||||||
|
`
|
||||||
|
|
||||||
export default getProductQuery
|
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,
|
config,
|
||||||
preview,
|
preview,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { pages } = await pagesPromise
|
const { pages } = await pagesPromise
|
||||||
const { categories } = await siteInfoPromise
|
const { categories } = await siteInfoPromise
|
||||||
const { product } = await productPromise
|
const { product } = await productPromise
|
||||||
const { products: relatedProducts } = await allProductsPromise
|
|
||||||
|
|
||||||
if (!product) {
|
if (!product) {
|
||||||
throw new Error(`Product with slug '${params!.slug}' not found`)
|
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 {
|
return {
|
||||||
props: {
|
props: {
|
||||||
pages,
|
pages,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user