diff --git a/components/product/Swatch/Swatch.tsx b/components/product/Swatch/Swatch.tsx index 34244321f..c38803b5b 100644 --- a/components/product/Swatch/Swatch.tsx +++ b/components/product/Swatch/Swatch.tsx @@ -47,7 +47,7 @@ const Swatch: FC & Props> = ({ )} - {variant === 'size' ? label : null} + {variant !== 'color' ? label : null} ) } diff --git a/framework/bigcommerce/product/use-search.tsx b/framework/bigcommerce/product/use-search.tsx index 0ee135032..59355e64e 100644 --- a/framework/bigcommerce/product/use-search.tsx +++ b/framework/bigcommerce/product/use-search.tsx @@ -1,16 +1,9 @@ import { SWRHook } from '@commerce/utils/types' import useSearch, { UseSearch } from '@commerce/product/use-search' -import type { SearchProductsData } from '../api/catalog/products' +import { SearchProductsData, SearchProductsInput } from '@commerce/types' export default useSearch as UseSearch -export type SearchProductsInput = { - search?: string - categoryId?: number - brandId?: number - sort?: string -} - export const handler: SWRHook< SearchProductsData, SearchProductsInput, diff --git a/framework/commerce/api/index.ts b/framework/commerce/api/index.ts index 77b2eeb7e..c15ca7a79 100644 --- a/framework/commerce/api/index.ts +++ b/framework/commerce/api/index.ts @@ -2,6 +2,7 @@ import type { RequestInit, Response } from '@vercel/fetch' export interface CommerceAPIConfig { locale?: string + locales?: string[] commerceUrl: string apiToken: string cartCookie: string diff --git a/framework/commerce/types.ts b/framework/commerce/types.ts index 86361fd9f..07069186e 100644 --- a/framework/commerce/types.ts +++ b/framework/commerce/types.ts @@ -1,6 +1,5 @@ import type { Wishlist as BCWishlist } from '../bigcommerce/api/wishlist' import type { Customer as BCCustomer } from '../bigcommerce/api/customers' -import type { SearchProductsData as BCSearchProductsData } from '../bigcommerce/api/catalog/products' export type Discount = { // The value of the discount, can be an amount or percentage @@ -97,8 +96,18 @@ export interface Wishlist extends BCWishlist {} // TODO: Properly define this type export interface Customer extends BCCustomer {} -// TODO: Properly define this type -export interface SearchProductsData extends BCSearchProductsData {} +export type SearchProductsData = { + products: Product[] + found: boolean +} + +export type SearchProductsInput = { + search?: string + categoryId?: string + brandId?: string + sort?: string + locale?: string +} /** * Cart mutations diff --git a/framework/shopify/api/index.ts b/framework/shopify/api/index.ts index 387ed02fc..4e23ce99c 100644 --- a/framework/shopify/api/index.ts +++ b/framework/shopify/api/index.ts @@ -43,7 +43,6 @@ export class Config { } const config = new Config({ - locale: 'en-US', commerceUrl: API_URL, apiToken: API_TOKEN!, cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, diff --git a/framework/shopify/api/utils/fetch-all-products.ts b/framework/shopify/api/utils/fetch-all-products.ts index 9fa70a5ee..45bad75c4 100644 --- a/framework/shopify/api/utils/fetch-all-products.ts +++ b/framework/shopify/api/utils/fetch-all-products.ts @@ -1,10 +1,12 @@ -import { ProductEdge } from '../../schema' +import { ProductEdge, QueryRoot, QueryRootProductsArgs } from '../../schema' import { ShopifyConfig } from '..' const fetchAllProducts = async ({ config, query, - variables, + variables = { + first: 250, + }, acc = [], cursor, }: { @@ -14,9 +16,20 @@ const fetchAllProducts = async ({ variables?: any cursor?: string }): Promise => { - const { data } = await config.fetch(query, { - variables: { ...variables, cursor }, - }) + const { fetch, locale } = config + const { data } = await fetch( + query, + { + variables: { ...variables, cursor }, + }, + { + ...(locale && { + headers: { + 'Accept-Locale': locale, + }, + }), + } + ) const edges: ProductEdge[] = data.products?.edges ?? [] const hasNextPage = data.products?.pageInfo?.hasNextPage diff --git a/framework/shopify/common/get-all-pages.ts b/framework/shopify/common/get-all-pages.ts index 54231ed03..46bbb37f7 100644 --- a/framework/shopify/common/get-all-pages.ts +++ b/framework/shopify/common/get-all-pages.ts @@ -1,14 +1,6 @@ import { getConfig, ShopifyConfig } from '../api' -import { PageEdge } from '../schema' -import { getAllPagesQuery } from '../utils/queries' - -type Variables = { - first?: number -} - -type ReturnType = { - pages: Page[] -} +import { QueryRoot, QueryRootPagesArgs } from '../schema' +import { normalizePages, getAllPagesQuery } from '../utils' export type Page = { id: string @@ -18,25 +10,40 @@ export type Page = { body: string } -const getAllPages = async (options?: { - variables?: Variables - config: ShopifyConfig - preview?: boolean -}): Promise => { - let { config, variables = { first: 250 } } = options ?? {} - config = getConfig(config) - const { locale } = config - const { data } = await config.fetch(getAllPagesQuery, { variables }) +export type GetAllPagesResult = Promise<{ + pages: Page[] +}> - const pages = data.pages?.edges?.map( - ({ node: { title: name, handle, ...node } }: PageEdge) => ({ - ...node, - url: `/${locale}/${handle}`, - name, - }) +const getAllPages = async (options?: { + variables?: QueryRootPagesArgs + config?: ShopifyConfig + preview?: boolean +}): GetAllPagesResult => { + let { config, variables } = options ?? {} + const { fetch, locale = 'en-US', locales = ['en-US'] } = getConfig(config) + + const { + data: { + pages: { edges }, + }, + } = await fetch( + getAllPagesQuery, + { + variables, + }, + { + headers: { + 'Accept-Language': locale, + }, + } ) - return { pages } + return { + pages: locales.reduce( + (arr, locale) => arr.concat(normalizePages(edges, locale)), + [] + ), + } } export default getAllPages diff --git a/framework/shopify/common/get-page.ts b/framework/shopify/common/get-page.ts index be934aa42..4efafcd7a 100644 --- a/framework/shopify/common/get-page.ts +++ b/framework/shopify/common/get-page.ts @@ -1,37 +1,41 @@ +import { Page as ShopifyPage, QueryRoot, QueryRootPagesArgs } from '../schema' +import { normalizePage, getPageQuery } from '../utils' import { getConfig, ShopifyConfig } from '../api' -import getPageQuery from '../utils/queries/get-page-query' -import { Page } from './get-all-pages' +import type { Page } from './get-all-pages' -type Variables = { +type GetPageInput = { id: string } -export type GetPageResult = T +type GetPageResult = { + page?: Page +} -const getPage = async (options: { - variables: Variables - config: ShopifyConfig +const getPage = async ({ + variables, + config, +}: { + variables: GetPageInput + config?: ShopifyConfig preview?: boolean }): Promise => { - let { config, variables } = options ?? {} + const { locale = 'en-US', fetch } = getConfig(config) - config = getConfig(config) - const { locale } = config + const { + data: { node: page }, + } = await fetch( + getPageQuery, + { + variables, + }, + { + headers: { + 'Accept-Language': locale, + }, + } + ) - const { data } = await config.fetch(getPageQuery, { - variables, - }) - const page = data.node - - return { - page: page - ? { - ...page, - name: page.title, - url: `/${locale}/${page.handle}`, - } - : null, - } + return page ? { page: normalizePage(page as ShopifyPage, locale) } : {} } export default getPage diff --git a/framework/shopify/fetcher.ts b/framework/shopify/fetcher.ts index a69150503..5ee23c103 100644 --- a/framework/shopify/fetcher.ts +++ b/framework/shopify/fetcher.ts @@ -8,13 +8,15 @@ const fetcher: Fetcher = async ({ variables, query, }) => { + const { locale, ...vars } = variables ?? {} return handleFetchResponse( await fetch(url, { method, - body: JSON.stringify({ query, variables }), + body: JSON.stringify({ query, variables: vars }), headers: { 'X-Shopify-Storefront-Access-Token': API_TOKEN!, 'Content-Type': 'application/json', + ...(locale && { 'Accept-Language': locale! }), }, }) ) diff --git a/framework/shopify/index.tsx b/framework/shopify/index.tsx index 5b25d6b21..377ece3c1 100644 --- a/framework/shopify/index.tsx +++ b/framework/shopify/index.tsx @@ -22,7 +22,6 @@ export type ShopifyConfig = Partial export type ShopifyProps = { children?: ReactNode - locale: string } & ShopifyConfig export function CommerceProvider({ children, ...config }: ShopifyProps) { diff --git a/framework/shopify/product/get-all-product-paths.ts b/framework/shopify/product/get-all-product-paths.ts index 4431d1e53..40cab56f3 100644 --- a/framework/shopify/product/get-all-product-paths.ts +++ b/framework/shopify/product/get-all-product-paths.ts @@ -1,4 +1,3 @@ -import { Product } from '@commerce/types' import { getConfig, ShopifyConfig } from '../api' import fetchAllProducts from '../api/utils/fetch-all-products' import { ProductEdge } from '../schema' diff --git a/framework/shopify/product/get-all-products.ts b/framework/shopify/product/get-all-products.ts index 14e486563..dfe44bcb3 100644 --- a/framework/shopify/product/get-all-products.ts +++ b/framework/shopify/product/get-all-products.ts @@ -1,9 +1,8 @@ -import { GraphQLFetcherResult } from '@commerce/api' +import { Product } from '@commerce/types' import { getConfig, ShopifyConfig } from '../api' -import { ProductEdge } from '../schema' +import { QueryRoot, QueryRootProductsArgs } from '../schema' import { getAllProductsQuery } from '../utils/queries' import { normalizeProduct } from '../utils/normalize' -import { Product } from '@commerce/types' type Variables = { first?: number @@ -21,16 +20,25 @@ const getAllProducts = async (options: { }): Promise => { let { config, variables = { first: 250 } } = options ?? {} config = getConfig(config) + let products: Product[] = [] - const { data }: GraphQLFetcherResult = await config.fetch( + const { data } = await config!.fetch( getAllProductsQuery, - { variables } + { + variables, + }, + { + ...(config.locale && { + headers: { + 'Accept-Language': config.locale!, + }, + }), + } ) - - const products = - data.products?.edges?.map(({ node: p }: ProductEdge) => - normalizeProduct(p) - ) ?? [] + products = [ + ...products, + ...data.products.edges.map(({ node: p }) => normalizeProduct(p)), + ] return { products, diff --git a/framework/shopify/product/get-product.ts b/framework/shopify/product/get-product.ts index 1d861e1a1..207d6c042 100644 --- a/framework/shopify/product/get-product.ts +++ b/framework/shopify/product/get-product.ts @@ -1,31 +1,43 @@ -import { GraphQLFetcherResult } from '@commerce/api' +import { Product } from '@commerce/types' +import { QueryRoot } from '../schema' import { getConfig, ShopifyConfig } from '../api' import { normalizeProduct, getProductQuery } from '../utils' -type Variables = { +export type GetProductInput = { slug: string } -type ReturnType = { - product: any +export type GetProductResult = { + product?: Product } -const getProduct = async (options: { - variables: Variables - config: ShopifyConfig +const getProduct = async ({ + variables, + config, +}: { + variables: GetProductInput + config?: ShopifyConfig preview?: boolean -}): Promise => { - let { config, variables } = options ?? {} - config = getConfig(config) +}): Promise => { + const { fetch, locale } = getConfig(config) - const { data }: GraphQLFetcherResult = await config.fetch(getProductQuery, { - variables, - }) - const { productByHandle } = data + const { + data: { productByHandle }, + } = await fetch( + getProductQuery, + { + variables, + }, + { + ...(locale && { + headers: { + 'Accept-Language': locale, + }, + }), + } + ) - return { - product: productByHandle ? normalizeProduct(productByHandle) : null, - } + return productByHandle ? { product: normalizeProduct(productByHandle) } : {} } export default getProduct diff --git a/framework/shopify/product/use-search.tsx b/framework/shopify/product/use-search.tsx index bf812af3d..db5570747 100644 --- a/framework/shopify/product/use-search.tsx +++ b/framework/shopify/product/use-search.tsx @@ -1,7 +1,9 @@ import { SWRHook } from '@commerce/utils/types' import useSearch, { UseSearch } from '@commerce/product/use-search' +import { SearchProductsInput, SearchProductsData } from '@commerce/types' import { ProductEdge } from '../schema' + import { getAllProductsQuery, getCollectionProductsQuery, @@ -9,22 +11,8 @@ import { normalizeProduct, } from '../utils' -import { Product } from '@commerce/types' - export default useSearch as UseSearch -export type SearchProductsInput = { - search?: string - categoryId?: string - brandId?: string - sort?: string -} - -export type SearchProductsData = { - products: Product[] - found: boolean -} - export const handler: SWRHook< SearchProductsData, SearchProductsInput, @@ -68,6 +56,7 @@ export const handler: SWRHook< ['categoryId', input.categoryId], ['brandId', input.brandId], ['sort', input.sort], + ['locale', input.locale], ], swrOptions: { revalidateOnFocus: false, diff --git a/framework/shopify/utils/get-categories.ts b/framework/shopify/utils/get-categories.ts index cce4b2ad7..c05bf7194 100644 --- a/framework/shopify/utils/get-categories.ts +++ b/framework/shopify/utils/get-categories.ts @@ -1,5 +1,5 @@ import { ShopifyConfig } from '../api' -import { CollectionEdge } from '../schema' +import { CollectionEdge, QueryRoot, QueryRootCollectionsArgs } from '../schema' import getSiteCollectionsQuery from './queries/get-all-collections-query' export type Category = { @@ -9,11 +9,22 @@ export type Category = { } const getCategories = async (config: ShopifyConfig): Promise => { - const { data } = await config.fetch(getSiteCollectionsQuery, { - variables: { - first: 250, + const { fetch, locale } = config + const { data } = await fetch( + getSiteCollectionsQuery, + { + variables: { + first: 250, + }, }, - }) + { + ...(locale && { + headers: { + 'Accept-Language': locale, + }, + }), + } + ) return ( data.collections?.edges?.map( diff --git a/framework/shopify/utils/get-search-variables.ts b/framework/shopify/utils/get-search-variables.ts index c1b40ae5d..4514be72d 100644 --- a/framework/shopify/utils/get-search-variables.ts +++ b/framework/shopify/utils/get-search-variables.ts @@ -1,11 +1,12 @@ import getSortVariables from './get-sort-variables' -import type { SearchProductsInput } from '../product/use-search' +import type { SearchProductsInput } from '@commerce/types' export const getSearchVariables = ({ brandId, search, categoryId, sort, + locale, }: SearchProductsInput) => { let query = '' @@ -20,6 +21,7 @@ export const getSearchVariables = ({ return { categoryId, query, + locale, ...getSortVariables(sort, !!categoryId), } } diff --git a/framework/shopify/utils/get-vendors.ts b/framework/shopify/utils/get-vendors.ts index 24843f177..31b26a088 100644 --- a/framework/shopify/utils/get-vendors.ts +++ b/framework/shopify/utils/get-vendors.ts @@ -18,9 +18,6 @@ const getVendors = async (config: ShopifyConfig): Promise => { const vendors = await fetchAllProducts({ config, query: getAllProductVendors, - variables: { - first: 250, - }, }) let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor) diff --git a/framework/shopify/utils/normalize.ts b/framework/shopify/utils/normalize.ts index 4ebc3a1ae..9fdc73f96 100644 --- a/framework/shopify/utils/normalize.ts +++ b/framework/shopify/utils/normalize.ts @@ -9,10 +9,14 @@ import { ProductVariantConnection, MoneyV2, ProductOption, + PageEdge, + Page as ShopifyPage, } from '../schema' import type { Cart, LineItem } from '../types' +import type { Page } from '../common/get-all-pages' + const money = ({ amount, currencyCode }: MoneyV2) => { return { value: +amount, @@ -39,6 +43,7 @@ const normalizeProductOption = ({ hexColors: [value], } } + return output }), } @@ -163,3 +168,15 @@ function normalizeLineItem({ ], } } + +export const normalizePage = ( + { title: name, handle, ...page }: ShopifyPage, + locale: string +): Page => ({ + ...page, + url: `/${locale}/${handle}`, + name, +}) + +export const normalizePages = (edges: PageEdge[], locale: string): Page[] => + edges?.map((edge) => normalizePage(edge.node, locale)) diff --git a/pages/[...pages].tsx b/pages/[...pages].tsx index 3f39845b5..7635a1755 100644 --- a/pages/[...pages].tsx +++ b/pages/[...pages].tsx @@ -9,16 +9,18 @@ import getSlug from '@lib/get-slug' import { missingLocaleInPages } from '@lib/usage-warns' import { getConfig } from '@framework/api' import getPage from '@framework/common/get-page' -import getAllPages from '@framework/common/get-all-pages' +import getAllPages, { Page } from '@framework/common/get-all-pages' import { defaultPageProps } from '@lib/defaults' export async function getStaticProps({ preview, params, locale, + locales, }: GetStaticPropsContext<{ pages: string[] }>) { - const config = getConfig({ locale }) - const { pages } = await getAllPages({ preview, config }) + const config = getConfig({ locale, locales }) + let { pages } = await getAllPages({ preview, config }) + const path = params?.pages.join('/') const slug = locale ? `${locale}/${path}` : path @@ -40,8 +42,10 @@ export async function getStaticProps({ } export async function getStaticPaths({ locales }: GetStaticPathsContext) { - const { pages } = await getAllPages() + const config = getConfig({ locales }) + let { pages } = await getAllPages({ config }) const [invalidPaths, log] = missingLocaleInPages() + const paths = pages .map((page) => page.url) .filter((url) => { diff --git a/pages/blog.tsx b/pages/blog.tsx index eca3b2295..2c37629d1 100644 --- a/pages/blog.tsx +++ b/pages/blog.tsx @@ -7,8 +7,9 @@ import { Container } from '@components/ui' export async function getStaticProps({ preview, locale, + locales, }: GetStaticPropsContext) { - const config = getConfig({ locale }) + const config = getConfig({ locale, locales }) const { pages } = await getAllPages({ config, preview }) return { props: { pages }, diff --git a/pages/cart.tsx b/pages/cart.tsx index cd5bedacc..e6252e2d9 100644 --- a/pages/cart.tsx +++ b/pages/cart.tsx @@ -11,8 +11,9 @@ import { CartItem } from '@components/cart' export async function getStaticProps({ preview, locale, + locales, }: GetStaticPropsContext) { - const config = getConfig({ locale }) + const config = getConfig({ locale, locales }) const { pages } = await getAllPages({ config, preview }) return { props: { pages }, diff --git a/pages/index.tsx b/pages/index.tsx index 3a84112e5..3be7a7cfe 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -12,8 +12,9 @@ import getAllPages from '@framework/common/get-all-pages' export async function getStaticProps({ preview, locale, + locales, }: GetStaticPropsContext) { - const config = getConfig({ locale }) + const config = getConfig({ locale, locales }) const { products } = await getAllProducts({ variables: { first: 12 }, diff --git a/pages/orders.tsx b/pages/orders.tsx index db4ab55b2..1cab29058 100644 --- a/pages/orders.tsx +++ b/pages/orders.tsx @@ -8,8 +8,9 @@ import getAllPages from '@framework/common/get-all-pages' export async function getStaticProps({ preview, locale, + locales, }: GetStaticPropsContext) { - const config = getConfig({ locale }) + const config = getConfig({ locale, locales }) const { pages } = await getAllPages({ config, preview }) return { props: { pages }, diff --git a/pages/product/[slug].tsx b/pages/product/[slug].tsx index 61420a8d9..9fb3a8686 100644 --- a/pages/product/[slug].tsx +++ b/pages/product/[slug].tsx @@ -16,9 +16,11 @@ export async function getStaticProps({ params, locale, preview, + locales, }: GetStaticPropsContext<{ slug: string }>) { - const config = getConfig({ locale }) + const config = getConfig({ locale, locales }) const { pages } = await getAllPages({ config, preview }) + const { product } = await getProduct({ variables: { slug: params!.slug }, config, diff --git a/pages/profile.tsx b/pages/profile.tsx index ec845c879..307027b09 100644 --- a/pages/profile.tsx +++ b/pages/profile.tsx @@ -8,8 +8,9 @@ import { Container, Text } from '@components/ui' export async function getStaticProps({ preview, locale, + locales, }: GetStaticPropsContext) { - const config = getConfig({ locale }) + const config = getConfig({ locale, locales }) const { pages } = await getAllPages({ config, preview }) return { props: { pages }, diff --git a/pages/search.tsx b/pages/search.tsx index 4100108bc..2c43f0c3a 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -37,8 +37,9 @@ import { Product } from '@commerce/types' export async function getStaticProps({ preview, locale, + locales, }: GetStaticPropsContext) { - const config = getConfig({ locale }) + const config = getConfig({ locale, locales }) const { pages } = await getAllPages({ config, preview }) const { categories, brands } = await getSiteInfo({ config, preview }) return { @@ -46,6 +47,7 @@ export async function getStaticProps({ pages, categories, brands, + locale, }, } } @@ -53,6 +55,7 @@ export async function getStaticProps({ export default function Search({ categories, brands, + locale, }: InferGetStaticPropsType) { const [activeFilter, setActiveFilter] = useState('') const [toggleFilter, setToggleFilter] = useState(false) @@ -78,6 +81,7 @@ export default function Search({ categoryId: activeCategory?.entityId, brandId: activeBrand?.entityId, sort: typeof sort === 'string' ? sort : '', + locale, }) const handleClick = (event: any, filter: string) => { diff --git a/pages/wishlist.tsx b/pages/wishlist.tsx index 0dddaf23d..4c62e26f7 100644 --- a/pages/wishlist.tsx +++ b/pages/wishlist.tsx @@ -12,6 +12,7 @@ import getAllPages from '@framework/common/get-all-pages' export async function getStaticProps({ preview, locale, + locales, }: GetStaticPropsContext) { // Disabling page if Feature is not available if (!process.env.COMMERCE_WISHLIST_ENABLED) { @@ -20,7 +21,7 @@ export async function getStaticProps({ } } - const config = getConfig({ locale }) + const config = getConfig({ locale, locales }) const { pages } = await getAllPages({ config, preview }) return { props: {