@faststore/core 3.43.0 → 3.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +55 -55
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/prerender-manifest.js +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/react-loadable-manifest.json +63 -63
- package/.next/routes-manifest.json +1 -1
- package/.next/server/chunks/1506.js +1 -1
- package/.next/server/chunks/2443.js +1 -0
- package/.next/server/chunks/2524.js +1 -0
- package/.next/server/chunks/4289.js +2 -362
- package/.next/server/chunks/4559.js +361 -0
- package/.next/server/chunks/4725.js +1 -0
- package/.next/server/chunks/4746.js +1 -1
- package/.next/server/chunks/5474.js +6 -0
- package/.next/server/chunks/5607.js +1 -0
- package/.next/server/chunks/563.js +1 -1
- package/.next/server/chunks/6594.js +1 -1
- package/.next/server/chunks/674.js +1 -1
- package/.next/server/chunks/6968.js +3 -2
- package/.next/server/chunks/7986.js +6 -1
- package/.next/server/chunks/804.js +1 -0
- package/.next/server/chunks/831.js +1 -1
- package/.next/server/chunks/8562.js +1 -1
- package/.next/server/chunks/8646.js +1 -1
- package/.next/server/chunks/ButtonSignIn.js +1 -1
- package/.next/server/chunks/UIBannerText.js +1 -1
- package/.next/server/chunks/UISKUMatrixSidebar.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/404.js +1 -1
- package/.next/server/pages/404.js.nft.json +1 -1
- package/.next/server/pages/500.js +1 -1
- package/.next/server/pages/500.js.nft.json +1 -1
- package/.next/server/pages/[...slug].js +1 -1
- package/.next/server/pages/[...slug].js.nft.json +1 -1
- package/.next/server/pages/[slug]/p.js +1 -1
- package/.next/server/pages/[slug]/p.js.nft.json +1 -1
- package/.next/server/pages/_app.js +1 -1
- package/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/server/pages/_document.js +1 -1
- package/.next/server/pages/_document.js.nft.json +1 -1
- package/.next/server/pages/_error.js +1 -1
- package/.next/server/pages/_error.js.nft.json +1 -1
- package/.next/server/pages/account/profile.js +1 -1
- package/.next/server/pages/account/profile.js.nft.json +1 -1
- package/.next/server/pages/account.js +1 -1
- package/.next/server/pages/account.js.nft.json +1 -1
- package/.next/server/pages/api/graphql.js +2 -1
- package/.next/server/pages/api/graphql.js.nft.json +1 -1
- package/.next/server/pages/api/health/live.js.nft.json +1 -1
- package/.next/server/pages/api/health/ready.js.nft.json +1 -1
- package/.next/server/pages/api/preview.js.nft.json +1 -1
- package/.next/server/pages/checkout.js +1 -1
- package/.next/server/pages/checkout.js.nft.json +1 -1
- package/.next/server/pages/en-US/404.html +2 -2
- package/.next/server/pages/en-US/500.html +2 -2
- package/.next/server/pages/en-US/checkout.html +2 -2
- package/.next/server/pages/en-US/login.html +2 -2
- package/.next/server/pages/en-US/s.html +2 -2
- package/.next/server/pages/en-US.html +2 -2
- package/.next/server/pages/index.js +1 -1
- package/.next/server/pages/index.js.nft.json +1 -1
- package/.next/server/pages/login.js +1 -1
- package/.next/server/pages/login.js.nft.json +1 -1
- package/.next/server/pages/s.js +1 -1
- package/.next/server/pages/s.js.nft.json +1 -1
- package/.next/static/U_9W0qMyv_4RQSLFdrtHC/_buildManifest.js +1 -0
- package/.next/static/chunks/{1153.ed17e19083f51bdb.js → 1153.8f0ac867c4ddce90.js} +1 -1
- package/.next/static/chunks/1173.ca14a5bc4e1264f4.js +1 -0
- package/.next/static/chunks/1418-e86227bcf9a5d486.js +28 -0
- package/.next/static/chunks/1552.8751332da83cadc5.js +1 -0
- package/.next/static/chunks/{6076.d80e37e35892bafc.js → 3684.3e6f435506c2c7c9.js} +1 -1
- package/.next/static/chunks/417.edbb75ab2f55482f.js +1 -0
- package/.next/static/chunks/{4349-6fc936580b772e67.js → 4349-87a956a73b97519c.js} +1 -1
- package/.next/static/chunks/{4625-9a273e78866c389b.js → 4625-553d20c86b0ca577.js} +1 -1
- package/.next/static/chunks/{4746.19eddeaeb33ea820.js → 4746.2e16ec0663a3873f.js} +1 -1
- package/.next/static/chunks/{4774.907a71a04126d57c.js → 4774.8e70c01dcd0ed4b3.js} +1 -1
- package/.next/static/chunks/{4865.3e2ae9feb511c870.js → 4865.4017be0f8e7c8cf0.js} +1 -1
- package/.next/static/chunks/5836.c5c244a8cbfa7f7d.js +1 -0
- package/.next/static/chunks/6335-6eb4ff84af184b88.js +1 -0
- package/.next/static/chunks/6536.d5260107222db13c.js +1 -0
- package/.next/static/chunks/{7195.e88924bd232c58b7.js → 7195.fd4cacc0bef1a233.js} +1 -1
- package/.next/static/chunks/7498-72289e704e7c7056.js +1 -0
- package/.next/static/chunks/7959.7c721505286572c0.js +1 -0
- package/.next/static/chunks/9.89f24163370f3a22.js +1 -0
- package/.next/static/chunks/BannerNewsletter.8982f42e60ed8828.js +1 -0
- package/.next/static/chunks/{ButtonSignIn.df0e3ce490436fe2.js → ButtonSignIn.bff8dc7b2133806c.js} +1 -1
- package/.next/static/chunks/CartItem.f2493be7c504f22f.js +1 -0
- package/.next/static/chunks/{CartSidebar.26d3e39bf93a368f.js → CartSidebar.aa9e43be37034654.js} +1 -1
- package/.next/static/chunks/{Gift.affb75f8f7bcb6db.js → Gift.375356d6a74e2693.js} +1 -1
- package/.next/static/chunks/Newsletter.9db4df2d4fb33227.js +1 -0
- package/.next/static/chunks/OrderSummary.e6e0a3af3c224b4e.js +1 -0
- package/.next/static/chunks/ProductShelf.9cea36116836c161.js +1 -0
- package/.next/static/chunks/ProductTiles.059a4a6360e1d4ea.js +1 -0
- package/.next/static/chunks/RegionModal.4594b707a1092551.js +1 -0
- package/.next/static/chunks/UIBannerText.45cf608e46cde628.js +1 -0
- package/.next/static/chunks/UIButton.3186f933c0ecfb31.js +1 -0
- package/.next/static/chunks/UISKUMatrixSidebar.9ac3980a60cfc358.js +1 -0
- package/.next/static/chunks/UIToast.221eb10681d2f642.js +1 -0
- package/.next/static/chunks/pages/{404-8e61ea158e7b314c.js → 404-6300c433469b7262.js} +1 -1
- package/.next/static/chunks/pages/{500-68f51177e64de6e7.js → 500-2d1dd344c8a8827f.js} +1 -1
- package/.next/static/chunks/pages/[...slug]-c1a62d58f5940747.js +1 -0
- package/.next/static/chunks/pages/[slug]/p-555dc09a5e78260e.js +1 -0
- package/.next/static/chunks/pages/_app-929466ed763a6d46.js +1 -0
- package/.next/static/chunks/pages/account/{profile-1fdb500de8f12b3a.js → profile-23b2e9fbbd95a30b.js} +1 -1
- package/.next/static/chunks/pages/{checkout-0dfe0dc9cee443ba.js → checkout-93b647fc45454d97.js} +1 -1
- package/.next/static/chunks/pages/{index-d36123e232b889d8.js → index-07bc320fc045538c.js} +1 -1
- package/.next/static/chunks/pages/{login-b59ea5561defc0d4.js → login-2454f1cd309c27d2.js} +1 -1
- package/.next/static/chunks/pages/{s-6120de2f57d674fc.js → s-9901e6857f517b8b.js} +1 -1
- package/.next/static/chunks/webpack-c2eed1e0b727b3ee.js +1 -0
- package/.next/static/css/b8945ac5f5327c34.css +1 -0
- package/.next/static/css/{f03f8973f7c72071.css → dbbb10bf2f162a58.css} +1 -1
- package/.next/trace +113 -111
- package/.turbo/turbo-build.log +25 -25
- package/.turbo/turbo-test.log +5 -5
- package/@generated/gql.ts +18 -2
- package/@generated/graphql.ts +58 -1
- package/@generated/persisted-documents.json +3 -1
- package/@generated/schema.graphql +14 -0
- package/CHANGELOG.md +6 -0
- package/cms/faststore/sections.json +110 -1
- package/discovery.config.default.js +6 -0
- package/package.json +6 -6
- package/src/Layout.tsx +2 -0
- package/src/components/cms/global/Components.ts +2 -0
- package/src/components/region/RegionBar/RegionBar.tsx +35 -2
- package/src/components/region/RegionButton/RegionButton.tsx +41 -6
- package/src/components/region/RegionModal/RegionModal.tsx +46 -34
- package/src/components/region/RegionModal/useRegion.ts +79 -0
- package/src/components/region/RegionModal/useRegionModal.ts +44 -0
- package/src/components/region/RegionPopover/RegionPopover.tsx +174 -0
- package/src/components/region/RegionPopover/index.ts +1 -0
- package/src/components/region/RegionPopover/section.module.scss +9 -0
- package/src/components/search/SearchInput/SearchInput.tsx +2 -2
- package/src/components/sections/Navbar/section.module.scss +3 -0
- package/src/components/sections/RegionBar/RegionBar.tsx +8 -3
- package/src/constants.ts +1 -0
- package/src/pages/[slug]/p.tsx +2 -1
- package/src/pages/_app.tsx +4 -2
- package/src/sdk/geolocation/useGeolocation.ts +42 -0
- package/src/sdk/product/index.ts +21 -0
- package/src/sdk/profile/index.ts +31 -0
- package/src/sdk/session/index.ts +37 -0
- package/src/sdk/ui/useOnClickOutside.ts +3 -38
- package/src/utils/utilities.ts +9 -1
- package/test/server/index.test.ts +1 -0
- package/.next/server/chunks/3105.js +0 -6
- package/.next/server/chunks/3779.js +0 -1
- package/.next/server/chunks/4444.js +0 -6
- package/.next/server/chunks/6030.js +0 -1
- package/.next/server/chunks/7337.js +0 -1
- package/.next/server/chunks/7675.js +0 -1
- package/.next/server/chunks/9594.js +0 -1
- package/.next/static/chunks/1173.5e86a9295010b785.js +0 -1
- package/.next/static/chunks/1552.b5a073e7ac834965.js +0 -1
- package/.next/static/chunks/3684.ba7a2a994988063b.js +0 -1
- package/.next/static/chunks/417.c39c1c5e5ef57b4a.js +0 -1
- package/.next/static/chunks/6167-ecb49640dcb9d566.js +0 -28
- package/.next/static/chunks/6335-5870fc075bf96b86.js +0 -1
- package/.next/static/chunks/6536.cd75ac69c241a35c.js +0 -1
- package/.next/static/chunks/7498-415859c993f5002b.js +0 -1
- package/.next/static/chunks/7959.9d31abae51a7e419.js +0 -1
- package/.next/static/chunks/8827.179b49f8ab3afe48.js +0 -1
- package/.next/static/chunks/9.e42e9b7463b94493.js +0 -1
- package/.next/static/chunks/BannerNewsletter.06b2b9724aa3c3ea.js +0 -1
- package/.next/static/chunks/CartItem.44cc90c698a69889.js +0 -1
- package/.next/static/chunks/Newsletter.d775e641d99322e6.js +0 -1
- package/.next/static/chunks/OrderSummary.8f34504b20564fec.js +0 -1
- package/.next/static/chunks/ProductShelf.0b5a71a93bca3148.js +0 -1
- package/.next/static/chunks/ProductTiles.a75612c0bcf990d6.js +0 -1
- package/.next/static/chunks/RegionModal.68b05bfd591d27fc.js +0 -1
- package/.next/static/chunks/UIBannerText.5ee380812ac3772c.js +0 -1
- package/.next/static/chunks/UIButton.c7a3273ac6ee5da5.js +0 -1
- package/.next/static/chunks/UISKUMatrixSidebar.c68540bda6d40e13.js +0 -1
- package/.next/static/chunks/UIToast.85ae30e3e8cc5ec7.js +0 -1
- package/.next/static/chunks/pages/[...slug]-2cfb2b1b8ee3b7a9.js +0 -1
- package/.next/static/chunks/pages/[slug]/p-3b513ae37c648620.js +0 -1
- package/.next/static/chunks/pages/_app-f02182ccd58f2781.js +0 -1
- package/.next/static/chunks/webpack-0dd5a14ceff64065.js +0 -1
- package/.next/static/css/31fb64e064998460.css +0 -1
- package/.next/static/f5jWOXDXh3GdBy9EK8IDc/_buildManifest.js +0 -1
- /package/.next/static/{f5jWOXDXh3GdBy9EK8IDc → U_9W0qMyv_4RQSLFdrtHC}/_ssgManifest.js +0 -0
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import type { RegionBarProps as UIRegionBarProps } from '@faststore/ui'
|
|
2
|
+
import { useEffect, useRef } from 'react'
|
|
2
3
|
|
|
3
4
|
import { useUI } from '@faststore/ui'
|
|
4
5
|
import { useSession } from 'src/sdk/session'
|
|
5
6
|
|
|
7
|
+
import { deliveryPromise, session as initialSession } from 'discovery.config'
|
|
6
8
|
import { useOverrideComponents } from 'src/sdk/overrides/OverrideContext'
|
|
9
|
+
import { textToTitleCase } from 'src/utils/utilities'
|
|
10
|
+
|
|
11
|
+
import { useRegionModal } from '../RegionModal/useRegionModal'
|
|
7
12
|
|
|
8
13
|
export interface RegionBarProps {
|
|
9
14
|
/**
|
|
@@ -43,8 +48,34 @@ function RegionBar({
|
|
|
43
48
|
ButtonIcon,
|
|
44
49
|
} = useOverrideComponents<'RegionBar'>()
|
|
45
50
|
|
|
46
|
-
const { openModal } = useUI()
|
|
47
|
-
const { postalCode } = useSession()
|
|
51
|
+
const { openModal, openPopover } = useUI()
|
|
52
|
+
const { city, postalCode } = useSession()
|
|
53
|
+
const { isValidationComplete } = useRegionModal()
|
|
54
|
+
const regionBarRef = useRef<HTMLDivElement>(null)
|
|
55
|
+
|
|
56
|
+
const defaultPostalCode =
|
|
57
|
+
!!initialSession?.postalCode && postalCode === initialSession.postalCode
|
|
58
|
+
|
|
59
|
+
// If location is not mandatory, and default zipCode is provided or if the user has not set a zipCode, show the popover.
|
|
60
|
+
const displayRegionPopover =
|
|
61
|
+
defaultPostalCode || (!postalCode && !deliveryPromise.mandatory)
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (!deliveryPromise.enabled) {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!isValidationComplete) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (isValidationComplete && displayRegionPopover && regionBarRef.current) {
|
|
73
|
+
openPopover({
|
|
74
|
+
isOpen: true,
|
|
75
|
+
triggerRef: regionBarRef,
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}, [isValidationComplete])
|
|
48
79
|
|
|
49
80
|
return (
|
|
50
81
|
<RegionBarWrapper.Component
|
|
@@ -69,7 +100,9 @@ function RegionBar({
|
|
|
69
100
|
// This decision can be reviewed later if needed
|
|
70
101
|
onButtonClick={openModal}
|
|
71
102
|
postalCode={postalCode}
|
|
103
|
+
city={textToTitleCase(city ?? '')}
|
|
72
104
|
{...otherProps}
|
|
105
|
+
ref={regionBarRef}
|
|
73
106
|
/>
|
|
74
107
|
)
|
|
75
108
|
}
|
|
@@ -1,21 +1,56 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useRef } from 'react'
|
|
2
2
|
|
|
3
|
-
import { Icon, useUI } from '@faststore/ui'
|
|
3
|
+
import { Button as UIButton, Icon as UIIcon, useUI } from '@faststore/ui'
|
|
4
|
+
import { deliveryPromise, session as initialSession } from 'discovery.config'
|
|
4
5
|
import { useSession } from 'src/sdk/session'
|
|
6
|
+
import { textToTitleCase } from 'src/utils/utilities'
|
|
7
|
+
|
|
8
|
+
import { useRegionModal } from '../RegionModal/useRegionModal'
|
|
5
9
|
|
|
6
10
|
function RegionButton({ icon, label }: { icon: string; label: string }) {
|
|
7
|
-
const { openModal } = useUI()
|
|
8
|
-
const { postalCode } = useSession()
|
|
11
|
+
const { openModal, openPopover } = useUI()
|
|
12
|
+
const { city, postalCode } = useSession()
|
|
13
|
+
const { isValidationComplete } = useRegionModal()
|
|
14
|
+
const regionButtonRef = useRef<HTMLButtonElement>(null)
|
|
15
|
+
|
|
16
|
+
const defaultPostalCode =
|
|
17
|
+
!!initialSession?.postalCode && postalCode === initialSession.postalCode
|
|
18
|
+
|
|
19
|
+
// If location is not mandatory, and default zipCode is provided or if the user has not set a zipCode, show the popover.
|
|
20
|
+
const displayRegionPopover =
|
|
21
|
+
defaultPostalCode || (!postalCode && !deliveryPromise.mandatory)
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (!deliveryPromise.enabled) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!isValidationComplete) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (
|
|
33
|
+
isValidationComplete &&
|
|
34
|
+
displayRegionPopover &&
|
|
35
|
+
regionButtonRef.current
|
|
36
|
+
) {
|
|
37
|
+
openPopover({
|
|
38
|
+
isOpen: true,
|
|
39
|
+
triggerRef: regionButtonRef,
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
}, [isValidationComplete])
|
|
9
43
|
|
|
10
44
|
return (
|
|
11
45
|
<UIButton
|
|
12
46
|
variant="tertiary"
|
|
13
47
|
size="small"
|
|
14
|
-
icon={<
|
|
48
|
+
icon={<UIIcon name={icon} width={18} height={18} weight="bold" />}
|
|
15
49
|
iconPosition="left"
|
|
16
50
|
onClick={openModal}
|
|
51
|
+
ref={regionButtonRef}
|
|
17
52
|
>
|
|
18
|
-
{postalCode
|
|
53
|
+
{city && postalCode ? `${textToTitleCase(city)}, ${postalCode}` : label}
|
|
19
54
|
</UIButton>
|
|
20
55
|
)
|
|
21
56
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
Icon,
|
|
3
|
-
type RegionModalProps as UIRegionModalProps,
|
|
4
|
-
useUI,
|
|
5
|
-
} from '@faststore/ui'
|
|
1
|
+
import dynamic from 'next/dynamic'
|
|
6
2
|
import { useRef, useState } from 'react'
|
|
7
3
|
|
|
8
|
-
import {
|
|
4
|
+
import type { RegionModalProps as UIRegionModalProps } from '@faststore/ui'
|
|
5
|
+
import { Icon, useUI } from '@faststore/ui'
|
|
6
|
+
|
|
7
|
+
import { deliveryPromise } from 'discovery.config'
|
|
8
|
+
import { useSession } from 'src/sdk/session'
|
|
9
|
+
|
|
10
|
+
import useRegion from './useRegion'
|
|
9
11
|
|
|
10
|
-
import dynamic from 'next/dynamic'
|
|
11
12
|
import styles from './section.module.scss'
|
|
12
13
|
|
|
13
14
|
const UIRegionModal = dynamic<UIRegionModalProps>(
|
|
@@ -17,7 +18,6 @@ const UIRegionModal = dynamic<UIRegionModalProps>(
|
|
|
17
18
|
),
|
|
18
19
|
{ ssr: false }
|
|
19
20
|
)
|
|
20
|
-
|
|
21
21
|
interface RegionModalProps {
|
|
22
22
|
title?: UIRegionModalProps['title']
|
|
23
23
|
description?: UIRegionModalProps['description']
|
|
@@ -25,6 +25,8 @@ interface RegionModalProps {
|
|
|
25
25
|
inputField?: {
|
|
26
26
|
label?: UIRegionModalProps['inputLabel']
|
|
27
27
|
errorMessage?: UIRegionModalProps['errorMessage']
|
|
28
|
+
noProductsAvailableErrorMessage?: UIRegionModalProps['errorMessage']
|
|
29
|
+
buttonActionText?: UIRegionModalProps['inputButtonActionText']
|
|
28
30
|
}
|
|
29
31
|
idkPostalCodeLink?: {
|
|
30
32
|
text?: string
|
|
@@ -40,7 +42,12 @@ function RegionModal({
|
|
|
40
42
|
title,
|
|
41
43
|
description,
|
|
42
44
|
closeButtonAriaLabel,
|
|
43
|
-
inputField: {
|
|
45
|
+
inputField: {
|
|
46
|
+
label: inputFieldLabel,
|
|
47
|
+
errorMessage: inputFieldErrorMessage,
|
|
48
|
+
noProductsAvailableErrorMessage: inputFieldNoProductsAvailableErrorMessage,
|
|
49
|
+
buttonActionText: inputButtonActionText,
|
|
50
|
+
},
|
|
44
51
|
idkPostalCodeLink: {
|
|
45
52
|
text: idkPostalCodeLinkText,
|
|
46
53
|
to: idkPostalCodeLinkTo,
|
|
@@ -49,35 +56,33 @@ function RegionModal({
|
|
|
49
56
|
}: RegionModalProps) {
|
|
50
57
|
const inputRef = useRef<HTMLInputElement>(null)
|
|
51
58
|
const { isValidating, ...session } = useSession()
|
|
52
|
-
const
|
|
59
|
+
const { modal: displayModal, closeModal } = useUI()
|
|
60
|
+
|
|
53
61
|
const [input, setInput] = useState<string>('')
|
|
54
|
-
const { modal: displayModal } = useUI()
|
|
55
62
|
|
|
56
|
-
const
|
|
57
|
-
const postalCode = inputRef.current?.value
|
|
63
|
+
const { loading, setRegion, regionError, setRegionError } = useRegion()
|
|
58
64
|
|
|
59
|
-
|
|
65
|
+
const handleSubmit = async () => {
|
|
66
|
+
if (isValidating) {
|
|
60
67
|
return
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
} catch (error) {
|
|
75
|
-
setErrorMessage(inputFieldErrorMessage)
|
|
76
|
-
}
|
|
70
|
+
await setRegion({
|
|
71
|
+
session,
|
|
72
|
+
onSuccess: () => {
|
|
73
|
+
setInput('')
|
|
74
|
+
closeModal()
|
|
75
|
+
},
|
|
76
|
+
postalCode: input,
|
|
77
|
+
errorMessage: inputFieldErrorMessage,
|
|
78
|
+
noProductsAvailableErrorMessage:
|
|
79
|
+
inputFieldNoProductsAvailableErrorMessage,
|
|
80
|
+
})
|
|
77
81
|
}
|
|
78
82
|
|
|
83
|
+
const isDismissible = !!(!deliveryPromise?.mandatory || session.postalCode)
|
|
79
84
|
const idkPostalCodeLinkProps: UIRegionModalProps['idkPostalCodeLinkProps'] = {
|
|
80
|
-
href: idkPostalCodeLinkTo
|
|
85
|
+
href: idkPostalCodeLinkTo,
|
|
81
86
|
children: (
|
|
82
87
|
<>
|
|
83
88
|
{idkPostalCodeLinkText}
|
|
@@ -106,15 +111,22 @@ function RegionModal({
|
|
|
106
111
|
inputRef={inputRef}
|
|
107
112
|
inputValue={input}
|
|
108
113
|
inputLabel={inputFieldLabel}
|
|
109
|
-
errorMessage={
|
|
110
|
-
idkPostalCodeLinkProps={
|
|
114
|
+
errorMessage={regionError}
|
|
115
|
+
idkPostalCodeLinkProps={
|
|
116
|
+
idkPostalCodeLinkTo ? idkPostalCodeLinkProps : null
|
|
117
|
+
}
|
|
111
118
|
onInput={(e) => {
|
|
112
|
-
|
|
119
|
+
regionError !== '' && setRegionError('')
|
|
113
120
|
setInput(e.currentTarget.value)
|
|
114
121
|
}}
|
|
115
122
|
onSubmit={handleSubmit}
|
|
116
|
-
fadeOutOnSubmit={
|
|
117
|
-
onClear={() =>
|
|
123
|
+
fadeOutOnSubmit={false}
|
|
124
|
+
onClear={() => {
|
|
125
|
+
setInput('')
|
|
126
|
+
setRegionError('')
|
|
127
|
+
}}
|
|
128
|
+
inputButtonActionText={loading ? '...' : inputButtonActionText}
|
|
129
|
+
dismissible={isDismissible}
|
|
118
130
|
/>
|
|
119
131
|
)}
|
|
120
132
|
</>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import type { Session } from '@faststore/sdk'
|
|
4
|
+
import { sessionStore, validateSession } from 'src/sdk/session'
|
|
5
|
+
import { getProductCount } from 'src/sdk/product'
|
|
6
|
+
import { deliveryPromise } from 'discovery.config'
|
|
7
|
+
|
|
8
|
+
type SetRegionProps = {
|
|
9
|
+
session: Session
|
|
10
|
+
postalCode: string | undefined
|
|
11
|
+
onSuccess?: () => void
|
|
12
|
+
errorMessage: string
|
|
13
|
+
noProductsAvailableErrorMessage?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type UseRegionValues = {
|
|
17
|
+
loading: boolean
|
|
18
|
+
regionError: string
|
|
19
|
+
setRegion: (props: SetRegionProps) => Promise<void>
|
|
20
|
+
setRegionError: (value: string) => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default function useRegion(): UseRegionValues {
|
|
24
|
+
const [loading, setLoading] = useState<boolean>(false)
|
|
25
|
+
const [regionError, setRegionError] = useState<string>('')
|
|
26
|
+
|
|
27
|
+
const setRegion = async ({
|
|
28
|
+
postalCode,
|
|
29
|
+
errorMessage,
|
|
30
|
+
session,
|
|
31
|
+
onSuccess,
|
|
32
|
+
noProductsAvailableErrorMessage,
|
|
33
|
+
}: SetRegionProps) => {
|
|
34
|
+
if (typeof postalCode !== 'string') {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setLoading(true)
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const newSession = {
|
|
42
|
+
...session,
|
|
43
|
+
postalCode,
|
|
44
|
+
geoCoordinates: null, // Revalidate geo coordinates in API when users set a new postal code
|
|
45
|
+
} as Session
|
|
46
|
+
|
|
47
|
+
const validatedSession = await validateSession(newSession)
|
|
48
|
+
|
|
49
|
+
if (deliveryPromise.enabled) {
|
|
50
|
+
// Check product availability for specific postal code
|
|
51
|
+
const productCount = await getProductCount()
|
|
52
|
+
if (productCount === 0) {
|
|
53
|
+
const errorFallback = `There are no products available for ${postalCode}.`
|
|
54
|
+
const noProductsAvailableError =
|
|
55
|
+
noProductsAvailableErrorMessage?.replace(/%s/g, () => postalCode)
|
|
56
|
+
|
|
57
|
+
setRegionError(noProductsAvailableError ?? errorFallback)
|
|
58
|
+
setLoading(false)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
sessionStore.set(validatedSession ?? newSession)
|
|
64
|
+
setRegionError('')
|
|
65
|
+
onSuccess?.() // Execute the post-validation action (close modal, etc.)
|
|
66
|
+
} catch (error) {
|
|
67
|
+
setRegionError(errorMessage)
|
|
68
|
+
} finally {
|
|
69
|
+
setLoading(false) // Reset loading to false when validation is complete
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
loading,
|
|
75
|
+
setRegion,
|
|
76
|
+
regionError,
|
|
77
|
+
setRegionError,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useUI } from '@faststore/ui'
|
|
2
|
+
import { deliveryPromise } from 'discovery.config'
|
|
3
|
+
import { useEffect, useRef, useState } from 'react'
|
|
4
|
+
import { sessionStore, useSession } from 'src/sdk/session'
|
|
5
|
+
|
|
6
|
+
export function useRegionModal() {
|
|
7
|
+
const { openModal: displayRegionModal } = useUI()
|
|
8
|
+
const { isValidating } = useSession()
|
|
9
|
+
|
|
10
|
+
// Ref to track the previous value of isValidating
|
|
11
|
+
const prevIsValidating = useRef(isValidating)
|
|
12
|
+
|
|
13
|
+
// State to track if validation is complete
|
|
14
|
+
const [isValidationComplete, setValidationComplete] = useState(false)
|
|
15
|
+
|
|
16
|
+
const openRegionModal = () => {
|
|
17
|
+
const { postalCode } = sessionStore.read()
|
|
18
|
+
if (!postalCode) {
|
|
19
|
+
displayRegionModal()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Effect to handle when isValidating changes from true to false
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!deliveryPromise.enabled) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check if validation has completed (isValidating changed from true to false)
|
|
30
|
+
if (prevIsValidating.current && !isValidating) {
|
|
31
|
+
setValidationComplete(true)
|
|
32
|
+
|
|
33
|
+
// If the postal code is not set and is mandatory, open the region modal
|
|
34
|
+
if (deliveryPromise.mandatory) {
|
|
35
|
+
openRegionModal()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Update the previous value of isValidating
|
|
40
|
+
prevIsValidating.current = isValidating
|
|
41
|
+
}, [openRegionModal])
|
|
42
|
+
|
|
43
|
+
return { openRegionModal, isValidationComplete }
|
|
44
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { PopoverProps as UIPopoverProps } from '@faststore/ui'
|
|
2
|
+
import {
|
|
3
|
+
Icon as UIIcon,
|
|
4
|
+
InputField as UIInputField,
|
|
5
|
+
Link as UILink,
|
|
6
|
+
Popover as UIPopover,
|
|
7
|
+
useUI,
|
|
8
|
+
} from '@faststore/ui'
|
|
9
|
+
import { useRef, useState } from 'react'
|
|
10
|
+
|
|
11
|
+
import useRegion from '../RegionModal/useRegion'
|
|
12
|
+
|
|
13
|
+
import { sessionStore, useSession } from 'src/sdk/session'
|
|
14
|
+
import { textToTitleCase } from 'src/utils/utilities'
|
|
15
|
+
import styles from './section.module.scss'
|
|
16
|
+
|
|
17
|
+
interface RegionPopoverProps {
|
|
18
|
+
title?: UIPopoverProps['title']
|
|
19
|
+
closeButtonAriaLabel?: UIPopoverProps['closeButtonAriaLabel']
|
|
20
|
+
inputField?: {
|
|
21
|
+
label?: string
|
|
22
|
+
errorMessage?: string
|
|
23
|
+
noProductsAvailableErrorMessage?: string
|
|
24
|
+
buttonActionText?: string
|
|
25
|
+
}
|
|
26
|
+
idkPostalCodeLink?: {
|
|
27
|
+
text?: string
|
|
28
|
+
to?: string
|
|
29
|
+
icon?: {
|
|
30
|
+
icon?: string
|
|
31
|
+
alt?: string
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
textBeforeLocation?: string
|
|
35
|
+
textAfterLocation?: string
|
|
36
|
+
description?: string
|
|
37
|
+
triggerRef?: UIPopoverProps['triggerRef']
|
|
38
|
+
onDismiss: UIPopoverProps['onDismiss']
|
|
39
|
+
offsetTop?: UIPopoverProps['offsetTop']
|
|
40
|
+
offsetLeft?: UIPopoverProps['offsetLeft']
|
|
41
|
+
placement?: UIPopoverProps['placement']
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function RegionPopover({
|
|
45
|
+
title = 'Set your location',
|
|
46
|
+
closeButtonAriaLabel,
|
|
47
|
+
inputField: {
|
|
48
|
+
label: inputFieldLabel,
|
|
49
|
+
errorMessage: inputFieldErrorMessage,
|
|
50
|
+
noProductsAvailableErrorMessage: inputFieldNoProductsAvailableErrorMessage,
|
|
51
|
+
buttonActionText: inputButtonActionText,
|
|
52
|
+
},
|
|
53
|
+
idkPostalCodeLink: {
|
|
54
|
+
text: idkPostalCodeLinkText,
|
|
55
|
+
to: idkPostalCodeLinkTo,
|
|
56
|
+
icon: { icon: idkPostalCodeLinkIcon, alt: idkPostalCodeLinkIconAlt },
|
|
57
|
+
},
|
|
58
|
+
textBeforeLocation = 'Your current location is:',
|
|
59
|
+
textAfterLocation = 'Use the field below to change it.',
|
|
60
|
+
description = 'Offers and availability vary by location.',
|
|
61
|
+
triggerRef,
|
|
62
|
+
offsetTop = 6,
|
|
63
|
+
offsetLeft,
|
|
64
|
+
placement = 'bottom-start',
|
|
65
|
+
}: RegionPopoverProps) {
|
|
66
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
67
|
+
const { isValidating, ...session } = useSession()
|
|
68
|
+
const { popover: displayPopover, closePopover } = useUI()
|
|
69
|
+
const { city, postalCode } = sessionStore.read()
|
|
70
|
+
const location = city ? `${textToTitleCase(city)}, ${postalCode}` : postalCode
|
|
71
|
+
|
|
72
|
+
const [input, setInput] = useState<string>('')
|
|
73
|
+
|
|
74
|
+
const { loading, setRegion, regionError, setRegionError } = useRegion()
|
|
75
|
+
|
|
76
|
+
const handleSubmit = async () => {
|
|
77
|
+
if (isValidating) {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
await setRegion({
|
|
82
|
+
session,
|
|
83
|
+
onSuccess: () => {
|
|
84
|
+
setInput('')
|
|
85
|
+
closePopover()
|
|
86
|
+
},
|
|
87
|
+
postalCode: input,
|
|
88
|
+
errorMessage: inputFieldErrorMessage,
|
|
89
|
+
noProductsAvailableErrorMessage:
|
|
90
|
+
inputFieldNoProductsAvailableErrorMessage,
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const idkPostalCodeLinkProps = {
|
|
95
|
+
href: idkPostalCodeLinkTo,
|
|
96
|
+
children: (
|
|
97
|
+
<>
|
|
98
|
+
{idkPostalCodeLinkText}
|
|
99
|
+
{!!idkPostalCodeLinkIcon && (
|
|
100
|
+
<UIIcon
|
|
101
|
+
name={idkPostalCodeLinkIcon}
|
|
102
|
+
aria-label={idkPostalCodeLinkIconAlt}
|
|
103
|
+
width={20}
|
|
104
|
+
height={20}
|
|
105
|
+
/>
|
|
106
|
+
)}
|
|
107
|
+
</>
|
|
108
|
+
),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const RegionPopoverContent = (
|
|
112
|
+
<>
|
|
113
|
+
<span data-fs-region-popover-description>
|
|
114
|
+
{postalCode ? (
|
|
115
|
+
<>
|
|
116
|
+
{textBeforeLocation} <span>{location}</span>. {textAfterLocation}
|
|
117
|
+
</>
|
|
118
|
+
) : (
|
|
119
|
+
<>{description}</>
|
|
120
|
+
)}
|
|
121
|
+
</span>
|
|
122
|
+
<UIInputField
|
|
123
|
+
data-fs-region-popover-input
|
|
124
|
+
id="region-popover-input-postal-code"
|
|
125
|
+
inputRef={inputRef}
|
|
126
|
+
label={inputFieldLabel}
|
|
127
|
+
actionable
|
|
128
|
+
value={input}
|
|
129
|
+
onInput={(e) => {
|
|
130
|
+
regionError !== '' && setRegionError('')
|
|
131
|
+
setInput(e.currentTarget.value)
|
|
132
|
+
}}
|
|
133
|
+
onSubmit={handleSubmit}
|
|
134
|
+
onClear={() => {
|
|
135
|
+
setInput('')
|
|
136
|
+
setRegionError('')
|
|
137
|
+
}}
|
|
138
|
+
buttonActionText={loading ? '...' : inputButtonActionText}
|
|
139
|
+
error={regionError}
|
|
140
|
+
/>
|
|
141
|
+
{idkPostalCodeLinkTo && (
|
|
142
|
+
<UILink data-fs-region-popover-link {...idkPostalCodeLinkProps} />
|
|
143
|
+
)}
|
|
144
|
+
</>
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<>
|
|
149
|
+
{displayPopover.isOpen && (
|
|
150
|
+
<div className={`${styles.section} section-region-popover`}>
|
|
151
|
+
<UIPopover
|
|
152
|
+
data-fs-region-popover
|
|
153
|
+
title={title}
|
|
154
|
+
isOpen={displayPopover.isOpen}
|
|
155
|
+
content={RegionPopoverContent}
|
|
156
|
+
placement={placement}
|
|
157
|
+
dismissible
|
|
158
|
+
triggerRef={triggerRef}
|
|
159
|
+
offsetTop={offsetTop}
|
|
160
|
+
offsetLeft={offsetLeft}
|
|
161
|
+
closeButtonAriaLabel={closeButtonAriaLabel}
|
|
162
|
+
onEntered={() => {
|
|
163
|
+
if (!postalCode && inputRef.current) {
|
|
164
|
+
inputRef.current.focus()
|
|
165
|
+
}
|
|
166
|
+
}}
|
|
167
|
+
/>
|
|
168
|
+
</div>
|
|
169
|
+
)}
|
|
170
|
+
</>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export default RegionPopover
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './RegionPopover'
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
.section {
|
|
2
|
+
@import "@faststore/ui/src/components/atoms/Icon/styles.scss";
|
|
3
|
+
@import "@faststore/ui/src/components/atoms/Input/styles.scss";
|
|
4
|
+
@import "@faststore/ui/src/components/atoms/Button/styles.scss";
|
|
5
|
+
@import "@faststore/ui/src/components/atoms/Link/styles.scss";
|
|
6
|
+
@import "@faststore/ui/src/components/molecules/InputField/styles.scss";
|
|
7
|
+
@import "@faststore/ui/src/components/molecules/Popover/styles.scss";
|
|
8
|
+
@import "@faststore/ui/src/components/organisms/RegionPopover/styles.scss";
|
|
9
|
+
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
Icon as UIIcon,
|
|
19
19
|
IconButton as UIIconButton,
|
|
20
20
|
SearchInput as UISearchInput,
|
|
21
|
+
useOnClickOutside,
|
|
21
22
|
} from '@faststore/ui'
|
|
22
23
|
|
|
23
24
|
import type {
|
|
@@ -27,10 +28,9 @@ import type {
|
|
|
27
28
|
|
|
28
29
|
import type { SearchProviderContextValue } from '@faststore/ui'
|
|
29
30
|
|
|
31
|
+
import type { NavbarProps } from 'src/components/sections/Navbar'
|
|
30
32
|
import useSearchHistory from 'src/sdk/search/useSearchHistory'
|
|
31
33
|
import useSuggestions from 'src/sdk/search/useSuggestions'
|
|
32
|
-
import useOnClickOutside from 'src/sdk/ui/useOnClickOutside'
|
|
33
|
-
import type { NavbarProps } from 'src/components/sections/Navbar'
|
|
34
34
|
|
|
35
35
|
import { formatSearchPath } from 'src/sdk/search/formatSearchPath'
|
|
36
36
|
|
|
@@ -14,10 +14,13 @@
|
|
|
14
14
|
@import "@faststore/ui/src/components/atoms/Logo/styles.scss";
|
|
15
15
|
@import "@faststore/ui/src/components/atoms/Overlay/styles.scss";
|
|
16
16
|
@import "@faststore/ui/src/components/atoms/Price/styles.scss";
|
|
17
|
+
@import "@faststore/ui/src/components/molecules/InputField/styles.scss";
|
|
17
18
|
@import "@faststore/ui/src/components/molecules/LinkButton/styles.scss";
|
|
18
19
|
@import "@faststore/ui/src/components/molecules/QuantitySelector/styles.scss";
|
|
19
20
|
@import "@faststore/ui/src/components/molecules/NavbarLinks/styles.scss";
|
|
21
|
+
@import "@faststore/ui/src/components/molecules/Popover/styles.scss";
|
|
20
22
|
@import "@faststore/ui/src/components/molecules/ProductPrice/styles.scss";
|
|
23
|
+
@import "@faststore/ui/src/components/organisms/RegionPopover/styles.scss";
|
|
21
24
|
@import "@faststore/ui/src/components/molecules/SearchAutoComplete/styles.scss";
|
|
22
25
|
@import "@faststore/ui/src/components/molecules/SearchDropdown/styles.scss";
|
|
23
26
|
@import "@faststore/ui/src/components/molecules/SearchHistory/styles.scss";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import useScreenResize from 'src/sdk/ui/useScreenResize'
|
|
1
2
|
import { getOverridableSection } from '../../..//sdk/overrides/getOverriddenSection'
|
|
2
3
|
import RegionBar, {
|
|
3
4
|
type RegionBarProps,
|
|
@@ -30,10 +31,14 @@ type RegionBarSectionProps = {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
function RegionBarSection({ ...otherProps }: RegionBarSectionProps) {
|
|
34
|
+
const { isDesktop } = useScreenResize()
|
|
35
|
+
|
|
33
36
|
return (
|
|
34
|
-
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
+
!isDesktop && (
|
|
38
|
+
<Section className={`${styles.section} section-region-bar`}>
|
|
39
|
+
<RegionBar {...otherProps} />
|
|
40
|
+
</Section>
|
|
41
|
+
)
|
|
37
42
|
)
|
|
38
43
|
}
|
|
39
44
|
|
package/src/constants.ts
CHANGED
package/src/pages/[slug]/p.tsx
CHANGED
package/src/pages/_app.tsx
CHANGED
|
@@ -3,17 +3,19 @@ import type { AppProps } from 'next/app'
|
|
|
3
3
|
import Layout from 'src/Layout'
|
|
4
4
|
import AnalyticsHandler from 'src/sdk/analytics'
|
|
5
5
|
import ErrorBoundary from 'src/sdk/error/ErrorBoundary'
|
|
6
|
+
import useGeolocation from 'src/sdk/geolocation/useGeolocation'
|
|
6
7
|
import SEO from '../../next-seo.config'
|
|
7
8
|
|
|
8
9
|
// FastStore UI's base styles
|
|
9
|
-
import '../styles/global/index.scss'
|
|
10
|
-
import '../plugins/index.scss'
|
|
11
10
|
import '../customizations/src/themes/index.scss'
|
|
11
|
+
import '../plugins/index.scss'
|
|
12
|
+
import '../styles/global/index.scss'
|
|
12
13
|
|
|
13
14
|
import { DefaultSeo } from 'next-seo'
|
|
14
15
|
|
|
15
16
|
function App({ Component, pageProps }: AppProps) {
|
|
16
17
|
const { key } = pageProps
|
|
18
|
+
useGeolocation()
|
|
17
19
|
|
|
18
20
|
return (
|
|
19
21
|
<ErrorBoundary>
|