diff --git a/lib/bigcommerce/api/catalog/handlers/get-products.ts b/lib/bigcommerce/api/catalog/handlers/get-products.ts index 3d0832409..4ae1f4f6e 100644 --- a/lib/bigcommerce/api/catalog/handlers/get-products.ts +++ b/lib/bigcommerce/api/catalog/handlers/get-products.ts @@ -1,4 +1,7 @@ -import getAllProducts from '../../operations/get-all-products' +import getAllProducts, { + Products, + Product, +} from '../../operations/get-all-products' import type { ProductsHandlers } from '../products' const SORT: { [key: string]: string | undefined } = { @@ -16,6 +19,9 @@ const getProducts: ProductsHandlers['getProducts'] = async ({ // Use a dummy base as we only care about the relative path const url = new URL('/v3/catalog/products', 'http://a') + // The limit should math the number of products returned by `getAllProducts` + url.searchParams.set('limit', '10') + if (search) url.searchParams.set('keyword', search) if (category && Number.isInteger(Number(category))) @@ -35,7 +41,7 @@ const getProducts: ProductsHandlers['getProducts'] = async ({ } // We only want the id of each product - url.searchParams.set('include_fields', 'id') + url.searchParams.set('include_fields', 'id,price') const { data } = await config.storeApiFetch<{ data: { id: number }[] }>( url.pathname + url.search @@ -43,7 +49,23 @@ const getProducts: ProductsHandlers['getProducts'] = async ({ const entityIds = data.map((p) => p.id) const found = entityIds.length > 0 // We want the GraphQL version of each product - const { products } = await getAllProducts({ variables: { entityIds } }) + const graphqlData = await getAllProducts({ variables: { entityIds } }) + // Put the products in an object that we can use to get them by id + const productsById = graphqlData.products.reduce<{ [k: number]: Product }>( + (prods, p) => { + prods[p.node.entityId] = p + return prods + }, + {} + ) + const products: Products = [] + + // Populate the products array with the graphql products, in the order + // assigned by the list of entity ids + entityIds.forEach((id) => { + const product = productsById[id] + if (product) products.push(product) + }) res.status(200).json({ data: { products, found } }) } diff --git a/pages/search.tsx b/pages/search.tsx index f2303417c..6fa720288 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -1,4 +1,3 @@ -import { useEffect, useState } from 'react' import { GetStaticPropsContext, InferGetStaticPropsType } from 'next' import { useRouter } from 'next/router' import Link from 'next/link' @@ -8,6 +7,13 @@ import useSearch from '@lib/bigcommerce/products/use-search' import { Layout } from '@components/core' import { Container, Grid } from '@components/ui' import { ProductCard } from '@components/product' +import { + filterQuery, + getCategoryPath, + getDesignerPath, + getSlug, + useSearchMeta, +} from '@utils/search' export async function getStaticProps({ preview }: GetStaticPropsContext) { const { categories, brands } = await getSiteInfo() @@ -180,47 +186,3 @@ export default function Search({ } Search.Layout = Layout - -function useSearchMeta(asPath: string) { - const [category, setCategory] = useState() - const [brand, setBrand] = useState() - - useEffect(() => { - const parts = asPath.split('/') - - let c = parts[2] - let b = parts[3] - - if (c === 'designers') { - c = parts[4] - } - - if (c !== category) setCategory(c) - if (b !== brand) setBrand(b) - }, [asPath]) - - return { category, brand } -} - -// Removes empty query parameters from the query object -const filterQuery = (query: any) => - Object.keys(query).reduce((obj, key) => { - if (query[key]?.length) { - obj[key] = query[key] - } - return obj - }, {}) - -// Remove trailing and leading slash -const getSlug = (path: string) => path.replace(/^\/|\/$/g, '') - -const getCategoryPath = (slug: string, brand?: string) => - `/search${brand ? `/designers/${brand}` : ''}${slug ? `/${slug}` : ''}` - -const getDesignerPath = (slug: string, category?: string) => { - const designer = slug.replace(/^brands/, 'designers') - - return `/search${designer ? `/${designer}` : ''}${ - category ? `/${category}` : '' - }` -} diff --git a/tsconfig.json b/tsconfig.json index ec924f865..4760611cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,8 @@ "paths": { "@lib/*": ["lib/*"], "@assets/*": ["assets/*"], - "@components/*": ["components/*"] + "@components/*": ["components/*"], + "@utils/*": ["utils/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], diff --git a/utils/search.tsx b/utils/search.tsx new file mode 100644 index 000000000..192c1e0b6 --- /dev/null +++ b/utils/search.tsx @@ -0,0 +1,45 @@ +import { useEffect, useState } from 'react' + +export function useSearchMeta(asPath: string) { + const [category, setCategory] = useState() + const [brand, setBrand] = useState() + + useEffect(() => { + const parts = asPath.split('/') + + let c = parts[2] + let b = parts[3] + + if (c === 'designers') { + c = parts[4] + } + + if (c !== category) setCategory(c) + if (b !== brand) setBrand(b) + }, [asPath]) + + return { category, brand } +} + +// Removes empty query parameters from the query object +export const filterQuery = (query: any) => + Object.keys(query).reduce((obj, key) => { + if (query[key]?.length) { + obj[key] = query[key] + } + return obj + }, {}) + +// Remove trailing and leading slash +export const getSlug = (path: string) => path.replace(/^\/|\/$/g, '') + +export const getCategoryPath = (slug: string, brand?: string) => + `/search${brand ? `/designers/${brand}` : ''}${slug ? `/${slug}` : ''}` + +export const getDesignerPath = (slug: string, category?: string) => { + const designer = slug.replace(/^brands/, 'designers') + + return `/search${designer ? `/${designer}` : ''}${ + category ? `/${category}` : '' + }` +}