Skip to content

Commit b0aa3d3

Browse files
lucasfp13lariciamota
authored andcommitted
feat: Display city and postalCode (#2772)
## What's the purpose of this pull request? This PR intends to display region data (on `RegionButton` and `RegionBar`) with `city` and `postalCode` information. ## How it works? For cases when users manually set their zip code, they should see both city name and zip code: <img width="342" alt="Screenshot 2025-04-09 at 09 25 16" src="https://github.com/user-attachments/assets/17b48ff7-37e6-497b-b520-a9d509787afa" /> <img width="256" alt="Screenshot 2025-04-09 at 09 25 02" src="https://github.com/user-attachments/assets/ac34f6d6-1e7f-44ad-99db-6ced59044187" /> Otherwise, if a default zip code is set on `discovery.config` and users do not manually set any zip code, they should see only the city name: <img width="382" alt="Screenshot 2025-04-09 at 09 24 08" src="https://github.com/user-attachments/assets/e9f0fb37-24cf-4e8b-80af-0b9129c5683c" /> <img width="201" alt="Screenshot 2025-04-09 at 09 24 24" src="https://github.com/user-attachments/assets/1f627fc7-9a1c-4bb9-bae4-959dccbfd141" /> ## How to test it? Test both scenarios described in the section above to validate everything is working and displayed as expected. ### Starters Deploy Preview vtex-sites/faststoreqa.store#769
1 parent 41d444c commit b0aa3d3

File tree

14 files changed

+86
-19
lines changed

14 files changed

+86
-19
lines changed

packages/api/src/__generated__/schema.ts

Lines changed: 4 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/resolvers/validateSession.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type {
88
} from '../../../__generated__/schema'
99
import ChannelMarshal from '../utils/channel'
1010

11-
async function getGeoCoordinates(
11+
async function getPreciseLocationData(
1212
clients: Context['clients'],
1313
country: string,
1414
postalCode: string
@@ -20,7 +20,7 @@ async function getGeoCoordinates(
2020
})
2121

2222
const [longitude, latitude] = address.geoCoordinates
23-
return { latitude, longitude }
23+
return { city: address.city, geoCoordinates: { latitude, longitude } }
2424
} catch (err) {
2525
console.error(
2626
`Error while getting geo coordinates for the current postal code (${postalCode}) and country (${country}).\n`
@@ -38,11 +38,19 @@ export const validateSession = async (
3838
const channel = ChannelMarshal.parse(oldSession.channel ?? '')
3939
const postalCode = String(oldSession.postalCode ?? '')
4040
const country = oldSession.country ?? ''
41-
42-
// Get geo coordinates if postal code and country are provided
41+
let city = oldSession.city ?? null
4342
let geoCoordinates = oldSession.geoCoordinates ?? null
44-
if (!geoCoordinates && postalCode !== '' && country !== '') {
45-
geoCoordinates = await getGeoCoordinates(clients, country, postalCode)
43+
44+
// Update location data if postal code and country are provided
45+
const shouldGetPreciseLocation = !city || !geoCoordinates
46+
if (shouldGetPreciseLocation && postalCode !== '' && country !== '') {
47+
const preciseLocation = await getPreciseLocationData(
48+
clients,
49+
country,
50+
postalCode
51+
)
52+
city = preciseLocation.city
53+
geoCoordinates = preciseLocation.geoCoordinates
4654
}
4755

4856
/**
@@ -56,9 +64,11 @@ export const validateSession = async (
5664
if (!!postalCode) {
5765
params.set('postalCode', postalCode)
5866
}
67+
5968
if (!!country) {
6069
params.set('country', country)
6170
}
71+
6272
if (!!geoCoordinates) {
6373
params.set(
6474
'geoCoordinates',
@@ -126,6 +136,7 @@ export const validateSession = async (
126136
}
127137
: null,
128138
geoCoordinates,
139+
city,
129140
}
130141

131142
if (deepEquals(oldSession, newSession)) {

packages/api/src/typeDefs/session.graphql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ type StoreSession {
159159
"""
160160
addressType: String
161161
"""
162+
Session city.
163+
"""
164+
city: String
165+
"""
162166
Session postal code.
163167
"""
164168
postalCode: String
@@ -217,6 +221,10 @@ input IStoreSession {
217221
"""
218222
addressType: String
219223
"""
224+
Session input city.
225+
"""
226+
city: String
227+
"""
220228
Session input postal code.
221229
"""
222230
postalCode: String

packages/components/src/molecules/RegionBar/RegionBar.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { Button } from '../../'
55

66
export interface RegionBarProps extends HTMLAttributes<HTMLDivElement> {
77
/**
8-
* Postal code string to be display in the component
8+
* City to be displayed in the component.
9+
*/
10+
city?: string
11+
/**
12+
* Postal code string to be display in the component.
913
*/
1014
postalCode?: string
1115
/**
@@ -28,16 +32,23 @@ export interface RegionBarProps extends HTMLAttributes<HTMLDivElement> {
2832
* A React component that will be rendered as an icon.
2933
*/
3034
buttonIcon?: ReactNode
35+
/**
36+
* Boolean to control whether postal code should be visible or not.
37+
* @default true
38+
*/
39+
shouldDisplayPostalCode?: boolean
3140
}
3241

3342
const RegionBar = forwardRef<HTMLDivElement, RegionBarProps>(function RegionBar(
3443
{
44+
city,
3545
postalCode,
3646
icon,
3747
label,
3848
editLabel,
3949
buttonIcon,
4050
onButtonClick,
51+
shouldDisplayPostalCode = true,
4152
...otherProps
4253
},
4354
ref
@@ -51,9 +62,12 @@ const RegionBar = forwardRef<HTMLDivElement, RegionBarProps>(function RegionBar(
5162
icon={buttonIcon}
5263
>
5364
{!!icon && icon}
54-
{postalCode ? (
65+
{city && postalCode ? (
5566
<>
56-
<span data-fs-region-bar-postal-code>{postalCode}</span>
67+
<span data-fs-region-bar-postal-code>
68+
{city}
69+
{shouldDisplayPostalCode && `, ${postalCode}`}
70+
</span>
5771
{!!editLabel && <span data-fs-region-bar-cta>{editLabel}</span>}
5872
</>
5973
) : (

packages/core/@generated/gql.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ const documents = {
5252
types.ClientProductGalleryQueryDocument,
5353
'\n query ClientProductQuery($locator: [IStoreSelectedFacet!]!) {\n ...ClientProduct\n product(locator: $locator) {\n ...ProductDetailsFragment_product\n }\n }\n':
5454
types.ClientProductQueryDocument,
55-
'\n query ClientProfileQuery($id: String!) {\n profile(id: $id) {\n addresses {\n country\n postalCode\n geoCoordinate\n }\n }\n }\n':
55+
'\n query ClientProfileQuery($id: String!) {\n profile(id: $id) {\n addresses {\n country\n postalCode\n geoCoordinate\n city\n }\n }\n }\n':
5656
types.ClientProfileQueryDocument,
5757
'\n query ClientSearchSuggestionsQuery(\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]\n ) {\n ...ClientSearchSuggestions\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n products {\n ...ProductSummary_product\n }\n }\n products {\n pageInfo {\n totalCount\n }\n }\n metadata {\n ...SearchEvent_metadata\n }\n }\n }\n':
5858
types.ClientSearchSuggestionsQueryDocument,
5959
'\n query ClientTopSearchSuggestionsQuery(\n $term: String!\n $selectedFacets: [IStoreSelectedFacet!]\n ) {\n ...ClientTopSearchSuggestions\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n }\n }\n }\n':
6060
types.ClientTopSearchSuggestionsQueryDocument,
61-
'\n mutation ValidateSession($session: IStoreSession!, $search: String!) {\n validateSession(session: $session, search: $search) {\n locale\n channel\n country\n addressType\n postalCode\n deliveryMode {\n deliveryChannel\n deliveryMethod\n deliveryWindow {\n startDate\n endDate\n }\n }\n geoCoordinates {\n latitude\n longitude\n }\n currency {\n code\n symbol\n }\n person {\n id\n email\n givenName\n familyName\n }\n b2b {\n customerId\n }\n marketingData {\n utmCampaign\n utmMedium\n utmSource\n utmiCampaign\n utmiPage\n utmiPart\n }\n }\n }\n':
61+
'\n mutation ValidateSession($session: IStoreSession!, $search: String!) {\n validateSession(session: $session, search: $search) {\n locale\n channel\n country\n addressType\n postalCode\n city\n deliveryMode {\n deliveryChannel\n deliveryMethod\n deliveryWindow {\n startDate\n endDate\n }\n }\n geoCoordinates {\n latitude\n longitude\n }\n currency {\n code\n symbol\n }\n person {\n id\n email\n givenName\n familyName\n }\n b2b {\n customerId\n }\n marketingData {\n utmCampaign\n utmMedium\n utmSource\n utmiCampaign\n utmiPage\n utmiPart\n }\n }\n }\n':
6262
types.ValidateSessionDocument,
6363
'\n query ClientShippingSimulationQuery(\n $postalCode: String!\n $country: String!\n $items: [IShippingItem!]!\n ) {\n ...ClientShippingSimulation\n shipping(items: $items, postalCode: $postalCode, country: $country) {\n logisticsInfo {\n slas {\n carrier\n price\n availableDeliveryWindows {\n startDateUtc\n endDateUtc\n price\n listPrice\n }\n shippingEstimate\n localizedEstimates\n deliveryChannel\n }\n }\n address {\n city\n neighborhood\n state\n }\n }\n }\n':
6464
types.ClientShippingSimulationQueryDocument,
@@ -190,7 +190,7 @@ export function gql(
190190
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
191191
*/
192192
export function gql(
193-
source: '\n query ClientProfileQuery($id: String!) {\n profile(id: $id) {\n addresses {\n country\n postalCode\n geoCoordinate\n }\n }\n }\n'
193+
source: '\n query ClientProfileQuery($id: String!) {\n profile(id: $id) {\n addresses {\n country\n postalCode\n geoCoordinate\n city\n }\n }\n }\n'
194194
): typeof import('./graphql').ClientProfileQueryDocument
195195
/**
196196
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
@@ -208,7 +208,7 @@ export function gql(
208208
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
209209
*/
210210
export function gql(
211-
source: '\n mutation ValidateSession($session: IStoreSession!, $search: String!) {\n validateSession(session: $session, search: $search) {\n locale\n channel\n country\n addressType\n postalCode\n deliveryMode {\n deliveryChannel\n deliveryMethod\n deliveryWindow {\n startDate\n endDate\n }\n }\n geoCoordinates {\n latitude\n longitude\n }\n currency {\n code\n symbol\n }\n person {\n id\n email\n givenName\n familyName\n }\n b2b {\n customerId\n }\n marketingData {\n utmCampaign\n utmMedium\n utmSource\n utmiCampaign\n utmiPage\n utmiPart\n }\n }\n }\n'
211+
source: '\n mutation ValidateSession($session: IStoreSession!, $search: String!) {\n validateSession(session: $session, search: $search) {\n locale\n channel\n country\n addressType\n postalCode\n city\n deliveryMode {\n deliveryChannel\n deliveryMethod\n deliveryWindow {\n startDate\n endDate\n }\n }\n geoCoordinates {\n latitude\n longitude\n }\n currency {\n code\n symbol\n }\n person {\n id\n email\n givenName\n familyName\n }\n b2b {\n customerId\n }\n marketingData {\n utmCampaign\n utmMedium\n utmSource\n utmiCampaign\n utmiPage\n utmiPart\n }\n }\n }\n'
212212
): typeof import('./graphql').ValidateSessionDocument
213213
/**
214214
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.

packages/core/@generated/graphql.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,8 @@ export type IStoreSession = {
320320
b2b: InputMaybe<IStoreB2B>
321321
/** Session input channel. */
322322
channel: InputMaybe<Scalars['String']['input']>
323+
/** Session input city. */
324+
city: InputMaybe<Scalars['String']['input']>
323325
/** Session input country. */
324326
country: Scalars['String']['input']
325327
/** Session input currency. */
@@ -1138,6 +1140,8 @@ export type StoreSession = {
11381140
b2b: Maybe<StoreB2B>
11391141
/** Session channel. */
11401142
channel: Maybe<Scalars['String']['output']>
1143+
/** Session city. */
1144+
city: Maybe<Scalars['String']['output']>
11411145
/** Session country. */
11421146
country: Scalars['String']['output']
11431147
/** Session currency. */
@@ -1733,6 +1737,7 @@ export type ClientProfileQueryQuery = {
17331737
country: string | null
17341738
postalCode: string | null
17351739
geoCoordinate: Array<number | null> | null
1740+
city: string | null
17361741
} | null> | null
17371742
} | null
17381743
}
@@ -1806,6 +1811,7 @@ export type ValidateSessionMutation = {
18061811
country: string
18071812
addressType: string | null
18081813
postalCode: string | null
1814+
city: string | null
18091815
deliveryMode: {
18101816
deliveryChannel: string
18111817
deliveryMethod: string
@@ -2428,7 +2434,7 @@ export const ClientProductQueryDocument = {
24282434
export const ClientProfileQueryDocument = {
24292435
__meta__: {
24302436
operationName: 'ClientProfileQuery',
2431-
operationHash: 'c35d67cd64ceb127f60b39cbc47133c634d980c0',
2437+
operationHash: '34ea14c0d4a57ddf9bc11e4be0cd2b5a6506d3d4',
24322438
},
24332439
} as unknown as TypedDocumentString<
24342440
ClientProfileQueryQuery,
@@ -2455,7 +2461,7 @@ export const ClientTopSearchSuggestionsQueryDocument = {
24552461
export const ValidateSessionDocument = {
24562462
__meta__: {
24572463
operationName: 'ValidateSession',
2458-
operationHash: '2c6e94b978eb50647873082daebcc5b332154cb1',
2464+
operationHash: '6189ed611a20d9d5fe8ebebf61c87c9c29a5cef4',
24592465
},
24602466
} as unknown as TypedDocumentString<
24612467
ValidateSessionMutation,

packages/core/discovery.config.default.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ module.exports = {
5252
country: 'USA',
5353
deliveryMode: null,
5454
addressType: null,
55+
city: null,
5556
postalCode: null,
5657
geoCoordinates: null,
5758
b2b: null,

packages/core/src/components/region/RegionBar/RegionBar.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type { RegionBarProps as UIRegionBarProps } from '@faststore/ui'
33
import { useUI } from '@faststore/ui'
44
import { useSession } from 'src/sdk/session'
55

6+
import { textToTitleCase } from 'src/utils/utilities'
7+
import { session as initialSession } from 'discovery.config'
68
import { useOverrideComponents } from 'src/sdk/overrides/OverrideContext'
79

810
export interface RegionBarProps {
@@ -44,7 +46,8 @@ function RegionBar({
4446
} = useOverrideComponents<'RegionBar'>()
4547

4648
const { openModal } = useUI()
47-
const { postalCode } = useSession()
49+
const { city, postalCode } = useSession()
50+
const shouldDisplayPostalCode = postalCode !== initialSession.postalCode
4851

4952
return (
5053
<RegionBarWrapper.Component
@@ -69,6 +72,8 @@ function RegionBar({
6972
// This decision can be reviewed later if needed
7073
onButtonClick={openModal}
7174
postalCode={postalCode}
75+
city={textToTitleCase(city ?? '')}
76+
shouldDisplayPostalCode={shouldDisplayPostalCode}
7277
{...otherProps}
7378
/>
7479
)

packages/core/src/components/region/RegionButton/RegionButton.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ import { Button as UIButton } from '@faststore/ui'
22

33
import { Icon, useUI } from '@faststore/ui'
44
import { useSession } from 'src/sdk/session'
5+
import { textToTitleCase } from 'src/utils/utilities'
6+
import { session as initialSession } from 'discovery.config'
57

68
function RegionButton({ icon, label }: { icon: string; label: string }) {
79
const { openModal } = useUI()
8-
const { postalCode } = useSession()
10+
const { city, postalCode } = useSession()
11+
const shouldDisplayPostalCode = postalCode !== initialSession.postalCode
912

1013
return (
1114
<UIButton
@@ -15,7 +18,9 @@ function RegionButton({ icon, label }: { icon: string; label: string }) {
1518
iconPosition="left"
1619
onClick={openModal}
1720
>
18-
{postalCode ?? label}
21+
{city && postalCode
22+
? `${textToTitleCase(city)}${shouldDisplayPostalCode ? `, ${postalCode}` : ''}`
23+
: label}
1924
</UIButton>
2025
)
2126
}

packages/core/src/sdk/profile/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const query = gql(`
1313
country
1414
postalCode
1515
geoCoordinate
16+
city
1617
}
1718
}
1819
}

packages/core/src/sdk/session/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const mutation = gql(`
2121
country
2222
addressType
2323
postalCode
24+
city
2425
deliveryMode {
2526
deliveryChannel
2627
deliveryMethod
@@ -72,6 +73,7 @@ export const validateSession = async (session: Session) => {
7273
if (address) {
7374
sessionStore.set({
7475
...session,
76+
city: address?.city,
7577
postalCode: address?.postalCode,
7678
geoCoordinates: {
7779
// the values come in the reverse expected order

packages/core/src/utils/utilities.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//Input "Example Text!". Output: example-text
1+
// Input "Example Text!". Output: example-text
22
export function textToKebabCase(text: string): string {
33
// Replace spaces and special characters with hyphens
44
let kebabCase = text.replace(/[^\w\s]/gi, '-')
@@ -11,3 +11,11 @@ export function textToKebabCase(text: string): string {
1111

1212
return kebabCase ?? ''
1313
}
14+
15+
// Input "EXAMPLE text!". Output: "Example Text!"
16+
export function textToTitleCase(text: string): string {
17+
return text.replace(
18+
/\S+/g,
19+
(word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
20+
)
21+
}

packages/sdk/src/session/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export interface Session {
4848
channel: string | null
4949
deliveryMode: DeliveryMode | null
5050
addressType: string | null
51+
city: string | null
5152
postalCode: string | null
5253
geoCoordinates: GeoCoordinates | null
5354
person: Person | null

packages/sdk/test/session/index.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const initialSession: Session = {
1313
channel: 'test-channel',
1414
deliveryMode: null,
1515
addressType: null,
16+
city: null,
1617
postalCode: null,
1718
geoCoordinates: null,
1819
person: null,

0 commit comments

Comments
 (0)