@faststore/core 4.10.1-dev.6 → 4.10.1-dev.7
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/.turbo/turbo-generate.log +3 -3
- package/.turbo/turbo-test.log +176 -167
- package/@generated/graphql.ts +1 -0
- package/@generated/schema.graphql +1 -1
- package/CHANGELOG.md +29 -0
- package/package.json +6 -6
- package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawer.tsx +3 -3
- package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerBody.tsx +5 -7
- package/src/components/account/Drawer/OrganizationSignInButton/OrganizationSignInButton.tsx +4 -1
- package/src/components/account/Layout/Layout.tsx +5 -7
- package/src/components/account/Menu/Menu.tsx +1 -1
- package/src/components/navigation/Navbar/Navbar.tsx +3 -3
- package/src/components/navigation/Navbar/NavbarAccountArea.tsx +5 -5
- package/src/components/navigation/NavbarSlider/NavbarSlider.tsx +3 -3
- package/src/experimental/myAccountCmsServerSideProps.ts +3 -3
- package/src/experimental/myAccountServerSideProps.ts +4 -4
- package/src/pages/[slug]/p.tsx +11 -2
- package/src/pages/api/fs/logout.ts +18 -10
- package/src/pages/api/fs/switch-contract.ts +170 -21
- package/src/pages/api/graphql.ts +4 -3
- package/src/pages/pvt/account/cards/index.tsx +5 -5
- package/src/pages/pvt/account/orders/[id].tsx +5 -5
- package/src/pages/pvt/account/orders/index.tsx +5 -5
- package/src/pages/pvt/account/profile.tsx +5 -5
- package/src/pages/pvt/account/quotes/index.tsx +5 -5
- package/src/pages/pvt/account/security.tsx +5 -5
- package/src/pages/pvt/account/user-details.tsx +6 -6
- package/src/sdk/account/getB2BSessionClaims.ts +1 -1
- package/src/sdk/account/getIsB2BBuyer.ts +28 -0
- package/src/sdk/account/refreshToken.ts +89 -11
- package/src/sdk/account/refreshTokenPaths.ts +25 -0
- package/src/sdk/account/useRefreshToken.ts +10 -5
- package/src/sdk/product/isProductNotFound.ts +13 -0
- package/src/sdk/product/useProductNotFoundRedirect.ts +43 -0
- package/src/sdk/product/useProductQuery.ts +29 -0
- package/src/sdk/session/index.ts +4 -4
- package/src/utils/getCheckoutParentDomain.ts +53 -0
- package/src/utils/trustedForwardedHost.ts +18 -0
- package/test/components/account/Layout.test.tsx +1 -1
- package/test/components/account/Menu.browser.test.tsx +7 -5
- package/test/components/account/OrganizationDrawer.browser.test.tsx +13 -13
- package/test/components/account/OrganizationDrawerBody.test.tsx +3 -3
- package/test/components/account/OrganizationSignInButton.browser.test.tsx +19 -4
- package/test/components/navigation/Navbar.browser.test.tsx +3 -3
- package/test/components/navigation/NavbarAccountArea.browser.test.tsx +5 -5
- package/test/components/navigation/NavbarSlider.browser.test.tsx +3 -3
- package/test/experimental/myAccountCmsServerSideProps.test.ts +5 -5
- package/test/pages/api/logout.test.ts +106 -0
- package/test/pages/api/switch-contract.test.ts +443 -15
- package/test/pages/pvt/account/cards/index.test.ts +3 -3
- package/test/sdk/account/getIsB2BBuyer.test.ts +52 -0
- package/test/sdk/account/refreshToken.browser.test.ts +10 -1
- package/test/sdk/account/refreshToken.test.ts +150 -18
- package/test/sdk/account/useRefreshToken.browser.test.ts +4 -1
- package/test/sdk/product/useProductNotFoundRedirect.browser.test.ts +83 -0
- package/test/sdk/product/useProductQuery.browser.test.ts +154 -0
- package/test/sdk/session/refreshTokenRoute.browser.test.ts +106 -0
- package/test/sdk/session/validateSession.browser.test.ts +4 -1
- package/test/utils/trustedForwardedHost.test.ts +21 -0
- package/src/sdk/account/getIsRepresentative.ts +0 -20
|
@@ -1280,7 +1280,7 @@ type StoreAuthor {
|
|
|
1280
1280
|
|
|
1281
1281
|
type StoreB2B {
|
|
1282
1282
|
customerId: String!
|
|
1283
|
-
isRepresentative: Boolean
|
|
1283
|
+
isRepresentative: Boolean @deprecated(reason: "Always true when `b2b` is set: a B2B session is identified by `unitId` and `customerId`. Check that `b2b` is not null instead. Scheduled for removal in the next major.")
|
|
1284
1284
|
unitName: String
|
|
1285
1285
|
unitId: String
|
|
1286
1286
|
firstName: String
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,35 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [4.10.1-dev.7](https://github.com/vtex/faststore/compare/v4.10.1-dev.6...v4.10.1-dev.7) (2026-10-28)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **api:** apply the unit and contract rule to accountProfile and guard non-string claims ([ab97c85](https://github.com/vtex/faststore/commit/ab97c85951e0c4b1a4f0f57a0e9a49560b74a81b))
|
|
11
|
+
- **api:** identify B2B sessions by organizational unit and contract ([36b7dc6](https://github.com/vtex/faststore/commit/36b7dc6eead7c8dd0ae38b15d4020b35ddafe8d2))
|
|
12
|
+
- **core:** address Sonar findings on the PDP 404 redirect ([47ccb81](https://github.com/vtex/faststore/commit/47ccb81b741a48d14a7c8da5361e23b08c15d3ca))
|
|
13
|
+
- **core:** expire parent-domain auth cookies on contract switch ([abe0845](https://github.com/vtex/faststore/commit/abe0845bcb487d1998294aab5ee5fd3a91e269e7))
|
|
14
|
+
- **core:** keep a single refresh route per session, like main ([afd6ce9](https://github.com/vtex/faststore/commit/afd6ce90126830d0c67f9e6bdf9521f95345c9f6))
|
|
15
|
+
- **core:** keep IP hosts host-only and Secure by scheme in switch-contract ([20ab5bf](https://github.com/vtex/faststore/commit/20ab5bf951b4be0dbb5ec09263b1e0c0aa32571e))
|
|
16
|
+
- **core:** keep switch-contract cookies host-only on previews ([940e76e](https://github.com/vtex/faststore/commit/940e76e6751293592cdb8b60d581be42c8c8e8dd))
|
|
17
|
+
- **core:** keep the PDP loading until the B2B 404 redirect ([7dc8008](https://github.com/vtex/faststore/commit/7dc80084c23e85e38d456a7bde0f505f38d11649))
|
|
18
|
+
- **core:** keep the switched contract on a sibling checkout host ([3b69fad](https://github.com/vtex/faststore/commit/3b69fad4594699c5b395ce86dbf19cb1821eba97))
|
|
19
|
+
- **core:** read the connection when x-forwarded-proto is absent ([6d4a7fa](https://github.com/vtex/faststore/commit/6d4a7fa9902d6d486e239001aeac94b51c1d06f6))
|
|
20
|
+
- **core:** redirect to /404 without reusing PDP path as data source ([158e653](https://github.com/vtex/faststore/commit/158e65305048eb3cf4d275324eef00d0c5bcb152))
|
|
21
|
+
- **core:** redirect to the 404 page for any shopper out of the assortment ([9ba660d](https://github.com/vtex/faststore/commit/9ba660d3f513512fd4bc0c27fb1750c6211a9182))
|
|
22
|
+
- **core:** redirect to the 404 page once per not-found product ([17bc72b](https://github.com/vtex/faststore/commit/17bc72b35291b9a847e1550555ecdaeb7805c40c))
|
|
23
|
+
- **core:** refresh the token through the Authenticator route ([8da50ef](https://github.com/vtex/faststore/commit/8da50efb6d9924385c2e2be98cdf0ca9a0564891))
|
|
24
|
+
- **core:** relay vid_rt on contract switch and bound the refreshAfter fallback ([d154bff](https://github.com/vtex/faststore/commit/d154bffc6523b51f391da0db928234223f693e8e))
|
|
25
|
+
- **core:** relay vid_rt on contract switch only with refresh token on ([0111217](https://github.com/vtex/faststore/commit/0111217b01079d4efdbd86bdee7be90a08bac8cf))
|
|
26
|
+
- **core:** retry transient PDP refetch failures, not 404s ([638f8a4](https://github.com/vtex/faststore/commit/638f8a47d878256239b4e1e43987fc6e287dade5))
|
|
27
|
+
- **core:** scope switch-contract auth cookie like VTEX ID ([dcab25f](https://github.com/vtex/faststore/commit/dcab25f861e022ec460606e234a0e8ac8e2ef2f7))
|
|
28
|
+
- **core:** scope the switched auth cookie on {account}.vtex.app ([b878178](https://github.com/vtex/faststore/commit/b87817886efd0632e61b30e6c4edb96975a4c05e))
|
|
29
|
+
- **core:** show 404 on the PDP when a B2B shopper cannot access the product ([45485ad](https://github.com/vtex/faststore/commit/45485adfb31b67e5310144cf8d37a0867b51a19c))
|
|
30
|
+
- **core:** skip retries for every not-found shape on the PDP refetch ([f7e808e](https://github.com/vtex/faststore/commit/f7e808ee4253ad5c966feb02c3b574abe5a742e3))
|
|
31
|
+
- **core:** surface a failed PDP product refetch so the 404 redirect can run ([d6a0923](https://github.com/vtex/faststore/commit/d6a0923125a68308913b122f8c2584e0fb5fcf32))
|
|
32
|
+
- **core:** use the Authenticator refresh route only for B2B sessions ([31d55cd](https://github.com/vtex/faststore/commit/31d55cd293b91646aba6147edfda84c37ba3fd17))
|
|
33
|
+
- **core:** use the B2B refresh route only with a unit and a contract ([bd76720](https://github.com/vtex/faststore/commit/bd76720a84fd4bae3e6fbea2d8b1bf3afdaabe02))
|
|
34
|
+
|
|
6
35
|
## [4.10.1-dev.6](https://github.com/vtex/faststore/compare/v4.10.1-dev.5...v4.10.1-dev.6) (2026-10-28)
|
|
7
36
|
|
|
8
37
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faststore/core",
|
|
3
|
-
"version": "4.10.1-dev.
|
|
3
|
+
"version": "4.10.1-dev.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -71,11 +71,11 @@
|
|
|
71
71
|
"style-loader": "^3.3.1",
|
|
72
72
|
"swr": "^2.2.5",
|
|
73
73
|
"use-sync-external-store": "^1.6.0",
|
|
74
|
-
"@faststore/api": "4.10.1-dev.
|
|
75
|
-
"@faststore/
|
|
76
|
-
"@faststore/
|
|
77
|
-
"@faststore/ui": "4.10.1-dev.
|
|
78
|
-
"@faststore/
|
|
74
|
+
"@faststore/api": "4.10.1-dev.7",
|
|
75
|
+
"@faststore/diagnostics": "4.10.1-dev.7",
|
|
76
|
+
"@faststore/sdk": "4.10.1-dev.7",
|
|
77
|
+
"@faststore/ui": "4.10.1-dev.7",
|
|
78
|
+
"@faststore/lighthouse": "4.10.1-dev.7"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@cypress/code-coverage": "^3.12.1",
|
|
@@ -23,7 +23,7 @@ type OrganizationDrawerView = 'menu' | 'switch'
|
|
|
23
23
|
type OrganizationDrawerProps = {
|
|
24
24
|
isOpen: boolean
|
|
25
25
|
closeDrawer: () => void
|
|
26
|
-
|
|
26
|
+
isB2BBuyer: boolean
|
|
27
27
|
/** Open straight into the contract switcher (My Account "Switch" button). */
|
|
28
28
|
initialView?: OrganizationDrawerView
|
|
29
29
|
}
|
|
@@ -142,7 +142,7 @@ export const doLogout = async (_event?: unknown) => {
|
|
|
142
142
|
export const OrganizationDrawer = ({
|
|
143
143
|
isOpen,
|
|
144
144
|
closeDrawer,
|
|
145
|
-
|
|
145
|
+
isB2BBuyer,
|
|
146
146
|
initialView = 'menu',
|
|
147
147
|
}: OrganizationDrawerProps) => {
|
|
148
148
|
const { fade, fadeOut } = useFadeEffect()
|
|
@@ -210,7 +210,7 @@ export const OrganizationDrawer = ({
|
|
|
210
210
|
}
|
|
211
211
|
/>
|
|
212
212
|
<OrganizationDrawerBody
|
|
213
|
-
|
|
213
|
+
isB2BBuyer={isB2BBuyer}
|
|
214
214
|
isOrgMember={isOrgMember}
|
|
215
215
|
/>
|
|
216
216
|
</div>
|
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
getMyAccountRoutes,
|
|
10
10
|
} from 'src/sdk/account/getMyAccountRoutes'
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const ROUTES_ONLY_FOR_B2B_BUYERS = new Set<string>([USER_DETAILS_ROUTE])
|
|
13
13
|
|
|
14
14
|
export const OrganizationDrawerBody = ({
|
|
15
|
-
|
|
15
|
+
isB2BBuyer,
|
|
16
16
|
isOrgMember,
|
|
17
17
|
}: {
|
|
18
|
-
|
|
18
|
+
isB2BBuyer: boolean
|
|
19
19
|
isOrgMember: boolean
|
|
20
20
|
}) => {
|
|
21
21
|
const navigationLabels = useAccountNavigationLabels()
|
|
@@ -26,11 +26,9 @@ export const OrganizationDrawerBody = ({
|
|
|
26
26
|
})
|
|
27
27
|
|
|
28
28
|
const routes = (
|
|
29
|
-
|
|
29
|
+
isB2BBuyer
|
|
30
30
|
? menuItems
|
|
31
|
-
: menuItems.filter(
|
|
32
|
-
({ route }) => !ROUTES_ONLY_FOR_REPRESENTATIVE.includes(route)
|
|
33
|
-
)
|
|
31
|
+
: menuItems.filter(({ route }) => !ROUTES_ONLY_FOR_B2B_BUYERS.has(route))
|
|
34
32
|
).filter(
|
|
35
33
|
({ route }) => isOrgMember || !ROUTES_ONLY_FOR_B2B_MEMBERS.includes(route)
|
|
36
34
|
)
|
|
@@ -6,9 +6,12 @@ import { OrganizationDrawer } from '../OrganizationDrawer/OrganizationDrawer'
|
|
|
6
6
|
|
|
7
7
|
export const OrganizationSignInButton = ({
|
|
8
8
|
icon: { alt, icon },
|
|
9
|
+
isB2BBuyer,
|
|
9
10
|
isRepresentative,
|
|
10
11
|
}: {
|
|
11
12
|
icon: { alt: string; icon: string }
|
|
13
|
+
isB2BBuyer?: boolean
|
|
14
|
+
/** @deprecated Use `isB2BBuyer`. */
|
|
12
15
|
isRepresentative?: boolean
|
|
13
16
|
}) => {
|
|
14
17
|
const [isOpen, setIsOpen] = useState(false)
|
|
@@ -50,7 +53,7 @@ export const OrganizationSignInButton = ({
|
|
|
50
53
|
<OrganizationDrawer
|
|
51
54
|
isOpen={isOpen}
|
|
52
55
|
closeDrawer={closeDrawer}
|
|
53
|
-
|
|
56
|
+
isB2BBuyer={Boolean(isB2BBuyer ?? isRepresentative)}
|
|
54
57
|
/>
|
|
55
58
|
)}
|
|
56
59
|
</>
|
|
@@ -13,16 +13,16 @@ import styles from '../section.module.scss'
|
|
|
13
13
|
|
|
14
14
|
export type LayoutProps = {
|
|
15
15
|
accountName: string
|
|
16
|
-
|
|
16
|
+
isB2BBuyer?: boolean
|
|
17
17
|
navigationLabels?: AccountNavigationLabels
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const ROUTES_ONLY_FOR_B2B_BUYERS = new Set<string>([USER_DETAILS_ROUTE])
|
|
21
21
|
|
|
22
22
|
const Layout = ({
|
|
23
23
|
children,
|
|
24
24
|
accountName,
|
|
25
|
-
|
|
25
|
+
isB2BBuyer = true,
|
|
26
26
|
navigationLabels,
|
|
27
27
|
}: PropsWithChildren<LayoutProps>) => {
|
|
28
28
|
const { b2b } = useSession()
|
|
@@ -41,11 +41,9 @@ const Layout = ({
|
|
|
41
41
|
// page. `useAdHocCard` is a separate, finer gate that only affects the
|
|
42
42
|
// Personal-tab rendering inside the page itself — it never hides the entry.
|
|
43
43
|
const routes = (
|
|
44
|
-
|
|
44
|
+
isB2BBuyer
|
|
45
45
|
? menuItems
|
|
46
|
-
: menuItems.filter(
|
|
47
|
-
({ route }) => !ROUTES_ONLY_FOR_REPRESENTATIVE.includes(route)
|
|
48
|
-
)
|
|
46
|
+
: menuItems.filter(({ route }) => !ROUTES_ONLY_FOR_B2B_BUYERS.has(route))
|
|
49
47
|
).filter(
|
|
50
48
|
({ route }) => isOrgMember || !ROUTES_ONLY_FOR_B2B_MEMBERS.includes(route)
|
|
51
49
|
)
|
|
@@ -79,7 +79,7 @@ const Menu = ({ avatarImageUrl, accountName, items }: MenuProps) => {
|
|
|
79
79
|
<OrganizationDrawer
|
|
80
80
|
isOpen
|
|
81
81
|
closeDrawer={() => setIsSwitcherOpen(false)}
|
|
82
|
-
|
|
82
|
+
isB2BBuyer={Boolean(b2b)}
|
|
83
83
|
initialView="switch"
|
|
84
84
|
/>
|
|
85
85
|
)}
|
|
@@ -132,9 +132,9 @@ function Navbar({
|
|
|
132
132
|
const isFaststoreMyAccountEnabled =
|
|
133
133
|
storeConfig.experimental?.enableFaststoreMyAccount
|
|
134
134
|
|
|
135
|
-
const
|
|
135
|
+
const isB2BBuyer = Boolean(b2b)
|
|
136
136
|
|
|
137
|
-
const isOrganizationEnabled = isFaststoreMyAccountEnabled &&
|
|
137
|
+
const isOrganizationEnabled = isFaststoreMyAccountEnabled && isB2BBuyer
|
|
138
138
|
const isLocalizationEnabled =
|
|
139
139
|
storeConfig.localization?.enabled &&
|
|
140
140
|
localizationButton?.shouldDisplayLocalizationButton
|
|
@@ -240,7 +240,7 @@ function Navbar({
|
|
|
240
240
|
<NavbarAccountArea
|
|
241
241
|
isSignInResolved={isSignInResolved}
|
|
242
242
|
isOrganizationEnabled={isOrganizationEnabled}
|
|
243
|
-
|
|
243
|
+
isB2BBuyer={isB2BBuyer}
|
|
244
244
|
signInButton={signInButton}
|
|
245
245
|
ButtonSignIn={ButtonSignIn}
|
|
246
246
|
skeletonAttr="data-fs-navbar-signin-skeleton"
|
|
@@ -10,9 +10,9 @@ type NavbarOverrides = ReturnType<typeof useOverrideComponents<'Navbar'>>
|
|
|
10
10
|
export type NavbarAccountAreaProps = {
|
|
11
11
|
/** Whether the B2B session has resolved enough to know what to render. */
|
|
12
12
|
isSignInResolved: boolean
|
|
13
|
-
/** Whether the
|
|
13
|
+
/** Whether the Organization sign-in area for B2B buyers is enabled. */
|
|
14
14
|
isOrganizationEnabled: boolean | undefined
|
|
15
|
-
|
|
15
|
+
isB2BBuyer?: boolean
|
|
16
16
|
signInButton: SectionNavbarProps['signInButton']
|
|
17
17
|
ButtonSignIn: NavbarOverrides['_experimentalButtonSignIn']
|
|
18
18
|
/** Data attribute toggled on the loading skeleton (Navbar vs NavbarSlider). */
|
|
@@ -24,13 +24,13 @@ export type NavbarAccountAreaProps = {
|
|
|
24
24
|
/**
|
|
25
25
|
* Renders the account/sign-in area shared by the desktop Navbar and the
|
|
26
26
|
* mobile NavbarSlider: a loading skeleton until the B2B session resolves,
|
|
27
|
-
* then either the Organization sign-in button (
|
|
27
|
+
* then either the Organization sign-in button (B2B buyers) or the
|
|
28
28
|
* storefront's own sign-in button.
|
|
29
29
|
*/
|
|
30
30
|
export const NavbarAccountArea = ({
|
|
31
31
|
isSignInResolved,
|
|
32
32
|
isOrganizationEnabled,
|
|
33
|
-
|
|
33
|
+
isB2BBuyer,
|
|
34
34
|
signInButton,
|
|
35
35
|
ButtonSignIn,
|
|
36
36
|
skeletonAttr,
|
|
@@ -48,7 +48,7 @@ export const NavbarAccountArea = ({
|
|
|
48
48
|
return (
|
|
49
49
|
<OrganizationSignInButton
|
|
50
50
|
icon={signInButton.icon}
|
|
51
|
-
|
|
51
|
+
isB2BBuyer={isB2BBuyer}
|
|
52
52
|
/>
|
|
53
53
|
)
|
|
54
54
|
}
|
|
@@ -54,8 +54,8 @@ function NavbarSlider({
|
|
|
54
54
|
const isFaststoreMyAccountEnabled =
|
|
55
55
|
storeConfig.experimental?.enableFaststoreMyAccount
|
|
56
56
|
|
|
57
|
-
const
|
|
58
|
-
const isOrganizationEnabled = isFaststoreMyAccountEnabled &&
|
|
57
|
+
const isB2BBuyer = Boolean(b2b)
|
|
58
|
+
const isOrganizationEnabled = isFaststoreMyAccountEnabled && isB2BBuyer
|
|
59
59
|
|
|
60
60
|
const isLocalizationEnabled =
|
|
61
61
|
storeConfig.localization?.enabled &&
|
|
@@ -92,7 +92,7 @@ function NavbarSlider({
|
|
|
92
92
|
<NavbarAccountArea
|
|
93
93
|
isSignInResolved={isSignInResolved}
|
|
94
94
|
isOrganizationEnabled={isOrganizationEnabled}
|
|
95
|
-
|
|
95
|
+
isB2BBuyer={isB2BBuyer}
|
|
96
96
|
signInButton={signInButton}
|
|
97
97
|
ButtonSignIn={ButtonSignIn}
|
|
98
98
|
skeletonAttr="data-fs-navbar-slider-signin-skeleton"
|
|
@@ -7,7 +7,7 @@ import type { Locator } from '@vtex/client-cms'
|
|
|
7
7
|
import type { GetServerSideProps } from 'next'
|
|
8
8
|
|
|
9
9
|
import { getGlobalSectionsData } from 'src/components/cms/GlobalSections'
|
|
10
|
-
import {
|
|
10
|
+
import { getIsB2BBuyer } from 'src/sdk/account/getIsB2BBuyer'
|
|
11
11
|
import type { AccountNavigationLabels } from 'src/sdk/account/getMyAccountRoutes'
|
|
12
12
|
import { validateUser } from 'src/sdk/account/validateUser'
|
|
13
13
|
import { execute } from 'src/server'
|
|
@@ -87,7 +87,7 @@ export function myAccountCmsServerSideProps(
|
|
|
87
87
|
return { redirect }
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
const
|
|
90
|
+
const isB2BBuyer = getIsB2BBuyer({
|
|
91
91
|
headers: context.req.headers as Record<string, string>,
|
|
92
92
|
account: storeConfig.api.storeId,
|
|
93
93
|
})
|
|
@@ -135,7 +135,7 @@ export function myAccountCmsServerSideProps(
|
|
|
135
135
|
props: {
|
|
136
136
|
globalSections: globalSectionsResult,
|
|
137
137
|
accountName: account.data?.accountProfile?.name ?? '',
|
|
138
|
-
|
|
138
|
+
isB2BBuyer,
|
|
139
139
|
pageSections,
|
|
140
140
|
navigationLabels: navigationData as AccountNavigationLabels,
|
|
141
141
|
},
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type GlobalSectionsData,
|
|
11
11
|
getGlobalSectionsData,
|
|
12
12
|
} from 'src/components/cms/GlobalSections'
|
|
13
|
-
import {
|
|
13
|
+
import { getIsB2BBuyer } from 'src/sdk/account/getIsB2BBuyer'
|
|
14
14
|
import { execute } from 'src/server'
|
|
15
15
|
|
|
16
16
|
import { validateUser } from 'src/sdk/account/validateUser'
|
|
@@ -23,7 +23,7 @@ import storeConfig from '../../discovery.config'
|
|
|
23
23
|
export type MyAccountProps = {
|
|
24
24
|
globalSections: GlobalSectionsData
|
|
25
25
|
accountName: string
|
|
26
|
-
|
|
26
|
+
isB2BBuyer?: boolean
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const query = gql(`
|
|
@@ -77,7 +77,7 @@ const getServerSidePropsBase: GetServerSideProps<
|
|
|
77
77
|
return { redirect }
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
const
|
|
80
|
+
const isB2BBuyer = getIsB2BBuyer({
|
|
81
81
|
headers: context.req.headers as Record<string, string>,
|
|
82
82
|
account: storeConfig.api.storeId,
|
|
83
83
|
})
|
|
@@ -115,7 +115,7 @@ const getServerSidePropsBase: GetServerSideProps<
|
|
|
115
115
|
props: {
|
|
116
116
|
globalSections: globalSectionsResult,
|
|
117
117
|
accountName: account.data?.accountProfile?.name ?? '',
|
|
118
|
-
|
|
118
|
+
isB2BBuyer,
|
|
119
119
|
},
|
|
120
120
|
}
|
|
121
121
|
}
|
package/src/pages/[slug]/p.tsx
CHANGED
|
@@ -44,6 +44,7 @@ import { LocalizedProductProvider } from 'src/sdk/localization/LocalizedProductC
|
|
|
44
44
|
import { getStoreURL } from 'src/sdk/localization/useLocalizationConfig'
|
|
45
45
|
import { getOfferUrl, useOffer } from 'src/sdk/offer'
|
|
46
46
|
import PageProvider, { type PDPContext } from 'src/sdk/overrides/PageProvider'
|
|
47
|
+
import { useProductNotFoundRedirect } from 'src/sdk/product/useProductNotFoundRedirect'
|
|
47
48
|
import { useProductQuery } from 'src/sdk/product/useProductQuery'
|
|
48
49
|
import { injectGlobalSections } from 'src/server/cms/global'
|
|
49
50
|
import type { PDPContentType } from 'src/server/cms/pdp'
|
|
@@ -185,7 +186,7 @@ function Page({
|
|
|
185
186
|
)
|
|
186
187
|
}
|
|
187
188
|
|
|
188
|
-
const { client, isValidating } = isClientOfferEnabled
|
|
189
|
+
const { client, isValidating, clientError } = isClientOfferEnabled
|
|
189
190
|
? (() => {
|
|
190
191
|
const offer = useOffer({ skuId: product.sku })
|
|
191
192
|
return {
|
|
@@ -195,6 +196,11 @@ function Page({
|
|
|
195
196
|
},
|
|
196
197
|
},
|
|
197
198
|
isValidating: offer.isValidating,
|
|
199
|
+
// This mode reads the offer straight from Intelligent Search and
|
|
200
|
+
// never runs `ClientProductQuery`, where the B2B contract assortment
|
|
201
|
+
// is checked, so there is no not-found to redirect on. Supporting
|
|
202
|
+
// the redirect here is out of scope for now.
|
|
203
|
+
clientError: undefined,
|
|
198
204
|
}
|
|
199
205
|
})()
|
|
200
206
|
: (() => {
|
|
@@ -204,15 +210,18 @@ function Page({
|
|
|
204
210
|
return {
|
|
205
211
|
client: productQuery.data,
|
|
206
212
|
isValidating: productQuery.isValidating,
|
|
213
|
+
clientError: productQuery.error,
|
|
207
214
|
}
|
|
208
215
|
})()
|
|
209
216
|
|
|
217
|
+
const isLeavingForNotFound = useProductNotFoundRedirect(clientError)
|
|
218
|
+
|
|
210
219
|
const { sections: globalSections, settings: globalSettings } =
|
|
211
220
|
globalSectionsProp ?? {}
|
|
212
221
|
const context = {
|
|
213
222
|
data: {
|
|
214
223
|
...deepmerge(server, client, { arrayMerge: overwriteMerge }),
|
|
215
|
-
isValidating,
|
|
224
|
+
isValidating: isValidating || isLeavingForNotFound,
|
|
216
225
|
},
|
|
217
226
|
globalSettings,
|
|
218
227
|
} as PDPContext
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
getCookieDomains,
|
|
8
8
|
getVtexCookieNames,
|
|
9
9
|
} from 'src/utils/clearCookies'
|
|
10
|
+
import { VID_RT_PATHS } from 'src/sdk/account/refreshTokenPaths'
|
|
11
|
+
import { getCheckoutParentDomain } from 'src/utils/getCheckoutParentDomain'
|
|
10
12
|
import { getRequestHostname } from 'src/utils/getRequestHostname'
|
|
11
13
|
|
|
12
14
|
const ADDITIONAL_COOKIES = ['CheckoutOrderFormOwnership'] as const
|
|
@@ -27,20 +29,26 @@ const handler: NextApiHandler = async (
|
|
|
27
29
|
try {
|
|
28
30
|
const hostname = getRequestHostname(request.headers.host)
|
|
29
31
|
const cookies = parse(request.headers.cookie ?? '')
|
|
30
|
-
const
|
|
32
|
+
const checkoutParent = hostname
|
|
33
|
+
? getCheckoutParentDomain(hostname)
|
|
34
|
+
: undefined
|
|
35
|
+
const domains = [
|
|
36
|
+
...(hostname ? getCookieDomains(hostname) : [undefined]),
|
|
37
|
+
...(checkoutParent ? [checkoutParent] : []),
|
|
38
|
+
]
|
|
31
39
|
const clearedCookies: string[] = []
|
|
32
40
|
|
|
33
41
|
const vtexCookieNames = getVtexCookieNames(Object.keys(cookies))
|
|
34
42
|
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
// vid_rt is scoped to the refresh routes, so the browser never sends it
|
|
44
|
+
// here and its presence can't be checked. Expire it on every path it can be scoped to.
|
|
45
|
+
if (discoveryConfig.experimental?.refreshToken) {
|
|
46
|
+
for (const path of VID_RT_PATHS) {
|
|
47
|
+
for (const domain of domains) {
|
|
48
|
+
clearedCookies.push(
|
|
49
|
+
expireCookieServer({ name: 'vid_rt', path, domain })
|
|
50
|
+
)
|
|
51
|
+
}
|
|
44
52
|
}
|
|
45
53
|
}
|
|
46
54
|
|