Fix Shopify translations issue

This commit is contained in:
cond0r 2021-05-22 09:28:00 +03:00
parent a4f56d1549
commit b4fb64a292
27 changed files with 211 additions and 133 deletions

View File

@ -47,7 +47,7 @@ const Swatch: FC<Omit<ButtonProps, 'variant'> & Props> = ({
<Check /> <Check />
</span> </span>
)} )}
{variant === 'size' ? label : null} {variant !== 'color' ? label : null}
</Button> </Button>
) )
} }

View File

@ -1,16 +1,9 @@
import { SWRHook } from '@commerce/utils/types' import { SWRHook } from '@commerce/utils/types'
import useSearch, { UseSearch } from '@commerce/product/use-search' 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<typeof handler> export default useSearch as UseSearch<typeof handler>
export type SearchProductsInput = {
search?: string
categoryId?: number
brandId?: number
sort?: string
}
export const handler: SWRHook< export const handler: SWRHook<
SearchProductsData, SearchProductsData,
SearchProductsInput, SearchProductsInput,

View File

@ -2,6 +2,7 @@ import type { RequestInit, Response } from '@vercel/fetch'
export interface CommerceAPIConfig { export interface CommerceAPIConfig {
locale?: string locale?: string
locales?: string[]
commerceUrl: string commerceUrl: string
apiToken: string apiToken: string
cartCookie: string cartCookie: string

View File

@ -1,6 +1,5 @@
import type { Wishlist as BCWishlist } from '../bigcommerce/api/wishlist' import type { Wishlist as BCWishlist } from '../bigcommerce/api/wishlist'
import type { Customer as BCCustomer } from '../bigcommerce/api/customers' import type { Customer as BCCustomer } from '../bigcommerce/api/customers'
import type { SearchProductsData as BCSearchProductsData } from '../bigcommerce/api/catalog/products'
export type Discount = { export type Discount = {
// The value of the discount, can be an amount or percentage // 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 // TODO: Properly define this type
export interface Customer extends BCCustomer {} export interface Customer extends BCCustomer {}
// TODO: Properly define this type export type SearchProductsData = {
export interface SearchProductsData extends BCSearchProductsData {} products: Product[]
found: boolean
}
export type SearchProductsInput = {
search?: string
categoryId?: string
brandId?: string
sort?: string
locale?: string
}
/** /**
* Cart mutations * Cart mutations

View File

@ -43,7 +43,6 @@ export class Config {
} }
const config = new Config({ const config = new Config({
locale: 'en-US',
commerceUrl: API_URL, commerceUrl: API_URL,
apiToken: API_TOKEN!, apiToken: API_TOKEN!,
cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE, cartCookie: SHOPIFY_CHECKOUT_ID_COOKIE,

View File

@ -1,10 +1,12 @@
import { ProductEdge } from '../../schema' import { ProductEdge, QueryRoot, QueryRootProductsArgs } from '../../schema'
import { ShopifyConfig } from '..' import { ShopifyConfig } from '..'
const fetchAllProducts = async ({ const fetchAllProducts = async ({
config, config,
query, query,
variables, variables = {
first: 250,
},
acc = [], acc = [],
cursor, cursor,
}: { }: {
@ -14,9 +16,20 @@ const fetchAllProducts = async ({
variables?: any variables?: any
cursor?: string cursor?: string
}): Promise<ProductEdge[]> => { }): Promise<ProductEdge[]> => {
const { data } = await config.fetch(query, { const { fetch, locale } = config
const { data } = await fetch<QueryRoot, QueryRootProductsArgs>(
query,
{
variables: { ...variables, cursor }, variables: { ...variables, cursor },
}) },
{
...(locale && {
headers: {
'Accept-Locale': locale,
},
}),
}
)
const edges: ProductEdge[] = data.products?.edges ?? [] const edges: ProductEdge[] = data.products?.edges ?? []
const hasNextPage = data.products?.pageInfo?.hasNextPage const hasNextPage = data.products?.pageInfo?.hasNextPage

View File

@ -1,14 +1,6 @@
import { getConfig, ShopifyConfig } from '../api' import { getConfig, ShopifyConfig } from '../api'
import { PageEdge } from '../schema' import { QueryRoot, QueryRootPagesArgs } from '../schema'
import { getAllPagesQuery } from '../utils/queries' import { normalizePages, getAllPagesQuery } from '../utils'
type Variables = {
first?: number
}
type ReturnType = {
pages: Page[]
}
export type Page = { export type Page = {
id: string id: string
@ -18,25 +10,40 @@ export type Page = {
body: string body: string
} }
const getAllPages = async (options?: { export type GetAllPagesResult = Promise<{
variables?: Variables pages: Page[]
config: ShopifyConfig }>
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {}
config = getConfig(config)
const { locale } = config
const { data } = await config.fetch(getAllPagesQuery, { variables })
const pages = data.pages?.edges?.map( const getAllPages = async (options?: {
({ node: { title: name, handle, ...node } }: PageEdge) => ({ variables?: QueryRootPagesArgs
...node, config?: ShopifyConfig
url: `/${locale}/${handle}`, preview?: boolean
name, }): GetAllPagesResult => {
}) let { config, variables } = options ?? {}
const { fetch, locale = 'en-US', locales = ['en-US'] } = getConfig(config)
const {
data: {
pages: { edges },
},
} = await fetch<QueryRoot, QueryRootPagesArgs>(
getAllPagesQuery,
{
variables,
},
{
headers: {
'Accept-Language': locale,
},
}
) )
return { pages } return {
pages: locales.reduce<Page[]>(
(arr, locale) => arr.concat(normalizePages(edges, locale)),
[]
),
}
} }
export default getAllPages export default getAllPages

View File

@ -1,37 +1,41 @@
import { Page as ShopifyPage, QueryRoot, QueryRootPagesArgs } from '../schema'
import { normalizePage, getPageQuery } from '../utils'
import { getConfig, ShopifyConfig } from '../api' import { getConfig, ShopifyConfig } from '../api'
import getPageQuery from '../utils/queries/get-page-query' import type { Page } from './get-all-pages'
import { Page } from './get-all-pages'
type Variables = { type GetPageInput = {
id: string id: string
} }
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T type GetPageResult = {
page?: Page
}
const getPage = async (options: { const getPage = async ({
variables: Variables variables,
config: ShopifyConfig config,
}: {
variables: GetPageInput
config?: ShopifyConfig
preview?: boolean preview?: boolean
}): Promise<GetPageResult> => { }): Promise<GetPageResult> => {
let { config, variables } = options ?? {} const { locale = 'en-US', fetch } = getConfig(config)
config = getConfig(config) const {
const { locale } = config data: { node: page },
} = await fetch<QueryRoot, GetPageInput>(
const { data } = await config.fetch(getPageQuery, { getPageQuery,
{
variables, variables,
}) },
const page = data.node {
headers: {
'Accept-Language': locale,
},
}
)
return { return page ? { page: normalizePage(page as ShopifyPage, locale) } : {}
page: page
? {
...page,
name: page.title,
url: `/${locale}/${page.handle}`,
}
: null,
}
} }
export default getPage export default getPage

View File

@ -8,13 +8,15 @@ const fetcher: Fetcher = async ({
variables, variables,
query, query,
}) => { }) => {
const { locale, ...vars } = variables ?? {}
return handleFetchResponse( return handleFetchResponse(
await fetch(url, { await fetch(url, {
method, method,
body: JSON.stringify({ query, variables }), body: JSON.stringify({ query, variables: vars }),
headers: { headers: {
'X-Shopify-Storefront-Access-Token': API_TOKEN!, 'X-Shopify-Storefront-Access-Token': API_TOKEN!,
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...(locale && { 'Accept-Language': locale! }),
}, },
}) })
) )

View File

@ -22,7 +22,6 @@ export type ShopifyConfig = Partial<CommerceConfig>
export type ShopifyProps = { export type ShopifyProps = {
children?: ReactNode children?: ReactNode
locale: string
} & ShopifyConfig } & ShopifyConfig
export function CommerceProvider({ children, ...config }: ShopifyProps) { export function CommerceProvider({ children, ...config }: ShopifyProps) {

View File

@ -1,4 +1,3 @@
import { Product } from '@commerce/types'
import { getConfig, ShopifyConfig } from '../api' import { getConfig, ShopifyConfig } from '../api'
import fetchAllProducts from '../api/utils/fetch-all-products' import fetchAllProducts from '../api/utils/fetch-all-products'
import { ProductEdge } from '../schema' import { ProductEdge } from '../schema'

View File

@ -1,9 +1,8 @@
import { GraphQLFetcherResult } from '@commerce/api' import { Product } from '@commerce/types'
import { getConfig, ShopifyConfig } from '../api' import { getConfig, ShopifyConfig } from '../api'
import { ProductEdge } from '../schema' import { QueryRoot, QueryRootProductsArgs } from '../schema'
import { getAllProductsQuery } from '../utils/queries' import { getAllProductsQuery } from '../utils/queries'
import { normalizeProduct } from '../utils/normalize' import { normalizeProduct } from '../utils/normalize'
import { Product } from '@commerce/types'
type Variables = { type Variables = {
first?: number first?: number
@ -21,16 +20,25 @@ const getAllProducts = async (options: {
}): Promise<ReturnType> => { }): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {} let { config, variables = { first: 250 } } = options ?? {}
config = getConfig(config) config = getConfig(config)
let products: Product[] = []
const { data }: GraphQLFetcherResult = await config.fetch( const { data } = await config!.fetch<QueryRoot, QueryRootProductsArgs>(
getAllProductsQuery, getAllProductsQuery,
{ variables } {
variables,
},
{
...(config.locale && {
headers: {
'Accept-Language': config.locale!,
},
}),
}
) )
products = [
const products = ...products,
data.products?.edges?.map(({ node: p }: ProductEdge) => ...data.products.edges.map(({ node: p }) => normalizeProduct(p)),
normalizeProduct(p) ]
) ?? []
return { return {
products, products,

View File

@ -1,31 +1,43 @@
import { GraphQLFetcherResult } from '@commerce/api' import { Product } from '@commerce/types'
import { QueryRoot } from '../schema'
import { getConfig, ShopifyConfig } from '../api' import { getConfig, ShopifyConfig } from '../api'
import { normalizeProduct, getProductQuery } from '../utils' import { normalizeProduct, getProductQuery } from '../utils'
type Variables = { export type GetProductInput = {
slug: string slug: string
} }
type ReturnType = { export type GetProductResult = {
product: any product?: Product
} }
const getProduct = async (options: { const getProduct = async ({
variables: Variables
config: ShopifyConfig
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables } = options ?? {}
config = getConfig(config)
const { data }: GraphQLFetcherResult = await config.fetch(getProductQuery, {
variables, variables,
}) config,
const { productByHandle } = data }: {
variables: GetProductInput
config?: ShopifyConfig
preview?: boolean
}): Promise<GetProductResult> => {
const { fetch, locale } = getConfig(config)
return { const {
product: productByHandle ? normalizeProduct(productByHandle) : null, data: { productByHandle },
} = await fetch<QueryRoot>(
getProductQuery,
{
variables,
},
{
...(locale && {
headers: {
'Accept-Language': locale,
},
}),
} }
)
return productByHandle ? { product: normalizeProduct(productByHandle) } : {}
} }
export default getProduct export default getProduct

View File

@ -1,7 +1,9 @@
import { SWRHook } from '@commerce/utils/types' import { SWRHook } from '@commerce/utils/types'
import useSearch, { UseSearch } from '@commerce/product/use-search' import useSearch, { UseSearch } from '@commerce/product/use-search'
import { SearchProductsInput, SearchProductsData } from '@commerce/types'
import { ProductEdge } from '../schema' import { ProductEdge } from '../schema'
import { import {
getAllProductsQuery, getAllProductsQuery,
getCollectionProductsQuery, getCollectionProductsQuery,
@ -9,22 +11,8 @@ import {
normalizeProduct, normalizeProduct,
} from '../utils' } from '../utils'
import { Product } from '@commerce/types'
export default useSearch as UseSearch<typeof handler> export default useSearch as UseSearch<typeof handler>
export type SearchProductsInput = {
search?: string
categoryId?: string
brandId?: string
sort?: string
}
export type SearchProductsData = {
products: Product[]
found: boolean
}
export const handler: SWRHook< export const handler: SWRHook<
SearchProductsData, SearchProductsData,
SearchProductsInput, SearchProductsInput,
@ -68,6 +56,7 @@ export const handler: SWRHook<
['categoryId', input.categoryId], ['categoryId', input.categoryId],
['brandId', input.brandId], ['brandId', input.brandId],
['sort', input.sort], ['sort', input.sort],
['locale', input.locale],
], ],
swrOptions: { swrOptions: {
revalidateOnFocus: false, revalidateOnFocus: false,

View File

@ -1,5 +1,5 @@
import { ShopifyConfig } from '../api' import { ShopifyConfig } from '../api'
import { CollectionEdge } from '../schema' import { CollectionEdge, QueryRoot, QueryRootCollectionsArgs } from '../schema'
import getSiteCollectionsQuery from './queries/get-all-collections-query' import getSiteCollectionsQuery from './queries/get-all-collections-query'
export type Category = { export type Category = {
@ -9,11 +9,22 @@ export type Category = {
} }
const getCategories = async (config: ShopifyConfig): Promise<Category[]> => { const getCategories = async (config: ShopifyConfig): Promise<Category[]> => {
const { data } = await config.fetch(getSiteCollectionsQuery, { const { fetch, locale } = config
const { data } = await fetch<QueryRoot, QueryRootCollectionsArgs>(
getSiteCollectionsQuery,
{
variables: { variables: {
first: 250, first: 250,
}, },
}) },
{
...(locale && {
headers: {
'Accept-Language': locale,
},
}),
}
)
return ( return (
data.collections?.edges?.map( data.collections?.edges?.map(

View File

@ -1,11 +1,12 @@
import getSortVariables from './get-sort-variables' import getSortVariables from './get-sort-variables'
import type { SearchProductsInput } from '../product/use-search' import type { SearchProductsInput } from '@commerce/types'
export const getSearchVariables = ({ export const getSearchVariables = ({
brandId, brandId,
search, search,
categoryId, categoryId,
sort, sort,
locale,
}: SearchProductsInput) => { }: SearchProductsInput) => {
let query = '' let query = ''
@ -20,6 +21,7 @@ export const getSearchVariables = ({
return { return {
categoryId, categoryId,
query, query,
locale,
...getSortVariables(sort, !!categoryId), ...getSortVariables(sort, !!categoryId),
} }
} }

View File

@ -18,9 +18,6 @@ const getVendors = async (config: ShopifyConfig): Promise<BrandEdge[]> => {
const vendors = await fetchAllProducts({ const vendors = await fetchAllProducts({
config, config,
query: getAllProductVendors, query: getAllProductVendors,
variables: {
first: 250,
},
}) })
let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor) let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor)

View File

@ -9,10 +9,14 @@ import {
ProductVariantConnection, ProductVariantConnection,
MoneyV2, MoneyV2,
ProductOption, ProductOption,
PageEdge,
Page as ShopifyPage,
} from '../schema' } from '../schema'
import type { Cart, LineItem } from '../types' import type { Cart, LineItem } from '../types'
import type { Page } from '../common/get-all-pages'
const money = ({ amount, currencyCode }: MoneyV2) => { const money = ({ amount, currencyCode }: MoneyV2) => {
return { return {
value: +amount, value: +amount,
@ -39,6 +43,7 @@ const normalizeProductOption = ({
hexColors: [value], hexColors: [value],
} }
} }
return output 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))

View File

@ -9,16 +9,18 @@ import getSlug from '@lib/get-slug'
import { missingLocaleInPages } from '@lib/usage-warns' import { missingLocaleInPages } from '@lib/usage-warns'
import { getConfig } from '@framework/api' import { getConfig } from '@framework/api'
import getPage from '@framework/common/get-page' 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' import { defaultPageProps } from '@lib/defaults'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
params, params,
locale, locale,
locales,
}: GetStaticPropsContext<{ pages: string[] }>) { }: GetStaticPropsContext<{ pages: string[] }>) {
const config = getConfig({ locale }) const config = getConfig({ locale, locales })
const { pages } = await getAllPages({ preview, config }) let { pages } = await getAllPages({ preview, config })
const path = params?.pages.join('/') const path = params?.pages.join('/')
const slug = locale ? `${locale}/${path}` : path const slug = locale ? `${locale}/${path}` : path
@ -40,8 +42,10 @@ export async function getStaticProps({
} }
export async function getStaticPaths({ locales }: GetStaticPathsContext) { export async function getStaticPaths({ locales }: GetStaticPathsContext) {
const { pages } = await getAllPages() const config = getConfig({ locales })
let { pages } = await getAllPages({ config })
const [invalidPaths, log] = missingLocaleInPages() const [invalidPaths, log] = missingLocaleInPages()
const paths = pages const paths = pages
.map((page) => page.url) .map((page) => page.url)
.filter((url) => { .filter((url) => {

View File

@ -7,8 +7,9 @@ import { Container } from '@components/ui'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
locale, locale,
locales,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
const config = getConfig({ locale }) const config = getConfig({ locale, locales })
const { pages } = await getAllPages({ config, preview }) const { pages } = await getAllPages({ config, preview })
return { return {
props: { pages }, props: { pages },

View File

@ -11,8 +11,9 @@ import { CartItem } from '@components/cart'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
locale, locale,
locales,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
const config = getConfig({ locale }) const config = getConfig({ locale, locales })
const { pages } = await getAllPages({ config, preview }) const { pages } = await getAllPages({ config, preview })
return { return {
props: { pages }, props: { pages },

View File

@ -12,8 +12,9 @@ import getAllPages from '@framework/common/get-all-pages'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
locale, locale,
locales,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
const config = getConfig({ locale }) const config = getConfig({ locale, locales })
const { products } = await getAllProducts({ const { products } = await getAllProducts({
variables: { first: 12 }, variables: { first: 12 },

View File

@ -8,8 +8,9 @@ import getAllPages from '@framework/common/get-all-pages'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
locale, locale,
locales,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
const config = getConfig({ locale }) const config = getConfig({ locale, locales })
const { pages } = await getAllPages({ config, preview }) const { pages } = await getAllPages({ config, preview })
return { return {
props: { pages }, props: { pages },

View File

@ -16,9 +16,11 @@ export async function getStaticProps({
params, params,
locale, locale,
preview, preview,
locales,
}: GetStaticPropsContext<{ slug: string }>) { }: GetStaticPropsContext<{ slug: string }>) {
const config = getConfig({ locale }) const config = getConfig({ locale, locales })
const { pages } = await getAllPages({ config, preview }) const { pages } = await getAllPages({ config, preview })
const { product } = await getProduct({ const { product } = await getProduct({
variables: { slug: params!.slug }, variables: { slug: params!.slug },
config, config,

View File

@ -8,8 +8,9 @@ import { Container, Text } from '@components/ui'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
locale, locale,
locales,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
const config = getConfig({ locale }) const config = getConfig({ locale, locales })
const { pages } = await getAllPages({ config, preview }) const { pages } = await getAllPages({ config, preview })
return { return {
props: { pages }, props: { pages },

View File

@ -37,8 +37,9 @@ import { Product } from '@commerce/types'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
locale, locale,
locales,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
const config = getConfig({ locale }) const config = getConfig({ locale, locales })
const { pages } = await getAllPages({ config, preview }) const { pages } = await getAllPages({ config, preview })
const { categories, brands } = await getSiteInfo({ config, preview }) const { categories, brands } = await getSiteInfo({ config, preview })
return { return {
@ -46,6 +47,7 @@ export async function getStaticProps({
pages, pages,
categories, categories,
brands, brands,
locale,
}, },
} }
} }
@ -53,6 +55,7 @@ export async function getStaticProps({
export default function Search({ export default function Search({
categories, categories,
brands, brands,
locale,
}: InferGetStaticPropsType<typeof getStaticProps>) { }: InferGetStaticPropsType<typeof getStaticProps>) {
const [activeFilter, setActiveFilter] = useState('') const [activeFilter, setActiveFilter] = useState('')
const [toggleFilter, setToggleFilter] = useState(false) const [toggleFilter, setToggleFilter] = useState(false)
@ -78,6 +81,7 @@ export default function Search({
categoryId: activeCategory?.entityId, categoryId: activeCategory?.entityId,
brandId: activeBrand?.entityId, brandId: activeBrand?.entityId,
sort: typeof sort === 'string' ? sort : '', sort: typeof sort === 'string' ? sort : '',
locale,
}) })
const handleClick = (event: any, filter: string) => { const handleClick = (event: any, filter: string) => {

View File

@ -12,6 +12,7 @@ import getAllPages from '@framework/common/get-all-pages'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
locale, locale,
locales,
}: GetStaticPropsContext) { }: GetStaticPropsContext) {
// Disabling page if Feature is not available // Disabling page if Feature is not available
if (!process.env.COMMERCE_WISHLIST_ENABLED) { 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 }) const { pages } = await getAllPages({ config, preview })
return { return {
props: { props: {