@faststore/core 4.9.0-dev.3 → 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.
Files changed (38) hide show
  1. package/.turbo/turbo-generate.log +3 -3
  2. package/.turbo/turbo-test.log +131 -131
  3. package/CHANGELOG.md +6 -0
  4. package/cms/faststore/my-account/components/cms_component__accountlistquotes.jsonc +240 -0
  5. package/cms/faststore/my-account/components/cms_component__accountnavigation.jsonc +76 -0
  6. package/cms/faststore/my-account/pages/cms_content_type__myaccountquotes.jsonc +18 -0
  7. package/package.json +6 -6
  8. package/src/components/account/Drawer/OrganizationDrawer/ContractSwitcher/ContractSwitcher.tsx +129 -26
  9. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerHeader.tsx +7 -1
  10. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotes.tsx +42 -11
  11. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountListQuotesTable.tsx +45 -15
  12. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountQuoteStatusBadge.tsx +11 -3
  13. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesFilterSlider/MyAccountQuotesFilterSlider.tsx +23 -0
  14. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesSelectedTags/MyAccountQuotesSelectedTags.tsx +35 -11
  15. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesStatusSelector/MyAccountQuotesStatusSelector.tsx +27 -10
  16. package/src/components/account/quotes/MyAccountListQuotes/index.ts +2 -0
  17. package/src/components/account/quotes/MyAccountListQuotes/quoteFilters.ts +16 -6
  18. package/src/components/account/quotes/MyAccountListQuotes/quotesLabels.ts +163 -0
  19. package/src/components/cms/account/Components.ts +2 -0
  20. package/src/components/sections/Account/AccountListQuotes.tsx +141 -0
  21. package/src/pages/pvt/account/quotes/index.tsx +59 -40
  22. package/src/sdk/account/accountPageContext.ts +17 -0
  23. package/src/sdk/account/getMyAccountRoutes.ts +17 -0
  24. package/src/sdk/account/resolveNonBlankLabels.ts +36 -0
  25. package/src/server/cms/myAccountDefaultSections.ts +2 -0
  26. package/src/utils/quoteStatus.ts +81 -0
  27. package/test/components/account/ContractSwitcher.browser.test.tsx +163 -0
  28. package/test/components/account/OrganizationDrawer.browser.test.tsx +4 -0
  29. package/test/components/account/OrganizationDrawerHeader.browser.test.tsx +44 -1
  30. package/test/components/quotes/MyAccountListQuotes.test.tsx +36 -2
  31. package/test/components/quotes/MyAccountListQuotesTable.test.tsx +68 -0
  32. package/test/components/quotes/MyAccountQuoteStatusBadge.test.tsx +25 -0
  33. package/test/components/quotes/MyAccountQuotesFilterSlider.test.tsx +52 -11
  34. package/test/components/quotes/MyAccountQuotesSelectedTags.test.tsx +32 -0
  35. package/test/components/quotes/MyAccountQuotesStatusSelector.test.tsx +100 -0
  36. package/test/components/quotes/quoteFilters.test.ts +34 -0
  37. package/test/components/sections/Account/AccountListQuotes.browser.test.tsx +122 -0
  38. package/test/server/cms/myAccountDefaultSections.test.ts +21 -0
