@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.
Files changed (89) hide show
  1. package/.turbo/turbo-generate.log +4 -4
  2. package/.turbo/turbo-test.log +137 -51
  3. package/@generated/cached-operations.json +4 -0
  4. package/@generated/gql.ts +30 -0
  5. package/@generated/graphql.ts +141 -0
  6. package/@generated/persisted-documents.json +5 -0
  7. package/@generated/schema.graphql +100 -0
  8. package/CHANGELOG.md +26 -0
  9. package/cms/faststore/components/cms_component__recommendationshelf.jsonc +86 -0
  10. package/cms/faststore/schema.json +89 -0
  11. package/discovery.config.default.js +3 -0
  12. package/discovery.config.js +10 -1
  13. package/discovery.config.local.example.js +62 -0
  14. package/package.json +6 -6
  15. package/public/icons.svg +4 -0
  16. package/src/Layout.tsx +6 -0
  17. package/src/components/account/Drawer/OrganizationDrawer/ContractSwitcher/ContractSwitcher.tsx +376 -0
  18. package/src/components/account/Drawer/OrganizationDrawer/ContractSwitcher/index.ts +2 -0
  19. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawer.tsx +52 -23
  20. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerBody.tsx +16 -6
  21. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerFooter.tsx +55 -0
  22. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerHeader.tsx +45 -5
  23. package/src/components/account/Drawer/OrganizationDrawer/index.ts +5 -0
  24. package/src/components/account/Drawer/OrganizationDrawer/section.module.scss +452 -54
  25. package/src/components/account/Layout/Layout.tsx +14 -5
  26. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotes.tsx +170 -0
  27. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountListQuotesTable.tsx +194 -0
  28. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountQuoteStatusBadge.tsx +15 -0
  29. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesFilterSlider/MyAccountQuotesFilterSlider.tsx +162 -0
  30. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesFilterSlider/section.module.scss +49 -0
  31. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesSelectedTags/MyAccountQuotesSelectedTags.tsx +125 -0
  32. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesStatusSelector/MyAccountQuotesStatusSelector.tsx +107 -0
  33. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesStatusSelector/styles.module.scss +144 -0
  34. package/src/components/account/quotes/MyAccountListQuotes/index.ts +2 -0
  35. package/src/components/account/quotes/MyAccountListQuotes/quoteFilters.ts +89 -0
  36. package/src/components/account/quotes/MyAccountListQuotes/styles.module.scss +174 -0
  37. package/src/components/cms/global/Components.ts +10 -0
  38. package/src/components/sections/ProductGallery/ProductGallery.tsx +1 -1
  39. package/src/components/sections/RecommendationShelf/RecommendationShelf.tsx +223 -0
  40. package/src/components/sections/RecommendationShelf/RecommendationShelf.types.ts +111 -0
  41. package/src/components/sections/RecommendationShelf/index.ts +1 -0
  42. package/src/components/sections/RecommendationShelf/section.module.scss +49 -0
  43. package/src/components/sections/RecommendationShelf/useRecommendationUserId.ts +49 -0
  44. package/src/components/sections/RecommendationShelf/useRecommendations.ts +55 -0
  45. package/src/components/sections/RecommendationShelf/vrn.ts +55 -0
  46. package/src/customizations/src/myAccount/extensions/quotes/after.tsx +5 -0
  47. package/src/customizations/src/myAccount/extensions/quotes/before.tsx +5 -0
  48. package/src/pages/[slug]/p.tsx +45 -0
  49. package/src/pages/pvt/account/quotes/index.tsx +264 -0
  50. package/src/sdk/account/changeContractToken.ts +119 -0
  51. package/src/sdk/account/clearPersistedSessionState.ts +26 -0
  52. package/src/sdk/account/getMyAccountRoutes.ts +7 -0
  53. package/src/sdk/account/refreshToken.ts +21 -8
  54. package/src/sdk/account/useAvailableContracts.ts +49 -0
  55. package/src/sdk/account/useSwitchContract.ts +60 -0
  56. package/src/sdk/analytics/hooks/useStartRecommendationSession.ts +96 -0
  57. package/src/sdk/analytics/utils.ts +9 -0
  58. package/src/utils/quoteStatus.ts +27 -0
  59. package/src/utils/retry.ts +69 -0
  60. package/test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx +235 -0
  61. package/test/components/RecommendationShelf/useRecommendationUserId.test.ts +69 -0
  62. package/test/components/RecommendationShelf/useRecommendations.test.ts +52 -0
  63. package/test/components/RecommendationShelf/vrn.test.ts +56 -0
  64. package/test/components/account/ContractSwitcher.browser.test.tsx +236 -0
  65. package/test/components/account/Layout.test.tsx +62 -0
  66. package/test/components/account/OrganizationDrawer.browser.test.tsx +197 -0
  67. package/test/components/account/OrganizationDrawerBody.test.tsx +52 -0
  68. package/test/components/account/OrganizationDrawerFooter.browser.test.tsx +54 -0
  69. package/test/components/account/OrganizationDrawerHeader.browser.test.tsx +55 -0
  70. package/test/components/account/doLogout.browser.test.ts +102 -0
  71. package/test/components/quotes/MyAccountListQuotes.test.tsx +232 -0
  72. package/test/components/quotes/MyAccountListQuotesTable.test.tsx +240 -0
  73. package/test/components/quotes/MyAccountQuoteStatusBadge.test.tsx +28 -0
  74. package/test/components/quotes/MyAccountQuotesFilterSlider.test.tsx +152 -0
  75. package/test/components/quotes/MyAccountQuotesSelectedTags.test.tsx +122 -0
  76. package/test/components/quotes/MyAccountQuotesStatusSelector.test.tsx +85 -0
  77. package/test/components/quotes/quoteFilters.test.ts +185 -0
  78. package/test/sdk/account/changeContractToken.browser.test.ts +157 -0
  79. package/test/sdk/account/clearPersistedSessionState.browser.test.ts +41 -0
  80. package/test/sdk/account/clearPersistedSessionState.test.ts +20 -0
  81. package/test/sdk/account/getMyAccountRoutes.test.ts +90 -0
  82. package/test/sdk/account/refreshToken.browser.test.ts +34 -0
  83. package/test/sdk/account/refreshToken.test.ts +87 -0
  84. package/test/sdk/account/useAvailableContracts.browser.test.ts +124 -0
  85. package/test/sdk/account/useSwitchContract.browser.test.ts +111 -0
  86. package/test/sdk/analytics/useStartRecommendationSession.test.ts +69 -0
  87. package/test/sdk/analytics/utils.test.ts +23 -0
  88. package/test/server/index.test.ts +5 -0
  89. package/test/utils/retry.test.ts +59 -0
