Add use-search hooks

Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
Chloe 2022-04-25 17:15:24 +07:00
parent 79b659048e
commit 2401a42b63
11 changed files with 206 additions and 13 deletions

View File

@ -7937,6 +7937,8 @@ export type AuthenticateMutation = {
export type CatalogItemsQueryVariables = Exact<{ export type CatalogItemsQueryVariables = Exact<{
first?: InputMaybe<Scalars['ConnectionLimitInt']> first?: InputMaybe<Scalars['ConnectionLimitInt']>
sortBy?: InputMaybe<CatalogItemSortByField> sortBy?: InputMaybe<CatalogItemSortByField>
sortOrder?: InputMaybe<SortOrder>
sortByPriceCurrencyCode?: InputMaybe<Scalars['String']>
tagIds?: InputMaybe< tagIds?: InputMaybe<
Array<InputMaybe<Scalars['ID']>> | InputMaybe<Scalars['ID']> Array<InputMaybe<Scalars['ID']>> | InputMaybe<Scalars['ID']>
> >
@ -8201,6 +8203,18 @@ export type GetProductBySlugQuery = {
} | null } | null
} }
export type GetShopCurrencyQueryVariables = Exact<{
id: Scalars['ID']
}>
export type GetShopCurrencyQuery = {
__typename?: 'Query'
shop?: {
__typename?: 'Shop'
currency: { __typename?: 'Currency'; code: string }
} | null
}
export type GetTagsQueryVariables = Exact<{ export type GetTagsQueryVariables = Exact<{
first: Scalars['ConnectionLimitInt'] first: Scalars['ConnectionLimitInt']
shopId: Scalars['ID'] shopId: Scalars['ID']

View File

@ -0,0 +1,56 @@
import type { ProductsEndpoint } from './products'
import getSearchVariables from '../../utils/get-search-variables'
import getSortVariables from '../../utils/get-sort-variables'
import { CatalogItemsQueryVariables } from '../../../../schema'
import getShopCurrencyQuery from '../../queries/get-shop-currency-query'
const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
body: { brandId, search, sort, categoryId },
res,
config,
commerce,
}) => {
let sortParams = getSortVariables(sort)
if (sortParams?.sortBy === 'featured' && !categoryId) {
sortParams = null
}
let currency: string | null = null
if (sortParams?.sortBy === 'minPrice') {
const {
data: {
shop: {
currency: { code },
},
},
} = await config.fetch(getShopCurrencyQuery, {
variables: { id: config.shopId },
})
currency = code
}
const { products } = await commerce.getAllProducts({
variables: {
...getSearchVariables({ brandId, search, categoryId }),
...(sortParams
? {
sortBy: sortParams.sortBy as CatalogItemsQueryVariables['sortBy'],
sortOrder:
sortParams.sortOrder as CatalogItemsQueryVariables['sortOrder'],
}
: {}),
...(currency ? { sortByPriceCurrencyCode: currency } : {}),
},
config,
})
res.status(200).json({
data: {
products,
found: !!products.length,
},
})
}
export default getProducts

View File

@ -1 +0,0 @@
export default function noopApi(...args: any[]): void {}

View File

@ -1 +1,20 @@
export default function noopApi(...args: any[]): void {} import { CommerceAPI, createEndpoint, GetAPISchema } from '@vercel/commerce/api'
import productsEndpoint from '@vercel/commerce/api/endpoints/catalog/products'
import type { ProductsSchema } from '../../../types/product'
import type { OpenCommerceAPI } from '../../index'
import getProducts from './get-products'
export type ProductsAPI = GetAPISchema<OpenCommerceAPI, ProductsSchema>
export type ProductsEndpoint = ProductsAPI['endpoint']
export const handlers: ProductsEndpoint['handlers'] = {
getProducts,
}
const productsApi = createEndpoint<ProductsAPI>({
handler: productsEndpoint,
handlers,
})
export default productsApi

View File