@@ -0,0 +1,240 @@
1
+ {
2
+ "$extends": ["#/$defs/base-component"],
3
+ "$componentKey": "AccountListQuotes",
4
+ "$componentTitle": "Account List Quotes",
5
+ "title": "Account List Quotes",
6
+ "description": "Quotes list section for My Account",
7
+ "type": "object",
8
+ "properties": {
9
+ "pageTitle": {
10
+ "type": "string",
11
+ "title": "Page Title",
12
+ "default": "Quotes"
13
+ },
14
+ "searchPlaceholder": {
15
+ "type": "string",
16
+ "title": "Search Placeholder",
17
+ "default": "Search"
18
+ },
19
+ "openFiltersAriaLabel": {
20
+ "type": "string",
21
+ "title": "Open Filters Aria Label",
22
+ "default": "Open Filters"
23
+ },
24
+ "filtersLabel": {
25
+ "type": "string",
26
+ "title": "Filters Label",
27
+ "default": "Filters"
28
+ },
29
+ "clearAllLabel": {
30
+ "type": "string",
31
+ "title": "Clear All Label",
32
+ "default": "Clear All"
33
+ },
34
+ "viewResultsLabel": {
35
+ "type": "string",
36
+ "title": "View Results Label",
37
+ "default": "View Results"
38
+ },
39
+ "noResultsLabel": {
40
+ "type": "string",
41
+ "title": "No Results Label",
42
+ "default": "No results found"
43
+ },
44
+ "noQuotesLabel": {
45
+ "type": "string",
46
+ "title": "No Quotes Label",
47
+ "default": "You don't have any quotes"
48
+ },
49
+ "statusFacetLabel": {
50
+ "type": "string",
51
+ "title": "Status Facet Label",
52
+ "default": "Status"
53
+ },
54
+ "createdAtFacetLabel": {
55
+ "type": "string",
56
+ "title": "Created At Facet Label",
57
+ "default": "Created Date"
58
+ },
59
+ "expiresAtFacetLabel": {
60
+ "type": "string",
61
+ "title": "Expires At Facet Label",
62
+ "default": "Expiry Date"
63
+ },
64
+ "removeStatusAriaLabel": {
65
+ "type": "string",
66
+ "title": "Remove Status Chip Aria Label",
67
+ "description": "Supports a \"{status}\" placeholder, e.g. \"Remove {status}\".",
68
+ "default": "Remove {status}"
69
+ },
70
+ "fromLabel": {
71
+ "type": "string",
72
+ "title": "From Label",
73
+ "default": "From"
74
+ },
75
+ "toLabel": {
76
+ "type": "string",
77
+ "title": "To Label",
78
+ "default": "To"
79
+ },
80
+ "invalidDateRangeLabel": {
81
+ "type": "string",
82
+ "title": "Invalid Date Range Label",
83
+ "default": "Invalid date range"
84
+ },
85
+ "paginationOfLabel": {
86
+ "type": "string",
87
+ "title": "Pagination Of Label",
88
+ "default": "of"
89
+ },
90
+ "previousPageLabel": {
91
+ "type": "string",
92
+ "title": "Previous Page Label",
93
+ "default": "Previous Page"
94
+ },
95
+ "nextPageLabel": {
96
+ "type": "string",
97
+ "title": "Next Page Label",
98
+ "default": "Next Page"
99
+ },
100
+ "createdTagLabel": {
101
+ "type": "string",
102
+ "title": "Created Tag Label",
103
+ "default": "Created"
104
+ },
105
+ "expiresTagLabel": {
106
+ "type": "string",
107
+ "title": "Expires Tag Label",
108
+ "default": "Expires"
109
+ },
110
+ "dateRangeFromLabel": {
111
+ "type": "string",
112
+ "title": "Date Range From Label",
113
+ "default": "from"
114
+ },
115
+ "dateRangeToLabel": {
116
+ "type": "string",
117
+ "title": "Date Range To Label",
118
+ "default": "to"
119
+ },
120
+ "dateRangeSeparatorLabel": {
121
+ "type": "string",
122
+ "title": "Date Range Separator Label",
123
+ "default": "to"
124
+ },
125
+ "createdByLabel": {
126
+ "type": "string",
127
+ "title": "Created By Label",
128
+ "default": "Created by"
129
+ },
130
+ "creationDateLabel": {
131
+ "type": "string",
132
+ "title": "Creation Date Label",
133
+ "default": "Creation date"
134
+ },
135
+ "expiresOnLabel": {
136
+ "type": "string",
137
+ "title": "Expires On Label",
138
+ "default": "Expires on"
139
+ },
140
+ "totalLabel": {
141
+ "type": "string",
142
+ "title": "Total Label",
143
+ "default": "Total"
144
+ },
145
+ "hourLabel": {
146
+ "type": "string",
147
+ "title": "Hour Label (singular)",
148
+ "default": "hour"
149
+ },
150
+ "hoursLabel": {
151
+ "type": "string",
152
+ "title": "Hours Label (plural)",
153
+ "default": "hours"
154
+ },
155
+ "dayLabel": {
156
+ "type": "string",
157
+ "title": "Day Label (singular)",
158
+ "default": "day"
159
+ },
160
+ "daysLabel": {
161
+ "type": "string",
162
+ "title": "Days Label (plural)",
163
+ "default": "days"
164
+ },
165
+ "weekLabel": {
166
+ "type": "string",
167
+ "title": "Week Label (singular)",
168
+ "default": "week"
169
+ },
170
+ "weeksLabel": {
171
+ "type": "string",
172
+ "title": "Weeks Label (plural)",
173
+ "default": "weeks"
174
+ },
175
+ "monthLabel": {
176
+ "type": "string",
177
+ "title": "Month Label (singular)",
178
+ "default": "month"
179
+ },
180
+ "monthsLabel": {
181
+ "type": "string",
182
+ "title": "Months Label (plural)",
183
+ "default": "months"
184
+ },
185
+ "leftSuffixLabel": {
186
+ "type": "string",
187
+ "title": "Left Suffix Label",
188
+ "default": "left"
189
+ },
190
+ "draftStatus": {
191
+ "type": "string",
192
+ "title": "Draft Status",
193
+ "default": "Draft"
194
+ },
195
+ "requestedStatus": {
196
+ "type": "string",
197
+ "title": "Requested Status",
198
+ "default": "Requested"
199
+ },
200
+ "inReviewStatus": {
201
+ "type": "string",
202
+ "title": "In Review Status",
203
+ "default": "In Review"
204
+ },
205
+ "revisedStatus": {
206
+ "type": "string",
207
+ "title": "Revised Status",
208
+ "default": "Revised"
209
+ },
210
+ "approvedStatus": {
211
+ "type": "string",
212
+ "title": "Approved Status",
213
+ "default": "Approved"
214
+ },
215
+ "declinedStatus": {
216
+ "type": "string",
217
+ "title": "Declined Status",
218
+ "default": "Declined"
219
+ },
220
+ "expiredStatus": {
221
+ "type": "string",
222
+ "title": "Expired Status",
223
+ "default": "Expired"
224
+ },
225
+ "convertedStatus": {
226
+ "type": "string",
227
+ "title": "Converted Status",
228
+ "default": "Converted"
229
+ },
230
+ "convertedToOrderStatus": {
231
+ "type": "string",
232
+ "title": "Converted To Order Status",
233
+ "default": "Converted to Order"
234
+ }
235
+ },
236
+ "readOnly": false,
237
+ "writeOnly": false,
238
+ "deprecated": false,
239
+ "$abstract": false
240
+ }
@@ -60,6 +60,82 @@
60
60
  "type": "string",
