@faststore/core 4.9.0-dev.2 → 4.9.0-dev.4
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 +130 -125
- package/CHANGELOG.md +10 -0
- package/cms/faststore/my-account/components/cms_component__accountlistquotes.jsonc +240 -0
- package/cms/faststore/my-account/components/cms_component__accountnavigation.jsonc +76 -0
- package/cms/faststore/my-account/pages/cms_content_type__myaccountquotes.jsonc +18 -0
- package/package.json +6 -6
- package/src/components/account/Drawer/OrganizationDrawer/ContractSwitcher/ContractSwitcher.tsx +129 -26
- package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerHeader.tsx +7 -1
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotes.tsx +42 -11
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountListQuotesTable.tsx +45 -15
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountQuoteStatusBadge.tsx +11 -3
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesFilterSlider/MyAccountQuotesFilterSlider.tsx +23 -0
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesSelectedTags/MyAccountQuotesSelectedTags.tsx +35 -11
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesStatusSelector/MyAccountQuotesStatusSelector.tsx +27 -10
- package/src/components/account/quotes/MyAccountListQuotes/index.ts +2 -0
- package/src/components/account/quotes/MyAccountListQuotes/quoteFilters.ts +16 -6
- package/src/components/account/quotes/MyAccountListQuotes/quotesLabels.ts +163 -0
- package/src/components/cms/account/Components.ts +2 -0
- package/src/components/sections/Account/AccountListQuotes.tsx +141 -0
- package/src/pages/pvt/account/quotes/index.tsx +59 -40
- package/src/sdk/account/accountPageContext.ts +17 -0
- package/src/sdk/account/getMyAccountRoutes.ts +17 -0
- package/src/sdk/account/resolveNonBlankLabels.ts +36 -0
- package/src/server/cms/myAccountDefaultSections.ts +2 -0
- package/src/utils/quoteStatus.ts +81 -0
- package/test/components/account/ContractSwitcher.browser.test.tsx +163 -0
- package/test/components/account/OrganizationDrawer.browser.test.tsx +4 -0
- package/test/components/account/OrganizationDrawerHeader.browser.test.tsx +44 -1
- package/test/components/quotes/MyAccountListQuotes.test.tsx +36 -2
- package/test/components/quotes/MyAccountListQuotesTable.test.tsx +68 -0
- package/test/components/quotes/MyAccountQuoteStatusBadge.test.tsx +25 -0
- package/test/components/quotes/MyAccountQuotesFilterSlider.test.tsx +52 -11
- package/test/components/quotes/MyAccountQuotesSelectedTags.test.tsx +32 -0
- package/test/components/quotes/MyAccountQuotesStatusSelector.test.tsx +100 -0
- package/test/components/quotes/quoteFilters.test.ts +34 -0
- package/test/components/sections/Account/AccountListQuotes.browser.test.tsx +122 -0
- package/test/server/cms/myAccountDefaultSections.test.ts +21 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Button, Icon, Link, SlideOverHeader } from '@faststore/ui'
|
|
2
2
|
import type { ReactNode } from 'react'
|
|
3
3
|
|
|
4
|
+
import { useAccountNavigationLabels } from 'src/sdk/account/accountPageContext'
|
|
5
|
+
|
|
4
6
|
export type OrganizationDrawerHeaderProps = {
|
|
5
7
|
onCloseDrawer?: () => void
|
|
6
8
|
contractImage?: ReactNode
|
|
@@ -25,6 +27,10 @@ export const OrganizationDrawerHeader = ({
|
|
|
25
27
|
onCloseDrawer,
|
|
26
28
|
onChangeContract,
|
|
27
29
|
}: OrganizationDrawerHeaderProps) => {
|
|
30
|
+
const navigationLabels = useAccountNavigationLabels()
|
|
31
|
+
const changeContractLabel =
|
|
32
|
+
navigationLabels?.changeContractLabel?.trim() || 'Change'
|
|
33
|
+
|
|
28
34
|
return (
|
|
29
35
|
<>
|
|
30
36
|
<SlideOverHeader onClose={() => onCloseDrawer?.()} />
|
|
@@ -68,7 +74,7 @@ export const OrganizationDrawerHeader = ({
|
|
|
68
74
|
variant="secondary"
|
|
69
75
|
onClick={onChangeContract}
|
|
70
76
|
>
|
|
71
|
-
|
|
77
|
+
{changeContractLabel}
|
|
72
78
|
</Button>
|
|
73
79
|
)}
|
|
74
80
|
</div>
|
|
@@ -24,6 +24,11 @@ import {
|
|
|
24
24
|
hasActiveFilters,
|
|
25
25
|
countActiveFilters,
|
|
26
26
|
} from './quoteFilters'
|
|
27
|
+
import type { MyAccountListQuotesSectionLabels } from './quotesLabels'
|
|
28
|
+
import {
|
|
29
|
+
pickQuoteStatusCmsLabels,
|
|
30
|
+
resolveMyAccountListQuotesLabels,
|
|
31
|
+
} from './quotesLabels'
|
|
27
32
|
import styles from './styles.module.scss'
|
|
28
33
|
|
|
29
34
|
export type MyAccountListQuotesProps = Readonly<{
|
|
@@ -39,6 +44,7 @@ export type MyAccountListQuotesProps = Readonly<{
|
|
|
39
44
|
expiresAtTo: string
|
|
40
45
|
label: string
|
|
41
46
|
}
|
|
47
|
+
labels?: MyAccountListQuotesSectionLabels
|
|
42
48
|
}>
|
|
43
49
|
|
|
44
50
|
export default function MyAccountListQuotes({
|
|
@@ -46,7 +52,10 @@ export default function MyAccountListQuotes({
|
|
|
46
52
|
total,
|
|
47
53
|
perPage,
|
|
48
54
|
filters,
|
|
55
|
+
labels: labelsProp,
|
|
49
56
|
}: MyAccountListQuotesProps) {
|
|
57
|
+
const labels = resolveMyAccountListQuotesLabels(labelsProp)
|
|
58
|
+
const statusCmsLabels = pickQuoteStatusCmsLabels(labelsProp)
|
|
50
59
|
const { isDesktop } = useScreenResize()
|
|
51
60
|
const searchInputRef = useRef(null) as MutableRefObject<SearchInputFieldRef>
|
|
52
61
|
|
|
@@ -68,7 +77,12 @@ export default function MyAccountListQuotes({
|
|
|
68
77
|
)
|
|
69
78
|
|
|
70
79
|
const selectedFacets = getSelectedFacets(filters)
|
|
71
|
-
const allFacets = getAllFacets(filters
|
|
80
|
+
const allFacets = getAllFacets(filters, {
|
|
81
|
+
statusFacetLabel: labels.statusFacetLabel,
|
|
82
|
+
createdAtFacetLabel: labels.createdAtFacetLabel,
|
|
83
|
+
expiresAtFacetLabel: labels.expiresAtFacetLabel,
|
|
84
|
+
statusCmsLabels,
|
|
85
|
+
})
|
|
72
86
|
|
|
73
87
|
const filter = useMyAccountFilter({ allFacets, selectedFacets })
|
|
74
88
|
const { openFilter, filter: displayFilter } = useUI()
|
|
@@ -79,13 +93,13 @@ export default function MyAccountListQuotes({
|
|
|
79
93
|
|
|
80
94
|
return (
|
|
81
95
|
<div className={styles.page}>
|
|
82
|
-
<AccountHeader pageTitle=
|
|
96
|
+
<AccountHeader pageTitle={labels.pageTitle} />
|
|
83
97
|
<div data-fs-list-quotes-controls>
|
|
84
98
|
<div data-fs-list-quotes-search-filters>
|
|
85
99
|
<SearchInputField
|
|
86
100
|
ref={searchInputRef}
|
|
87
101
|
data-fs-search-input-field-list-quotes
|
|
88
|
-
placeholder=
|
|
102
|
+
placeholder={labels.searchPlaceholder}
|
|
89
103
|
onBlur={() => {
|
|
90
104
|
handleSearchChange(searchInputRef.current.inputRef.value)
|
|
91
105
|
}}
|
|
@@ -107,7 +121,7 @@ export default function MyAccountListQuotes({
|
|
|
107
121
|
name="FadersHorizontal"
|
|
108
122
|
width={24}
|
|
109
123
|
height={24}
|
|
110
|
-
aria-label=
|
|
124
|
+
aria-label={labels.openFiltersAriaLabel}
|
|
111
125
|
/>
|
|
112
126
|
}
|
|
113
127
|
iconPosition="left"
|
|
@@ -119,23 +133,34 @@ export default function MyAccountListQuotes({
|
|
|
119
133
|
openFilter()
|
|
120
134
|
}}
|
|
121
135
|
>
|
|
122
|
-
|
|
136
|
+
{labels.filtersLabel}
|
|
123
137
|
{activeFilterCount > 0 && (
|
|
124
138
|
<span data-fs-filter-count>{activeFilterCount}</span>
|
|
125
139
|
)}
|
|
126
140
|
</Button>
|
|
127
141
|
</div>
|
|
128
142
|
{isDesktop && total > 0 && (
|
|
129
|
-
<Pagination
|
|
143
|
+
<Pagination
|
|
144
|
+
page={filters.page}
|
|
145
|
+
total={total}
|
|
146
|
+
perPage={perPage}
|
|
147
|
+
labels={labels}
|
|
148
|
+
/>
|
|
130
149
|
)}
|
|
131
150
|
</div>
|
|
132
151
|
|
|
133
152
|
{displayFilter && (
|
|
134
153
|
<MyAccountQuotesFilterSlider
|
|
135
154
|
{...filter}
|
|
136
|
-
title=
|
|
137
|
-
clearButtonLabel=
|
|
138
|
-
applyButtonLabel=
|
|
155
|
+
title={labels.filtersLabel}
|
|
156
|
+
clearButtonLabel={labels.clearAllLabel}
|
|
157
|
+
applyButtonLabel={labels.viewResultsLabel}
|
|
158
|
+
statusLabel={labels.statusFacetLabel}
|
|
159
|
+
removeStatusAriaLabel={labels.removeStatusAriaLabel}
|
|
160
|
+
fromLabel={labels.fromLabel}
|
|
161
|
+
toLabel={labels.toLabel}
|
|
162
|
+
invalidDateRangeLabel={labels.invalidDateRangeLabel}
|
|
163
|
+
statusCmsLabels={statusCmsLabels}
|
|
139
164
|
testId="my-account-quotes-filter-slider"
|
|
140
165
|
/>
|
|
141
166
|
)}
|
|
@@ -150,7 +175,7 @@ export default function MyAccountListQuotes({
|
|
|
150
175
|
weight="thin"
|
|
151
176
|
/>
|
|
152
177
|
}
|
|
153
|
-
title={hasFilters ?
|
|
178
|
+
title={hasFilters ? labels.noResultsLabel : labels.noQuotesLabel}
|
|
154
179
|
bkgColor="light"
|
|
155
180
|
/>
|
|
156
181
|
) : (
|
|
@@ -159,11 +184,17 @@ export default function MyAccountListQuotes({
|
|
|
159
184
|
total={total}
|
|
160
185
|
perPage={perPage}
|
|
161
186
|
filters={filters}
|
|
187
|
+
labels={labels}
|
|
162
188
|
/>
|
|
163
189
|
)}
|
|
164
190
|
|
|
165
191
|
{!isDesktop && total > 0 && (
|
|
166
|
-
<Pagination
|
|
192
|
+
<Pagination
|
|
193
|
+
page={filters.page}
|
|
194
|
+
total={total}
|
|
195
|
+
perPage={perPage}
|
|
196
|
+
labels={labels}
|
|
197
|
+
/>
|
|
167
198
|
)}
|
|
168
199
|
</div>
|
|
169
200
|
)
|
|
@@ -4,6 +4,12 @@ import MyAccountQuoteStatusBadge from './MyAccountQuoteStatusBadge'
|
|
|
4
4
|
import { useSession } from 'src/sdk/session'
|
|
5
5
|
import useScreenResize from 'src/sdk/ui/useScreenResize'
|
|
6
6
|
import type { ServerListQuotesQueryQuery } from '@generated/graphql'
|
|
7
|
+
import type { MyAccountListQuotesSectionLabels } from '../quotesLabels'
|
|
8
|
+
import {
|
|
9
|
+
formatRelativeExpiryLabel,
|
|
10
|
+
pickQuoteStatusCmsLabels,
|
|
11
|
+
resolveMyAccountListQuotesLabels,
|
|
12
|
+
} from '../quotesLabels'
|
|
7
13
|
|
|
8
14
|
function formatDateTime(isoString: string, locale: string) {
|
|
9
15
|
if (!isoString) return ''
|
|
@@ -29,7 +35,10 @@ function formatDateShort(isoString: string, locale: string) {
|
|
|
29
35
|
})
|
|
30
36
|
}
|
|
31
37
|
|
|
32
|
-
function getRelativeExpiry(
|
|
38
|
+
function getRelativeExpiry(
|
|
39
|
+
isoString: string,
|
|
40
|
+
labels: Required<MyAccountListQuotesSectionLabels>
|
|
41
|
+
): string | null {
|
|
33
42
|
const diffMs = new Date(isoString).getTime() - Date.now()
|
|
34
43
|
if (diffMs <= 0) return null
|
|
35
44
|
|
|
@@ -38,10 +47,10 @@ function getRelativeExpiry(isoString: string): string | null {
|
|
|
38
47
|
const weeks = Math.floor(days / 7)
|
|
39
48
|
const months = Math.floor(days / 30)
|
|
40
49
|
|
|
41
|
-
if (hours < 24) return
|
|
42
|
-
if (days < 14) return
|
|
43
|
-
if (weeks < 8) return
|
|
44
|
-
return
|
|
50
|
+
if (hours < 24) return formatRelativeExpiryLabel(hours, 'hour', labels)
|
|
51
|
+
if (days < 14) return formatRelativeExpiryLabel(days, 'day', labels)
|
|
52
|
+
if (weeks < 8) return formatRelativeExpiryLabel(weeks, 'week', labels)
|
|
53
|
+
return formatRelativeExpiryLabel(months, 'month', labels)
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
function formatAmount(amount: number, locale: string, currencyCode: string) {
|
|
@@ -65,17 +74,21 @@ type MyAccountListQuotesTableProps = Readonly<{
|
|
|
65
74
|
expiresAtTo: string
|
|
66
75
|
label: string
|
|
67
76
|
}
|
|
77
|
+
labels?: MyAccountListQuotesSectionLabels
|
|
68
78
|
}>
|
|
69
79
|
|
|
70
80
|
export function Pagination({
|
|
71
81
|
page,
|
|
72
82
|
total,
|
|
73
83
|
perPage,
|
|
84
|
+
labels: labelsProp,
|
|
74
85
|
}: Readonly<{
|
|
75
86
|
page: number
|
|
76
87
|
total: number
|
|
77
88
|
perPage: number
|
|
89
|
+
labels?: MyAccountListQuotesSectionLabels
|
|
78
90
|
}>) {
|
|
91
|
+
const labels = resolveMyAccountListQuotesLabels(labelsProp)
|
|
79
92
|
const totalPages = Math.ceil(total / perPage)
|
|
80
93
|
const firstIndexLabel = page === 1 ? 1 : (page - 1) * perPage + 1
|
|
81
94
|
const lastIndexLabel = Math.min(firstIndexLabel + perPage - 1, total)
|
|
@@ -95,14 +108,14 @@ export function Pagination({
|
|
|
95
108
|
|
|
96
109
|
return (
|
|
97
110
|
<div data-fs-list-orders-table-pagination>
|
|
98
|
-
<p>{`${firstIndexLabel} — ${lastIndexLabel}
|
|
111
|
+
<p>{`${firstIndexLabel} — ${lastIndexLabel} ${labels.paginationOfLabel} ${total}`}</p>
|
|
99
112
|
<IconButton
|
|
100
113
|
size="small"
|
|
101
114
|
variant="tertiary"
|
|
102
115
|
disabled={page === 1}
|
|
103
116
|
onClick={() => handlePageChange(page - 1)}
|
|
104
117
|
icon={<Icon name="CaretLeft" />}
|
|
105
|
-
aria-label=
|
|
118
|
+
aria-label={labels.previousPageLabel}
|
|
106
119
|
/>
|
|
107
120
|
<IconButton
|
|
108
121
|
size="small"
|
|
@@ -110,7 +123,7 @@ export function Pagination({
|
|
|
110
123
|
disabled={page === totalPages}
|
|
111
124
|
onClick={() => handlePageChange(page + 1)}
|
|
112
125
|
icon={<Icon name="CaretRight" />}
|
|
113
|
-
aria-label=
|
|
126
|
+
aria-label={labels.nextPageLabel}
|
|
114
127
|
/>
|
|
115
128
|
</div>
|
|
116
129
|
)
|
|
@@ -121,7 +134,10 @@ export default function MyAccountListQuotesTable({
|
|
|
121
134
|
total,
|
|
122
135
|
perPage,
|
|
123
136
|
filters,
|
|
137
|
+
labels: labelsProp,
|
|
124
138
|
}: MyAccountListQuotesTableProps) {
|
|
139
|
+
const labels = resolveMyAccountListQuotesLabels(labelsProp)
|
|
140
|
+
const statusCmsLabels = pickQuoteStatusCmsLabels(labelsProp)
|
|
125
141
|
const { isDesktop } = useScreenResize()
|
|
126
142
|
const { locale, currency } = useSession()
|
|
127
143
|
const currencyCode = currency.code
|
|
@@ -130,7 +146,7 @@ export default function MyAccountListQuotesTable({
|
|
|
130
146
|
<>
|
|
131
147
|
<div data-fs-quotes-list>
|
|
132
148
|
{listQuotes.list.map((item) => {
|
|
133
|
-
const relativeExpiry = getRelativeExpiry(item.expiresAt)
|
|
149
|
+
const relativeExpiry = getRelativeExpiry(item.expiresAt, labels)
|
|
134
150
|
const formattedAmount = formatAmount(
|
|
135
151
|
item.amount,
|
|
136
152
|
locale,
|
|
@@ -150,20 +166,26 @@ export default function MyAccountListQuotesTable({
|
|
|
150
166
|
<div data-fs-quotes-list-col-dates>
|
|
151
167
|
{item.createdBy && (
|
|
152
168
|
<div data-fs-quotes-list-date-group>
|
|
153
|
-
<span data-fs-quotes-list-date-label>
|
|
169
|
+
<span data-fs-quotes-list-date-label>
|
|
170
|
+
{labels.createdByLabel}
|
|
171
|
+
</span>
|
|
154
172
|
<span data-fs-quotes-list-date-value>
|
|
155
173
|
{item.createdBy}
|
|
156
174
|
</span>
|
|
157
175
|
</div>
|
|
158
176
|
)}
|
|
159
177
|
<div data-fs-quotes-list-date-group>
|
|
160
|
-
<span data-fs-quotes-list-date-label>
|
|
178
|
+
<span data-fs-quotes-list-date-label>
|
|
179
|
+
{labels.creationDateLabel}
|
|
180
|
+
</span>
|
|
161
181
|
<span data-fs-quotes-list-date-value>
|
|
162
182
|
{formatDateTime(item.createdAt, locale)}
|
|
163
183
|
</span>
|
|
164
184
|
</div>
|
|
165
185
|
<div data-fs-quotes-list-date-group>
|
|
166
|
-
<span data-fs-quotes-list-date-label>
|
|
186
|
+
<span data-fs-quotes-list-date-label>
|
|
187
|
+
{labels.expiresOnLabel}
|
|
188
|
+
</span>
|
|
167
189
|
<span data-fs-quotes-list-date-value>
|
|
168
190
|
{formatDateShort(item.expiresAt, locale)}
|
|
169
191
|
{relativeExpiry && (
|
|
@@ -179,16 +201,24 @@ export default function MyAccountListQuotesTable({
|
|
|
179
201
|
|
|
180
202
|
{/* Right: badge + total */}
|
|
181
203
|
<div data-fs-quotes-list-col-status>
|
|
182
|
-
<MyAccountQuoteStatusBadge
|
|
204
|
+
<MyAccountQuoteStatusBadge
|
|
205
|
+
status={item.status}
|
|
206
|
+
statusCmsLabels={statusCmsLabels}
|
|
207
|
+
/>
|
|
183
208
|
<p data-fs-quotes-list-total>
|
|
184
|
-
|
|
209
|
+
{labels.totalLabel} <strong>{formattedAmount}</strong>
|
|
185
210
|
</p>
|
|
186
211
|
</div>
|
|
187
212
|
</div>
|
|
188
213
|
)
|
|
189
214
|
})}
|
|
190
215
|
</div>
|
|
191
|
-
<Pagination
|
|
216
|
+
<Pagination
|
|
217
|
+
page={filters.page}
|
|
218
|
+
total={total}
|
|
219
|
+
perPage={perPage}
|
|
220
|
+
labels={labels}
|
|
221
|
+
/>
|
|
192
222
|
</>
|
|
193
223
|
)
|
|
194
224
|
}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { QuoteStatusCmsLabels } from 'src/utils/quoteStatus'
|
|
2
|
+
import { getLocalizedQuoteStatusMap } from 'src/utils/quoteStatus'
|
|
2
3
|
|
|
3
|
-
function MyAccountQuoteStatusBadge({
|
|
4
|
-
|
|
4
|
+
function MyAccountQuoteStatusBadge({
|
|
5
|
+
status,
|
|
6
|
+
statusCmsLabels,
|
|
7
|
+
}: Readonly<{
|
|
8
|
+
status: string
|
|
9
|
+
statusCmsLabels?: QuoteStatusCmsLabels
|
|
10
|
+
}>) {
|
|
11
|
+
const statusMap = getLocalizedQuoteStatusMap(statusCmsLabels)
|
|
12
|
+
const entry = statusMap[status as keyof typeof statusMap]
|
|
5
13
|
return (
|
|
6
14
|
<span
|
|
7
15
|
data-fs-my-account-badge
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
MyAccountFilter_FacetsFragment,
|
|
7
7
|
useMyAccountFilter,
|
|
8
8
|
} from 'src/sdk/search/useMyAccountFilter'
|
|
9
|
+
import type { QuoteStatusCmsLabels } from 'src/utils/quoteStatus'
|
|
9
10
|
import MyAccountQuotesStatusSelector from '../MyAccountQuotesStatusSelector/MyAccountQuotesStatusSelector'
|
|
10
11
|
import styles from './section.module.scss'
|
|
11
12
|
|
|
@@ -15,6 +16,13 @@ export interface MyAccountQuotesFilterSliderProps {
|
|
|
15
16
|
title?: string
|
|
16
17
|
clearButtonLabel?: string
|
|
17
18
|
applyButtonLabel?: string
|
|
19
|
+
statusLabel?: string
|
|
20
|
+
/** Supports a "{status}" placeholder, e.g. "Remove {status}". */
|
|
21
|
+
removeStatusAriaLabel?: string
|
|
22
|
+
fromLabel?: string
|
|
23
|
+
toLabel?: string
|
|
24
|
+
invalidDateRangeLabel?: string
|
|
25
|
+
statusCmsLabels?: QuoteStatusCmsLabels
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
function MyAccountQuotesFilterSlider({
|
|
@@ -26,6 +34,12 @@ function MyAccountQuotesFilterSlider({
|
|
|
26
34
|
title,
|
|
27
35
|
clearButtonLabel,
|
|
28
36
|
applyButtonLabel,
|
|
37
|
+
statusLabel,
|
|
38
|
+
removeStatusAriaLabel,
|
|
39
|
+
fromLabel,
|
|
40
|
+
toLabel,
|
|
41
|
+
invalidDateRangeLabel,
|
|
42
|
+
statusCmsLabels,
|
|
29
43
|
}: MyAccountQuotesFilterSliderProps & ReturnType<typeof useMyAccountFilter>) {
|
|
30
44
|
const createdDateRangeRef = useRef<{
|
|
31
45
|
clear: () => void
|
|
@@ -115,6 +129,9 @@ function MyAccountQuotesFilterSlider({
|
|
|
115
129
|
{statusFacet && (
|
|
116
130
|
<MyAccountQuotesStatusSelector
|
|
117
131
|
value={selectedStatusValues}
|
|
132
|
+
statusLabel={statusLabel}
|
|
133
|
+
removeStatusAriaLabel={removeStatusAriaLabel}
|
|
134
|
+
statusCmsLabels={statusCmsLabels}
|
|
118
135
|
onChange={(newSelected) => {
|
|
119
136
|
const added = newSelected.filter(
|
|
120
137
|
(v) => !selectedStatusValues.includes(v)
|
|
@@ -143,6 +160,9 @@ function MyAccountQuotesFilterSlider({
|
|
|
143
160
|
from={facet.from}
|
|
144
161
|
to={facet.to}
|
|
145
162
|
setDisabled={setDisabled}
|
|
163
|
+
fromLabel={fromLabel}
|
|
164
|
+
toLabel={toLabel}
|
|
165
|
+
invalidDateRangeLabel={invalidDateRangeLabel}
|
|
146
166
|
/>
|
|
147
167
|
)}
|
|
148
168
|
{facet.key === 'expiresAt' && (
|
|
@@ -151,6 +171,9 @@ function MyAccountQuotesFilterSlider({
|
|
|
151
171
|
from={facet.from}
|
|
152
172
|
to={facet.to}
|
|
153
173
|
setDisabled={setDisabled}
|
|
174
|
+
fromLabel={fromLabel}
|
|
175
|
+
toLabel={toLabel}
|
|
176
|
+
invalidDateRangeLabel={invalidDateRangeLabel}
|
|
154
177
|
/>
|
|
155
178
|
)}
|
|
156
179
|
</div>
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { Button } from '@faststore/ui'
|
|
2
2
|
import { useSession } from 'src/sdk/session'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getLocalizedQuoteStatusMap,
|
|
5
|
+
type QuoteStatusCmsLabels,
|
|
6
|
+
} from 'src/utils/quoteStatus'
|
|
7
|
+
import type { MyAccountListQuotesSectionLabels } from '../quotesLabels'
|
|
8
|
+
import {
|
|
9
|
+
pickQuoteStatusCmsLabels,
|
|
10
|
+
resolveMyAccountListQuotesLabels,
|
|
11
|
+
} from '../quotesLabels'
|
|
4
12
|
import { formatFilterDate } from '../quoteFilters'
|
|
5
13
|
|
|
6
14
|
type MyAccountQuotesSelectedTagsProps = Readonly<{
|
|
@@ -21,34 +29,42 @@ type MyAccountQuotesSelectedTagsProps = Readonly<{
|
|
|
21
29
|
| 'expiresAtTo',
|
|
22
30
|
value: string
|
|
23
31
|
) => void
|
|
32
|
+
labels?: MyAccountListQuotesSectionLabels
|
|
24
33
|
}>
|
|
25
34
|
|
|
26
35
|
function formatDateRangeLabel(
|
|
27
36
|
from: string | undefined,
|
|
28
37
|
to: string | undefined,
|
|
29
|
-
locale: string
|
|
38
|
+
locale: string,
|
|
39
|
+
labels: Required<MyAccountListQuotesSectionLabels>
|
|
30
40
|
) {
|
|
31
41
|
if (from && to) {
|
|
32
|
-
return `${formatFilterDate(from, locale)}
|
|
42
|
+
return `${formatFilterDate(from, locale)} ${labels.dateRangeSeparatorLabel} ${formatFilterDate(to, locale)}`
|
|
33
43
|
}
|
|
34
44
|
if (from) {
|
|
35
|
-
return
|
|
45
|
+
return `${labels.dateRangeFromLabel} ${formatFilterDate(from, locale)}`
|
|
36
46
|
}
|
|
37
|
-
return
|
|
47
|
+
return `${labels.dateRangeToLabel} ${formatFilterDate(to!, locale)}`
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
function Tags({
|
|
41
51
|
filters,
|
|
42
52
|
onRemoveFilter,
|
|
43
|
-
|
|
53
|
+
labels,
|
|
54
|
+
statusCmsLabels,
|
|
55
|
+
}: Pick<MyAccountQuotesSelectedTagsProps, 'filters' | 'onRemoveFilter'> & {
|
|
56
|
+
labels: Required<MyAccountListQuotesSectionLabels>
|
|
57
|
+
statusCmsLabels?: QuoteStatusCmsLabels
|
|
58
|
+
}) {
|
|
44
59
|
const { locale } = useSession()
|
|
45
60
|
const { createdAtFrom, createdAtTo, expiresAtFrom, expiresAtTo, status } =
|
|
46
61
|
filters
|
|
62
|
+
const statusMap = getLocalizedQuoteStatusMap(statusCmsLabels)
|
|
47
63
|
|
|
48
64
|
const createdTag = (createdAtFrom || createdAtTo) && (
|
|
49
65
|
<div key="created-date-range" data-fs-list-orders-selected-tag>
|
|
50
66
|
<span data-fs-list-orders-selected-tag-label>
|
|
51
|
-
{
|
|
67
|
+
{`${labels.createdTagLabel}: ${formatDateRangeLabel(createdAtFrom, createdAtTo, locale, labels)}`}
|
|
52
68
|
</span>
|
|
53
69
|
<button
|
|
54
70
|
data-fs-list-orders-selected-tag-clear
|
|
@@ -62,7 +78,7 @@ function Tags({
|
|
|
62
78
|
const expiresTag = (expiresAtFrom || expiresAtTo) && (
|
|
63
79
|
<div key="expires-date-range" data-fs-list-orders-selected-tag>
|
|
64
80
|
<span data-fs-list-orders-selected-tag-label>
|
|
65
|
-
{
|
|
81
|
+
{`${labels.expiresTagLabel}: ${formatDateRangeLabel(expiresAtFrom, expiresAtTo, locale, labels)}`}
|
|
66
82
|
</span>
|
|
67
83
|
<button
|
|
68
84
|
data-fs-list-orders-selected-tag-clear
|
|
@@ -75,7 +91,7 @@ function Tags({
|
|
|
75
91
|
|
|
76
92
|
const statusTags = (status || []).map((value) => (
|
|
77
93
|
<div key={`status-${value}`} data-fs-list-orders-selected-tag>
|
|
78
|
-
<span>{
|
|
94
|
+
<span>{statusMap[value as keyof typeof statusMap]?.label ?? value}</span>
|
|
79
95
|
<button
|
|
80
96
|
data-fs-list-orders-selected-tag-clear
|
|
81
97
|
onClick={() => onRemoveFilter('status', value)}
|
|
@@ -98,7 +114,10 @@ function MyAccountQuotesSelectedTags({
|
|
|
98
114
|
filters,
|
|
99
115
|
onClearAll,
|
|
100
116
|
onRemoveFilter,
|
|
117
|
+
labels: labelsProp,
|
|
101
118
|
}: MyAccountQuotesSelectedTagsProps) {
|
|
119
|
+
const labels = resolveMyAccountListQuotesLabels(labelsProp)
|
|
120
|
+
const statusCmsLabels = pickQuoteStatusCmsLabels(labelsProp)
|
|
102
121
|
const hasFilters = Object.entries(filters).some(([, values]) =>
|
|
103
122
|
Array.isArray(values) ? values.length > 0 : Boolean(values)
|
|
104
123
|
)
|
|
@@ -107,14 +126,19 @@ function MyAccountQuotesSelectedTags({
|
|
|
107
126
|
<>
|
|
108
127
|
{hasFilters && (
|
|
109
128
|
<div data-fs-list-orders-selected-tags>
|
|
110
|
-
<Tags
|
|
129
|
+
<Tags
|
|
130
|
+
filters={filters}
|
|
131
|
+
onRemoveFilter={onRemoveFilter}
|
|
132
|
+
labels={labels}
|
|
133
|
+
statusCmsLabels={statusCmsLabels}
|
|
134
|
+
/>
|
|
111
135
|
<Button
|
|
112
136
|
variant="tertiary"
|
|
113
137
|
size="small"
|
|
114
138
|
data-fs-list-orders-selected-tags-clear-all-button
|
|
115
139
|
onClick={onClearAll}
|
|
116
140
|
>
|
|
117
|
-
|
|
141
|
+
{labels.clearAllLabel}
|
|
118
142
|
</Button>
|
|
119
143
|
</div>
|
|
120
144
|
)}
|
|
@@ -1,23 +1,37 @@
|
|
|
1
|
-
import { useEffect, useId, useRef, useState } from 'react'
|
|
1
|
+
import { useEffect, useId, useMemo, useRef, useState } from 'react'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
defaultMyAccountListQuotesLabels,
|
|
5
|
+
formatRemoveStatusAriaLabel,
|
|
6
|
+
} from '../quotesLabels'
|
|
7
|
+
import type { QuoteStatusCmsLabels } from 'src/utils/quoteStatus'
|
|
8
|
+
import { getLocalizedQuoteStatusMap } from 'src/utils/quoteStatus'
|
|
4
9
|
import styles from './styles.module.scss'
|
|
5
10
|
|
|
6
11
|
type MyAccountQuotesStatusSelectorProps = Readonly<{
|
|
7
12
|
value: string[]
|
|
8
13
|
onChange: (selected: string[]) => void
|
|
14
|
+
statusLabel?: string
|
|
15
|
+
/** Supports a "{status}" placeholder, e.g. "Remove {status}". */
|
|
16
|
+
removeStatusAriaLabel?: string
|
|
17
|
+
statusCmsLabels?: QuoteStatusCmsLabels
|
|
9
18
|
}>
|
|
10
19
|
|
|
11
|
-
const statusEntries = Object.entries(quoteStatusMap).map(([key, entry]) => ({
|
|
12
|
-
key,
|
|
13
|
-
label: entry.label,
|
|
14
|
-
variant: entry.variant,
|
|
15
|
-
}))
|
|
16
|
-
|
|
17
20
|
export default function MyAccountQuotesStatusSelector({
|
|
18
21
|
value,
|
|
19
22
|
onChange,
|
|
23
|
+
statusLabel = defaultMyAccountListQuotesLabels.statusFacetLabel,
|
|
24
|
+
removeStatusAriaLabel = defaultMyAccountListQuotesLabels.removeStatusAriaLabel,
|
|
25
|
+
statusCmsLabels,
|
|
20
26
|
}: MyAccountQuotesStatusSelectorProps) {
|
|
27
|
+
const statusEntries = useMemo(() => {
|
|
28
|
+
const statusMap = getLocalizedQuoteStatusMap(statusCmsLabels)
|
|
29
|
+
return Object.entries(statusMap).map(([key, entry]) => ({
|
|
30
|
+
key,
|
|
31
|
+
label: entry.label,
|
|
32
|
+
variant: entry.variant,
|
|
33
|
+
}))
|
|
34
|
+
}, [statusCmsLabels])
|
|
21
35
|
const [open, setOpen] = useState(false)
|
|
22
36
|
const ref = useRef<HTMLDivElement>(null)
|
|
23
37
|
const labelId = useId()
|
|
@@ -47,7 +61,7 @@ export default function MyAccountQuotesStatusSelector({
|
|
|
47
61
|
return (
|
|
48
62
|
<div className={styles.wrapper} ref={ref} data-fs-quotes-status-selector>
|
|
49
63
|
<span id={labelId} className={styles.label}>
|
|
50
|
-
|
|
64
|
+
{statusLabel}
|
|
51
65
|
</span>
|
|
52
66
|
<div data-fs-quotes-status-input-wrapper>
|
|
53
67
|
<div className={styles.input} data-open={open || undefined}>
|
|
@@ -64,7 +78,10 @@ export default function MyAccountQuotesStatusSelector({
|
|
|
64
78
|
type="button"
|
|
65
79
|
className={styles.chipRemove}
|
|
66
80
|
onClick={(e) => remove(key, e)}
|
|
67
|
-
aria-label={
|
|
81
|
+
aria-label={formatRemoveStatusAriaLabel(
|
|
82
|
+
removeStatusAriaLabel,
|
|
83
|
+
entry?.label ?? key
|
|
84
|
+
)}
|
|
68
85
|
>
|
|
69
86
|
×
|
|
70
87
|
</button>
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { default as MyAccountListQuotes } from './MyAccountListQuotes'
|
|
2
2
|
export type { MyAccountListQuotesProps } from './MyAccountListQuotes'
|
|
3
|
+
export { defaultMyAccountListQuotesLabels } from './quotesLabels'
|
|
4
|
+
export type { MyAccountListQuotesSectionLabels } from './quotesLabels'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getLocalizedQuoteStatusMap } from 'src/utils/quoteStatus'
|
|
2
|
+
import type { QuoteStatusCmsLabels } from 'src/utils/quoteStatus'
|
|
2
3
|
|
|
3
4
|
export type QuoteFilters = {
|
|
4
5
|
page: number
|
|
@@ -20,13 +21,22 @@ export function getSelectedFacets(filters: QuoteFilters): QuoteSelectedFacet[] {
|
|
|
20
21
|
return facets
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
export
|
|
24
|
+
export type QuoteFacetLabels = {
|
|
25
|
+
statusFacetLabel?: string
|
|
26
|
+
createdAtFacetLabel?: string
|
|
27
|
+
expiresAtFacetLabel?: string
|
|
28
|
+
statusCmsLabels?: QuoteStatusCmsLabels
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getAllFacets(filters: QuoteFilters, labels?: QuoteFacetLabels) {
|
|
32
|
+
const statusMap = getLocalizedQuoteStatusMap(labels?.statusCmsLabels)
|
|
33
|
+
|
|
24
34
|
return [
|
|
25
35
|
{
|
|
26
36
|
__typename: 'StoreFacetBoolean' as const,
|
|
27
37
|
key: 'status',
|
|
28
|
-
label: 'Status',
|
|
29
|
-
values: Object.entries(
|
|
38
|
+
label: labels?.statusFacetLabel ?? 'Status',
|
|
39
|
+
values: Object.entries(statusMap).map(([key, { label }]) => ({
|
|
30
40
|
label,
|
|
31
41
|
quantity: 0,
|
|
32
42
|
selected: false,
|
|
@@ -36,14 +46,14 @@ export function getAllFacets(filters: QuoteFilters) {
|
|
|
36
46
|
{
|
|
37
47
|
__typename: 'StoreFacetRange' as const,
|
|
38
48
|
key: 'createdAt',
|
|
39
|
-
label: 'Created Date',
|
|
49
|
+
label: labels?.createdAtFacetLabel ?? 'Created Date',
|
|
40
50
|
from: filters.createdAtFrom,
|
|
41
51
|
to: filters.createdAtTo,
|
|
42
52
|
},
|
|
43
53
|
{
|
|
44
54
|
__typename: 'StoreFacetRange' as const,
|
|
45
55
|
key: 'expiresAt',
|
|
46
|
-
label: 'Expiry Date',
|
|
56
|
+
label: labels?.expiresAtFacetLabel ?? 'Expiry Date',
|
|
47
57
|
from: filters.expiresAtFrom,
|
|
48
58
|
to: filters.expiresAtTo,
|
|
49
59
|
},
|