@faststore/core 4.10.1 → 4.10.2
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 +278 -235
- package/@generated/graphql.ts +1 -0
- package/@generated/schema.graphql +1 -1
- package/CHANGELOG.md +86 -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 +18 -4
- 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 +101 -60
- package/src/pages/pvt/account/403.tsx +5 -4
- 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 +15 -11
- package/src/sdk/product/isProductNotFound.ts +13 -0
- package/src/sdk/product/usePageProductsQuery.ts +80 -21
- package/src/sdk/product/useProductNotFoundRedirect.ts +43 -0
- package/src/sdk/product/useProductQuery.ts +29 -0
- package/src/sdk/session/index.ts +46 -34
- package/src/server/cms/index.ts +59 -7
- package/src/server/cms/pdp.ts +10 -5
- package/src/server/cms/plp.ts +10 -5
- package/src/server/content/promiseCache.ts +68 -0
- package/src/server/content/service.ts +71 -51
- package/src/utils/getCheckoutParentDomain.ts +53 -0
- package/src/utils/isLocalHost.ts +2 -16
- package/src/utils/shouldBypassRefreshToken.ts +63 -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/graphql.test.ts +175 -0
- package/test/pages/api/logout.test.ts +106 -0
- package/test/pages/api/switch-contract.test.ts +443 -15
- package/test/pages/pvt/account/403.test.ts +67 -0
- 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 +72 -0
- package/test/sdk/product/usePageProductsQuery.browser.test.ts +260 -0
- package/test/sdk/product/usePageProductsQuery.deliveryPromise.browser.test.ts +130 -0
- 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 +111 -5
- package/test/server/cms/index.test.ts +102 -9
- package/test/server/content/promiseCache.test.ts +150 -0
- package/test/server/content/service.test.ts +277 -11
- package/test/utils/shouldBypassRefreshToken.browser.test.ts +38 -0
- package/test/utils/shouldBypassRefreshToken.test.ts +98 -0
- package/test/utils/trustedForwardedHost.test.ts +21 -0
- package/src/sdk/account/getIsRepresentative.ts +0 -20
- package/test/utils/isLocalHost.browser.test.ts +0 -37
package/@generated/graphql.ts
CHANGED
|
@@ -1307,6 +1307,7 @@ export type StoreB2B = {
|
|
|
1307
1307
|
contractName: Maybe<Scalars['String']['output']>;
|
|
1308
1308
|
customerId: Scalars['String']['output'];
|
|
1309
1309
|
firstName: Maybe<Scalars['String']['output']>;
|
|
1310
|
+
/** @deprecated 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. */
|
|
1310
1311
|
isRepresentative: Maybe<Scalars['Boolean']['output']>;
|
|
1311
1312
|
lastName: Maybe<Scalars['String']['output']>;
|
|
1312
1313
|
organizationManager: Maybe<Scalars['Boolean']['output']>;
|
|
@@ -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,12 +3,98 @@
|
|
|
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.2](https://github.com/vtex/faststore/compare/v4.10.2-dev.0...v4.10.2) (2026-10-29)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @faststore/core
|
|
9
|
+
|
|
10
|
+
## [4.10.2-dev.0](https://github.com/vtex/faststore/compare/v4.10.1-dev.7...v4.10.2-dev.0) (2026-10-29)
|
|
11
|
+
|
|
12
|
+
## [4.10.1](https://github.com/vtex/faststore/compare/v4.10.1-dev.1...v4.10.1) (2026-10-23)
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
- **core:** cache Content Platform template list across page generations ([#3542](https://github.com/vtex/faststore/issues/3542)) ([ab44199](https://github.com/vtex/faststore/commit/ab441997841b5f47b4dcc65d82fdfbb03fb579f2)), closes [#3384](https://github.com/vtex/faststore/issues/3384)
|
|
17
|
+
|
|
6
18
|
## [4.10.1](https://github.com/vtex/faststore/compare/v4.10.0...v4.10.1) (2026-10-23)
|
|
7
19
|
|
|
8
20
|
### Bug Fixes
|
|
9
21
|
|
|
10
22
|
- **core:** cache Content Platform template list across page generations ([#3542](https://github.com/vtex/faststore/issues/3542)) ([ab44199](https://github.com/vtex/faststore/commit/ab441997841b5f47b4dcc65d82fdfbb03fb579f2)), closes [#3384](https://github.com/vtex/faststore/issues/3384)
|
|
11
23
|
|
|
24
|
+
## [4.10.1-dev.7](https://github.com/vtex/faststore/compare/v4.10.1-dev.6...v4.10.1-dev.7) (2026-10-28)
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
- **api:** apply the unit and contract rule to accountProfile and guard non-string claims ([ab97c85](https://github.com/vtex/faststore/commit/ab97c85951e0c4b1a4f0f57a0e9a49560b74a81b))
|
|
29
|
+
- **api:** identify B2B sessions by organizational unit and contract ([36b7dc6](https://github.com/vtex/faststore/commit/36b7dc6eead7c8dd0ae38b15d4020b35ddafe8d2))
|
|
30
|
+
- **core:** address Sonar findings on the PDP 404 redirect ([47ccb81](https://github.com/vtex/faststore/commit/47ccb81b741a48d14a7c8da5361e23b08c15d3ca))
|
|
31
|
+
- **core:** expire parent-domain auth cookies on contract switch ([abe0845](https://github.com/vtex/faststore/commit/abe0845bcb487d1998294aab5ee5fd3a91e269e7))
|
|
32
|
+
- **core:** keep a single refresh route per session, like main ([afd6ce9](https://github.com/vtex/faststore/commit/afd6ce90126830d0c67f9e6bdf9521f95345c9f6))
|
|
33
|
+
- **core:** keep IP hosts host-only and Secure by scheme in switch-contract ([20ab5bf](https://github.com/vtex/faststore/commit/20ab5bf951b4be0dbb5ec09263b1e0c0aa32571e))
|
|
34
|
+
- **core:** keep switch-contract cookies host-only on previews ([940e76e](https://github.com/vtex/faststore/commit/940e76e6751293592cdb8b60d581be42c8c8e8dd))
|
|
35
|
+
- **core:** keep the PDP loading until the B2B 404 redirect ([7dc8008](https://github.com/vtex/faststore/commit/7dc80084c23e85e38d456a7bde0f505f38d11649))
|
|
36
|
+
- **core:** keep the switched contract on a sibling checkout host ([3b69fad](https://github.com/vtex/faststore/commit/3b69fad4594699c5b395ce86dbf19cb1821eba97))
|
|
37
|
+
- **core:** read the connection when x-forwarded-proto is absent ([6d4a7fa](https://github.com/vtex/faststore/commit/6d4a7fa9902d6d486e239001aeac94b51c1d06f6))
|
|
38
|
+
- **core:** redirect to /404 without reusing PDP path as data source ([158e653](https://github.com/vtex/faststore/commit/158e65305048eb3cf4d275324eef00d0c5bcb152))
|
|
39
|
+
- **core:** redirect to the 404 page for any shopper out of the assortment ([9ba660d](https://github.com/vtex/faststore/commit/9ba660d3f513512fd4bc0c27fb1750c6211a9182))
|
|
40
|
+
- **core:** redirect to the 404 page once per not-found product ([17bc72b](https://github.com/vtex/faststore/commit/17bc72b35291b9a847e1550555ecdaeb7805c40c))
|
|
41
|
+
- **core:** refresh the token through the Authenticator route ([8da50ef](https://github.com/vtex/faststore/commit/8da50efb6d9924385c2e2be98cdf0ca9a0564891))
|
|
42
|
+
- **core:** relay vid_rt on contract switch and bound the refreshAfter fallback ([d154bff](https://github.com/vtex/faststore/commit/d154bffc6523b51f391da0db928234223f693e8e))
|
|
43
|
+
- **core:** relay vid_rt on contract switch only with refresh token on ([0111217](https://github.com/vtex/faststore/commit/0111217b01079d4efdbd86bdee7be90a08bac8cf))
|
|
44
|
+
- **core:** retry transient PDP refetch failures, not 404s ([638f8a4](https://github.com/vtex/faststore/commit/638f8a47d878256239b4e1e43987fc6e287dade5))
|
|
45
|
+
- **core:** scope switch-contract auth cookie like VTEX ID ([dcab25f](https://github.com/vtex/faststore/commit/dcab25f861e022ec460606e234a0e8ac8e2ef2f7))
|
|
46
|
+
- **core:** scope the switched auth cookie on {account}.vtex.app ([b878178](https://github.com/vtex/faststore/commit/b87817886efd0632e61b30e6c4edb96975a4c05e))
|
|
47
|
+
- **core:** show 404 on the PDP when a B2B shopper cannot access the product ([45485ad](https://github.com/vtex/faststore/commit/45485adfb31b67e5310144cf8d37a0867b51a19c))
|
|
48
|
+
- **core:** skip retries for every not-found shape on the PDP refetch ([f7e808e](https://github.com/vtex/faststore/commit/f7e808ee4253ad5c966feb02c3b574abe5a742e3))
|
|
49
|
+
- **core:** surface a failed PDP product refetch so the 404 redirect can run ([d6a0923](https://github.com/vtex/faststore/commit/d6a0923125a68308913b122f8c2584e0fb5fcf32))
|
|
50
|
+
- **core:** use the Authenticator refresh route only for B2B sessions ([31d55cd](https://github.com/vtex/faststore/commit/31d55cd293b91646aba6147edfda84c37ba3fd17))
|
|
51
|
+
- **core:** use the B2B refresh route only with a unit and a contract ([bd76720](https://github.com/vtex/faststore/commit/bd76720a84fd4bae3e6fbea2d8b1bf3afdaabe02))
|
|
52
|
+
|
|
53
|
+
## [4.10.1-dev.6](https://github.com/vtex/faststore/compare/v4.10.1-dev.5...v4.10.1-dev.6) (2026-10-28)
|
|
54
|
+
|
|
55
|
+
### Bug Fixes
|
|
56
|
+
|
|
57
|
+
- **core:** document that gallery scope keys reach the request URL ([dc61bdf](https://github.com/vtex/faststore/commit/dc61bdf2411b88ac15d29066a668a26f521785de))
|
|
58
|
+
- **core:** hash the B2B contract scope sent with gallery queries ([f8bfac9](https://github.com/vtex/faststore/commit/f8bfac9f5dd4fbda848982a701e54eb1f3d2c14b))
|
|
59
|
+
- **core:** refetch PLP first page for B2B buyers so the contract assortment applies ([72ddfdf](https://github.com/vtex/faststore/commit/72ddfdf06b7cc7a74aca7036cce37c400fef2cc0))
|
|
60
|
+
- **core:** tell B2B buyers apart by org unit in the gallery cache key ([c7fe237](https://github.com/vtex/faststore/commit/c7fe237088515c40a49f34b46297022279c57f93))
|
|
61
|
+
- **core:** widen the B2B contract scope hash to 53 bits ([f05f556](https://github.com/vtex/faststore/commit/f05f556c135b914af7c1058633ca50144a41669c))
|
|
62
|
+
|
|
63
|
+
## [4.10.1-dev.5](https://github.com/vtex/faststore/compare/v4.10.1-dev.4...v4.10.1-dev.5) (2026-10-28)
|
|
64
|
+
|
|
65
|
+
### Bug Fixes
|
|
66
|
+
|
|
67
|
+
- **core:** bypass refresh-token flow on WebOps preview hosts ([0c8db36](https://github.com/vtex/faststore/commit/0c8db363b2f62ff054e3f3524cd15951b3aafe91))
|
|
68
|
+
- **core:** detect preview hosts from x-forwarded-host on the server ([ec5d8a7](https://github.com/vtex/faststore/commit/ec5d8a7c70eca06c90153fe7cb5b58aa192bfb46))
|
|
69
|
+
|
|
70
|
+
## [4.10.1-dev.4](https://github.com/vtex/faststore/compare/v4.10.1-dev.3...v4.10.1-dev.4) (2026-10-28)
|
|
71
|
+
|
|
72
|
+
**Note:** Version bump only for package @faststore/core
|
|
73
|
+
|
|
74
|
+
## [4.10.1-dev.3](https://github.com/vtex/faststore/compare/v4.10.1-dev.2...v4.10.1-dev.3) (2026-10-27)
|
|
75
|
+
|
|
76
|
+
### Bug Fixes
|
|
77
|
+
|
|
78
|
+
- **core:** address review feedback on CMS cache ([8cec85f](https://github.com/vtex/faststore/commit/8cec85f730643dc72914c8477e9021c7437d8921))
|
|
79
|
+
- **core:** cache global and single CMS content across page generations ([6f20530](https://github.com/vtex/faststore/commit/6f205308216be07cb567595ff5ed59f6a229d876))
|
|
80
|
+
- **core:** do not cache empty required CMS content ([21a2dd3](https://github.com/vtex/faststore/commit/21a2dd3a1379c5b60ea96003e0addd6173f2923e))
|
|
81
|
+
|
|
82
|
+
## [4.10.1-dev.2](https://github.com/vtex/faststore/compare/v4.10.1-dev.1...v4.10.1-dev.2) (2026-10-27)
|
|
83
|
+
|
|
84
|
+
### Bug Fixes
|
|
85
|
+
|
|
86
|
+
- **core:** keep CMS author emails off public page props ([a63c6b1](https://github.com/vtex/faststore/commit/a63c6b1b880055a9ceb6395ad3bba04ad2ce4a95))
|
|
87
|
+
|
|
88
|
+
## [4.10.1-dev.1](https://github.com/vtex/faststore/compare/v4.10.1-dev.0...v4.10.1-dev.1) (2026-10-21)
|
|
89
|
+
|
|
90
|
+
### Bug Fixes
|
|
91
|
+
|
|
92
|
+
- **core:** cache Content Platform template list across page generations ([#3526](https://github.com/vtex/faststore/issues/3526)) ([b03ed33](https://github.com/vtex/faststore/commit/b03ed332bd8c4ea5ef4bb834b8590728946852e5)), closes [#3384](https://github.com/vtex/faststore/issues/3384) [#3384](https://github.com/vtex/faststore/issues/3384)
|
|
93
|
+
|
|
94
|
+
## [4.10.1-dev.0](https://github.com/vtex/faststore/compare/v4.10.0...v4.10.1-dev.0) (2026-10-15)
|
|
95
|
+
|
|
96
|
+
**Note:** Version bump only for package @faststore/core
|
|
97
|
+
|
|
12
98
|
# [4.10.0](https://github.com/vtex/faststore/compare/v4.10.0-dev.1...v4.10.0) (2026-10-15)
|
|
13
99
|
|
|
14
100
|
**Note:** Version bump only for package @faststore/core
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faststore/core",
|
|
3
|
-
"version": "4.10.
|
|
3
|
+
"version": "4.10.2",
|
|
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.
|
|
75
|
-
"@faststore/
|
|
76
|
-
"@faststore/
|
|
77
|
-
"@faststore/
|
|
78
|
-
"@faststore/
|
|
74
|
+
"@faststore/api": "4.10.2",
|
|
75
|
+
"@faststore/diagnostics": "4.10.2",
|
|
76
|
+
"@faststore/sdk": "4.10.2",
|
|
77
|
+
"@faststore/ui": "4.10.2",
|
|
78
|
+
"@faststore/lighthouse": "4.10.2"
|
|
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'
|
|
@@ -85,14 +86,18 @@ const OG_PRODUCT_CONDITION_BY_SCHEMA: Record<string, string> = {
|
|
|
85
86
|
'https://schema.org/UsedCondition': 'used',
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
type Props =
|
|
89
|
+
type Props = {
|
|
90
|
+
sections: PDPContentType['sections']
|
|
91
|
+
settings: PDPContentType['settings']
|
|
89
92
|
data: ServerProductQueryQuery
|
|
90
93
|
globalSections: GlobalSectionsData
|
|
94
|
+
offers: ReturnType<typeof toProductJsonLdOffer>
|
|
91
95
|
meta: {
|
|
92
96
|
title: string
|
|
93
97
|
description: string
|
|
94
98
|
canonical: string
|
|
95
99
|
}
|
|
100
|
+
key: string
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
// Array merging strategy from deepmerge that makes client arrays overwrite server array
|
|
@@ -181,7 +186,7 @@ function Page({
|
|
|
181
186
|
)
|
|
182
187
|
}
|
|
183
188
|
|
|
184
|
-
const { client, isValidating } = isClientOfferEnabled
|
|
189
|
+
const { client, isValidating, clientError } = isClientOfferEnabled
|
|
185
190
|
? (() => {
|
|
186
191
|
const offer = useOffer({ skuId: product.sku })
|
|
187
192
|
return {
|
|
@@ -191,6 +196,11 @@ function Page({
|
|
|
191
196
|
},
|
|
192
197
|
},
|
|
193
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,
|
|
194
204
|
}
|
|
195
205
|
})()
|
|
196
206
|
: (() => {
|
|
@@ -200,15 +210,18 @@ function Page({
|
|
|
200
210
|
return {
|
|
201
211
|
client: productQuery.data,
|
|
202
212
|
isValidating: productQuery.isValidating,
|
|
213
|
+
clientError: productQuery.error,
|
|
203
214
|
}
|
|
204
215
|
})()
|
|
205
216
|
|
|
217
|
+
const isLeavingForNotFound = useProductNotFoundRedirect(clientError)
|
|
218
|
+
|
|
206
219
|
const { sections: globalSections, settings: globalSettings } =
|
|
207
220
|
globalSectionsProp ?? {}
|
|
208
221
|
const context = {
|
|
209
222
|
data: {
|
|
210
223
|
...deepmerge(server, client, { arrayMerge: overwriteMerge }),
|
|
211
|
-
isValidating,
|
|
224
|
+
isValidating: isValidating || isLeavingForNotFound,
|
|
212
225
|
},
|
|
213
226
|
globalSettings,
|
|
214
227
|
} as PDPContext
|
|
@@ -500,7 +513,8 @@ export const getStaticProps: GetStaticProps<
|
|
|
500
513
|
return {
|
|
501
514
|
props: {
|
|
502
515
|
data,
|
|
503
|
-
|
|
516
|
+
sections: cmsPage.sections,
|
|
517
|
+
settings: cmsPage.settings,
|
|
504
518
|
meta,
|
|
505
519
|
offers,
|
|
506
520
|
globalSections: globalSectionsResult,
|
|
@@ -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
|
|