Skip to content

Commit 9e4c9b4

Browse files
feat: Check products availability when setting postal code (#2817)
## What's the purpose of this pull request? This PR intends to handle the scenario when shoppers are setting postal code and there is no available products for that specific location. ## How it works? For all Delivery Promises scenarios, when shoppers are setting their postal code an input error should be displayed if there is no available products for that location. ## How to test it? - If there is a testing postal code with no products available you can use it on `RegionModal` or `RegionPopover`; - Or for local environment you can change the condition at `useSetLocation` hook to mock the `no products` scenario; ### Starters Deploy Preview vtex-sites/faststoreqa.store#788 --------- Co-authored-by: Larícia Mota <[email protected]>
1 parent bd38f3e commit 9e4c9b4

File tree

11 files changed

+141
-0
lines changed

11 files changed

+141
-0
lines changed

packages/api/src/__generated__/schema.ts

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/api/src/platforms/vtex/clients/search/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
ProductSearchResult,
1717
Suggestion,
1818
} from './types/ProductSearchResult'
19+
import type { ProductCountResult } from './types/ProductCountResult'
1920

2021
export type Sort =
2122
| 'price:desc'
@@ -265,10 +266,26 @@ export const IntelligentSearch = (
265266
const facets = (args: Omit<SearchArgs, 'type'>) =>
266267
search<FacetSearchResult>({ ...args, type: 'facets' })
267268

269+
const productCount = (
270+
args: Pick<SearchArgs, 'query'>
271+
): Promise<ProductCountResult> => {
272+
const params = new URLSearchParams()
273+
274+
if (args?.query) {
275+
params.append('query', args.query.toString())
276+
}
277+
278+
return fetchAPI(
279+
`${base}/_v/api/intelligent-search/catalog_count?${params.toString()}`,
280+
{ headers }
281+
)
282+
}
283+
268284
return {
269285
facets,
270286
products,
271287
suggestedTerms,
272288
topSearches,
289+
productCount,
273290
}
274291
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface ProductCountResult {
2+
/**
3+
* @description Total product count.
4+
*/
5+
total: number
6+
}

packages/api/src/platforms/vtex/resolvers/query.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
QuerySearchArgs,
99
QuerySellersArgs,
1010
QueryShippingArgs,
11+
QueryProductCountArgs,
1112
} from '../../../__generated__/schema'
1213
import { BadRequestError, NotFoundError } from '../../errors'
1314
import type { CategoryTree } from '../clients/commerce/types/CategoryTree'
@@ -361,4 +362,19 @@ export const Query = {
361362

362363
return { addresses: parsedAddresses }
363364
},
365+
productCount: async (
366+
_: unknown,
367+
{ term }: QueryProductCountArgs,
368+
ctx: Context
369+
) => {
370+
const {
371+
clients: { search },
372+
} = ctx
373+
374+
const result = await search.productCount({
375+
query: term ?? undefined,
376+
})
377+
378+
return result
379+
},
364380
}

packages/api/src/typeDefs/query.graphql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,13 @@ input IGeoCoordinates {
199199
longitude: Float!
200200
}
201201

