forked from crowetic/commerce
Added locale metadata for products and product page
This commit is contained in:
parent
4b7f05de1b
commit
3654cb47d5
@ -78,6 +78,15 @@ export const productInfoFragment = /* GraphQL */ `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
localeMeta: metafields(namespace: $locale, keys: ["name", "description"])
|
||||||
|
@include(if: $hasLocale) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
${responsiveImageFragment}
|
${responsiveImageFragment}
|
||||||
|
@ -28,6 +28,9 @@ export type ProductImageVariables = Pick<
|
|||||||
>
|
>
|
||||||
|
|
||||||
export interface BigcommerceConfigOptions extends CommerceAPIConfig {
|
export interface BigcommerceConfigOptions extends CommerceAPIConfig {
|
||||||
|
// Indicates if the returned metadata with translations should be applied to the
|
||||||
|
// data or returned as it is
|
||||||
|
applyLocale?: boolean
|
||||||
images?: Images
|
images?: Images
|
||||||
storeApiUrl: string
|
storeApiUrl: string
|
||||||
storeApiToken: string
|
storeApiToken: string
|
||||||
@ -113,6 +116,7 @@ const config = new Config({
|
|||||||
cartCookie: process.env.BIGCOMMERCE_CART_COOKIE ?? 'bc_cartId',
|
cartCookie: process.env.BIGCOMMERCE_CART_COOKIE ?? 'bc_cartId',
|
||||||
cartCookieMaxAge: ONE_DAY * 30,
|
cartCookieMaxAge: ONE_DAY * 30,
|
||||||
fetch: fetchGraphqlApi,
|
fetch: fetchGraphqlApi,
|
||||||
|
applyLocale: true,
|
||||||
// REST API only
|
// REST API only
|
||||||
storeApiUrl: STORE_API_URL,
|
storeApiUrl: STORE_API_URL,
|
||||||
storeApiToken: STORE_API_TOKEN,
|
storeApiToken: STORE_API_TOKEN,
|
||||||
|
@ -4,11 +4,14 @@ import type {
|
|||||||
} from '@lib/bigcommerce/schema'
|
} from '@lib/bigcommerce/schema'
|
||||||
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
||||||
import filterEdges from '../utils/filter-edges'
|
import filterEdges from '../utils/filter-edges'
|
||||||
|
import setProductLocaleMeta from '../utils/set-product-locale-meta'
|
||||||
import { productConnectionFragment } from '../fragments/product'
|
import { productConnectionFragment } from '../fragments/product'
|
||||||
import { BigcommerceConfig, getConfig, Images, ProductImageVariables } from '..'
|
import { BigcommerceConfig, getConfig, Images, ProductImageVariables } from '..'
|
||||||
|
|
||||||
export const getAllProductsQuery = /* GraphQL */ `
|
export const getAllProductsQuery = /* GraphQL */ `
|
||||||
query getAllProducts(
|
query getAllProducts(
|
||||||
|
$hasLocale: Boolean = false
|
||||||
|
$locale: String = "null"
|
||||||
$entityIds: [Int!]
|
$entityIds: [Int!]
|
||||||
$first: Int = 10
|
$first: Int = 10
|
||||||
$imgSmallWidth: Int = 320
|
$imgSmallWidth: Int = 320
|
||||||
@ -69,7 +72,10 @@ export type ProductTypes =
|
|||||||
| 'newestProducts'
|
| 'newestProducts'
|
||||||
|
|
||||||
export type ProductVariables = { field?: ProductTypes } & Images &
|
export type ProductVariables = { field?: ProductTypes } & Images &
|
||||||
Omit<GetAllProductsQueryVariables, ProductTypes | keyof ProductImageVariables>
|
Omit<
|
||||||
|
GetAllProductsQueryVariables,
|
||||||
|
ProductTypes | keyof ProductImageVariables | 'hasLocale'
|
||||||
|
>
|
||||||
|
|
||||||
async function getAllProducts(opts?: {
|
async function getAllProducts(opts?: {
|
||||||
variables?: ProductVariables
|
variables?: ProductVariables
|
||||||
@ -96,9 +102,12 @@ async function getAllProducts({
|
|||||||
} = {}): Promise<GetAllProductsResult> {
|
} = {}): Promise<GetAllProductsResult> {
|
||||||
config = getConfig(config)
|
config = getConfig(config)
|
||||||
|
|
||||||
|
const locale = vars.locale || config.locale
|
||||||
const variables: GetAllProductsQueryVariables = {
|
const variables: GetAllProductsQueryVariables = {
|
||||||
...config.imageVariables,
|
...config.imageVariables,
|
||||||
...vars,
|
...vars,
|
||||||
|
locale,
|
||||||
|
hasLocale: !!locale,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FIELDS.includes(field)) {
|
if (!FIELDS.includes(field)) {
|
||||||
@ -115,11 +124,16 @@ async function getAllProducts({
|
|||||||
query,
|
query,
|
||||||
{ variables }
|
{ variables }
|
||||||
)
|
)
|
||||||
const products = data.site?.[field]?.edges
|
const edges = data.site?.[field]?.edges
|
||||||
|
const products = filterEdges(edges as RecursiveRequired<typeof edges>)
|
||||||
|
|
||||||
return {
|
if (locale && config.applyLocale) {
|
||||||
products: filterEdges(products as RecursiveRequired<typeof products>),
|
products.forEach((product: RecursivePartial<ProductEdge>) => {
|
||||||
|
if (product.node) setProductLocaleMeta(product.node)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { products }
|
||||||
}
|
}
|
||||||
|
|
||||||
export default getAllProducts
|
export default getAllProducts
|
||||||
|
@ -3,11 +3,14 @@ import type {
|
|||||||
GetProductQueryVariables,
|
GetProductQueryVariables,
|
||||||
} from 'lib/bigcommerce/schema'
|
} from 'lib/bigcommerce/schema'
|
||||||
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
|
||||||
|
import setProductLocaleMeta from '../utils/set-product-locale-meta'
|
||||||
import { productInfoFragment } from '../fragments/product'
|
import { productInfoFragment } from '../fragments/product'
|
||||||
import { BigcommerceConfig, getConfig, Images } from '..'
|
import { BigcommerceConfig, getConfig, Images } from '..'
|
||||||
|
|
||||||
export const getProductQuery = /* GraphQL */ `
|
export const getProductQuery = /* GraphQL */ `
|
||||||
query getProduct(
|
query getProduct(
|
||||||
|
$hasLocale: Boolean = false
|
||||||
|
$locale: String = "null"
|
||||||
$path: String!
|
$path: String!
|
||||||
$imgSmallWidth: Int = 320
|
$imgSmallWidth: Int = 320
|
||||||
$imgSmallHeight: Int
|
$imgSmallHeight: Int
|
||||||
@ -42,8 +45,10 @@ export type GetProductResult<
|
|||||||
T extends { product?: any } = { product?: ProductNode }
|
T extends { product?: any } = { product?: ProductNode }
|
||||||
> = T
|
> = T
|
||||||
|
|
||||||
export type ProductVariables = Images &
|
export type ProductVariables = Images & { locale?: string } & (
|
||||||
({ path: string; slug?: never } | { path?: never; slug: string })
|
| { path: string; slug?: never }
|
||||||
|
| { path?: never; slug: string }
|
||||||
|
)
|
||||||
|
|
||||||
async function getProduct(opts: {
|
async function getProduct(opts: {
|
||||||
variables: ProductVariables
|
variables: ProductVariables
|
||||||
@ -66,9 +71,13 @@ async function getProduct({
|
|||||||
config?: BigcommerceConfig
|
config?: BigcommerceConfig
|
||||||
}): Promise<GetProductResult> {
|
}): Promise<GetProductResult> {
|
||||||
config = getConfig(config)
|
config = getConfig(config)
|
||||||
|
|
||||||
|
const locale = vars.locale || config.locale
|
||||||
const variables: GetProductQueryVariables = {
|
const variables: GetProductQueryVariables = {
|
||||||
...config.imageVariables,
|
...config.imageVariables,
|
||||||
...vars,
|
...vars,
|
||||||
|
locale,
|
||||||
|
hasLocale: !!locale,
|
||||||
path: slug ? `/${slug}/` : vars.path!,
|
path: slug ? `/${slug}/` : vars.path!,
|
||||||
}
|
}
|
||||||
const { data } = await config.fetch<RecursivePartial<GetProductQuery>>(
|
const { data } = await config.fetch<RecursivePartial<GetProductQuery>>(
|
||||||
@ -78,6 +87,10 @@ async function getProduct({
|
|||||||
const product = data.site?.route?.node
|
const product = data.site?.route?.node
|
||||||
|
|
||||||
if (product?.__typename === 'Product') {
|
if (product?.__typename === 'Product') {
|
||||||
|
if (locale && config.applyLocale) {
|
||||||
|
setProductLocaleMeta(product)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
product: product as RecursiveRequired<typeof product>,
|
product: product as RecursiveRequired<typeof product>,
|
||||||
}
|
}
|
||||||
|
21
lib/bigcommerce/api/utils/set-product-locale-meta.ts
Normal file
21
lib/bigcommerce/api/utils/set-product-locale-meta.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import type { ProductNode } from '../operations/get-all-products'
|
||||||
|
import type { RecursivePartial } from './types'
|
||||||
|
|
||||||
|
export default function setProductLocaleMeta(
|
||||||
|
node: RecursivePartial<ProductNode>
|
||||||
|
) {
|
||||||
|
if (node.localeMeta?.edges) {
|
||||||
|
node.localeMeta.edges = node.localeMeta.edges.filter((edge) => {
|
||||||
|
const { key, value } = edge?.node ?? {}
|
||||||
|
if (key && key in node) {
|
||||||
|
;(node as any)[key] = value
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!node.localeMeta.edges.length) {
|
||||||
|
delete node.localeMeta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
lib/bigcommerce/schema.d.ts
vendored
18
lib/bigcommerce/schema.d.ts
vendored
@ -1807,6 +1807,20 @@ export type ProductInfoFragment = { __typename?: 'Product' } & Pick<
|
|||||||
>
|
>
|
||||||
>
|
>
|
||||||
}
|
}
|
||||||
|
localeMeta: { __typename?: 'MetafieldConnection' } & {
|
||||||
|
edges?: Maybe<
|
||||||
|
Array<
|
||||||
|
Maybe<
|
||||||
|
{ __typename?: 'MetafieldEdge' } & {
|
||||||
|
node: { __typename?: 'Metafields' } & Pick<
|
||||||
|
Metafields,
|
||||||
|
'key' | 'value'
|
||||||
|
>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProductConnnectionFragment = {
|
export type ProductConnnectionFragment = {
|
||||||
@ -1848,6 +1862,8 @@ export type GetAllProductPathsQuery = { __typename?: 'Query' } & {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type GetAllProductsQueryVariables = Exact<{
|
export type GetAllProductsQueryVariables = Exact<{
|
||||||
|
hasLocale?: Maybe<Scalars['Boolean']>
|
||||||
|
locale?: Maybe<Scalars['String']>
|
||||||
entityIds?: Maybe<Array<Scalars['Int']>>
|
entityIds?: Maybe<Array<Scalars['Int']>>
|
||||||
first?: Maybe<Scalars['Int']>
|
first?: Maybe<Scalars['Int']>
|
||||||
imgSmallWidth?: Maybe<Scalars['Int']>
|
imgSmallWidth?: Maybe<Scalars['Int']>
|
||||||
@ -1880,6 +1896,8 @@ export type GetAllProductsQuery = { __typename?: 'Query' } & {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type GetProductQueryVariables = Exact<{
|
export type GetProductQueryVariables = Exact<{
|
||||||
|
hasLocale?: Maybe<Scalars['Boolean']>
|
||||||
|
locale?: Maybe<Scalars['String']>
|
||||||
path: Scalars['String']
|
path: Scalars['String']
|
||||||
imgSmallWidth?: Maybe<Scalars['Int']>
|
imgSmallWidth?: Maybe<Scalars['Int']>
|
||||||
imgSmallHeight?: Maybe<Scalars['Int']>
|
imgSmallHeight?: Maybe<Scalars['Int']>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
|
import { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
|
||||||
|
import { getConfig } from '@lib/bigcommerce/api'
|
||||||
import getAllProducts from '@lib/bigcommerce/api/operations/get-all-products'
|
import getAllProducts from '@lib/bigcommerce/api/operations/get-all-products'
|
||||||
import getSiteInfo from '@lib/bigcommerce/api/operations/get-site-info'
|
import getSiteInfo from '@lib/bigcommerce/api/operations/get-site-info'
|
||||||
import getAllPages from '@lib/bigcommerce/api/operations/get-all-pages'
|
import getAllPages from '@lib/bigcommerce/api/operations/get-all-pages'
|
||||||
@ -12,19 +13,22 @@ export async function getStaticProps({
|
|||||||
preview,
|
preview,
|
||||||
locale,
|
locale,
|
||||||
}: GetStaticPropsContext) {
|
}: GetStaticPropsContext) {
|
||||||
console.log('LOCALE', locale)
|
const config = getConfig({ locale })
|
||||||
|
|
||||||
const { products: featuredProducts } = await getAllProducts({
|
const { products: featuredProducts } = await getAllProducts({
|
||||||
variables: { field: 'featuredProducts', first: 6 },
|
variables: { field: 'featuredProducts', first: 6 },
|
||||||
|
config,
|
||||||
})
|
})
|
||||||
const { products: bestSellingProducts } = await getAllProducts({
|
const { products: bestSellingProducts } = await getAllProducts({
|
||||||
variables: { field: 'bestSellingProducts', first: 6 },
|
variables: { field: 'bestSellingProducts', first: 6 },
|
||||||
|
config,
|
||||||
})
|
})
|
||||||
const { products: newestProducts } = await getAllProducts({
|
const { products: newestProducts } = await getAllProducts({
|
||||||
variables: { field: 'newestProducts', first: 12 },
|
variables: { field: 'newestProducts', first: 12 },
|
||||||
|
config,
|
||||||
})
|
})
|
||||||
const { categories, brands } = await getSiteInfo()
|
const { categories, brands } = await getSiteInfo({ config })
|
||||||
const { pages } = await getAllPages()
|
const { pages } = await getAllPages({ config })
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
|
import {
|
||||||
|
GetStaticPathsContext,
|
||||||
|
GetStaticPropsContext,
|
||||||
|
InferGetStaticPropsType,
|
||||||
|
} from 'next'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
|
import { getConfig } from '@lib/bigcommerce/api'
|
||||||
import getAllPages from '@lib/bigcommerce/api/operations/get-all-pages'
|
import getAllPages from '@lib/bigcommerce/api/operations/get-all-pages'
|
||||||
import getProduct from '@lib/bigcommerce/api/operations/get-product'
|
import getProduct from '@lib/bigcommerce/api/operations/get-product'
|
||||||
import { Layout } from '@components/core'
|
import { Layout } from '@components/core'
|
||||||
@ -8,9 +13,15 @@ import getAllProductPaths from '@lib/bigcommerce/api/operations/get-all-product-
|
|||||||
|
|
||||||
export async function getStaticProps({
|
export async function getStaticProps({
|
||||||
params,
|
params,
|
||||||
|
locale,
|
||||||
}: GetStaticPropsContext<{ slug: string }>) {
|
}: GetStaticPropsContext<{ slug: string }>) {
|
||||||
const { pages } = await getAllPages()
|
const config = getConfig({ locale })
|
||||||
const { product } = await getProduct({ variables: { slug: params!.slug } })
|
|
||||||
|
const { pages } = await getAllPages({ config })
|
||||||
|
const { product } = await getProduct({
|
||||||
|
variables: { slug: params!.slug },
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
|
||||||
if (!product) {
|
if (!product) {
|
||||||
throw new Error(`Product with slug '${params!.slug}' not found`)
|
throw new Error(`Product with slug '${params!.slug}' not found`)
|
||||||
@ -22,11 +33,19 @@ export async function getStaticProps({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths({ locales }: GetStaticPathsContext) {
|
||||||
const { products } = await getAllProductPaths()
|
const { products } = await getAllProductPaths()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
paths: products.map((product) => `/product${product.node.path}`),
|
paths: locales
|
||||||
|
? locales.reduce<string[]>((arr, locale) => {
|
||||||
|
// Add a product path for every locale
|
||||||
|
products.forEach((product) => {
|
||||||
|
arr.push(`/${locale}/product${product.node.path}`)
|
||||||
|
})
|
||||||
|
return arr
|
||||||
|
}, [])
|
||||||
|
: products.map((product) => `/product${product.node.path}`),
|
||||||
// If your store has tons of products, enable fallback mode to improve build times!
|
// If your store has tons of products, enable fallback mode to improve build times!
|
||||||
fallback: false,
|
fallback: false,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user