@ -15,7 +15,7 @@ export default function getAllProductsOperation({
commerce, commerce,
}: OperationContext<Provider>) { }: OperationContext<Provider>) {
async function getAllProducts<T extends GetAllProductsOperation>(opts?: { async function getAllProducts<T extends GetAllProductsOperation>(opts?: {
variables?: T['variables'] variables?: Omit<CatalogItemsQueryVariables, 'shopIds'>
config?: Partial<OpenCommerceConfig> config?: Partial<OpenCommerceConfig>
preview?: boolean preview?: boolean
}): Promise<T['data']> }): Promise<T['data']>
@ -26,7 +26,7 @@ export default function getAllProductsOperation({
config, config,
}: { }: {
query?: string query?: string
variables?: T['variables'] variables?: Omit<CatalogItemsQueryVariables, 'shopIds'>
config?: Partial<OpenCommerceConfig> config?: Partial<OpenCommerceConfig>
preview?: boolean preview?: boolean
} = {}): Promise<T['data']> { } = {}): Promise<T['data']> {

View File

@ -2,6 +2,8 @@ const catalogItemsQuery = /* GraphQL */ `
query catalogItems( query catalogItems(
$first: ConnectionLimitInt = 250 $first: ConnectionLimitInt = 250
$sortBy: CatalogItemSortByField = updatedAt $sortBy: CatalogItemSortByField = updatedAt
$sortOrder: SortOrder = desc
$sortByPriceCurrencyCode: String
$tagIds: [ID] $tagIds: [ID]
$shopIds: [ID]! $shopIds: [ID]!
$searchQuery: String $searchQuery: String
@ -9,9 +11,11 @@ const catalogItemsQuery = /* GraphQL */ `
catalogItems( catalogItems(
first: $first first: $first
sortBy: $sortBy sortBy: $sortBy
sortOrder: $sortOrder
tagIds: $tagIds tagIds: $tagIds
shopIds: $shopIds shopIds: $shopIds
searchQuery: $searchQuery searchQuery: $searchQuery
sortByPriceCurrencyCode: $sortByPriceCurrencyCode
) { ) {
pageInfo { pageInfo {
hasNextPage hasNextPage

View File

@ -0,0 +1,11 @@
const getShopCurrencyQuery = /* GraphQL */ `
query getShopCurrency($id: ID!) {
shop(id: $id) {
currency {
code
}
}
}
`
export default getShopCurrencyQuery

View File

@ -0,0 +1,36 @@
export type SearchProductsInput = {
search?: string
categoryId?: number | string
brandId?: number | string
locale?: string
}
const getSearchVariables = ({
brandId,
search,
categoryId,
}: SearchProductsInput) => {
let searchQuery = ''
let tagIdsParam = {}
if (search) {
searchQuery += search
}
if (brandId) {
searchQuery += `${search ? ' ' : ''}${brandId}`
}
if (categoryId) {
tagIdsParam = {
tagIds: [categoryId],
}
}
return {
searchQuery,
...tagIdsParam,
}
}
export default getSearchVariables

View File

@ -0,0 +1,25 @@
type SortByField = 'minPrice' | 'featured' | 'createdAt'
const getSortVariables = (sort?: string) => {
if (!sort) return null
const [_sort, direction] = sort.split('-')
const SORT: { [key: string]: SortByField | undefined } = {
price: 'minPrice',
trending: 'featured',
latest: 'createdAt',
}
const sortValue = SORT[_sort]
if (sortValue && direction) {
return {
sortBy: sortValue,
sortOrder: direction,
}
}
return null
}
export default getSortVariables

View File

@ -1,17 +1,45 @@
import { SWRHook } from '@vercel/commerce/utils/types' import { SWRHook } from '@vercel/commerce/utils/types'
import useSearch, { UseSearch } from '@vercel/commerce/product/use-search' import useSearch, { UseSearch } from '@vercel/commerce/product/use-search'
import type { SearchProductsHook } from '../types/product'
export default useSearch as UseSearch<typeof handler> export default useSearch as UseSearch<typeof handler>
export const handler: SWRHook<any> = { export const handler: SWRHook<SearchProductsHook> = {
fetchOptions: { fetchOptions: {
query: '', url: '/api/catalog/products',
method: 'GET',
}, },
async fetcher({ input, options, fetch }) {}, async fetcher({
useHook: () => () => { input: { search, categoryId, brandId, sort },
return { options,
data: { fetch,
products: [], }) {
}, // Use a dummy base as we only care about the relative path
} const url = new URL(options.url!, 'http://a')
if (search) url.searchParams.set('search', search)
if (categoryId) url.searchParams.set('categoryId', String(categoryId))
if (brandId) url.searchParams.set('brandId', String(brandId))
if (sort) url.searchParams.set('sort', sort)
return fetch({
url: url.pathname + url.search,
method: options.method,
})
}, },
useHook:
({ useData }) =>
(input = {}) => {
return useData({
input: [
['search', input.search],
['categoryId', input.categoryId],
['brandId', input.brandId],
['sort', input.sort],
],
swrOptions: {
revalidateOnFocus: false,
...input.swrOptions,
},
})
},
} }

View File

@ -54,6 +54,7 @@ export default function Search({ categories, brands }: SearchPropsType) {
locale, locale,
}) })
console.log({ data })
const handleClick = (event: any, filter: string) => { const handleClick = (event: any, filter: string) => {
if (filter !== activeFilter) { if (filter !== activeFilter) {
setToggleFilter(true) setToggleFilter(true)