202+
type ProductCountResult {
203+
"""
204+
Total product count.
205+
"""
206+
total: Int!
207+
}
208+
202209
type Query {
203210
"""
204211
Returns the details of a product based on the specified locator.
@@ -349,6 +356,17 @@ type Query {
349356
id: String!
350357
): Profile
351358
@cacheControl(scope: "public", sMaxAge: 120, staleWhileRevalidate: 3600)
359+
360+
"""
361+
Returns the total product count information based on a specific location accessible through the VTEX segment cookie.
362+
"""
363+
productCount(
364+
"""
365+
Search term.
366+
"""
367+
term: String
368+
): ProductCountResult
369+
@cacheControl(scope: "public", sMaxAge: 120, staleWhileRevalidate: 3600)
352370
}
353371

354372
"""

packages/api/test/integration/schema.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ const QUERIES = [
6868
'redirect',
6969
'sellers',
7070
'profile',
71+
'productCount',
7172
]
7273

7374
const MUTATIONS = ['validateCart', 'validateSession', 'subscribeToNewsletter']

packages/core/@generated/gql.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const documents = {
4444
types.ValidateCartMutationDocument,
4545
'\n mutation SubscribeToNewsletter($data: IPersonNewsletter!) {\n subscribeToNewsletter(data: $data) {\n id\n }\n }\n':
4646
types.SubscribeToNewsletterDocument,
47+
'\n query ClientProductCountQuery($term: String) {\n productCount(term: $term) {\n total\n }\n }\n':
48+
types.ClientProductCountQueryDocument,
4749
'\n query ClientAllVariantProductsQuery($locator: [IStoreSelectedFacet!]!) {\n product(locator: $locator) {\n ...ProductSKUMatrixSidebarFragment_product\n }\n }\n':
4850
types.ClientAllVariantProductsQueryDocument,
4951
'\n query ClientManyProductsQuery(\n $first: Int!\n $after: String\n $sort: StoreSort!\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]!\n $sponsoredCount: Int\n ) {\n ...ClientManyProducts\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n sponsoredCount: $sponsoredCount\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n ...ProductSummary_product\n }\n }\n }\n }\n }\n':
@@ -162,6 +164,12 @@ export function gql(
162164
export function gql(
163165
source: '\n mutation SubscribeToNewsletter($data: IPersonNewsletter!) {\n subscribeToNewsletter(data: $data) {\n id\n }\n }\n'
164166
): typeof import('./graphql').SubscribeToNewsletterDocument
167+
/**
168+
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
169+
*/
170+
export function gql(
171+
source: '\n query ClientProductCountQuery($term: String) {\n productCount(term: $term) {\n total\n }\n }\n'
172+
): typeof import('./graphql').ClientProductCountQueryDocument
165173
/**
166174
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
167175
*/

packages/core/@generated/graphql.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,11 @@ export type PickupStoreInfo = {
471471
isPickupStore: Maybe<Scalars['Boolean']['output']>
472472
}
473473

474+
export type ProductCountResult = {
475+
/** Total product count. */
476+
total: Scalars['Int']['output']
477+
}
478+
474479
export type Profile = {
475480
/** Collection of user's address */
476481
addresses: Maybe<Array<Maybe<ProfileAddress>>>
@@ -514,6 +519,8 @@ export type Query = {
514519
collection: StoreCollection
515520
/** Returns the details of a product based on the specified locator. */
516521
product: StoreProduct
522+
/** Returns information about total product count based on VTEX segment cookie. */
523+
productCount: Maybe<ProductCountResult>
517524
/** Returns information about the profile. */
518525
profile: Maybe<Profile>
519526
/** Returns if there's a redirect for a search. */
@@ -544,6 +551,10 @@ export type QueryProductArgs = {
544551
locator: Array<IStoreSelectedFacet>
545552
}
546553

554+
export type QueryProductCountArgs = {
555+
term: InputMaybe<Scalars['String']['input']>
556+
}
557+
547558
export type QueryProfileArgs = {
548559
id: Scalars['String']['input']
549560
}
@@ -1540,6 +1551,14 @@ export type SubscribeToNewsletterMutation = {
15401551
subscribeToNewsletter: { id: string } | null
15411552
}
15421553

1554+
export type ClientProductCountQueryQueryVariables = Exact<{
1555+
term: InputMaybe<Scalars['String']['input']>
1556+
}>
1557+
1558+
export type ClientProductCountQueryQuery = {
1559+
productCount: { total: number } | null
1560+
}
1561+
15431562
export type ClientAllVariantProductsQueryQueryVariables = Exact<{
15441563
locator: Array<IStoreSelectedFacet> | IStoreSelectedFacet
15451564
}>
@@ -2395,6 +2414,15 @@ export const SubscribeToNewsletterDocument = {
23952414
SubscribeToNewsletterMutation,
23962415
SubscribeToNewsletterMutationVariables
23972416
>
2417+
export const ClientProductCountQueryDocument = {
2418+
__meta__: {
2419+
operationName: 'ClientProductCountQuery',
2420+
operationHash: 'dc912e7272e3d9f5ced206837df87f544d39d0a5',
2421+
},
2422+
} as unknown as TypedDocumentString<
2423+
ClientProductCountQueryQuery,
2424+
ClientProductCountQueryQueryVariables
2425+
>
23982426
export const ClientAllVariantProductsQueryDocument = {
23992427
__meta__: {
24002428
operationName: 'ClientAllVariantProductsQuery',

packages/core/src/components/region/RegionModal/useSetLocation.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { useState } from 'react'
22

33
import type { Session } from '@faststore/sdk'
44
import { sessionStore, validateSession } from 'src/sdk/session'
5+
import { getProductCount } from 'src/sdk/product'
6+
import { deliveryPromise } from 'discovery.config'
57

68
interface UseSetLocationParams {
79
input: string
@@ -49,6 +51,16 @@ export function useSetLocation(): UseSetLocationParams {
4951

5052
const validatedSession = await validateSession(newSession)
5153

54+
if (deliveryPromise.enabled) {
55+
// Check product availability for specific location
56+
const productCount = await getProductCount()
57+
if (productCount === 0) {
58+
setErrorMessage(`There are no products available for ${postalCode}.`)
59+
setLoading(false)
60+
return
61+
}
62+
}
63+
5264
sessionStore.set(validatedSession ?? newSession)
5365
resetInputField()
5466
onSuccess?.() // Execute the post-validation action (close modal, etc.)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { gql } from '@generated'
2+
3+
import type {
4+
ClientProductCountQueryQuery as Query,
5+
ClientProductCountQueryQueryVariables as Variables,
6+
} from '@generated/graphql'
7+
import { request } from '../graphql/request'
8+
9+
export const query = gql(`
10+
query ClientProductCountQuery($term: String) {
11+
productCount(term: $term) {
12+
total
13+
}
14+
}
15+
`)
16+
17+
export const getProductCount = async (term?: string) => {
18+
const { productCount } = await request<Query, Variables>(query, { term })
19+
20+
return productCount.total
21+
}

packages/core/test/server/index.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const QUERIES = [
7272
'redirect',
7373
'sellers',
7474
'profile',
75+
'productCount',
7576
]
7677

7778
const MUTATIONS = ['validateCart', 'validateSession', 'subscribeToNewsletter']

0 commit comments

Comments
 (0)