query all products for vendors & paths, improve search

This commit is contained in:
cond0r 2021-02-12 09:56:03 +02:00
parent 1450492826
commit 8d801ce5d7
20 changed files with 315 additions and 126 deletions

View File

@ -7,7 +7,8 @@ import {
SHOPIFY_CHECKOUT_ID_COOKIE,
SHOPIFY_CHECKOUT_URL_COOKIE,
SHOPIFY_CUSTOMER_TOKEN_COOKIE,
} from '@framework/provider'
} from '@framework/const'
import { getConfig } from '..'
import associateCustomerWithCheckoutMutation from '@framework/utils/mutations/associate-customer-with-checkout'

View File

@ -0,0 +1,41 @@
import { ProductEdge } from '@framework/schema'
import { ShopifyConfig } from '..'
const fetchAllProducts = async ({
config,
query,
variables,
acc = [],
cursor,
}: {
config: ShopifyConfig
query: string
acc?: ProductEdge[]
variables?: any
cursor?: string
}): Promise<ProductEdge[]> => {
const { data } = await config.fetch(query, {
variables: { ...variables, cursor },
})
const edges: ProductEdge[] = data?.products?.edges ?? []
const hasNextPage = data?.products?.pageInfo?.hasNextPage
acc = acc.concat(edges)
if (hasNextPage) {
const cursor = edges.pop()?.cursor
if (cursor) {
return fetchAllProducts({
config,
query,
variables,
acc,
cursor,
})
}
}
return acc
}
export default fetchAllProducts

View File

@ -1,7 +1,8 @@
import {
SHOPIFY_CHECKOUT_ID_COOKIE,
SHOPIFY_CHECKOUT_URL_COOKIE,
} from '@framework/provider'
} from '@framework/const'
import checkoutCreateMutation from '@framework/utils/mutations/checkout-create'
import Cookies from 'js-cookie'

View File

