Skip to content

Commit

Permalink
feat: display product gallery without blink
Browse files Browse the repository at this point in the history
  • Loading branch information
pedromtec committed Nov 14, 2024
1 parent 0f32844 commit 91a64b1
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 19 deletions.
2 changes: 2 additions & 0 deletions packages/api/src/__generated__/schema.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 44 additions & 5 deletions packages/api/src/platforms/vtex/clients/search/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Context, Options } from '../../'
import type { IStoreSelectedFacet } from '../../../../__generated__/schema'
import { getStoreCookie } from '../../utils/cookies'
import type { SelectedFacet } from '../../utils/facets'
import type { FuzzyFacet, OperatorFacet, SelectedFacet } from '../../utils/facets'
import { fetchAPI } from '../fetch'
import type {
Facet,
Expand Down Expand Up @@ -43,12 +43,33 @@ export interface ProductLocator {

const POLICY_KEY = 'trade-policy'
const REGION_KEY = 'region-id'
const CHANNEL_KEYS = new Set([POLICY_KEY, REGION_KEY])
const FUZZY_KEY = 'fuzzy'
const OPERATOR_KEY = 'operator'

const EXTRA_FACETS_KEYS = new Set([
POLICY_KEY,
REGION_KEY,
FUZZY_KEY,
OPERATOR_KEY,
])

export const isFacetBoolean = (
facet: Facet
): facet is Facet<FacetValueBoolean> => facet.type === 'TEXT'

const isFuzzyFacet = (facet: SelectedFacet): facet is FuzzyFacet => {
return (
facet.key === 'fuzzy' &&
(facet.value === '0' || facet.value === '1' || facet.value === 'auto')
)
}

const isOperatorFacet = (facet: SelectedFacet): facet is OperatorFacet => {
return (
facet.key === 'operator' && (facet.value === 'and' || facet.value === 'or')
)
}

export const IntelligentSearch = (
{ account, environment, hideUnavailableItems, simulationBehavior, showSponsored }: Options,
ctx: Context
Expand Down Expand Up @@ -87,7 +108,9 @@ export const IntelligentSearch = (
}

const addDefaultFacets = (facets: SelectedFacet[]) => {
const withDefaultFacets = facets.filter(({ key }) => !CHANNEL_KEYS.has(key))
const withDefaultFacets = facets.filter(
({ key }) => !EXTRA_FACETS_KEYS.has(key)
)

const policyFacet =
facets.find(({ key }) => key === POLICY_KEY) ?? getPolicyFacet()
Expand All @@ -106,25 +129,41 @@ export const IntelligentSearch = (
return withDefaultFacets
}

const addSearchParamsFacets = (
facets: SelectedFacet[],
params: URLSearchParams
) => {
const fuzzyFacet = facets.find(({ key }) => key === FUZZY_KEY) ?? null
const operatorFacet = facets.find(({ key }) => key === OPERATOR_KEY) ?? null

if (fuzzyFacet && isFuzzyFacet(fuzzyFacet)) {
params.append(FUZZY_KEY, fuzzyFacet.value)
}

if (operatorFacet && isOperatorFacet(operatorFacet)) {
params.append(OPERATOR_KEY, operatorFacet.value)
}
}

const search = <T>({
query = '',
page,
count,
sort = '',
selectedFacets = [],
type,
fuzzy = 'auto',
showInvisibleItems,
}: SearchArgs): Promise<T> => {
const params = new URLSearchParams({
page: (page + 1).toString(),
count: count.toString(),
query,
sort,
fuzzy,
locale: ctx.storage.locale,
})

addSearchParamsFacets(selectedFacets, params)

if (showInvisibleItems) {
params.append('show-invisible-items', 'true')
}
Expand Down
7 changes: 2 additions & 5 deletions packages/api/src/platforms/vtex/resolvers/searchResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,13 @@ export const StoreSearchResult: Record<string, Resolver<Root>> = {

return filteredFacets
},
metadata: async ({ searchArgs, productSearchPromise }) => {
if (!searchArgs.query) {
return null
}

metadata: async ({ productSearchPromise }) => {
const productSearchResult = await productSearchPromise

return {
isTermMisspelled: productSearchResult.correction?.misspelled ?? false,
logicalOperator: productSearchResult.operator,
fuzzy: productSearchResult.fuzzy
}
},
}
10 changes: 10 additions & 0 deletions packages/api/src/platforms/vtex/utils/facets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ export const FACET_CROSS_SELLING_MAP = {
suggestions: "suggestions",
} as const

export type FuzzyFacet = {
key: 'fuzzy'
value: '0' | '1' | 'auto'
}

export type OperatorFacet = {
key: 'operator'
value: 'and' | 'or'
}

/**
* Transform facets from the store to VTEX platform facets.
* For instance, the channel in Store becomes trade-policy and regionId in VTEX's realm
Expand Down
5 changes: 5 additions & 0 deletions packages/api/src/typeDefs/query.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ type SearchMetadata {
Logical operator used to run the search.
"""
logicalOperator: String!

"""
Indicates how the search engine corrected the misspelled word by using fuzzy logic.
"""y
fuzzy: String
}

"""
Expand Down
4 changes: 2 additions & 2 deletions packages/core/@generated/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const documents = {
types.SubscribeToNewsletterDocument,
'\n query ClientManyProductsQuery(\n $first: Int!\n $after: String\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientManyProducts\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n ...ProductSummary_product\n }\n }\n }\n }\n }\n':
types.ClientManyProductsQueryDocument,
'\n query ClientProductGalleryQuery(\n $first: Int!\n $after: String!\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientProductGallery\n redirect(term: $term, selectedFacets: $selectedFacets) {\n url\n }\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n facets {\n ...Filter_facets\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n\n fragment SearchEvent_metadata on SearchMetadata {\n isTermMisspelled\n logicalOperator\n }\n':
'\n query ClientProductGalleryQuery(\n $first: Int!\n $after: String!\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientProductGallery\n redirect(term: $term, selectedFacets: $selectedFacets) {\n url\n }\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n facets {\n ...Filter_facets\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n\n fragment SearchEvent_metadata on SearchMetadata {\n isTermMisspelled\n logicalOperator\n fuzzy\n }\n':
types.ClientProductGalleryQueryDocument,
'\n query ClientProductQuery($locator: [IStoreSelectedFacet!]!) {\n ...ClientProduct\n product(locator: $locator) {\n ...ProductDetailsFragment_product\n }\n }\n':
types.ClientProductQueryDocument,
Expand Down Expand Up @@ -158,7 +158,7 @@ export function gql(
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(
source: '\n query ClientProductGalleryQuery(\n $first: Int!\n $after: String!\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientProductGallery\n redirect(term: $term, selectedFacets: $selectedFacets) {\n url\n }\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n facets {\n ...Filter_facets\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n\n fragment SearchEvent_metadata on SearchMetadata {\n isTermMisspelled\n logicalOperator\n }\n'
source: '\n query ClientProductGalleryQuery(\n $first: Int!\n $after: String!\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n ) {\n ...ClientProductGallery\n redirect(term: $term, selectedFacets: $selectedFacets) {\n url\n }\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n }\n facets {\n ...Filter_facets\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n\n fragment SearchEvent_metadata on SearchMetadata {\n isTermMisspelled\n logicalOperator\n fuzzy\n }\n'
): typeof import('./graphql').ClientProductGalleryQueryDocument
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
Expand Down
20 changes: 16 additions & 4 deletions packages/core/@generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,8 @@ export type QueryShippingArgs = {

/** Search result. */
export type SearchMetadata = {
/** Indicates how the search engine corrected the misspelled word by using fuzzy logic. */
fuzzy: Maybe<Scalars['String']['output']>
/** Indicates if the search term was misspelled. */
isTermMisspelled: Scalars['Boolean']['output']
/** Logical operator used to run the search. */
Expand Down Expand Up @@ -1504,13 +1506,18 @@ export type ClientProductGalleryQueryQuery = {
max: { selected: number; absolute: number }
}
>
metadata: { isTermMisspelled: boolean; logicalOperator: string } | null
metadata: {
isTermMisspelled: boolean
logicalOperator: string
fuzzy: string | null
} | null
}
}

export type SearchEvent_MetadataFragment = {
isTermMisspelled: boolean
logicalOperator: string
fuzzy: string | null
}

export type ClientProductQueryQueryVariables = Exact<{
Expand Down Expand Up @@ -1597,7 +1604,11 @@ export type ClientSearchSuggestionsQueryQuery = {
}>
}
products: { pageInfo: { totalCount: number } }
metadata: { isTermMisspelled: boolean; logicalOperator: string } | null
metadata: {
isTermMisspelled: boolean
logicalOperator: string
fuzzy: string | null
} | null
}
}

Expand Down Expand Up @@ -2047,6 +2058,7 @@ export const SearchEvent_MetadataFragmentDoc = new TypedDocumentString(
fragment SearchEvent_metadata on SearchMetadata {
isTermMisspelled
logicalOperator
fuzzy
}
`,
{ fragmentName: 'SearchEvent_metadata' }
Expand Down Expand Up @@ -2099,7 +2111,7 @@ export const ClientManyProductsQueryDocument = {
export const ClientProductGalleryQueryDocument = {
__meta__: {
operationName: 'ClientProductGalleryQuery',
operationHash: '177fe68cb385737b0901fc9e105f0a4813e18a20',
operationHash: 'bfc40da32b60f9404a4adb96b0856e3fbb04b076',
},
} as unknown as TypedDocumentString<
ClientProductGalleryQueryQuery,
Expand All @@ -2117,7 +2129,7 @@ export const ClientProductQueryDocument = {
export const ClientSearchSuggestionsQueryDocument = {
__meta__: {
operationName: 'ClientSearchSuggestionsQuery',
operationHash: '4d9f934764d8578aea08673b8ba57e8bf738f534',
operationHash: '47e48eaee91d16a4237eb2c1241bc2ed3e2ad9bb',
},
} as unknown as TypedDocumentString<
ClientSearchSuggestionsQueryQuery,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useSearch } from '@faststore/sdk'

import type { SearchContentType } from 'src/server/cms'
import type { SearchPageContextType } from 'src/pages/s'
import { useProductGalleryQuery } from 'src/sdk/product/useProductGalleryQuery'
import { findFacetValue, useProductGalleryQuery } from 'src/sdk/product/useProductGalleryQuery'
import Section from 'src/components/sections/Section'
import EmptyState from 'src/components/sections/EmptyState'
import ProductGalleryStyles from 'src/components/sections/ProductGallery/section.module.scss'
Expand All @@ -28,6 +28,7 @@ export type SearchWrapperProps = {
serverData: SearchPageContextType
}


export default function SearchWrapper({
itemsPerPage,
searchContentType,
Expand All @@ -37,6 +38,8 @@ export default function SearchWrapper({
const {
state: { term, sort, selectedFacets },
} = useSearch()


const { data: pageProductGalleryData, isValidating } = useProductGalleryQuery(
{
term,
Expand All @@ -45,11 +48,17 @@ export default function SearchWrapper({
selectedFacets,
}
)

if (isValidating || !pageProductGalleryData) {
return <EmptySearch />
}

const hasFuzzy = findFacetValue(selectedFacets, 'fuzzy')

if(!hasFuzzy) {
return <EmptySearch />
}

// Redirect when there are registered Intelligent Search redirects on VTEX Admin
if (pageProductGalleryData?.redirect?.url) {
router.replace(pageProductGalleryData?.redirect?.url, null, {
Expand Down
27 changes: 27 additions & 0 deletions packages/core/src/sdk/product/useProductGalleryQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
ClientProductGalleryQueryQueryVariables as Variables,
} from '@generated/graphql'
import type { IntelligentSearchQueryEvent } from 'src/sdk/analytics/types'
import { useSearch } from '@faststore/sdk'

/**
* This query is run on the browser and contains
Expand Down Expand Up @@ -53,6 +54,7 @@ export const query = gql(`
fragment SearchEvent_metadata on SearchMetadata {
isTermMisspelled
logicalOperator
fuzzy
}
`)

Expand All @@ -63,13 +65,22 @@ type ProductGalleryQueryOptions = {
term: ClientManyProductsQueryQueryVariables['term']
}

export const findFacetValue = (
facets: Facet[],
searchParam: string
): string | null => {
const facet = facets.find(({ key }) => key === searchParam)
return facet?.value ?? null
}

export const useProductGalleryQuery = ({
term,
sort,
selectedFacets,
itemsPerPage,
}: ProductGalleryQueryOptions) => {
const { locale } = useSession()
const { state, setState } = useSearch()
const localizedVariables = useLocalizedVariables({
first: itemsPerPage,
after: '0',
Expand All @@ -80,6 +91,22 @@ export const useProductGalleryQuery = ({

return useQuery<Query, Variables>(query, localizedVariables, {
onSuccess: (data) => {
if (data) {
console.log({data})
const fuzzyFacetValue = findFacetValue(selectedFacets, 'fuzzy')
const operatorFacetValue = findFacetValue(selectedFacets, 'operator')

if (!fuzzyFacetValue && !operatorFacetValue) {
setState({
...state,
selectedFacets: [
...selectedFacets,
{ key: 'fuzzy', value: data.search.metadata?.fuzzy },
{ key: 'operator', value: data.search.metadata?.logicalOperator },
],
})
}
}
if (data && term) {
import('@faststore/sdk').then(({ sendAnalyticsEvent }) => {
sendAnalyticsEvent<IntelligentSearchQueryEvent>({
Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/sdk/search/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ export const useApplySearchState = () => {
const router = useRouter()

return useCallback(
(url: URL) => router.push(`${url.pathname}${url.search}`),
(url: URL) => {
const newUrl = `${url.pathname}${url.search}`
return url.searchParams.has('fuzzy') && url.searchParams.has('operator')
? router.replace(newUrl)
: router.push(newUrl)
},
[router]
)
}

0 comments on commit 91a64b1

Please sign in to comment.