61
61
  "title": "Contract Label",
62
62
  "default": "Contract"
63
+ },
64
+ "changeContractLabel": {
65
+ "type": "string",
66
+ "title": "Change Contract Label",
67
+ "default": "Change"
68
+ },
69
+ "contractSwitcherTitleLabel": {
70
+ "type": "string",
71
+ "title": "Contract Switcher Title Label",
72
+ "default": "Change contract"
73
+ },
74
+ "contractSwitcherBackLabel": {
75
+ "type": "string",
76
+ "title": "Contract Switcher Back Aria Label",
77
+ "default": "Back to account menu"
78
+ },
79
+ "contractSwitcherCloseLabel": {
80
+ "type": "string",
81
+ "title": "Contract Switcher Close Aria Label",
82
+ "default": "Close"
83
+ },
84
+ "contractSwitcherCurrentSessionLabel": {
85
+ "type": "string",
86
+ "title": "Contract Switcher Current Session Label",
87
+ "default": "Current session"
88
+ },
89
+ "contractSwitcherSearchPlaceholder": {
90
+ "type": "string",
91
+ "title": "Contract Switcher Search Placeholder",
92
+ "default": "Search"
93
+ },
94
+ "contractSwitcherSearchAriaLabel": {
95
+ "type": "string",
96
+ "title": "Contract Switcher Search Aria Label",
97
+ "default": "Search contracts"
98
+ },
99
+ "contractSwitcherClearSearchLabel": {
100
+ "type": "string",
101
+ "title": "Contract Switcher Clear Search Aria Label",
102
+ "default": "Clear search"
103
+ },
104
+ "contractSwitcherAvailableCountLabel": {
105
+ "type": "string",
106
+ "title": "Contract Switcher Available Count Label",
107
+ "description": "Supports a \"{count}\" placeholder, e.g. \"Select one of {count} available contracts:\".",
108
+ "default": "Select one of {count} available contracts:"
109
+ },
110
+ "contractSwitcherNoMatchLabel": {
111
+ "type": "string",
112
+ "title": "Contract Switcher No Match Label",
113
+ "default": "No contracts match your search."
114
+ },
115
+ "contractSwitcherEmptyLabel": {
116
+ "type": "string",
117
+ "title": "Contract Switcher Empty Label",
118
+ "default": "No other contracts are available for your organization."
119
+ },
120
+ "contractSwitcherLoadErrorLabel": {
121
+ "type": "string",
122
+ "title": "Contract Switcher Load Error Label",
123
+ "default": "We couldn't load your contracts. Please try again."
124
+ },
125
+ "contractSwitcherSwitchErrorLabel": {
126
+ "type": "string",
127
+ "title": "Contract Switcher Switch Error Label",
128
+ "default": "We couldn't switch your contract. The previous contract is still active."
129
+ },
130
+ "contractSwitcherCancelLabel": {
131
+ "type": "string",
132
+ "title": "Contract Switcher Cancel Label",
133
+ "default": "Cancel"
134
+ },
135
+ "contractSwitcherConfirmLabel": {
136
+ "type": "string",
137
+ "title": "Contract Switcher Confirm Label",
138
+ "default": "Confirm"
63
139
  }
