@faststore/core 4.4.1-dev.0 → 4.5.0-dev.1
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 +4 -4
- package/.turbo/turbo-test.log +137 -51
- package/@generated/cached-operations.json +4 -0
- package/@generated/gql.ts +30 -0
- package/@generated/graphql.ts +141 -0
- package/@generated/persisted-documents.json +5 -0
- package/@generated/schema.graphql +100 -0
- package/CHANGELOG.md +26 -0
- package/cms/faststore/components/cms_component__recommendationshelf.jsonc +86 -0
- package/cms/faststore/schema.json +89 -0
- package/discovery.config.default.js +3 -0
- package/discovery.config.js +10 -1
- package/discovery.config.local.example.js +62 -0
- package/package.json +6 -6
- package/public/icons.svg +4 -0
- package/src/Layout.tsx +6 -0
- package/src/components/account/Drawer/OrganizationDrawer/ContractSwitcher/ContractSwitcher.tsx +376 -0
- package/src/components/account/Drawer/OrganizationDrawer/ContractSwitcher/index.ts +2 -0
- package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawer.tsx +52 -23
- package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerBody.tsx +16 -6
- package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerFooter.tsx +55 -0
- package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerHeader.tsx +45 -5
- package/src/components/account/Drawer/OrganizationDrawer/index.ts +5 -0
- package/src/components/account/Drawer/OrganizationDrawer/section.module.scss +452 -54
- package/src/components/account/Layout/Layout.tsx +14 -5
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotes.tsx +170 -0
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountListQuotesTable.tsx +194 -0
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountQuoteStatusBadge.tsx +15 -0
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesFilterSlider/MyAccountQuotesFilterSlider.tsx +162 -0
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesFilterSlider/section.module.scss +49 -0
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesSelectedTags/MyAccountQuotesSelectedTags.tsx +125 -0
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesStatusSelector/MyAccountQuotesStatusSelector.tsx +107 -0
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesStatusSelector/styles.module.scss +144 -0
- package/src/components/account/quotes/MyAccountListQuotes/index.ts +2 -0
- package/src/components/account/quotes/MyAccountListQuotes/quoteFilters.ts +89 -0
- package/src/components/account/quotes/MyAccountListQuotes/styles.module.scss +174 -0
- package/src/components/cms/global/Components.ts +10 -0
- package/src/components/sections/ProductGallery/ProductGallery.tsx +1 -1
- package/src/components/sections/RecommendationShelf/RecommendationShelf.tsx +223 -0
- package/src/components/sections/RecommendationShelf/RecommendationShelf.types.ts +111 -0
- package/src/components/sections/RecommendationShelf/index.ts +1 -0
- package/src/components/sections/RecommendationShelf/section.module.scss +49 -0
- package/src/components/sections/RecommendationShelf/useRecommendationUserId.ts +49 -0
- package/src/components/sections/RecommendationShelf/useRecommendations.ts +55 -0
- package/src/components/sections/RecommendationShelf/vrn.ts +55 -0
- package/src/customizations/src/myAccount/extensions/quotes/after.tsx +5 -0
- package/src/customizations/src/myAccount/extensions/quotes/before.tsx +5 -0
- package/src/pages/[slug]/p.tsx +45 -0
- package/src/pages/pvt/account/quotes/index.tsx +264 -0
- package/src/sdk/account/changeContractToken.ts +119 -0
- package/src/sdk/account/clearPersistedSessionState.ts +26 -0
- package/src/sdk/account/getMyAccountRoutes.ts +7 -0
- package/src/sdk/account/refreshToken.ts +21 -8
- package/src/sdk/account/useAvailableContracts.ts +49 -0
- package/src/sdk/account/useSwitchContract.ts +60 -0
- package/src/sdk/analytics/hooks/useStartRecommendationSession.ts +96 -0
- package/src/sdk/analytics/utils.ts +9 -0
- package/src/utils/quoteStatus.ts +27 -0
- package/src/utils/retry.ts +69 -0
- package/test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx +235 -0
- package/test/components/RecommendationShelf/useRecommendationUserId.test.ts +69 -0
- package/test/components/RecommendationShelf/useRecommendations.test.ts +52 -0
- package/test/components/RecommendationShelf/vrn.test.ts +56 -0
- package/test/components/account/ContractSwitcher.browser.test.tsx +236 -0
- package/test/components/account/Layout.test.tsx +62 -0
- package/test/components/account/OrganizationDrawer.browser.test.tsx +197 -0
- package/test/components/account/OrganizationDrawerBody.test.tsx +52 -0
- package/test/components/account/OrganizationDrawerFooter.browser.test.tsx +54 -0
- package/test/components/account/OrganizationDrawerHeader.browser.test.tsx +55 -0
- package/test/components/account/doLogout.browser.test.ts +102 -0
- package/test/components/quotes/MyAccountListQuotes.test.tsx +232 -0
- package/test/components/quotes/MyAccountListQuotesTable.test.tsx +240 -0
- package/test/components/quotes/MyAccountQuoteStatusBadge.test.tsx +28 -0
- package/test/components/quotes/MyAccountQuotesFilterSlider.test.tsx +152 -0
- package/test/components/quotes/MyAccountQuotesSelectedTags.test.tsx +122 -0
- package/test/components/quotes/MyAccountQuotesStatusSelector.test.tsx +85 -0
- package/test/components/quotes/quoteFilters.test.ts +185 -0
- package/test/sdk/account/changeContractToken.browser.test.ts +157 -0
- package/test/sdk/account/clearPersistedSessionState.browser.test.ts +41 -0
- package/test/sdk/account/clearPersistedSessionState.test.ts +20 -0
- package/test/sdk/account/getMyAccountRoutes.test.ts +90 -0
- package/test/sdk/account/refreshToken.browser.test.ts +34 -0
- package/test/sdk/account/refreshToken.test.ts +87 -0
- package/test/sdk/account/useAvailableContracts.browser.test.ts +124 -0
- package/test/sdk/account/useSwitchContract.browser.test.ts +111 -0
- package/test/sdk/analytics/useStartRecommendationSession.test.ts +69 -0
- package/test/sdk/analytics/utils.test.ts +23 -0
- package/test/server/index.test.ts +5 -0
- package/test/utils/retry.test.ts +59 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SlideOver, useFadeEffect } from '@faststore/ui'
|
|
2
|
+
import { useEffect, useState } from 'react'
|
|
2
3
|
|
|
3
4
|
import { getStoreURL } from 'src/sdk/localization/useLocalizationConfig'
|
|
4
5
|
import { useSession } from 'src/sdk/session'
|
|
@@ -9,12 +10,15 @@ import {
|
|
|
9
10
|
getVtexCookieNames,
|
|
10
11
|
} from 'src/utils/clearCookies'
|
|
11
12
|
import storeConfig from '../../../../../discovery.config'
|
|
12
|
-
import {
|
|
13
|
+
import { ContractSwitcher } from './ContractSwitcher'
|
|
13
14
|
import { OrganizationDrawerBody } from './OrganizationDrawerBody'
|
|
15
|
+
import { OrganizationDrawerFooter } from './OrganizationDrawerFooter'
|
|
14
16
|
import { OrganizationDrawerHeader } from './OrganizationDrawerHeader'
|
|
15
17
|
import { setReloadAfterLogoutReturn } from './useReloadAfterLogoutReturn'
|
|
16
18
|
import styles from './section.module.scss'
|
|
17
19
|
|
|
20
|
+
type OrganizationDrawerView = 'menu' | 'switch'
|
|
21
|
+
|
|
18
22
|
type OrganizationDrawerProps = {
|
|
19
23
|
isOpen: boolean
|
|
20
24
|
closeDrawer: () => void
|
|
@@ -125,7 +129,9 @@ export const doLogout = async (_event?: unknown) => {
|
|
|
125
129
|
} finally {
|
|
126
130
|
setReloadAfterLogoutReturn()
|
|
127
131
|
window.location.assign(
|
|
128
|
-
`${storeConfig.secureSubdomain}/api/vtexid/pub/logout?scope=${
|
|
132
|
+
`${storeConfig.secureSubdomain}/api/vtexid/pub/logout?scope=${
|
|
133
|
+
storeConfig.api.storeId
|
|
134
|
+
}&returnUrl=${getStoreURL()}`
|
|
129
135
|
)
|
|
130
136
|
}
|
|
131
137
|
}
|
|
@@ -137,17 +143,30 @@ export const OrganizationDrawer = ({
|
|
|
137
143
|
}: OrganizationDrawerProps) => {
|
|
138
144
|
const { fade, fadeOut } = useFadeEffect()
|
|
139
145
|
const { b2b, person } = useSession()
|
|
146
|
+
const [view, setView] = useState<OrganizationDrawerView>('menu')
|
|
147
|
+
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
if (!isOpen) {
|
|
150
|
+
setView('menu')
|
|
151
|
+
}
|
|
152
|
+
}, [isOpen])
|
|
140
153
|
|
|
141
154
|
const contractName =
|
|
142
|
-
b2b?.contractName
|
|
143
|
-
|
|
144
|
-
''
|
|
155
|
+
b2b?.contractName?.trim() ||
|
|
156
|
+
b2b?.unitName?.trim() ||
|
|
157
|
+
`${(person?.givenName ?? '').trim()}` ||
|
|
158
|
+
'Organization'
|
|
145
159
|
|
|
146
160
|
const contractUrl = b2b?.unitId
|
|
147
161
|
? `/pvt/organization-account/org-unit/${b2b?.unitId}`
|
|
148
162
|
: null
|
|
149
163
|
|
|
150
164
|
const isOrganizationManager = b2b?.organizationManager || false
|
|
165
|
+
// The switcher is only meaningful for B2B buyers tied to an Organization Unit.
|
|
166
|
+
// Buyers with a single (or no alternative) contract see the empty state inside
|
|
167
|
+
// the switcher; we don't fetch the contract count eagerly to protect TTFB.
|
|
168
|
+
const canSwitchContract = Boolean(b2b?.unitId)
|
|
169
|
+
const isOrgMember = Boolean(b2b?.unitId)
|
|
151
170
|
|
|
152
171
|
return (
|
|
153
172
|
<SlideOver
|
|
@@ -162,24 +181,34 @@ export const OrganizationDrawer = ({
|
|
|
162
181
|
className: `section ${styles.section} section-organization-drawer`,
|
|
163
182
|
}}
|
|
164
183
|
>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
184
|
+
{view === 'switch' ? (
|
|
185
|
+
<ContractSwitcher onBack={() => setView('menu')} onClose={fadeOut} />
|
|
186
|
+
) : (
|
|
187
|
+
<div data-fs-organization-drawer-menu>
|
|
188
|
+
<div data-fs-organization-drawer-menu-scroll>
|
|
189
|
+
<OrganizationDrawerHeader
|
|
190
|
+
onCloseDrawer={closeDrawer}
|
|
191
|
+
contractName={contractName}
|
|
192
|
+
contractUrl={contractUrl}
|
|
193
|
+
onChangeContract={
|
|
194
|
+
canSwitchContract ? () => setView('switch') : undefined
|
|
195
|
+
}
|
|
196
|
+
/>
|
|
197
|
+
<OrganizationDrawerBody
|
|
198
|
+
isRepresentative={isRepresentative}
|
|
199
|
+
isOrgMember={isOrgMember}
|
|
200
|
+
/>
|
|
201
|
+
</div>
|
|
202
|
+
<OrganizationDrawerFooter
|
|
203
|
+
orgName={b2b?.unitName ?? ''}
|
|
204
|
+
userName={b2b?.userName ?? ''}
|
|
205
|
+
userEmail={b2b?.userEmail ?? ''}
|
|
206
|
+
showManageLink={isOrganizationManager}
|
|
207
|
+
manageUrl={contractUrl ?? undefined}
|
|
208
|
+
onLogoutClick={doLogout}
|
|
209
|
+
/>
|
|
210
|
+
</div>
|
|
211
|
+
)}
|
|
183
212
|
</SlideOver>
|
|
184
213
|
)
|
|
185
214
|
}
|
|
@@ -3,6 +3,7 @@ import Link from 'src/components/ui/Link'
|
|
|
3
3
|
import menuRoutes from 'src/customizations/src/myAccount/navigation'
|
|
4
4
|
import { useAccountNavigationLabels } from 'src/sdk/account/accountPageContext'
|
|
5
5
|
import {
|
|
6
|
+
ROUTES_ONLY_FOR_B2B_MEMBERS,
|
|
6
7
|
USER_DETAILS_ROUTE,
|
|
7
8
|
getExtraMyAccountRoutes,
|
|
8
9
|
getMyAccountRoutes,
|
|
@@ -12,7 +13,11 @@ const ROUTES_ONLY_FOR_REPRESENTATIVE = [USER_DETAILS_ROUTE]
|
|
|
12
13
|
|
|
13
14
|
export const OrganizationDrawerBody = ({
|
|
14
15
|
isRepresentative,
|
|
15
|
-
|
|
16
|
+
isOrgMember,
|
|
17
|
+
}: {
|
|
18
|
+
isRepresentative: boolean
|
|
19
|
+
isOrgMember: boolean
|
|
20
|
+
}) => {
|
|
16
21
|
const navigationLabels = useAccountNavigationLabels()
|
|
17
22
|
|
|
18
23
|
const menuItems = getMyAccountRoutes({
|
|
@@ -20,11 +25,16 @@ export const OrganizationDrawerBody = ({
|
|
|
20
25
|
labels: navigationLabels,
|
|
21
26
|
})
|
|
22
27
|
|
|
23
|
-
const routes =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const routes = (
|
|
29
|
+
isRepresentative
|
|
30
|
+
? menuItems
|
|
31
|
+
: menuItems.filter(
|
|
32
|
+
({ route }) => !ROUTES_ONLY_FOR_REPRESENTATIVE.includes(route)
|
|
33
|
+
)
|
|
34
|
+
).filter(
|
|
35
|
+
({ route }) => isOrgMember || !ROUTES_ONLY_FOR_B2B_MEMBERS.includes(route)
|
|
36
|
+
)
|
|
37
|
+
|
|
28
38
|
return (
|
|
29
39
|
<div data-fs-organization-drawer-body>
|
|
30
40
|
<div data-fs-organization-drawer-body-contents>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Icon, IconButton, Link } from '@faststore/ui'
|
|
2
|
+
|
|
3
|
+
export interface OrganizationDrawerFooterProps {
|
|
4
|
+
orgName: string
|
|
5
|
+
userName: string
|
|
6
|
+
userEmail?: string
|
|
7
|
+
showManageLink?: boolean
|
|
8
|
+
manageUrl?: string
|
|
9
|
+
onLogoutClick?: () => void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const OrganizationDrawerFooter = ({
|
|
13
|
+
orgName,
|
|
14
|
+
userName,
|
|
15
|
+
userEmail,
|
|
16
|
+
showManageLink,
|
|
17
|
+
manageUrl,
|
|
18
|
+
onLogoutClick,
|
|
19
|
+
}: OrganizationDrawerFooterProps) => {
|
|
20
|
+
return (
|
|
21
|
+
<div data-fs-organization-drawer-footer>
|
|
22
|
+
<div data-fs-organization-drawer-footer-org>
|
|
23
|
+
<div data-fs-organization-drawer-footer-org-info>
|
|
24
|
+
<Icon
|
|
25
|
+
data-fs-organization-drawer-footer-org-icon
|
|
26
|
+
name="FolderOpen"
|
|
27
|
+
width={18}
|
|
28
|
+
height={18}
|
|
29
|
+
/>
|
|
30
|
+
<span data-fs-organization-drawer-footer-org-name>{orgName}</span>
|
|
31
|
+
</div>
|
|
32
|
+
{showManageLink && manageUrl && (
|
|
33
|
+
<Link href={manageUrl} data-fs-organization-drawer-footer-manage>
|
|
34
|
+
Manage
|
|
35
|
+
</Link>
|
|
36
|
+
)}
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div data-fs-organization-drawer-footer-user>
|
|
40
|
+
<div data-fs-organization-drawer-footer-user-info>
|
|
41
|
+
<p data-fs-organization-drawer-footer-user-name>{userName}</p>
|
|
42
|
+
{userEmail && (
|
|
43
|
+
<p data-fs-organization-drawer-footer-user-email>{userEmail}</p>
|
|
44
|
+
)}
|
|
45
|
+
</div>
|
|
46
|
+
<IconButton
|
|
47
|
+
data-fs-organization-drawer-footer-logout
|
|
48
|
+
aria-label="Log out"
|
|
49
|
+
icon={<Icon name="SignOut" width={20} height={20} />}
|
|
50
|
+
onClick={onLogoutClick}
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -1,18 +1,29 @@
|
|
|
1
|
-
import { Link, SlideOverHeader } from '@faststore/ui'
|
|
1
|
+
import { Button, Icon, Link, SlideOverHeader } from '@faststore/ui'
|
|
2
2
|
import type { ReactNode } from 'react'
|
|
3
3
|
|
|
4
4
|
export type OrganizationDrawerHeaderProps = {
|
|
5
5
|
onCloseDrawer?: () => void
|
|
6
6
|
contractImage?: ReactNode
|
|
7
7
|
contractName: string
|
|
8
|
-
contractUrl: string
|
|
8
|
+
contractUrl: string | null
|
|
9
|
+
/**
|
|
10
|
+
* Marks the active contract as the Organization Unit's default (star).
|
|
11
|
+
*/
|
|
12
|
+
isDefault?: boolean
|
|
13
|
+
/**
|
|
14
|
+
* When provided, renders the "Change" CTA next to the active contract,
|
|
15
|
+
* opening the contract switcher (Figma node 103-5434).
|
|
16
|
+
*/
|
|
17
|
+
onChangeContract?: () => void
|
|
9
18
|
}
|
|
10
19
|
|
|
11
20
|
export const OrganizationDrawerHeader = ({
|
|
12
21
|
contractUrl,
|
|
13
22
|
contractName,
|
|
14
23
|
contractImage,
|
|
24
|
+
isDefault,
|
|
15
25
|
onCloseDrawer,
|
|
26
|
+
onChangeContract,
|
|
16
27
|
}: OrganizationDrawerHeaderProps) => {
|
|
17
28
|
return (
|
|
18
29
|
<>
|
|
@@ -20,6 +31,7 @@ export const OrganizationDrawerHeader = ({
|
|
|
20
31
|
<div data-fs-organization-drawer-header>
|
|
21
32
|
<Link
|
|
22
33
|
data-fs-organization-drawer-header-contract-link
|
|
34
|
+
aria-label={contractName}
|
|
23
35
|
{...(contractUrl && { href: contractUrl })}
|
|
24
36
|
>
|
|
25
37
|
<div data-fs-organization-drawer-header-contract-image>
|
|
@@ -29,10 +41,38 @@ export const OrganizationDrawerHeader = ({
|
|
|
29
41
|
</span>
|
|
30
42
|
)}
|
|
31
43
|
</div>
|
|
32
|
-
<h1 data-fs-organization-drawer-header-contract-name>
|
|
33
|
-
{contractName}
|
|
34
|
-
</h1>
|
|
35
44
|
</Link>
|
|
45
|
+
<div data-fs-organization-drawer-header-row>
|
|
46
|
+
<Link
|
|
47
|
+
data-fs-organization-drawer-header-contract-name-link
|
|
48
|
+
{...(contractUrl && { href: contractUrl })}
|
|
49
|
+
>
|
|
50
|
+
<h1 data-fs-organization-drawer-header-contract-name>
|
|
51
|
+
{contractName}
|
|
52
|
+
</h1>
|
|
53
|
+
</Link>
|
|
54
|
+
{(isDefault || onChangeContract) && (
|
|
55
|
+
<div data-fs-organization-drawer-header-actions>
|
|
56
|
+
{isDefault && (
|
|
57
|
+
<Icon
|
|
58
|
+
data-fs-organization-drawer-header-default
|
|
59
|
+
name="Star"
|
|
60
|
+
width={20}
|
|
61
|
+
height={20}
|
|
62
|
+
/>
|
|
63
|
+
)}
|
|
64
|
+
{onChangeContract && (
|
|
65
|
+
<Button
|
|
66
|
+
data-fs-organization-drawer-header-change
|
|
67
|
+
variant="secondary"
|
|
68
|
+
onClick={onChangeContract}
|
|
69
|
+
>
|
|
70
|
+
Change
|
|
71
|
+
</Button>
|
|
72
|
+
)}
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
</div>
|
|
36
76
|
</div>
|
|
37
77
|
</>
|
|
38
78
|
)
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export { OrganizationDrawer } from './OrganizationDrawer'
|
|
2
2
|
export { OrganizationDrawerBody } from './OrganizationDrawerBody'
|
|
3
|
+
export { OrganizationDrawerFooter } from './OrganizationDrawerFooter'
|
|
4
|
+
export type { OrganizationDrawerFooterProps } from './OrganizationDrawerFooter'
|
|
3
5
|
export { OrganizationDrawerHeader } from './OrganizationDrawerHeader'
|
|
6
|
+
export type { OrganizationDrawerHeaderProps } from './OrganizationDrawerHeader'
|
|
7
|
+
export { ContractSwitcher } from './ContractSwitcher'
|
|
8
|
+
export type { ContractSwitcherProps } from './ContractSwitcher'
|