diff --git a/components/core/Searchbar/Searchbar.tsx b/components/core/Searchbar/Searchbar.tsx index 865e00a49..28e8f7387 100644 --- a/components/core/Searchbar/Searchbar.tsx +++ b/components/core/Searchbar/Searchbar.tsx @@ -24,14 +24,21 @@ const Searchbar: FC = ({ className }) => { { e.preventDefault() if (e.key === 'Enter') { - router.push({ - pathname: `/search`, - query: { q: e.currentTarget.value }, - }) + const q = e.currentTarget.value + + router.push( + { + pathname: `/search`, + query: q ? { q } : {}, + }, + undefined, + { shallow: true } + ) } }} /> diff --git a/lib/bigcommerce/api/catalog/handlers/get-products.ts b/lib/bigcommerce/api/catalog/handlers/get-products.ts index afcf27a5d..c8c42b862 100644 --- a/lib/bigcommerce/api/catalog/handlers/get-products.ts +++ b/lib/bigcommerce/api/catalog/handlers/get-products.ts @@ -1,3 +1,4 @@ +import getAllProducts from '../../operations/get-all-products' import type { ProductsHandlers } from '../products' // Return current cart info @@ -11,9 +12,18 @@ const getProducts: ProductsHandlers['getProducts'] = async ({ if (search) url.searchParams.set('keyword', search) - const { data } = await config.storeApiFetch(url.pathname + url.search) + // We only want the id of each product + url.searchParams.set('include_fields', 'id') - res.status(200).json({ data }) + const { data } = await config.storeApiFetch<{ data: { id: number }[] }>( + url.pathname + url.search + ) + 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 } }) + + res.status(200).json({ data: { products, found } }) } export default getProducts diff --git a/lib/bigcommerce/api/catalog/products.ts b/lib/bigcommerce/api/catalog/products.ts index 0633846c6..31dc2d2e7 100644 --- a/lib/bigcommerce/api/catalog/products.ts +++ b/lib/bigcommerce/api/catalog/products.ts @@ -1,4 +1,3 @@ -import type { definitions } from '../definitions/catalog' import isAllowedMethod from '../utils/is-allowed-method' import createApiHandler, { BigcommerceApiHandler, @@ -6,22 +5,24 @@ import createApiHandler, { } from '../utils/create-api-handler' import { BigcommerceApiError } from '../utils/errors' import getProducts from './handlers/get-products' +import { Products } from '../operations/get-all-products' -export type Product = definitions['product_Full'] +export type SearchProductsData = { + products: Products + found: boolean +} export type ProductsHandlers = { - getProducts: BigcommerceHandler + getProducts: BigcommerceHandler } const METHODS = ['GET'] // TODO: a complete implementation should have schema validation for `req.body` -const cartApi: BigcommerceApiHandler = async ( - req, - res, - config, - handlers -) => { +const cartApi: BigcommerceApiHandler< + SearchProductsData, + ProductsHandlers +> = async (req, res, config, handlers) => { if (!isAllowedMethod(req, res, METHODS)) return try { diff --git a/lib/bigcommerce/api/fragments/product.ts b/lib/bigcommerce/api/fragments/product.ts index 501345cba..0157f50c1 100644 --- a/lib/bigcommerce/api/fragments/product.ts +++ b/lib/bigcommerce/api/fragments/product.ts @@ -17,6 +17,7 @@ export const productInfoFragment = /* GraphQL */ ` path brand { name + entityId } description prices { diff --git a/lib/bigcommerce/api/operations/get-all-products.ts b/lib/bigcommerce/api/operations/get-all-products.ts index b584d0dd9..e1b094cac 100644 --- a/lib/bigcommerce/api/operations/get-all-products.ts +++ b/lib/bigcommerce/api/operations/get-all-products.ts @@ -9,6 +9,7 @@ import { BigcommerceConfig, getConfig, Images, ProductImageVariables } from '..' export const getAllProductsQuery = /* GraphQL */ ` query getAllProducts( + $entityIds: [Int!] $first: Int = 10 $imgSmallWidth: Int = 320 $imgSmallHeight: Int @@ -20,7 +21,7 @@ export const getAllProductsQuery = /* GraphQL */ ` $imgXLHeight: Int ) { site { - products(first: $first) { + products(first: $first, entityIds: $entityIds) { pageInfo { startCursor endCursor diff --git a/lib/bigcommerce/products/use-search.tsx b/lib/bigcommerce/products/use-search.tsx index b6953bd62..345c7ffbc 100644 --- a/lib/bigcommerce/products/use-search.tsx +++ b/lib/bigcommerce/products/use-search.tsx @@ -1,8 +1,7 @@ -import useSWR, { ConfigInterface } from 'swr' -import { HookDeps, HookFetcher } from '@lib/commerce/utils/types' +import { ConfigInterface } from 'swr' +import { HookFetcher } from '@lib/commerce/utils/types' import useCommerceSearch from '@lib/commerce/products/use-search' -import type { Product } from '../api/catalog/products' -import { useCommerce } from '..' +import type { SearchProductsData } from '../api/catalog/products' const defaultOpts = { url: '/api/bigcommerce/catalog/products', @@ -13,7 +12,7 @@ export type SearchProductsInput = { search?: string } -export const fetcher: HookFetcher = ( +export const fetcher: HookFetcher = ( options, { search }, fetch @@ -34,7 +33,7 @@ export function extendHook( swrOptions?: ConfigInterface ) { const useSearch = (input: SearchProductsInput = {}) => { - const response = useCommerceSearch( + const response = useCommerceSearch( defaultOpts, [['search', input.search]], customFetcher, diff --git a/lib/bigcommerce/schema.d.ts b/lib/bigcommerce/schema.d.ts index 0bf3d42f0..4e81f6c99 100644 --- a/lib/bigcommerce/schema.d.ts +++ b/lib/bigcommerce/schema.d.ts @@ -1674,7 +1674,7 @@ export type ProductInfoFragment = { __typename?: 'Product' } & Pick< Product, 'entityId' | 'name' | 'path' | 'description' > & { - brand?: Maybe<{ __typename?: 'Brand' } & Pick> + brand?: Maybe<{ __typename?: 'Brand' } & Pick> prices?: Maybe< { __typename?: 'Prices' } & { price: { __typename?: 'Money' } & Pick @@ -1759,6 +1759,7 @@ export type GetAllProductPathsQuery = { __typename?: 'Query' } & { } export type GetAllProductsQueryVariables = Exact<{ + entityIds?: Maybe> first?: Maybe imgSmallWidth?: Maybe imgSmallHeight?: Maybe diff --git a/lib/commerce/utils/use-data.tsx b/lib/commerce/utils/use-data.tsx index 08b39091b..0f395f0e1 100644 --- a/lib/commerce/utils/use-data.tsx +++ b/lib/commerce/utils/use-data.tsx @@ -3,23 +3,27 @@ import type { HookInput, HookFetcher, HookFetcherOptions } from './types' import { useCommerce } from '..' export default function useData( - options: HookFetcherOptions, + options: HookFetcherOptions | (() => HookFetcherOptions | null), input: HookInput, fetcherFn: HookFetcher, swrOptions?: ConfigInterface ) { const { fetcherRef } = useCommerce() - const fetcher = (url?: string, query?: string, ...args: any[]) => - fetcherFn( + const fetcher = (url?: string, query?: string, ...args: any[]) => { + return fetcherFn( { url, query }, args.reduce((obj, val, i) => { - obj[input[i][1]!] = val + obj[input[i][0]!] = val return obj }, {}), fetcherRef.current ) + } const response = useSWR( - [options.url, options.query, ...input.map((e) => e[1])], + () => { + const opts = typeof options === 'function' ? options() : options + return opts ? [opts.url, opts.query, ...input.map((e) => e[1])] : null + }, fetcher, swrOptions ) diff --git a/pages/search.tsx b/pages/search.tsx index 81ad40c08..4e89150b0 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -1,5 +1,4 @@ import { GetStaticPropsContext, InferGetStaticPropsType } from 'next' -import getAllProducts from '@lib/bigcommerce/api/operations/get-all-products' import getSiteInfo from '@lib/bigcommerce/api/operations/get-site-info' import useSearch from '@lib/bigcommerce/products/use-search' import { Layout } from '@components/core' @@ -8,23 +7,22 @@ import { ProductCard } from '@components/product' import { useRouter } from 'next/router' export async function getStaticProps({ preview }: GetStaticPropsContext) { - const { products } = await getAllProducts() const { categories, brands } = await getSiteInfo() return { - props: { products, categories, brands }, + props: { categories, brands }, } } export default function Home({ - products, categories, brands, }: InferGetStaticPropsType) { const router = useRouter() - const search = useSearch({ search: router.query.search as string }) - - console.log('SEARCH', search) + const { q } = router.query + const { data } = useSearch({ + search: typeof q === 'string' ? q : '', + }) return (
@@ -51,18 +49,23 @@ export default function Home({
-
- Showing 8 results for "{router.query.q}" -
- + {data ? ( + <> + {q && ( +
+ {data.found ? ( + <>Showing {data.products.length} results for + ) : ( + <>There are no products that match + )}{' '} + "{q}" +
+ )} + + + ) : ( +
Searching...
+ )}