@@ -0,0 +1,170 @@
1
+ import {
2
+ Button,
3
+ EmptyState,
4
+ Icon as UIIcon,
5
+ SearchInputField,
6
+ useUI,
7
+ type SearchInputFieldRef,
8
+ } from '@faststore/ui'
9
+ import { useRef, useEffect, type MutableRefObject } from 'react'
10
+
11
+ import type { ServerListQuotesQueryQuery } from '@generated/graphql'
12
+
13
+ import AccountHeader from '../../components/Header'
14
+ import { useDebounce } from 'src/sdk/account/useDebounce'
15
+ import { useMyAccountFilter } from 'src/sdk/search/useMyAccountFilter'
16
+ import useScreenResize from 'src/sdk/ui/useScreenResize'
17
+ import MyAccountListQuotesTable, {
18
+ Pagination,
19
+ } from './MyAccountListQuotesTable/MyAccountListQuotesTable'
20
+ import MyAccountQuotesFilterSlider from './MyAccountQuotesFilterSlider/MyAccountQuotesFilterSlider'
21
+ import {
22
+ getSelectedFacets,
23
+ getAllFacets,
24
+ hasActiveFilters,
25
+ countActiveFilters,
26
+ } from './quoteFilters'
27
+ import styles from './styles.module.scss'
28
+
29
+ export type MyAccountListQuotesProps = Readonly<{
30
+ listQuotes: ServerListQuotesQueryQuery['listUserQuotes']
31
+ total: number
32
+ perPage: number
33
+ filters: {
34
+ page: number
35
+ status: string[]
36
+ createdAtFrom: string
37
+ createdAtTo: string
38
+ expiresAtFrom: string
39
+ expiresAtTo: string
40
+ label: string
41
+ }
42
+ }>
43
+
44
+ export default function MyAccountListQuotes({
45
+ listQuotes,
46
+ total,
47
+ perPage,
48
+ filters,
49
+ }: MyAccountListQuotesProps) {
50
+ const { isDesktop } = useScreenResize()
51
+ const searchInputRef = useRef(null) as MutableRefObject<SearchInputFieldRef>
52
+
53
+ useEffect(() => {
54
+ if (!searchInputRef.current?.inputRef) return
55
+ searchInputRef.current.inputRef.value = filters.label ?? ''
56
+ }, [filters.label])
57
+
58
+ const handleSearchChange = useDebounce(
59
+ (value: string) => {
60
+ const params = new URLSearchParams(globalThis.location.search)
61
+ params.delete('label')
62
+ params.delete('page')
63
+ if (value) params.set('label', value)
64
+ globalThis.location.href = `/pvt/account/quotes?${params.toString()}`
65
+ },
66
+ 300,
67
+ filters.label
68
+ )
69
+
70
+ const selectedFacets = getSelectedFacets(filters)
71
+ const allFacets = getAllFacets(filters)
72
+
73
+ const filter = useMyAccountFilter({ allFacets, selectedFacets })
74
+ const { openFilter, filter: displayFilter } = useUI()
75
+
76
+ const hasFilters = hasActiveFilters(filters)
77
+ const activeFilterCount = countActiveFilters(filters)
78
+ const isEmpty = (listQuotes?.list.length ?? 0) === 0
79
+
80
+ return (
81
+ <div className={styles.page}>
82
+ <AccountHeader pageTitle="Quotes" />
83
+ <div data-fs-list-quotes-controls>
84
+ <div data-fs-list-quotes-search-filters>
85
+ <SearchInputField
86
+ ref={searchInputRef}
87
+ data-fs-search-input-field-list-quotes
88
+ placeholder="Search"
89
+ onBlur={() => {
90
+ handleSearchChange(searchInputRef.current.inputRef.value)
91
+ }}
92
+ onKeyDown={(e) => {
93
+ if (e.key === 'Enter') {
94
+ handleSearchChange(searchInputRef.current.inputRef.value)
95
+ }
96
+ }}
97
+ onSubmit={() => {
98
+ handleSearchChange(searchInputRef.current.inputRef.value)
99
+ }}
100
+ />
101
+ <Button
102
+ data-fs-list-quotes-search-filters-button
103
+ size="small"
104
+ variant="tertiary"
105
+ icon={
106
+ <UIIcon
107
+ name="FadersHorizontal"
108
+ width={24}
109
+ height={24}
110
+ aria-label="Open Filters"
111
+ />
112
+ }
113
+ iconPosition="left"
114
+ onClick={() => {
115
+ filter.dispatch({
116
+ type: 'selectFacets',
117
+ payload: getSelectedFacets(filters),
118
+ })
119
+ openFilter()
120
+ }}
121
+ >
122
+ Filters
123
+ {activeFilterCount > 0 && (
124
+ <span data-fs-filter-count>{activeFilterCount}</span>
125
+ )}
126
+ </Button>
127
+ </div>
128
+ {isDesktop && total > 0 && (
129
+ <Pagination page={filters.page} total={total} perPage={perPage} />
130
+ )}
131
+ </div>
132
+
133
+ {displayFilter && (
134
+ <MyAccountQuotesFilterSlider
135
+ {...filter}
136
+ title="Filters"
137
+ clearButtonLabel="Clear All"
138
+ applyButtonLabel="View Results"
139
+ testId="my-account-quotes-filter-slider"
140
+ />
141
+ )}
142
+
143
+ {isEmpty ? (
144
+ <EmptyState
145
+ titleIcon={
146
+ <UIIcon
147
+ name={hasFilters ? 'MagnifyingGlass' : 'Bag2'}
148
+ width={56}
149
+ height={56}
150
+ weight="thin"
151
+ />
152
+ }
153
+ title={hasFilters ? 'No results found' : "You don't have any quotes"}
154
+ bkgColor="light"
155
+ />
156
+ ) : (
157
+ <MyAccountListQuotesTable
158
+ listQuotes={listQuotes}
159
+ total={total}
160
+ perPage={perPage}
161
+ filters={filters}
162
+ />
163
+ )}
164
+
165
+ {!isDesktop && total > 0 && (
166
+ <Pagination page={filters.page} total={total} perPage={perPage} />
167
+ )}
168
+ </div>
169
+ )
170
+ }
@@ -0,0 +1,194 @@
1
+ import { Icon, IconButton } from '@faststore/ui'
2
+
3
+ import MyAccountQuoteStatusBadge from './MyAccountQuoteStatusBadge'
4
+ import { useSession } from 'src/sdk/session'
5
+ import useScreenResize from 'src/sdk/ui/useScreenResize'
6
+ import type { ServerListQuotesQueryQuery } from '@generated/graphql'
7
+
8
+ function formatDateTime(isoString: string, locale: string) {
9
+ if (!isoString) return ''
10
+ const parsed = new Date(isoString)
11
+ if (Number.isNaN(parsed.getTime())) return isoString
12
+ return parsed.toLocaleString(locale, {
13
+ year: 'numeric',
14
+ month: 'short',
15
+ day: 'numeric',
16
+ hour: 'numeric',
17
+ minute: '2-digit',
18
+ })
19
+ }
20
+
21
+ function formatDateShort(isoString: string, locale: string) {
22
+ if (!isoString) return ''
23
+ const parsed = new Date(isoString)
24
+ if (Number.isNaN(parsed.getTime())) return isoString
25
+ return parsed.toLocaleDateString(locale, {
26
+ year: 'numeric',
27
+ month: 'short',
28
+ day: 'numeric',
29
+ })
30
+ }
31
+
32
+ function getRelativeExpiry(isoString: string): string | null {
33
+ const diffMs = new Date(isoString).getTime() - Date.now()
34
+ if (diffMs <= 0) return null
35
+
36
+ const hours = Math.floor(diffMs / 3_600_000)
37
+ const days = Math.floor(diffMs / 86_400_000)
38
+ const weeks = Math.floor(days / 7)
39
+ const months = Math.floor(days / 30)
40
+
41
+ if (hours < 24) return `${hours} ${hours === 1 ? 'hour' : 'hours'} left`
42
+ if (days < 14) return `${days} ${days === 1 ? 'day' : 'days'} left`
43
+ if (weeks < 8) return `${weeks} ${weeks === 1 ? 'week' : 'weeks'} left`
44
+ return `${months} ${months === 1 ? 'month' : 'months'} left`
45
+ }
46
+
47
+ function formatAmount(amount: number, locale: string, currencyCode: string) {
48
+ return new Intl.NumberFormat(locale, {
49
+ style: 'currency',
50
+ currency: currencyCode,
51
+ minimumFractionDigits: 2,
52
+ }).format(amount)
53
+ }
54
+
55
+ type MyAccountListQuotesTableProps = Readonly<{
56
+ listQuotes: ServerListQuotesQueryQuery['listUserQuotes']
57
+ total: number
58
+ perPage: number
59
+ filters: {
60
+ page: number
61
+ status: string[]
62
+ createdAtFrom: string
63
+ createdAtTo: string
64
+ expiresAtFrom: string
65
+ expiresAtTo: string
66
+ label: string
67
+ }
68
+ }>
69
+
70
+ export function Pagination({
71
+ page,
72
+ total,
73
+ perPage,
74
+ }: Readonly<{
75
+ page: number
76
+ total: number
77
+ perPage: number
78
+ }>) {
79
+ const totalPages = Math.ceil(total / perPage)
80
+ const firstIndexLabel = page === 1 ? 1 : (page - 1) * perPage + 1
81
+ const lastIndexLabel = Math.min(firstIndexLabel + perPage - 1, total)
82
+
83
+ const handlePageChange = (newPage: number) => {
84
+ const params = new URLSearchParams(globalThis.location.search)
85
+ if (newPage === 1 || newPage === 0) {
86
+ params.delete('page')
87
+ } else {
88
+ params.set('page', String(newPage))
89
+ }
90
+ const search = params.toString()
91
+ globalThis.location.href = search
92
+ ? `/pvt/account/quotes?${search}`
93
+ : '/pvt/account/quotes'
94
+ }
95
+
96
+ return (
97
+ <div data-fs-list-orders-table-pagination>
98
+ <p>{`${firstIndexLabel} — ${lastIndexLabel} of ${total}`}</p>
99
+ <IconButton
100
+ size="small"
101
+ variant="tertiary"
102
+ disabled={page === 1}
103
+ onClick={() => handlePageChange(page - 1)}
104
+ icon={<Icon name="CaretLeft" />}
105
+ aria-label="Previous Page"
106
+ />
107
+ <IconButton
108
+ size="small"
109
+ variant="tertiary"
110
+ disabled={page === totalPages}
111
+ onClick={() => handlePageChange(page + 1)}
112
+ icon={<Icon name="CaretRight" />}
113
+ aria-label="Next Page"
114
+ />
115
+ </div>
116
+ )
117
+ }
118
+
119
+ export default function MyAccountListQuotesTable({
120
+ listQuotes,
121
+ total,
122
+ perPage,
123
+ filters,
124
+ }: MyAccountListQuotesTableProps) {
125
+ const { isDesktop } = useScreenResize()
126
+ const { locale, currency } = useSession()
127
+ const currencyCode = currency.code
128
+
129
+ return (
130
+ <>
131
+ <div data-fs-quotes-list>
132
+ {listQuotes.list.map((item) => {
133
+ const relativeExpiry = getRelativeExpiry(item.expiresAt)
134
+ const formattedAmount = formatAmount(
135
+ item.amount,
136
+ locale,
137
+ currencyCode
138
+ )
139
+
140
+ return (
141
+ <div data-fs-quotes-list-row key={item.id}>
142
+ {/* Left: ID + label */}
143
+ <div data-fs-quotes-list-col-main>
144
+ <p data-fs-quotes-list-id>{item.id}</p>
145
+ {item.label && <p data-fs-quotes-list-label>{item.label}</p>}
146
+ </div>
147
+
148
+ {/* Middle: created by + dates (desktop only) */}
149
+ {isDesktop && (
150
+ <div data-fs-quotes-list-col-dates>
151
+ {item.createdBy && (
152
+ <div data-fs-quotes-list-date-group>
153
+ <span data-fs-quotes-list-date-label>Created by</span>
154
+ <span data-fs-quotes-list-date-value>
155
+ {item.createdBy}
156
+ </span>
157
+ </div>
158
+ )}
159
+ <div data-fs-quotes-list-date-group>
160
+ <span data-fs-quotes-list-date-label>Creation date</span>
161
+ <span data-fs-quotes-list-date-value>
162
+ {formatDateTime(item.createdAt, locale)}
163
+ </span>
164
+ </div>
165
+ <div data-fs-quotes-list-date-group>
166
+ <span data-fs-quotes-list-date-label>Expires on</span>
167
+ <span data-fs-quotes-list-date-value>
168
+ {formatDateShort(item.expiresAt, locale)}
169
+ {relativeExpiry && (
170
+ <span data-fs-quotes-list-relative-expiry>
171
+ {' · '}
172
+ {relativeExpiry}
173
+ </span>
174
+ )}
175
+ </span>
176
+ </div>
177
+ </div>
178
+ )}
179
+
180
+ {/* Right: badge + total */}
181
+ <div data-fs-quotes-list-col-status>
182
+ <MyAccountQuoteStatusBadge status={item.status} />
183
+ <p data-fs-quotes-list-total>
184
+ Total <strong>{formattedAmount}</strong>
185
+ </p>
186
+ </div>
187
+ </div>
188
+ )
189
+ })}
190
+ </div>
191
+ <Pagination page={filters.page} total={total} perPage={perPage} />
192
+ </>
193
+ )
194
+ }
@@ -0,0 +1,15 @@
1
+ import { quoteStatusMap, type QuoteStatusKey } from 'src/utils/quoteStatus'
2
+
3
+ function MyAccountQuoteStatusBadge({ status }: Readonly<{ status: string }>) {
4
+ const entry = quoteStatusMap[status as QuoteStatusKey]
5
+ return (
6
+ <span
7
+ data-fs-my-account-badge
8
+ data-fs-my-account-badge-variant={entry?.variant ?? 'neutral'}
9
+ >
10
+ {entry?.label ?? status}
11
+ </span>
12
+ )
13
+ }
14
+
15
+ export default MyAccountQuoteStatusBadge
@@ -0,0 +1,162 @@
1
+ import { FilterSlider as UIFilterSlider } from '@faststore/ui'
2
+
3
+ import { useRef, useState } from 'react'
4
+ import MyAccountFilterFacetDateRange from 'src/components/account/orders/ListOrders/FilterSlider/FilterFacetDateRange/FilterFacetDateRange'
5
+ import type {
6
+ MyAccountFilter_FacetsFragment,
7
+ useMyAccountFilter,
8
+ } from 'src/sdk/search/useMyAccountFilter'
9
+ import MyAccountQuotesStatusSelector from '../MyAccountQuotesStatusSelector/MyAccountQuotesStatusSelector'
10
+ import styles from './section.module.scss'
11
+
12
+ export interface MyAccountQuotesFilterSliderProps {
13
+ testId?: string
14
+ facets: MyAccountFilter_FacetsFragment[]
15
+ title?: string
16
+ clearButtonLabel?: string
17
+ applyButtonLabel?: string
18
+ }
19
+
20
+ function MyAccountQuotesFilterSlider({
21
+ facets,
22
+ testId,
23
+ dispatch,
24
+ expanded,
25
+ selected,
26
+ title,
27
+ clearButtonLabel,
28
+ applyButtonLabel,
29
+ }: MyAccountQuotesFilterSliderProps & ReturnType<typeof useMyAccountFilter>) {
30
+ const createdDateRangeRef = useRef<{
31
+ clear: () => void
32
+ getDataRangeFacet: () => {
33
+ key: string
34
+ value: { from: string; to: string }
35
+ }
36
+ }>(null)
37
+
38
+ const expiresDateRangeRef = useRef<{
39
+ clear: () => void
40
+ getDataRangeFacet: () => {
41
+ key: string
42
+ value: { from: string; to: string }
43
+ }
44
+ }>(null)
45
+
46
+ const [disabled, setDisabled] = useState(false)
47
+
48
+ const handleApply = () => {
49
+ const createdRange = createdDateRangeRef.current?.getDataRangeFacet()?.value
50
+ const expiresRange = expiresDateRangeRef.current?.getDataRangeFacet()?.value
51
+
52
+ const params = new URLSearchParams(globalThis.location.search)
53
+ params.delete('page')
54
+ params.delete('status')
55
+ params.delete('createdAtFrom')
56
+ params.delete('createdAtTo')
57
+ params.delete('expiresAtFrom')
58
+ params.delete('expiresAtTo')
59
+
60
+ const statusFacets = selected.filter((f) => f.key === 'status')
61
+ statusFacets.forEach((f) => params.append('status', f.value))
62
+
63
+ if (createdRange?.from?.trim()) {
64
+ params.set('createdAtFrom', createdRange.from.trim())
65
+ }
66
+ if (createdRange?.to?.trim()) {
67
+ params.set('createdAtTo', createdRange.to.trim())
68
+ }
69
+ if (expiresRange?.from?.trim()) {
70
+ params.set('expiresAtFrom', expiresRange.from.trim())
71
+ }
72
+ if (expiresRange?.to?.trim()) {
73
+ params.set('expiresAtTo', expiresRange.to.trim())
74
+ }
75
+
76
+ const search = params.toString()
77
+ globalThis.location.href = search
78
+ ? `/pvt/account/quotes?${search}`
79
+ : '/pvt/account/quotes'
80
+ }
81
+
82
+ const statusFacet = facets.find(
83
+ (f) => f.__typename === 'StoreFacetBoolean' && f.key === 'status'
84
+ )
85
+ const rangeFacets = facets.filter((f) => f.__typename === 'StoreFacetRange')
86
+ const selectedStatusValues = selected
87
+ .filter((f) => f.key === 'status')
88
+ .map((f) => f.value)
89
+
90
+ return (
91
+ <UIFilterSlider
92
+ overlayProps={{
93
+ className: `section ${styles.section} section-filter-slider`,
94
+ }}
95
+ title={title}
96
+ size="partial"
97
+ direction="rightSide"
98
+ clearBtnProps={{
99
+ variant: 'secondary',
100
+ onClick: () => {
101
+ createdDateRangeRef.current?.clear()
102
+ expiresDateRangeRef.current?.clear()
103
+ dispatch({ type: 'selectFacets', payload: [] })
104
+ },
105
+ children: clearButtonLabel ?? 'Clear All',
106
+ }}
107
+ applyBtnProps={{
108
+ variant: 'primary',
109
+ onClick: handleApply,
110
+ disabled,
111
+ children: applyButtonLabel ?? 'Apply',
112
+ }}
113
+ onClose={() => {}}
114
+ >
115
+ {statusFacet && (
116
+ <MyAccountQuotesStatusSelector
117
+ value={selectedStatusValues}
118
+ onChange={(newSelected) => {
119
+ const added = newSelected.filter(
120
+ (v) => !selectedStatusValues.includes(v)
121
+ )
122
+ const removed = selectedStatusValues.filter(
123
+ (v) => !newSelected.includes(v)
124
+ )
125
+ ;[...added, ...removed].forEach((v) =>
126
+ dispatch({
127
+ type: 'toggleFacet',
128
+ payload: { key: 'status', value: v },
129
+ })
130
+ )
131
+ }}
132
+ />
133
+ )}
134
+ {rangeFacets.map((facet, index) => (
135
+ <div
136
+ key={`${testId}-${facet.label}-${index}`}
137
+ data-fs-quotes-filter-group
138
+ >
139
+ <p data-fs-quotes-filter-group-label>{facet.label}</p>
140
+ {facet.key === 'createdAt' && (
141
+ <MyAccountFilterFacetDateRange
142
+ ref={createdDateRangeRef}
143
+ from={facet.from}
144
+ to={facet.to}
145
+ setDisabled={setDisabled}
146
+ />
147
+ )}
148
+ {facet.key === 'expiresAt' && (
149
+ <MyAccountFilterFacetDateRange
150
+ ref={expiresDateRangeRef}
151
+ from={facet.from}
152
+ to={facet.to}
153
+ setDisabled={setDisabled}
154
+ />
155
+ )}
156
+ </div>
157
+ ))}
158
+ </UIFilterSlider>
159
+ )
160
+ }
161
+
162
+ export default MyAccountQuotesFilterSlider
@@ -0,0 +1,49 @@
1
+ @use "sass:meta";
2
+
3
+ .section {
4
+ @include meta.load-css("~@faststore/ui/src/components/atoms/Button/styles.scss");
5
+ @include meta.load-css("~@faststore/ui/src/components/atoms/Badge/styles.scss");
6
+ @include meta.load-css("~@faststore/ui/src/components/atoms/Checkbox/styles.scss");
7
+ @include meta.load-css("~@faststore/ui/src/components/atoms/Icon/styles.scss");
8
+ @include meta.load-css("~@faststore/ui/src/components/atoms/Input/styles.scss");
9
+ @include meta.load-css("~@faststore/ui/src/components/atoms/Link/styles.scss");
10
+ @include meta.load-css("~@faststore/ui/src/components/atoms/List/styles.scss");
11
+ @include meta.load-css("~@faststore/ui/src/components/atoms/Slider/styles.scss");
12
+ @include meta.load-css("~@faststore/ui/src/components/molecules/Toggle/styles.scss");
13
+ @include meta.load-css("~@faststore/ui/src/components/molecules/DiscountBadge/styles.scss");
14
+ @include meta.load-css("~@faststore/ui/src/components/molecules/Accordion/styles.scss");
15
+ @include meta.load-css("~@faststore/ui/src/components/molecules/InputField/styles.scss");
16
+ @include meta.load-css("~@faststore/ui/src/components/molecules/Modal/styles.scss");
17
+ @include meta.load-css("~@faststore/ui/src/components/molecules/SelectField/styles.scss");
18
+ @include meta.load-css("~@faststore/ui/src/components/organisms/Filter/styles.scss");
19
+ @include meta.load-css("~@faststore/ui/src/components/organisms/FilterSlider/styles.scss");
20
+ @include meta.load-css("~@faststore/ui/src/components/organisms/SlideOver/styles.scss");
21
+ @include meta.load-css("../../../orders/ListOrders/FilterSlider/FilterFacetDateRange/styles.scss");
22
+
23
+ [data-fs-slide-over] {
24
+ --fs-slide-over-partial-width-notebook: calc(100% / 2.2);
25
+ --fs-slide-over-partial-max-width-notebook: calc(var(--fs-grid-breakpoint-notebook) / 2.2);
26
+ --fs-filter-slider-content-padding: 0 var(--fs-spacing-7);
27
+ }
28
+
29
+ [data-fs-badge] {
30
+ display: none;
31
+ visibility: hidden;
32
+ }
33
+
34
+ // Status selector inside the filter drawer
35
+ [data-fs-quotes-status-selector] {
36
+ padding: var(--fs-spacing-5) var(--fs-spacing-7) 0;
37
+ }
38
+
39
+ [data-fs-quotes-filter-group] {
40
+ padding: var(--fs-spacing-5) var(--fs-spacing-7) 0;
41
+ }
42
+
43
+ [data-fs-quotes-filter-group-label] {
44
+ margin: 0 0 var(--fs-spacing-3);
45
+ font-size: var(--fs-text-size-base);
46
+ font-weight: var(--fs-text-weight-regular);
47
+ color: var(--fs-color-text);
48
+ }
49
+ }