@ -19,14 +19,12 @@ const getAllPages = async (options?: {
config = getConfig(config)
const { data } = await config.fetch(getAllPagesQuery, { variables })
const edges = data?.pages?.edges
const pages = data.pages.edges.map(({ node }: PageEdge) => {
return {
...node,
name: node.handle,
url: `${config!.locale}/${node.handle}`,
}
})
const pages = edges?.map(({ node }: PageEdge) => ({
...node,
url: node.handle,
}))
return { pages }
}

View File

@ -1,27 +1,39 @@
import { ShopifyConfig, getConfig } from '../api'
import type { Page } from '../types'
import { GraphQLFetcherResult } from '@commerce/api'
export type { Page }
import { getConfig, ShopifyConfig } from '../api'
import getPageQuery from '@framework/utils/queries/get-page-query'
import { Page, PageEdge } from '@framework/schema'
export type GetPageResult<T extends { page?: any } = { page?: Page }> = T
export type PageVariables = {
id: string
type Variables = {
slug: string
}
async function getPage({
url,
variables,
config,
preview,
}: {
url?: string
variables: PageVariables
config?: ShopifyConfig
type ReturnType = {
page: any
}
const getPage = async (options: {
variables: Variables
config: ShopifyConfig
preview?: boolean
}): Promise<GetPageResult> {
}): Promise<ReturnType> => {
let { config, variables } = options ?? {}
config = getConfig(config)
return {}
const { data }: GraphQLFetcherResult = await config.fetch(getPageQuery, {
variables,
})
const page: Page = data?.pageByHandle
return {
page: page
? {
...page,
url: page?.handle,
}
: null,
}
}
export default getPage

View File

@ -1,30 +1,30 @@
import { CollectionEdge } from '@framework/schema'
import getCategories, { Category } from '@framework/utils/get-categories'
import getVendors, { Brands } from '@framework/utils/get-vendors'
import { getConfig, ShopifyConfig } from '../api'
import getAllCollectionsQuery from '../utils/queries/get-all-collections-query'
export type GetSiteInfoResult<
T extends { categories: any[]; brands: any[] } = {
categories: Category[]
brands: Brands
}
> = T
const getSiteInfo = async (options?: {
variables?: any
config: ShopifyConfig
preview?: boolean
}) => {
let { config, variables = { first: 250 } } = options ?? {}
}): Promise<GetSiteInfoResult> => {
let { config } = options ?? {}
config = getConfig(config)
const { data } = await config.fetch(getAllCollectionsQuery, { variables })
const edges = data.collections?.edges ?? []
const categories = edges.map(
({ node: { id: entityId, title: name, handle } }: CollectionEdge) => ({
entityId,
name,
path: `/${handle}`,
})
)
const categories = await getCategories(config)
const brands = await getVendors(config)
return {
categories,
brands: [],
brands,
}
}

View File

@ -1,4 +1,5 @@
import { getConfig, ShopifyConfig } from '../api'
import fetchAllProducts from '../api/utils/fetch-all-products'
import { ProductEdge } from '../schema'
import getAllProductsPathsQuery from '../utils/queries/get-all-products-paths-query'
@ -9,21 +10,19 @@ type ReturnType = {
const getAllProductPaths = async (options?: {
variables?: any
config?: ShopifyConfig
previe?: boolean
preview?: boolean
}): Promise<ReturnType> => {
let { config, variables = { first: 250 } } = options ?? {}
config = getConfig(config)
const { data } = await config.fetch(getAllProductsPathsQuery, {
const products = await fetchAllProducts({
config,
query: getAllProductsPathsQuery,
variables,
})
const edges = data?.products?.edges
const productInfo = data?.products?.productInfo
const hasNextPage = productInfo?.hasNextPage
return {
products: edges?.map(({ node: { handle } }: ProductEdge) => ({
products: products?.map(({ node: { handle } }: ProductEdge) => ({
node: {
path: `/${handle}`,
},

View File

@ -1,22 +1,22 @@
import useCommerceSearch from '@commerce/products/use-search'
import getAllProductsQuery from '@framework/utils/queries/get-all-products-query'
import {
getAllProductsQuery,
getCollectionProductsQuery,
} from '@framework/utils/queries'
import type { Product } from 'framework/bigcommerce/schema'
import type { HookFetcher } from '@commerce/utils/types'
import type { SwrOptions } from '@commerce/utils/use-data'
import type { ProductEdge } from '@framework/schema'
import {
searchByProductType,
searchByTag,
} from '@framework/utils/get-search-variables'
import getSearchVariables from '@framework/utils/get-search-variables'
import sortBy from '@framework/utils/get-sort-variables'
import { normalizeProduct } from '@framework/lib/normalize'
export type SearchProductsInput = {
search?: string
categoryPath?: string
categoryId?: string
brandId?: string
sort?: string
}
@ -32,22 +32,20 @@ export type SearchProductsData = {
export const fetcher: HookFetcher<
SearchRequestProductsData,
SearchProductsInput
> = (options, { search, categoryPath, sort }, fetch) => {
> = (options, input, fetch) => {
return fetch({
query: options?.query,
method: options?.method,
variables: {
...searchByProductType(search),
...searchByTag(categoryPath),
...sortBy(sort),
...getSearchVariables(input),
},
}).then(
({ products }): SearchProductsData => {
(resp): SearchProductsData => {
const edges = resp.products?.edges
return {
products: products?.edges?.map(({ node: p }: ProductEdge) =>
normalizeProduct(p)
),
found: !!products?.edges?.length,
products: edges?.map(({ node: p }: ProductEdge) => normalizeProduct(p)),
found: !!edges?.length,
}
}
)
@ -64,7 +62,8 @@ export function extendHook(
},
[
['search', input.search],
['categoryPath', input.categoryPath],
['categoryId', input.categoryId],
['brandId', input.brandId],
['sort', input.sort],
],
customFetcher,

View File

@ -9,6 +9,7 @@ import { normalizeCart } from './lib/normalize'
import { Cart } from './types'
import handleFetchResponse from './utils/handle-fetch-response'
import { getCheckoutQuery } from './utils/queries'
const useCart: HookHandler<
Cart | null,
@ -19,8 +20,7 @@ const useCart: HookHandler<
{ isEmpty?: boolean }
> = {
fetchOptions: {
url: '/api/bigcommerce/cart',
method: 'GET',
query: getCheckoutQuery,
},
swrOptions: {
revalidateOnFocus: false,
@ -38,7 +38,7 @@ const useCart: HookHandler<
},
}
const fetcher: Fetcher = async ({ method = 'GET', variables, query }) => {
const fetcher: Fetcher = async ({ method = 'POST', variables, query }) => {
return handleFetchResponse(
await fetch(API_URL, {
method,

View File

@ -0,0 +1,29 @@
import { ShopifyConfig } from '@framework/api'
import { CollectionEdge } from '@framework/schema'
import getSiteCollectionsQuery from './queries/get-all-collections-query'
export type Category = {
endityId: string
name: string
path: string
}
const getCategories = async (config: ShopifyConfig): Promise<Category[]> => {
const { data } = await config.fetch(getSiteCollectionsQuery, {
variables: {
first: 250,
},
})
return (
data?.collections?.edges?.map(
({ node: { title: name, handle } }: CollectionEdge) => ({
entityId: handle,
name,
path: `/${handle}`,
})
) ?? []
)
}
export default getCategories

View File

@ -1,15 +1,30 @@
export const searchByProductType = (search?: string) => {
return search
? {
query: `product_type:${search}`,
}
: {}
import { SearchProductsInput } from '@framework/product/use-search'
import getSortVariables from './get-sort-variables'
export const getSearchVariables = ({
categoryId,
brandId,
search,
sort,
}: SearchProductsInput) => {
let query = ''
if (search) {
query += `product_type:${search} OR title:${search} OR tag:${search}`
}
if (categoryId) {
query += `tag:${categoryId}`
}
if (brandId) {
query += `${categoryId ? ' AND ' : ''}vendor:${brandId}`
}
return {
query,
...getSortVariables(sort),
}
}
export const searchByTag = (categoryPath?: string) => {
return categoryPath
? {
query: `tag:${categoryPath}`,
}
: {}
}
export default getSearchVariables

View File

@ -0,0 +1,36 @@
import { ShopifyConfig } from '@framework/api'
import fetchAllProducts from '@framework/api/utils/fetch-all-products'
import getAllProductVendors from './queries/get-all-product-vendors-query'
export type BrandNode = {
name: string
path: string
}
export type BrandEdge = {
node: BrandNode
}
export type Brands = BrandEdge[]
const getVendors = async (config: ShopifyConfig): Promise<BrandEdge[]> => {
const vendors = await fetchAllProducts({
config,
query: getAllProductVendors,
variables: {
first: 250,
},
})
let vendorsStrings = vendors.map(({ node: { vendor } }) => vendor)
return [...new Set(vendorsStrings)].map((v) => ({
node: {
entityId: v,
name: v,
path: `brands/${v}`,
},
}))
}
export default getVendors

View File

@ -0,0 +1,17 @@
const getAllProductVendors = /* GraphQL */ `
query getAllProductVendors($first: Int = 250, $cursor: String) {
products(first: $first, after: $cursor) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
vendor
}
cursor
}
}
}
`
export default getAllProductVendors

View File

@ -1,6 +1,6 @@
const getAllProductsPathsQuery = /* GraphQL */ `
query getAllProductPaths($first: Int!) {
products(first: $first) {
query getAllProductPaths($first: Int!, $cursor: String) {
products(first: $first, after: $cursor) {
pageInfo {
hasNextPage
hasPreviousPage
@ -9,6 +9,7 @@ const getAllProductsPathsQuery = /* GraphQL */ `
node {
handle
}
cursor
}
}
}

View File

@ -1,3 +1,46 @@
export const productsFragment = `
products(
first: $first
sortKey: $sortKey
reverse: $reverse
query: $query
) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
id
title
vendor
handle
description
priceRange {
minVariantPrice {
amount
currencyCode
}
}
images(first: 1) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
originalSrc
altText
width
height
}
}
}
}
}
}
`
const getAllProductsQuery = /* GraphQL */ `
query getAllProducts(
$first: Int = 250
@ -5,46 +48,7 @@ const getAllProductsQuery = /* GraphQL */ `
$sortKey: ProductSortKeys = RELEVANCE
$reverse: Boolean = false
) {
products(
first: $first
sortKey: $sortKey
reverse: $reverse
query: $query
) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
id
title
vendor
handle
description
priceRange {
minVariantPrice {
amount
currencyCode
}
}
images(first: 1) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
originalSrc
altText
width
height
}
}
}
}
}
}
${productsFragment}
}
`
export default getAllProductsQuery

View File

@ -0,0 +1,17 @@
import { productsFragment } from './get-all-products-query'
const getCollectionProductsQuery = /* GraphQL */ `
query getProductsFromCollection(
$categoryHandle: String!
$first: Int = 250
$query: String = ""
$sortKey: ProductSortKeys = RELEVANCE
$reverse: Boolean = false
) {
collectionByHandle(handle: $categoryHandle)
{
${productsFragment}
}
}
`
export default getCollectionProductsQuery

View File

@ -0,0 +1,17 @@
export const getPageQuery = /* GraphQL */ `
query($first: Int!) {
pages(first: $first) {
edges {
node {
id
title
handle
body
bodySummary
url
}
}
}
}
`
export default getPageQuery

View File

@ -2,6 +2,9 @@ export { default as getSiteCollectionsQuery } from './get-all-collections-query'
export { default as getProductQuery } from './get-all-products-paths-query'
export { default as getAllProductsQuery } from './get-all-products-query'
export { default as getAllProductsPathtsQuery } from './get-all-products-paths-query'
export { default as getAllProductVendors } from './get-all-product-vendors-query'
export { default as getCollectionProductsQuery } from './get-collection-products-query'
export { default as getCheckoutQuery } from './get-checkout-query'
export { default as getAllPagesQuery } from './get-all-pages-query'
export { default as getPageQuery } from './get-page-query'
export { default as getCustomerQuery } from './get-checkout-query'

View File

@ -31,7 +31,7 @@ export async function getStaticProps({
brands,
pages,
},
revalidate: 14400,
revalidate: 1440,
}
}

View File

@ -71,7 +71,6 @@ export default function Search({
const { data } = useSearch({
search: typeof q === 'string' ? q : '',
categoryId: activeCategory?.entityId,
categoryPath: activeCategory?.path,
brandId: activeBrand?.entityId,
sort: typeof sort === 'string' ? sort : '',
})