4
0
forked from crowetic/commerce

Added locale metadata for products and product page

This commit is contained in:
Luis Alvarez 2020-10-25 15:14:54 -05:00
parent 4b7f05de1b
commit 3654cb47d5
8 changed files with 116 additions and 14 deletions

View File

@ -78,6 +78,15 @@ export const productInfoFragment = /* GraphQL */ `
}
}
}
localeMeta: metafields(namespace: $locale, keys: ["name", "description"])
@include(if: $hasLocale) {
edges {
node {
key
value
}
}
}
}
${responsiveImageFragment}

View File

@ -28,6 +28,9 @@ export type ProductImageVariables = Pick<
>
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
storeApiUrl: string
storeApiToken: string
@ -113,6 +116,7 @@ const config = new Config({
cartCookie: process.env.BIGCOMMERCE_CART_COOKIE ?? 'bc_cartId',
cartCookieMaxAge: ONE_DAY * 30,
fetch: fetchGraphqlApi,
applyLocale: true,
// REST API only
storeApiUrl: STORE_API_URL,
storeApiToken: STORE_API_TOKEN,

View File

@ -4,11 +4,14 @@ import type {
} from '@lib/bigcommerce/schema'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import filterEdges from '../utils/filter-edges'
import setProductLocaleMeta from '../utils/set-product-locale-meta'
import { productConnectionFragment } from '../fragments/product'
import { BigcommerceConfig, getConfig, Images, ProductImageVariables } from '..'
export const getAllProductsQuery = /* GraphQL */ `
query getAllProducts(
$hasLocale: Boolean = false
$locale: String = "null"
$entityIds: [Int!]
$first: Int = 10
$imgSmallWidth: Int = 320
@ -69,7 +72,10 @@ export type ProductTypes =
| 'newestProducts'
export type ProductVariables = { field?: ProductTypes } & Images &
Omit<GetAllProductsQueryVariables, ProductTypes | keyof ProductImageVariables>
Omit<
GetAllProductsQueryVariables,
ProductTypes | keyof ProductImageVariables | 'hasLocale'
>
async function getAllProducts(opts?: {
variables?: ProductVariables
@ -96,9 +102,12 @@ async function getAllProducts({
} = {}): Promise<GetAllProductsResult> {
config = getConfig(config)
const locale = vars.locale || config.locale
const variables: GetAllProductsQueryVariables = {
...config.imageVariables,
...vars,
locale,
hasLocale: !!locale,
}
if (!FIELDS.includes(field)) {
@ -115,11 +124,16 @@ async function getAllProducts({
query,
{ variables }
)
const products = data.site?.[field]?.edges
const edges = data.site?.[field]?.edges
const products = filterEdges(edges as RecursiveRequired<typeof edges>)
return {
products: filterEdges(products as RecursiveRequired<typeof products>),
if (locale && config.applyLocale) {
products.forEach((product: RecursivePartial<ProductEdge>) => {
if (product.node) setProductLocaleMeta(product.node)
})
}
return { products }
}
export default getAllProducts

View File

@ -3,11 +3,14 @@ import type {
GetProductQueryVariables,
} from 'lib/bigcommerce/schema'
import type { RecursivePartial, RecursiveRequired } from '../utils/types'
import setProductLocaleMeta from '../utils/set-product-locale-meta'
import { productInfoFragment } from '../fragments/product'
import { BigcommerceConfig, getConfig, Images } from '..'
export const getProductQuery = /* GraphQL */ `
query getProduct(
$hasLocale: Boolean = false
$locale: String = "null"
$path: String!
$imgSmallWidth: Int = 320
$imgSmallHeight: Int
@ -42,8 +45,10 @@ export type GetProductResult<
T extends { product?: any } = { product?: ProductNode }
> = T
export type ProductVariables = Images &
({ path: string; slug?: never } | { path?: never; slug: string })
export type ProductVariables = Images & { locale?: string } & (
| { path: string; slug?: never }
| { path?: never; slug: string }
)
async function getProduct(opts: {
variables: ProductVariables
@ -66,9 +71,13 @@ async function getProduct({
config?: BigcommerceConfig
}): Promise<GetProductResult> {
config = getConfig(config)
const locale = vars.locale || config.locale
const variables: GetProductQueryVariables = {
...config.imageVariables,
...vars,
locale,
hasLocale: !!locale,
path: slug ? `/${slug}/` : vars.path!,
}
const { data } = await config.fetch<RecursivePartial<GetProductQuery>>(
@ -78,6 +87,10 @@ async function getProduct({
const product = data.site?.route?.node
if (product?.__typename === 'Product') {
if (locale && config.applyLocale) {
setProductLocaleMeta(product)
}
return {
product: product as RecursiveRequired<typeof product>,
}

View 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
}
}
}

View File

@ -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 = {
@ -1848,6 +1862,8 @@ export type GetAllProductPathsQuery = { __typename?: 'Query' } & {
}
export type GetAllProductsQueryVariables = Exact<{
hasLocale?: Maybe<Scalars['Boolean']>
locale?: Maybe<Scalars['String']>
entityIds?: Maybe<Array<Scalars['Int']>>
first?: Maybe<Scalars['Int']>
imgSmallWidth?: Maybe<Scalars['Int']>
@ -1880,6 +1896,8 @@ export type GetAllProductsQuery = { __typename?: 'Query' } & {
}
export type GetProductQueryVariables = Exact<{
hasLocale?: Maybe<Scalars['Boolean']>
locale?: Maybe<Scalars['String']>
path: Scalars['String']
imgSmallWidth?: Maybe<Scalars['Int']>
imgSmallHeight?: Maybe<Scalars['Int']>

View File

@ -1,5 +1,6 @@
import { useMemo } from 'react'
import { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
import { getConfig } from '@lib/bigcommerce/api'
import getAllProducts from '@lib/bigcommerce/api/operations/get-all-products'
import getSiteInfo from '@lib/bigcommerce/api/operations/get-site-info'
import getAllPages from '@lib/bigcommerce/api/operations/get-all-pages'
@ -12,19 +13,22 @@ export async function getStaticProps({
preview,
locale,
}: GetStaticPropsContext) {
console.log('LOCALE', locale)
const config = getConfig({ locale })
const { products: featuredProducts } = await getAllProducts({
variables: { field: 'featuredProducts', first: 6 },
config,
})
const { products: bestSellingProducts } = await getAllProducts({
variables: { field: 'bestSellingProducts', first: 6 },
config,
})
const { products: newestProducts } = await getAllProducts({
variables: { field: 'newestProducts', first: 12 },
config,
})
const { categories, brands } = await getSiteInfo()
const { pages } = await getAllPages()
const { categories, brands } = await getSiteInfo({ config })
const { pages } = await getAllPages({ config })
return {
props: {

View File

@ -1,5 +1,10 @@
import { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
import {
GetStaticPathsContext,
GetStaticPropsContext,
InferGetStaticPropsType,
} from 'next'
import { useRouter } from 'next/router'
import { getConfig } from '@lib/bigcommerce/api'
import getAllPages from '@lib/bigcommerce/api/operations/get-all-pages'
import getProduct from '@lib/bigcommerce/api/operations/get-product'
import { Layout } from '@components/core'
@ -8,9 +13,15 @@ import getAllProductPaths from '@lib/bigcommerce/api/operations/get-all-product-
export async function getStaticProps({
params,
locale,
}: GetStaticPropsContext<{ slug: string }>) {
const { pages } = await getAllPages()
const { product } = await getProduct({ variables: { slug: params!.slug } })
const config = getConfig({ locale })
const { pages } = await getAllPages({ config })
const { product } = await getProduct({
variables: { slug: params!.slug },
config,
})
if (!product) {
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()
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!
fallback: false,
}