64
140
  },
65
141
  "readOnly": false,
@@ -0,0 +1,18 @@
1
+ {
2
+ "myAccountQuotes": {
3
+ "$extends": ["#/components/base-page-template"],
4
+ "$singleton": true,
5
+ "type": "object",
6
+ "title": "My Account — Quotes",
7
+ "properties": {
8
+ "sections": {
9
+ "$ref": "#/$defs/$ALLOW_ALL_COMPONENTS"
10
+ }
11
+ },
12
+ "readOnly": false,
13
+ "writeOnly": false,
14
+ "deprecated": false,
15
+ "$abstract": false,
16
+ "identifierKeys": []
17
+ }
18
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "4.9.0-dev.3",
3
+ "version": "4.9.0-dev.4",
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.9.0-dev.3",
75
- "@faststore/diagnostics": "4.9.0-dev.3",
76
- "@faststore/lighthouse": "4.9.0-dev.3",
77
- "@faststore/sdk": "4.9.0-dev.3",
78
- "@faststore/ui": "4.9.0-dev.3"
74
+ "@faststore/api": "4.9.0-dev.4",
75
+ "@faststore/diagnostics": "4.9.0-dev.4",
76
+ "@faststore/lighthouse": "4.9.0-dev.4",
77
+ "@faststore/sdk": "4.9.0-dev.4",
78
+ "@faststore/ui": "4.9.0-dev.4"
79
79
  },
80
80
  "devDependencies": {
81
81
  "@cypress/code-coverage": "^3.12.1",
@@ -1,10 +1,85 @@
1
1
  import { Button, Icon, IconButton, Loader } from '@faststore/ui'
2
2
  import { useState, type ReactNode } from 'react'
3
3
 
4
+ import { useAccountNavigationLabels } from 'src/sdk/account/accountPageContext'
5
+ import type { AccountNavigationLabels } from 'src/sdk/account/getMyAccountRoutes'
6
+ import {
7
+ resolveNonBlankLabels,
8
+ splitOnFirstToken,
9
+ } from 'src/sdk/account/resolveNonBlankLabels'
4
10
  import { useAvailableContracts } from 'src/sdk/account/useAvailableContracts'
5
11
  import { useSwitchContract } from 'src/sdk/account/useSwitchContract'
6
12
  import { useSession } from 'src/sdk/session'
7
13
 
14
+ /** English fallbacks used when the CMS doesn't provide a `navigationLabels` override. */
15
+ const defaultContractSwitcherLabels = {
16
+ titleLabel: 'Change contract',
17
+ backLabel: 'Back to account menu',
18
+ closeLabel: 'Close',
19
+ currentSessionLabel: 'Current session',
20
+ searchPlaceholder: 'Search',
21
+ searchAriaLabel: 'Search contracts',
22
+ clearSearchLabel: 'Clear search',
23
+ availableCountLabel: 'Select one of {count} available contracts:',
24
+ noMatchLabel: 'No contracts match your search.',
25
+ emptyLabel: 'No other contracts are available for your organization.',
26
+ loadErrorLabel: "We couldn't load your contracts. Please try again.",
27
+ switchErrorLabel:
28
+ "We couldn't switch your contract. The previous contract is still active.",
29
+ cancelLabel: 'Cancel',
30
+ confirmLabel: 'Confirm',
31
+ }
32
+
33
+ type ContractSwitcherLabels = typeof defaultContractSwitcherLabels
34
+
35
+ const SWITCHER_CMS_KEYS = {
36
+ titleLabel: 'contractSwitcherTitleLabel',
37
+ backLabel: 'contractSwitcherBackLabel',
38
+ closeLabel: 'contractSwitcherCloseLabel',
39
+ currentSessionLabel: 'contractSwitcherCurrentSessionLabel',
40
+ searchPlaceholder: 'contractSwitcherSearchPlaceholder',
41
+ searchAriaLabel: 'contractSwitcherSearchAriaLabel',
42
+ clearSearchLabel: 'contractSwitcherClearSearchLabel',
43
+ availableCountLabel: 'contractSwitcherAvailableCountLabel',
44
+ noMatchLabel: 'contractSwitcherNoMatchLabel',
45
+ emptyLabel: 'contractSwitcherEmptyLabel',
46
+ loadErrorLabel: 'contractSwitcherLoadErrorLabel',
47
+ switchErrorLabel: 'contractSwitcherSwitchErrorLabel',
48
+ cancelLabel: 'contractSwitcherCancelLabel',
49
+ confirmLabel: 'contractSwitcherConfirmLabel',
50
+ } as const satisfies Record<
51
+ keyof ContractSwitcherLabels,
52
+ keyof AccountNavigationLabels
53
+ >
54
+
55
+ function resolveContractSwitcherLabels(
56
+ navigationLabels?: AccountNavigationLabels
57
+ ): ContractSwitcherLabels {
58
+ const overrides: Partial<ContractSwitcherLabels> = {}
59
+
60
+ if (navigationLabels) {
61
+ for (const key of Object.keys(
62
+ SWITCHER_CMS_KEYS
63
+ ) as (keyof ContractSwitcherLabels)[]) {
64
+ overrides[key] = navigationLabels[SWITCHER_CMS_KEYS[key]]
65
+ }
66
+ }
67
+
68
+ return resolveNonBlankLabels(defaultContractSwitcherLabels, overrides)
69
+ }
70
+
71
+ type ContractSwitcherLabelsProps = {
72
+ labels: ContractSwitcherLabels
73
+ }
74
+
75
+ /**
76
+ * Splits `availableCountLabel` on the `{count}` placeholder so the count can be
77
+ * wrapped in a `<strong>`, keeping the CMS convention of plain string labels
78
+ * (no templating engine) while still allowing surrounding text to be localized.
79
+ */
80
+ const splitAvailableCountLabel = (template: string) =>
81
+ splitOnFirstToken(template, '{count}')
82
+
8
83
  export type ContractSwitcherProps = {
9
84
  /** Returns to the drawer menu view (also used by Cancel). */
10
85
  onBack: () => void
@@ -23,6 +98,7 @@ type SwitcherContract = {
23
98
  const initialOf = (name: string) => name.trim().charAt(0).toUpperCase()
24
99
 
25
100
  type ContractSwitcherContentProps = {
101
+ labels: ContractSwitcherLabels
26
102
  loading: boolean
27
103
  error: Error | null
28
104
  currentContract: SwitcherContract | null
@@ -42,19 +118,20 @@ const ContractSwitcherLoading = () => (
42
118
  </div>
43
119
  )
44
120
 
45
- const ContractSwitcherLoadError = () => (
121
+ const ContractSwitcherLoadError = ({ labels }: ContractSwitcherLabelsProps) => (
46
122
  <div data-fs-contract-switcher-message role="alert">
47
- <p>We couldn't load your contracts. Please try again.</p>
123
+ <p>{labels.loadErrorLabel}</p>
48
124
  </div>
49
125
  )
50
126
 
51
127
  const ContractSwitcherCurrentSession = ({
128
+ labels,
52
129
  currentContract,
53
- }: {
130
+ }: ContractSwitcherLabelsProps & {
54
131
  currentContract: SwitcherContract | null
55
132
  }) => (
56
133
  <div data-fs-contract-switcher-current>
57
- <span data-fs-contract-switcher-label>Current session</span>
134
+ <span data-fs-contract-switcher-label>{labels.currentSessionLabel}</span>
58
135
  {currentContract && (
59
136
  <div data-fs-contract-switcher-current-card>
60
137
  <span data-fs-contract-switcher-avatar aria-hidden="true">
@@ -77,13 +154,16 @@ const ContractSwitcherCurrentSession = ({
77
154
  </div>
78
155
  )
79
156
 
80
- const ContractSwitcherEmptyAlternatives = () => (
157
+ const ContractSwitcherEmptyAlternatives = ({
158
+ labels,
159
+ }: ContractSwitcherLabelsProps) => (
81
160
  <div data-fs-contract-switcher-message data-fs-contract-switcher-empty>
82
- <p>No other contracts are available for your organization.</p>
161
+ <p>{labels.emptyLabel}</p>
83
162
  </div>
84
163
  )
85
164
 
86
165
  type ContractSwitcherAlternativesProps = {
166
+ labels: ContractSwitcherLabels
87
167
  alternatives: SwitcherContract[]
88
168
  ordered: SwitcherContract[]
89
169
  search: string
@@ -94,27 +174,28 @@ type ContractSwitcherAlternativesProps = {
94
174
  }
95
175
 
96
176
  const ContractSwitcherSearch = ({
177
+ labels,
97
178
  search,
98
179
  onSearchChange,
99
180
  onSearchClear,
100
181
  }: Pick<
101
182
  ContractSwitcherAlternativesProps,
102
- 'search' | 'onSearchChange' | 'onSearchClear'
183
+ 'labels' | 'search' | 'onSearchChange' | 'onSearchClear'
103
184
  >) => (
104
185
  <div data-fs-contract-switcher-search>
105
186
  <Icon name="MagnifyingGlass" width={20} height={20} />
106
187
  <input
107
188
  type="text"
108
189
  value={search}
109
- placeholder="Search"
110
- aria-label="Search contracts"
190
+ placeholder={labels.searchPlaceholder}
191
+ aria-label={labels.searchAriaLabel}
111
192
  onChange={(event) => onSearchChange(event.target.value)}
112
193
  />
113
194
  {search && (
114
195
  <button
115
196
  type="button"
116
197
  data-fs-contract-switcher-search-clear
117
- aria-label="Clear search"
198
+ aria-label={labels.clearSearchLabel}
118
199
  onClick={onSearchClear}
119
200
  >
120
201
  <Icon name="X" width={18} height={18} />
@@ -167,6 +248,7 @@ const ContractSwitcherOptionList = ({
167
248
  )
168
249
 
169
250
  const ContractSwitcherAlternatives = ({
251
+ labels,
170
252
  alternatives,
171
253
  ordered,
172
254
  search,
@@ -176,14 +258,14 @@ const ContractSwitcherAlternatives = ({
176
258
  onSelectContract,
177
259
  }: ContractSwitcherAlternativesProps) => {
178
260
  if (alternatives.length === 0) {
179
- return <ContractSwitcherEmptyAlternatives />
261
+ return <ContractSwitcherEmptyAlternatives labels={labels} />
180
262
  }
181
263
 
182
264
  let listContent: ReactNode
183
265
  if (ordered.length === 0) {
184
266
  listContent = (
185
267
  <div data-fs-contract-switcher-message>
186
- <p>No contracts match your search.</p>
268
+ <p>{labels.noMatchLabel}</p>
187
269
  </div>
188
270
  )
189
271
  } else {
@@ -196,13 +278,25 @@ const ContractSwitcherAlternatives = ({
196
278
  )
197
279
  }
198
280
 
281
+ const { found, prefix, suffix } = splitAvailableCountLabel(
282
+ labels.availableCountLabel
283
+ )
284
+
199
285
  return (
200
286
  <>
201
287
  <p data-fs-contract-switcher-count>
202
- Select one of <strong>{alternatives.length}</strong> available
203
- contracts:
288
+ {found ? (
289
+ <>
290
+ {prefix}
291
+ <strong>{alternatives.length}</strong>
292
+ {suffix}
293
+ </>
294
+ ) : (
295
+ labels.availableCountLabel
296
+ )}
204
297
  </p>
205
298
  <ContractSwitcherSearch
299
+ labels={labels}
206
300
  search={search}
207
301
  onSearchChange={onSearchChange}
208
302
  onSearchClear={onSearchClear}
@@ -212,15 +306,16 @@ const ContractSwitcherAlternatives = ({
212
306
  )
213
307
  }
214
308
 
215
- const ContractSwitcherSwitchError = () => (
309
+ const ContractSwitcherSwitchError = ({
310
+ labels,
311
+ }: ContractSwitcherLabelsProps) => (
216
312
  <div data-fs-contract-switcher-message role="alert">
217
- <p>
218
- We couldn't switch your contract. The previous contract is still active.
219
- </p>
313
+ <p>{labels.switchErrorLabel}</p>
220
314
  </div>
221
315
  )
222
316
 
223
317
  const ContractSwitcherContent = ({
318
+ labels,
224
319
  loading,
225
320
  error,
226
321
  currentContract,
@@ -238,13 +333,17 @@ const ContractSwitcherContent = ({
238
333
  }
239
334
 
240
335
  if (error) {
241
- return <ContractSwitcherLoadError />
336
+ return <ContractSwitcherLoadError labels={labels} />
242
337
  }
243
338
 
244
339
  return (
245
340
  <>
246
- <ContractSwitcherCurrentSession currentContract={currentContract} />
341
+ <ContractSwitcherCurrentSession
342
+ labels={labels}
343
+ currentContract={currentContract}
344
+ />
247
345
  <ContractSwitcherAlternatives
346
+ labels={labels}
248
347
  alternatives={alternatives}
249
348
  ordered={ordered}
250
349
  search={search}
@@ -253,7 +352,7 @@ const ContractSwitcherContent = ({
253
352
  onSearchClear={onSearchClear}
254
353
  onSelectContract={onSelectContract}
255
354
  />
256
- {switchError && <ContractSwitcherSwitchError />}
355
+ {switchError && <ContractSwitcherSwitchError labels={labels} />}
257
356
  </>
258
357
  )
259
358
  }
@@ -268,6 +367,9 @@ export const ContractSwitcher = ({
268
367
  onBack,
269
368
  onClose,
270
369
  }: ContractSwitcherProps) => {
370
+ const navigationLabels = useAccountNavigationLabels()
371
+ const labels = resolveContractSwitcherLabels(navigationLabels)
372
+
271
373
  const { contracts, loading, error } = useAvailableContracts(true)
272
374
  const { b2b } = useSession()
273
375
  const {
@@ -327,14 +429,14 @@ export const ContractSwitcher = ({
327
429
  <header data-fs-contract-switcher-header>
328
430
  <IconButton
329
431
  data-fs-contract-switcher-back
330
- aria-label="Back to account menu"
432
+ aria-label={labels.backLabel}
331
433
  icon={<Icon name="ArrowLeft" width={20} height={20} />}
332
434
  onClick={onBack}
333
435
  />
334
- <h2 data-fs-contract-switcher-title>Change contract</h2>
436
+ <h2 data-fs-contract-switcher-title>{labels.titleLabel}</h2>
335
437
  <IconButton
336
438
  data-fs-contract-switcher-close
337
- aria-label="Close"
439
+ aria-label={labels.closeLabel}
338
440
  icon={<Icon name="X" width={20} height={20} />}
339
441
  onClick={onClose}
340
442
  />
@@ -342,6 +444,7 @@ export const ContractSwitcher = ({
342
444
 
343
445
  <div data-fs-contract-switcher-content>
344
446
  <ContractSwitcherContent
447
+ labels={labels}
345
448
  loading={loading}
346
449
  error={error}
347
450
  currentContract={currentContract}
@@ -358,7 +461,7 @@ export const ContractSwitcher = ({
358
461
 
359
462
  <footer data-fs-contract-switcher-footer>
360
463
  <Button variant="tertiary" onClick={onBack}>
361
- Cancel
464
+ {labels.cancelLabel}
362
465
  </Button>
363
466
  <Button
364
467
  variant="primary"
@@ -366,7 +469,7 @@ export const ContractSwitcher = ({
366
469
  loading={switching}
367
470
  onClick={handleConfirm}
368
471
  >
369
- Confirm
472
+ {labels.confirmLabel}
370
473
  </Button>
371
474
  </footer>
372
475
  </section>