@vtex/faststore-plugin-buyer-portal 2.0.20 → 2.0.21

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 (19) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/docs/i18n/locales.json +39 -0
  3. package/docs/i18n/migration-inventory.md +78 -0
  4. package/package.json +1 -1
  5. package/src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx +28 -15
  6. package/src/features/contracts/components/ConfirmRemoveDrawer/ConfirmRemoveDrawer.tsx +15 -12
  7. package/src/features/contracts/components/ContractListItem/ContractListItem.tsx +6 -4
  8. package/src/features/contracts/components/ContractSelectAllRow/ContractSelectAllRow.tsx +5 -1
  9. package/src/features/contracts/components/ContractSelectionBar/ContractSelectionBar.tsx +5 -2
  10. package/src/features/contracts/components/ContractSelectionList/ContractSelectionList.tsx +4 -1
  11. package/src/features/contracts/components/ContractSwitchOverlay/ContractSwitchOverlay.tsx +5 -2
  12. package/src/features/contracts/components/ContractsListHeader/ContractsListHeader.tsx +4 -1
  13. package/src/features/contracts/components/ErrorRemoveDrawer/ErrorRemoveDrawer.tsx +12 -15
  14. package/src/features/contracts/components/UnableToRemoveContractsDrawer/UnableToRemoveContractsDrawer.tsx +11 -8
  15. package/src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx +27 -20
  16. package/src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx +28 -26
  17. package/src/features/contracts/layouts/ContractListingLayout/ContractSettingsNav.tsx +9 -6
  18. package/src/features/shared/utils/constants.ts +1 -1
  19. package/src/features/users/components/RolesMultiSelect/RolesMultiSelect.tsx +9 -10
package/CHANGELOG.md CHANGED
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.0.21] - 2026-08-13
11
+
12
+ ### Changed
13
+ - Replaced hardcoded, user-facing strings in the `contracts` feature (multi-contract/SUMA drawers, list, selection bar, listing/information layouts) and in `RolesMultiSelect` with `useLocalization()`/`t()` calls, adding the corresponding keys to the CMS message catalog
14
+
10
15
  ## [2.0.20] - 2026-08-12
11
16
 
12
17
  ### Added
@@ -827,7 +832,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
827
832
  - Add CHANGELOG file
828
833
  - Add README file
829
834
 
830
- [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v2.0.20...HEAD
835
+ [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v2.0.21...HEAD
831
836
  [1.3.55]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.54...v1.3.55
832
837
  [1.3.54]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.53...v1.3.54
833
838
  [1.3.53]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.52...v1.3.53
@@ -925,6 +930,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
925
930
  [2.0.10]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v2.0.9...v2.0.10
926
931
  [2.0.9]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/2.0.9
927
932
 
933
+ [2.0.21]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v2.0.20...v2.0.21
928
934
  [2.0.20]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v2.0.19...v2.0.20
929
935
  [2.0.19]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v2.0.18...v2.0.19
930
936
  [2.0.18]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v2.0.17...v2.0.18
@@ -14,6 +14,7 @@
14
14
  "shared.buttons.create": "Create",
15
15
  "shared.buttons.tryAgain": "Try again",
16
16
  "shared.buttons.loadMore": "Load More",
17
+ "shared.buttons.gotIt": "Got it",
17
18
  "shared.labels.loading": "Loading...",
18
19
  "shared.labels.name": "Name",
19
20
  "shared.labels.email": "Email",
@@ -377,6 +378,8 @@
377
378
  "users.dropdown.resetPassword": "Reset password",
378
379
  "users.dropdown.changeOrganizationalUnit": "Change organizational unit",
379
380
  "users.components.UsersCard.manageUsers": "Manage users",
381
+ "users.rolesMultiSelect.customTag": "Custom",
382
+ "users.rolesMultiSelect.noMatchingRoles": "No matching roles",
380
383
  "budgets.titles.budgets": "Budgets",
381
384
  "budgets.titles.financeAndCompliance": "Finance and Compliance",
382
385
  "budgets.labels.remainingBalance": "Remaining balance",
@@ -692,6 +695,42 @@
692
695
  "contracts.actions.viewContracts": "View contracts",
693
696
  "contracts.dropdown.open": "Open",
694
697
  "contracts.dropdown.edit": "Edit",
698
+ "contracts.labels.contract": "contract",
699
+ "contracts.labels.contracts": "contracts",
700
+ "contracts.labels.selectAllCount": "Select all ({0})",
701
+ "contracts.labels.selectedCount": "{0} selected",
702
+ "contracts.actions.setAsDefault": "Set as default",
703
+ "contracts.addDrawer.title": "Add contracts",
704
+ "contracts.addDrawer.description": "Add available contracts to {0}",
705
+ "contracts.addDrawer.paginationRange": "{0}–{1} of {2}",
706
+ "contracts.addDrawer.loading": "Loading contracts…",
707
+ "contracts.addDrawer.noSearchResults": "No contracts match your search.",
708
+ "contracts.addDrawer.noneAvailable": "No contracts available to add.",
709
+ "contracts.removeDrawer.title": "Remove {0}",
710
+ "contracts.removeDrawer.question": "Are you sure you want to remove {0} {1} from {2}?",
711
+ "contracts.removeDrawer.warning": "Removed contracts can be added again later, but any specific configurations for these contracts within this organizational unit will be lost. This will prevent users in this organization unit from accessing these contracts during store navigation and checkout.",
712
+ "contracts.removeDrawer.unableTitle": "Unable to remove contracts",
713
+ "contracts.removeDrawer.error": "An error occurred while removing the contract. Please try again.",
714
+ "contracts.removeDrawer.minimumContractWarning": "Every organizational unit must have at least one contract available. This ensures that users within {0} can always access store features like navigation and checkout.",
715
+ "contracts.removeDrawer.deselectHint": "To proceed, deselect at least one contract to keep it in this organizational unit.",
716
+ "contracts.removeDrawer.unableDeselectHint": "To proceed, deselect at least one contract to keep it in this {0}. When an organizational unit has only one contract, it is automatically set as the default.",
717
+ "contracts.listing.emptyState": "No contracts found for this organizational unit.",
718
+ "contracts.listing.interval": "{0} — {1} of {2}",
719
+ "contracts.listing.infoTooltip": "Contracts define the commercial rules applied during purchasing, such as addresses, payment methods, credit cards and other checkout settings.",
720
+ "contracts.nav.basicInformation": "Basic information",
721
+ "contracts.nav.assortment": "Assortment",
722
+ "contracts.nav.costCenters": "Cost centers",
723
+ "contracts.nav.departments": "Departments",
724
+ "contracts.nav.poNumbers": "PO numbers",
725
+ "contracts.details.createdDate": "Created date",
726
+ "contracts.toasts.addError": "Failed to add contracts. Please try again.",
727
+ "contracts.toasts.switchError": "Failed to switch contract. Please try again.",
728
+ "contracts.toasts.setDefaultSuccess": "Default contract updated successfully",
729
+ "contracts.toasts.setDefaultError": "Failed to set default contract. Please try again.",
730
+ "contracts.toasts.removeSuccess": "{0} contract{1} removed successfully",
731
+ "contracts.toasts.removeError": "Failed to remove contracts. Please try again.",
732
+ "contracts.toasts.refreshError": "Contracts removed, but we couldn't refresh your active contract. Please reload the page.",
733
+ "contracts.toasts.loadError": "Failed to load contract information.",
695
734
  "profile.titles.profile": "Profile",
696
735
  "profile.titles.details": "Details",
697
736
  "profile.labels.creationDate": "Creation Date",
@@ -487,6 +487,9 @@ Strings defined in `src/features/shared/` — canonical source for all `shared.*
487
487
  | `shared.buttons.delete` | "Delete" | `src/features/users/components/UserDropdownMenu/UserDropdownMenu.tsx` | 100 |
488
488
  | `users.components.UsersCard.manageUsers` | "Manage users" | `src/features/users/components/UsersCard/UsersCard.tsx` | 32 |
489
489
  | `users.titles.users` | "Users" | `src/features/users/components/UsersCard/UsersCard.tsx` | 35 |
490
+ | `users.forms.roles` | "Roles" | `src/features/users/components/RolesMultiSelect/RolesMultiSelect.tsx` | 244 |
491
+ | `users.rolesMultiSelect.customTag` | "Custom" | `src/features/users/components/RolesMultiSelect/RolesMultiSelect.tsx` | 302 |
492
+ | `users.rolesMultiSelect.noMatchingRoles` | "No matching roles" | `src/features/users/components/RolesMultiSelect/RolesMultiSelect.tsx` | 258 |
490
493
 
491
494
  ---
492
495
 
@@ -914,6 +917,81 @@ Strings defined in `src/features/shared/` — canonical source for all `shared.*
914
917
  | `contracts.actions.viewContracts` | "View contracts" | `src/features/contracts/components/ContractsCard/ContractsCard.tsx` | 36 |
915
918
  | `contracts.dropdown.open` | "Open" | `src/features/contracts/components/ContractsCard/ContractsCard.tsx` | 81 |
916
919
  | `contracts.dropdown.edit` | "Edit" | `src/features/contracts/components/ContractsCard/ContractsCard.tsx` | 90 |
920
+ | `contracts.toasts.addError` | "Failed to add contracts. Please try again." | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 53 |
921
+ | `contracts.addDrawer.title` | "Add contracts" | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 145 |
922
+ | `contracts.addDrawer.description` | "Add available contracts to {0}" | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 151 |
923
+ | `shared.placeholders.search` | "Search" | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 161 |
924
+ | `contracts.addDrawer.paginationRange` | "{0}–{1} of {2}" | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 190 |
925
+ | `contracts.addDrawer.loading` | "Loading contracts…" | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 218 |
926
+ | `contracts.addDrawer.noSearchResults` | "No contracts match your search." | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 224 |
927
+ | `contracts.addDrawer.noneAvailable` | "No contracts available to add." | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 225 |
928
+ | `contracts.labels.selectAllCount` | "Select all ({0})" | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 251 |
929
+ | `contracts.labels.selectedCount` | "{0} selected" | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 299 |
930
+ | `shared.buttons.cancel` | "Cancel" | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 309 |
931
+ | `shared.buttons.add` | "Add" | `src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx` | 318 |
932
+ | `contracts.labels.contract` | "contract" | `src/features/contracts/components/ConfirmRemoveDrawer/ConfirmRemoveDrawer.tsx` | 17 |
933
+ | `contracts.labels.contracts` | "contracts" | `src/features/contracts/components/ConfirmRemoveDrawer/ConfirmRemoveDrawer.tsx` | 17 |
934
+ | `contracts.removeDrawer.title` | "Remove {0}" | `src/features/contracts/components/ConfirmRemoveDrawer/ConfirmRemoveDrawer.tsx` | 22 |
935
+ | `contracts.removeDrawer.question` | "Are you sure you want to remove {0} {1} from {2}?" | `src/features/contracts/components/ConfirmRemoveDrawer/ConfirmRemoveDrawer.tsx` | 27 |
936
+ | `contracts.removeDrawer.warning` | "Removed contracts can be added again later, but any specific configurations for these contracts within this organizational unit will be lost..." | `src/features/contracts/components/ConfirmRemoveDrawer/ConfirmRemoveDrawer.tsx` | 31 |
937
+ | `shared.buttons.cancel` | "Cancel" | `src/features/contracts/components/ConfirmRemoveDrawer/ConfirmRemoveDrawer.tsx` | 44 |
938
+ | `shared.buttons.remove` | "Remove" | `src/features/contracts/components/ConfirmRemoveDrawer/ConfirmRemoveDrawer.tsx` | 53 |
939
+ | `contracts.toasts.switchError` | "Failed to switch contract. Please try again." | `src/features/contracts/components/ContractListItem/ContractListItem.tsx` | 42 |
940
+ | `contracts.actions.setAsDefault` | "Set as default" | `src/features/contracts/components/ContractListItem/ContractListItem.tsx` | 95 |
941
+ | `contracts.actions.setAsDefault` | "Set as default" | `src/features/contracts/components/ContractListItem/ContractListItem.tsx` | 100 |
942
+ | `shared.buttons.remove` | "Remove" | `src/features/contracts/components/ContractListItem/ContractListItem.tsx` | 109 |
943
+ | `contracts.labels.selectAllCount` | "Select all ({0})" | `src/features/contracts/components/ContractSelectAllRow/ContractSelectAllRow.tsx` | 34 |
944
+ | `contracts.labels.selectedCount` | "{0} selected" | `src/features/contracts/components/ContractSelectionBar/ContractSelectionBar.tsx` | 32 |
945
+ | `shared.buttons.remove` | "Remove" | `src/features/contracts/components/ContractSelectionBar/ContractSelectionBar.tsx` | 42 |
946
+ | `contracts.listing.emptyState` | "No contracts found for this organizational unit." | `src/features/contracts/components/ContractSelectionList/ContractSelectionList.tsx` | 27 |
947
+ | `layouts.tabs.contract` | "Contract" | `src/features/contracts/components/ContractSwitchOverlay/ContractSwitchOverlay.tsx` | 29 |
948
+ | `layouts.titles.profile` | "Profile" | `src/features/contracts/components/ContractSwitchOverlay/ContractSwitchOverlay.tsx` | 32 |
949
+ | `contracts.listing.interval` | "{0} — {1} of {2}" | `src/features/contracts/components/ContractsListHeader/ContractsListHeader.tsx` | 35 |
950
+ | `contracts.removeDrawer.unableTitle` | "Unable to remove contracts" | `src/features/contracts/components/ErrorRemoveDrawer/ErrorRemoveDrawer.tsx` | 16 |
951
+ | `contracts.removeDrawer.error` | "An error occurred while removing the contract. Please try again." | `src/features/contracts/components/ErrorRemoveDrawer/ErrorRemoveDrawer.tsx` | 22 |
952
+ | `contracts.removeDrawer.minimumContractWarning` | "Every organizational unit must have at least one contract available..." | `src/features/contracts/components/ErrorRemoveDrawer/ErrorRemoveDrawer.tsx` | 27 |
953
+ | `contracts.removeDrawer.deselectHint` | "To proceed, deselect at least one contract to keep it in this organizational unit." | `src/features/contracts/components/ErrorRemoveDrawer/ErrorRemoveDrawer.tsx` | 33 |
954
+ | `shared.buttons.cancel` | "Cancel" | `src/features/contracts/components/ErrorRemoveDrawer/ErrorRemoveDrawer.tsx` | 47 |
955
+ | `shared.buttons.tryAgain` | "Try again" | `src/features/contracts/components/ErrorRemoveDrawer/ErrorRemoveDrawer.tsx` | 54 |
956
+ | `shared.buttons.gotIt` | "Got it" | `src/features/contracts/components/ErrorRemoveDrawer/ErrorRemoveDrawer.tsx` | 63 |
957
+ | `contracts.removeDrawer.unableTitle` | "Unable to remove contracts" | `src/features/contracts/components/UnableToRemoveContractsDrawer/UnableToRemoveContractsDrawer.tsx` | 23 |
958
+ | `contracts.removeDrawer.minimumContractWarning` | "Every organizational unit must have at least one contract available..." | `src/features/contracts/components/UnableToRemoveContractsDrawer/UnableToRemoveContractsDrawer.tsx` | 31 |
959
+ | `contracts.removeDrawer.unableDeselectHint` | "To proceed, deselect at least one contract to keep it in this {0}..." | `src/features/contracts/components/UnableToRemoveContractsDrawer/UnableToRemoveContractsDrawer.tsx` | 36 |
960
+ | `shared.buttons.gotIt` | "Got it" | `src/features/contracts/components/UnableToRemoveContractsDrawer/UnableToRemoveContractsDrawer.tsx` | 47 |
961
+ | `contracts.nav.basicInformation` | "Basic information" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 46 |
962
+ | `layouts.navigation.addresses` | "Addresses" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 52 |
963
+ | `layouts.navigation.paymentMethods` | "Payment methods" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 56 |
964
+ | `layouts.navigation.creditCards` | "Credit cards" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 61 |
965
+ | `contracts.nav.assortment` | "Assortment" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 66 |
966
+ | `layouts.navigation.accountingFields` | "Accounting fields" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 72 |
967
+ | `contracts.nav.costCenters` | "Cost centers" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 82 |
968
+ | `contracts.nav.departments` | "Departments" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 90 |
969
+ | `contracts.nav.poNumbers` | "PO numbers" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 100 |
970
+ | `contracts.toasts.setDefaultSuccess` | "Default contract updated successfully" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 168 |
971
+ | `contracts.toasts.setDefaultError` | "Failed to set default contract. Please try again." | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 182 |
972
+ | `layouts.navigation.contracts` | "Contracts" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 217 |
973
+ | `contracts.actions.setAsDefault` | "Set as default" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 235 |
974
+ | `contracts.actions.setAsDefault` | "Set as default" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 243 |
975
+ | `shared.buttons.remove` | "Remove" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 251 |
976
+ | `contracts.nav.basicInformation` | "Basic information" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 267 |
977
+ | `shared.labels.name` | "Name" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 271 |
978
+ | `shared.labels.email` | "Email" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 277 |
979
+ | `contracts.details.createdDate` | "Created date" | `src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx` | 283 |
980
+ | `contracts.listing.infoTooltip` | "Contracts define the commercial rules applied during purchasing..." | `src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx` | 59 |
981
+ | `contracts.toasts.refreshError` | "Contracts removed, but we couldn't refresh your active contract. Please reload the page." | `src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx` | 149 |
982
+ | `contracts.toasts.removeSuccess` | "{0} contract{1} removed successfully" | `src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx` | 162 |
983
+ | `contracts.toasts.removeError` | "Failed to remove contracts. Please try again." | `src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx` | 178 |
984
+ | `contracts.toasts.setDefaultSuccess` | "Default contract updated successfully" | `src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx` | 209 |
985
+ | `contracts.toasts.setDefaultError` | "Failed to set default contract. Please try again." | `src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx` | 224 |
986
+ | `contracts.toasts.loadError` | "Failed to load contract information." | `src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx` | 246 |
987
+ | `contracts.titles.contracts` | "Contracts" | `src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx` | 328 |
988
+ | `orgUnitDetails.links.manageContractSettings` | "Manage contract settings" | `src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx` | 343 |
989
+ | `contracts.nav.basicInformation` | "Basic information" | `src/features/contracts/layouts/ContractListingLayout/ContractSettingsNav.tsx` | 34 |
990
+ | `layouts.navigation.addresses` | "Addresses" | `src/features/contracts/layouts/ContractListingLayout/ContractSettingsNav.tsx` | 38 |
991
+ | `layouts.navigation.paymentMethods` | "Payment methods" | `src/features/contracts/layouts/ContractListingLayout/ContractSettingsNav.tsx` | 42 |
992
+ | `layouts.navigation.creditCards` | "Credit cards" | `src/features/contracts/layouts/ContractListingLayout/ContractSettingsNav.tsx` | 46 |
993
+ | `contracts.nav.assortment` | "Assortment" | `src/features/contracts/layouts/ContractListingLayout/ContractSettingsNav.tsx` | 50 |
994
+ | `layouts.navigation.accountingFields` | "Accounting fields" | `src/features/contracts/layouts/ContractListingLayout/ContractSettingsNav.tsx` | 54 |
917
995
 
918
996
  ---
919
997
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "2.0.20",
3
+ "version": "2.0.21",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -8,6 +8,7 @@ import {
8
8
  Icon,
9
9
  LetterHighlight,
10
10
  } from "../../../shared/components";
11
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
11
12
  import { useAddContracts, useListAvailableContracts } from "../../hooks";
12
13
  import {
13
14
  buildRangeSet,
@@ -30,6 +31,7 @@ export const AddContractsDrawer = ({
30
31
  onSuccess,
31
32
  ...drawerProps
32
33
  }: AddContractsDrawerProps) => {
34
+ const { t } = useLocalization();
33
35
  const { pushToast } = useUI();
34
36
 
35
37
  const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
@@ -50,7 +52,7 @@ export const AddContractsDrawer = ({
50
52
  onError: () => {
51
53
  drawerProps.onDismiss?.();
52
54
  pushToast({
53
- message: "Failed to add contracts. Please try again.",
55
+ message: t("contracts.toasts.addError"),
54
56
  status: "ERROR",
55
57
  });
56
58
  },
@@ -142,23 +144,24 @@ export const AddContractsDrawer = ({
142
144
  return (
143
145
  <BasicDrawer size="large" {...drawerProps}>
144
146
  <BasicDrawer.Heading
145
- title="Add contracts"
147
+ title={t("contracts.addDrawer.title")}
146
148
  onClose={isAddContractsLoading ? undefined : drawerProps.onDismiss}
147
149
  />
148
150
 
149
151
  <BasicDrawer.Body data-fs-bp-add-contracts-drawer-body>
150
152
  <p data-fs-bp-add-contracts-drawer-description>
151
- Add available contracts to{" "}
152
- <strong data-fs-bp-add-contracts-drawer-unit-name>
153
- {orgUnitName}
154
- </strong>
153
+ {t("contracts.addDrawer.description", [
154
+ <strong data-fs-bp-add-contracts-drawer-unit-name key="orgUnitName">
155
+ {orgUnitName}
156
+ </strong>,
157
+ ])}
155
158
  </p>
156
159
 
157
160
  <div data-fs-bp-add-contracts-drawer-toolbar>
158
161
  <div data-fs-bp-add-contracts-drawer-search>
159
162
  <input
160
163
  type="text"
161
- placeholder="Search"
164
+ placeholder={t("shared.placeholders.search")}
162
165
  value={searchQuery}
163
166
  onChange={handleSearchChange}
164
167
  disabled={isDisabled}
@@ -187,7 +190,11 @@ export const AddContractsDrawer = ({
187
190
  aria-label="Pagination"
188
191
  >
189
192
  <span data-fs-bp-add-contracts-drawer-pagination-range>
190
- {rangeStart}–{rangeEnd} of {filteredContracts.length}
193
+ {t("contracts.addDrawer.paginationRange", [
194
+ rangeStart,
195
+ rangeEnd,
196
+ filteredContracts.length,
197
+ ])}
191
198
  </span>
192
199
  <button
193
200
  type="button"
@@ -215,14 +222,14 @@ export const AddContractsDrawer = ({
215
222
 
216
223
  {availableContractsLoading ? (
217
224
  <div data-fs-bp-add-contracts-drawer-loading aria-busy="true">
218
- Loading contracts…
225
+ {t("contracts.addDrawer.loading")}
219
226
  </div>
220
227
  ) : filteredContracts.length === 0 ? (
221
228
  <div data-fs-bp-add-contracts-drawer-empty>
222
229
  <p>
223
230
  {searchQuery
224
- ? "No contracts match your search."
225
- : "No contracts available to add."}
231
+ ? t("contracts.addDrawer.noSearchResults")
232
+ : t("contracts.addDrawer.noneAvailable")}
226
233
  </p>
227
234
  </div>
228
235
  ) : (
@@ -248,7 +255,11 @@ export const AddContractsDrawer = ({
248
255
  <Icon name="Check" width={12} height={12} />
249
256
  )}
250
257
  </div>
251
- <span>Select all ({filteredContracts.length})</span>
258
+ <span>
259
+ {t("contracts.labels.selectAllCount", [
260
+ filteredContracts.length,
261
+ ])}
262
+ </span>
252
263
  </div>
253
264
 
254
265
  {displayedContracts.map((contract) => {
@@ -296,7 +307,9 @@ export const AddContractsDrawer = ({
296
307
  >
297
308
  <Icon name="X" width={14} height={14} />
298
309
  </button>
299
- <span>{selectedIds.size} selected</span>
310
+ <span>
311
+ {t("contracts.labels.selectedCount", [selectedIds.size])}
312
+ </span>
300
313
  </>
301
314
  )}
302
315
  </div>
@@ -306,7 +319,7 @@ export const AddContractsDrawer = ({
306
319
  type="button"
307
320
  disabled={isDisabled}
308
321
  >
309
- Cancel
322
+ {t("shared.buttons.cancel")}
310
323
  </BasicDrawer.Button>
311
324
  <BasicDrawer.Button
312
325
  variant="confirm"
@@ -315,7 +328,7 @@ export const AddContractsDrawer = ({
315
328
  disabled={selectedIds.size === 0 || isDisabled}
316
329
  isLoading={isAddContractsLoading}
317
330
  >
318
- Add
331
+ {t("shared.buttons.add")}
319
332
  </BasicDrawer.Button>
320
333
  </BasicDrawer.Footer>
321
334
  </BasicDrawer>
@@ -1,4 +1,5 @@
1
1
  import { BasicDrawer, type BasicDrawerProps } from "../../../shared/components";
2
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
2
3
 
3
4
  export type ConfirmRemoveDrawerProps = Omit<BasicDrawerProps, "children"> & {
4
5
  orgUnitName: string;
@@ -14,25 +15,27 @@ export const ConfirmRemoveDrawer = ({
14
15
  isLoading = false,
15
16
  ...drawerProps
16
17
  }: ConfirmRemoveDrawerProps) => {
17
- const label = count === 1 ? "contract" : "contracts";
18
+ const { t } = useLocalization();
19
+ const label =
20
+ count === 1
21
+ ? t("contracts.labels.contract")
22
+ : t("contracts.labels.contracts");
18
23
 
19
24
  return (
20
25
  <BasicDrawer {...drawerProps}>
21
26
  <BasicDrawer.Heading
22
- title={`Remove ${label}`}
27
+ title={t("contracts.removeDrawer.title", [label])}
23
28
  onClose={drawerProps.onDismiss}
24
29
  />
25
30
  <BasicDrawer.Body data-fs-bp-confirm-remove-drawer-body>
26
31
  <p>
27
- Are you sure you want to remove <strong>{count}</strong> {label} from{" "}
28
- <strong>{orgUnitName}</strong>?
29
- </p>
30
- <p>
31
- Removed contracts can be added again later, but any specific
32
- configurations for these contracts within this organizational unit
33
- will be lost. This will prevent users in this organization unit from
34
- accessing these contracts during store navigation and checkout.
32
+ {t("contracts.removeDrawer.question", [
33
+ <strong key="count">{count}</strong>,
34
+ label,
35
+ <strong key="orgUnitName">{orgUnitName}</strong>,
36
+ ])}
35
37
  </p>
38
+ <p>{t("contracts.removeDrawer.warning")}</p>
36
39
  </BasicDrawer.Body>
37
40
  <BasicDrawer.Footer>
38
41
  <BasicDrawer.Button
@@ -41,7 +44,7 @@ export const ConfirmRemoveDrawer = ({
41
44
  type="button"
42
45
  disabled={isLoading}
43
46
  >
44
- Cancel
47
+ {t("shared.buttons.cancel")}
45
48
  </BasicDrawer.Button>
46
49
  <BasicDrawer.Button
47
50
  variant="danger"
@@ -50,7 +53,7 @@ export const ConfirmRemoveDrawer = ({
50
53
  isLoading={isLoading}
51
54
  disabled={isLoading}
52
55
  >
53
- Remove
56
+ {t("shared.buttons.remove")}
54
57
  </BasicDrawer.Button>
55
58
  </BasicDrawer.Footer>
56
59
  </BasicDrawer>
@@ -2,6 +2,7 @@ import { Dropdown, DropdownItem, useUI } from "@faststore/ui";
2
2
 
3
3
  import { BasicDropdownMenu, LetterHighlight } from "../../../shared/components";
4
4
  import Icon from "../../../shared/components/Icon/Icon";
5
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
5
6
  import { useSwitchContract } from "../../hooks";
6
7
  import { ContractSwitchOverlay } from "../ContractSwitchOverlay/ContractSwitchOverlay";
7
8
 
@@ -24,6 +25,7 @@ export const ContractListItem = ({
24
25
  onRemove,
25
26
  onSetDefault,
26
27
  }: ContractListItemProps) => {
28
+ const { t } = useLocalization();
27
29
  const displayName = contract.name ?? contract.email;
28
30
  const { pushToast } = useUI();
29
31
  const { switchContract, loading } = useSwitchContract();
@@ -39,7 +41,7 @@ export const ContractListItem = ({
39
41
  const switched = await switchContract(contract.id, { orgUnitId });
40
42
  if (!switched) {
41
43
  pushToast({
42
- message: "Failed to switch contract. Please try again.",
44
+ message: t("contracts.toasts.switchError"),
43
45
  status: "ERROR",
44
46
  });
45
47
  }
@@ -92,12 +94,12 @@ export const ContractListItem = ({
92
94
  {contract.isDefault ? (
93
95
  <DropdownItem disabled>
94
96
  <Icon name="Star" width={16} height={16} />
95
- Set as default
97
+ {t("contracts.actions.setAsDefault")}
96
98
  </DropdownItem>
97
99
  ) : (
98
100
  <DropdownItem onClick={onSetDefault}>
99
101
  <Icon name="StarOutline" width={16} height={16} />
100
- Set as default
102
+ {t("contracts.actions.setAsDefault")}
101
103
  </DropdownItem>
102
104
  )}
103
105
  <BasicDropdownMenu.Separator />
@@ -106,7 +108,7 @@ export const ContractListItem = ({
106
108
  onClick={onRemove}
107
109
  >
108
110
  <Icon name="CircleRemove" width={16} height={16} />
109
- Remove
111
+ {t("shared.buttons.remove")}
110
112
  </DropdownItem>
111
113
  </BasicDropdownMenu>
112
114
  </Dropdown>
@@ -1,4 +1,5 @@
1
1
  import Icon from "../../../shared/components/Icon/Icon";
2
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
2
3
 
3
4
  export type ContractSelectAllRowProps = {
4
5
  total: number;
@@ -11,6 +12,7 @@ export const ContractSelectAllRow = ({
11
12
  selectedCount,
12
13
  onToggle,
13
14
  }: ContractSelectAllRowProps) => {
15
+ const { t } = useLocalization();
14
16
  const allSelected = total > 0 && selectedCount >= total;
15
17
  const indeterminate = selectedCount > 0 && !allSelected;
16
18
 
@@ -31,7 +33,9 @@ export const ContractSelectAllRow = ({
31
33
  >
32
34
  {allSelected && <Icon name="Check" width={12} height={12} />}
33
35
  </div>
34
- <span data-fs-bp-contract-select-all-label>Select all ({total})</span>
36
+ <span data-fs-bp-contract-select-all-label>
37
+ {t("contracts.labels.selectAllCount", [total])}
38
+ </span>
35
39
  </div>
36
40
  );
37
41
  };
@@ -1,6 +1,7 @@
1
1
  import { Icon as UIIcon } from "@faststore/ui";
2
2
 
3
3
  import Icon from "../../../shared/components/Icon/Icon";
4
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
4
5
 
5
6
  export type ContractSelectionBarProps = {
6
7
  selectedCount: number;
@@ -15,6 +16,8 @@ export const ContractSelectionBar = ({
15
16
  onRemove,
16
17
  canRemove = true,
17
18
  }: ContractSelectionBarProps) => {
19
+ const { t } = useLocalization();
20
+
18
21
  if (selectedCount === 0) return null;
19
22
 
20
23
  return (
@@ -29,7 +32,7 @@ export const ContractSelectionBar = ({
29
32
  </button>
30
33
 
31
34
  <span data-fs-bp-contract-selection-bar-count>
32
- {selectedCount} selected
35
+ {t("contracts.labels.selectedCount", [selectedCount])}
33
36
  </span>
34
37
 
35
38
  {canRemove && (
@@ -39,7 +42,7 @@ export const ContractSelectionBar = ({
39
42
  onClick={onRemove}
40
43
  >
41
44
  <Icon name="MinusCircle" width={20} height={20} />
42
- Remove
45
+ {t("shared.buttons.remove")}
43
46
  </button>
44
47
  )}
45
48
  </div>
@@ -1,3 +1,4 @@
1
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
1
2
  import { ContractListItem } from "../ContractListItem/ContractListItem";
2
3
 
3
4
  import type { ContractData } from "../../types";
@@ -21,10 +22,12 @@ export const ContractSelectionList = ({
21
22
  onItemRemove,
22
23
  onSetDefault,
23
24
  }: ContractSelectionListProps) => {
25
+ const { t } = useLocalization();
26
+
24
27
  if (contracts.length === 0) {
25
28
  return (
26
29
  <div data-fs-bp-contract-selection-list-empty>
27
- <p>No contracts found for this organizational unit.</p>
30
+ <p>{t("contracts.listing.emptyState")}</p>
28
31
  </div>
29
32
  );
30
33
  }
@@ -1,5 +1,6 @@
1
1
  import { ContractTabsLayout } from "../../../shared/layouts/ContractTabsLayout/ContractTabsLayout";
2
2
  import { LoadingTabsLayoutContent } from "../../../shared/layouts/LoadingTabsLayout/LoadingTabsLayout";
3
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
3
4
 
4
5
  export type ContractSwitchOverlayProps = {
5
6
  orgUnitId: string;
@@ -19,6 +20,8 @@ export const ContractSwitchOverlay = ({
19
20
  orgUnitId,
20
21
  contractId,
21
22
  }: ContractSwitchOverlayProps) => {
23
+ const { t } = useLocalization();
24
+
22
25
  return (
23
26
  <div data-fs-bp-contract-switch-overlay role="alert" aria-busy="true">
24
27
  <ContractTabsLayout
@@ -26,10 +29,10 @@ export const ContractSwitchOverlay = ({
26
29
  orgUnitId={orgUnitId}
27
30
  contractName=""
28
31
  contractId={contractId}
29
- pageName="Contract"
32
+ pageName={t("layouts.tabs.contract")}
30
33
  loading
31
34
  >
32
- <LoadingTabsLayoutContent title="Profile" />
35
+ <LoadingTabsLayoutContent title={t("layouts.titles.profile")} />
33
36
  </ContractTabsLayout>
34
37
  </div>
35
38
  );
@@ -1,5 +1,6 @@
1
1
  import { InternalSearch } from "../../../shared/components";
2
2
  import Icon from "../../../shared/components/Icon/Icon";
3
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
3
4
 
4
5
  export type ContractsListHeaderProps = {
5
6
  searchTerm: string;
@@ -24,6 +25,8 @@ export const ContractsListHeader = ({
24
25
  onPrev,
25
26
  onNext,
26
27
  }: ContractsListHeaderProps) => {
28
+ const { t } = useLocalization();
29
+
27
30
  return (
28
31
  <div data-fs-bp-contracts-list-header>
29
32
  <div data-fs-bp-contracts-list-header-search>
@@ -32,7 +35,7 @@ export const ContractsListHeader = ({
32
35
 
33
36
  <div data-fs-bp-contracts-list-header-pagination>
34
37
  <span data-fs-bp-contracts-list-header-interval>
35
- {from} — {to} of {total}
38
+ {t("contracts.listing.interval", [from, to, total])}
36
39
  </span>
37
40
  <button
38
41
  type="button"
@@ -1,4 +1,5 @@
1
1
  import { BasicDrawer, type BasicDrawerProps } from "../../../shared/components";
2
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
2
3
 
3
4
  export type ErrorRemoveDrawerProps = Omit<BasicDrawerProps, "children"> & {
4
5
  orgUnitName: string;
@@ -10,29 +11,25 @@ export const ErrorRemoveDrawer = ({
10
11
  onRetry,
11
12
  ...drawerProps
12
13
  }: ErrorRemoveDrawerProps) => {
14
+ const { t } = useLocalization();
15
+
13
16
  return (
14
17
  <BasicDrawer {...drawerProps}>
15
18
  <BasicDrawer.Heading
16
- title="Unable to remove contracts"
19
+ title={t("contracts.removeDrawer.unableTitle")}
17
20
  onClose={drawerProps.onDismiss}
18
21
  />
19
22
  <BasicDrawer.Body data-fs-bp-error-remove-drawer-body>
20
23
  {onRetry ? (
21
- <p>
22
- An error occurred while removing the contract. Please try again.
23
- </p>
24
+ <p>{t("contracts.removeDrawer.error")}</p>
24
25
  ) : (
25
26
  <>
26
27
  <p>
27
- Every organizational unit must have at least one contract
28
- available. This ensures that users within{" "}
29
- <strong>{orgUnitName}</strong> can always access store features
30
- like navigation and checkout.
31
- </p>
32
- <p>
33
- To proceed, deselect at least one contract to keep it in this
34
- organizational unit.
28
+ {t("contracts.removeDrawer.minimumContractWarning", [
29
+ <strong key="orgUnitName">{orgUnitName}</strong>,
30
+ ])}
35
31
  </p>
32
+ <p>{t("contracts.removeDrawer.deselectHint")}</p>
36
33
  </>
37
34
  )}
38
35
  </BasicDrawer.Body>
@@ -44,14 +41,14 @@ export const ErrorRemoveDrawer = ({
44
41
  onClick={drawerProps.onDismiss}
45
42
  type="button"
46
43
  >
47
- Cancel
44
+ {t("shared.buttons.cancel")}
48
45
  </BasicDrawer.Button>
49
46
  <BasicDrawer.Button
50
47
  variant="confirm"
51
48
  onClick={onRetry}
52
49
  type="button"
53
50
  >
54
- Try again
51
+ {t("shared.buttons.tryAgain")}
55
52
  </BasicDrawer.Button>
56
53
  </>
57
54
  ) : (
@@ -60,7 +57,7 @@ export const ErrorRemoveDrawer = ({
60
57
  onClick={drawerProps.onDismiss}
61
58
  type="button"
62
59
  >
63
- Got it
60
+ {t("shared.buttons.gotIt")}
64
61
  </BasicDrawer.Button>
65
62
  )}
66
63
  </BasicDrawer.Footer>
@@ -1,5 +1,6 @@
1
1
  import { BasicDrawer, type BasicDrawerProps } from "../../../shared/components";
2
2
  import Icon from "../../../shared/components/Icon/Icon";
3
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
3
4
 
4
5
  export type UnableToRemoveContractsDrawerProps = Omit<
5
6
  BasicDrawerProps,
@@ -17,10 +18,12 @@ export const UnableToRemoveContractsDrawer = ({
17
18
  orgUnitName,
18
19
  ...drawerProps
19
20
  }: UnableToRemoveContractsDrawerProps) => {
21
+ const { t } = useLocalization();
22
+
20
23
  return (
21
24
  <BasicDrawer {...drawerProps}>
22
25
  <BasicDrawer.Heading
23
- title="Unable to remove contracts"
26
+ title={t("contracts.removeDrawer.unableTitle")}
24
27
  onClose={drawerProps.onDismiss}
25
28
  />
26
29
  <BasicDrawer.Body data-fs-bp-unable-to-remove-contracts-drawer-body>
@@ -28,14 +31,14 @@ export const UnableToRemoveContractsDrawer = ({
28
31
  <Icon name="InfoSolid" width={28} height={28} />
29
32
  </span>
30
33
  <p>
31
- Every organizational unit must have at least one contract available.
32
- This ensures that users within <strong>{orgUnitName}</strong> can
33
- always access store features like navigation and checkout.
34
+ {t("contracts.removeDrawer.minimumContractWarning", [
35
+ <strong key="orgUnitName">{orgUnitName}</strong>,
36
+ ])}
34
37
  </p>
35
38
  <p>
36
- To proceed, deselect at least one contract to keep it in this{" "}
37
- <strong>organizational unit</strong>. When an organizational unit has
38
- only one contract, it is automatically set as the default.
39
+ {t("contracts.removeDrawer.unableDeselectHint", [
40
+ <strong key="organizationalUnit">organizational unit</strong>,
41
+ ])}
39
42
  </p>
40
43
  </BasicDrawer.Body>
41
44
  <BasicDrawer.Footer>
@@ -44,7 +47,7 @@ export const UnableToRemoveContractsDrawer = ({
44
47
  onClick={drawerProps.onDismiss}
45
48
  type="button"
46
49
  >
47
- Got it
50
+ {t("shared.buttons.gotIt")}
48
51
  </BasicDrawer.Button>
49
52
  </BasicDrawer.Footer>
50
53
  </BasicDrawer>
@@ -15,6 +15,7 @@ import {
15
15
  import { useBuyerPortal, useDrawerProps } from "../../../shared/hooks";
16
16
  import { useMutation } from "../../../shared/hooks/useMutation";
17
17
  import { GlobalLayout } from "../../../shared/layouts";
18
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
18
19
  import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
19
20
  import { ConfirmRemoveDrawer } from "../../components/ConfirmRemoveDrawer/ConfirmRemoveDrawer";
20
21
  import { ErrorRemoveDrawer } from "../../components/ErrorRemoveDrawer/ErrorRemoveDrawer";
@@ -39,37 +40,39 @@ const ContractInformationNav = ({
39
40
  orgUnitId: string;
40
41
  contractId: string;
41
42
  }) => {
43
+ const { t } = useLocalization();
44
+
42
45
  return (
43
46
  <nav data-fs-bp-contract-information-nav>
44
47
  <ul data-fs-bp-contract-information-nav-list>
45
48
  <li data-fs-bp-contract-information-nav-item-active>
46
- <span>Basic information</span>
49
+ <span>{t("contracts.nav.basicInformation")}</span>
47
50
  </li>
48
51
  <VerticalNav.Link
49
52
  link={buyerPortalRoutes.addresses({ orgUnitId, contractId })}
50
53
  >
51
- Addresses
54
+ {t("layouts.navigation.addresses")}
52
55
  </VerticalNav.Link>
53
56
  <VerticalNav.Link
54
57
  link={buyerPortalRoutes.paymentMethods({ orgUnitId, contractId })}
55
58
  >
56
- Payment methods
59
+ {t("layouts.navigation.paymentMethods")}
57
60
  </VerticalNav.Link>
58
61
  <VerticalNav.Link
59
62
  link={buyerPortalRoutes.creditCards({ orgUnitId, contractId })}
60
63
  >
61
- Credit cards
64
+ {t("layouts.navigation.creditCards")}
62
65
  </VerticalNav.Link>
63
66
  <VerticalNav.Link
64
67
  link={buyerPortalRoutes.productAssortment({ orgUnitId, contractId })}
65
68
  >
66
- Assortment
69
+ {t("contracts.nav.assortment")}
67
70
  </VerticalNav.Link>
68
71
  </ul>
69
72
 
70
73
  <div data-fs-bp-contract-information-nav-section>
71
74
  <span data-fs-bp-contract-information-nav-section-title>
72
- Accounting fields
75
+ {t("layouts.navigation.accountingFields")}
73
76
  </span>
74
77
  <ul data-fs-bp-contract-information-nav-list>
75
78
  <VerticalNav.Link
@@ -79,7 +82,7 @@ const ContractInformationNav = ({
79
82
  accountingFieldId: "cost-centers",
80
83
  })}
81
84
  >
82
- Cost centers
85
+ {t("contracts.nav.costCenters")}
83
86
  </VerticalNav.Link>
84
87
  <VerticalNav.Link
85
88
  link={buyerPortalRoutes.accountingFields({
@@ -88,7 +91,7 @@ const ContractInformationNav = ({
88
91
  accountingFieldId: "departments",
89
92
  })}
90
93
  >
91
- Departments
94
+ {t("contracts.nav.departments")}
92
95
  </VerticalNav.Link>
93
96
  <VerticalNav.Link
94
97
  link={buyerPortalRoutes.accountingFields({
@@ -97,7 +100,7 @@ const ContractInformationNav = ({
97
100
  accountingFieldId: "po-numbers",
98
101
  })}
99
102
  >
100
- PO numbers
103
+ {t("contracts.nav.poNumbers")}
101
104
  </VerticalNav.Link>
102
105
  </ul>
103
106
  </div>
@@ -108,6 +111,7 @@ const ContractInformationNav = ({
108
111
  export const ContractInformationLayout = ({
109
112
  data: { orgUnit, contracts, contractId },
110
113
  }: ContractInformationLayoutProps) => {
114
+ const { t } = useLocalization();
111
115
  const router = useRouter();
112
116
  const { broadcast } = useContractSwitchChannel();
113
117
  const { featureFlags } = useBuyerPortal();
@@ -165,7 +169,7 @@ export const ContractInformationLayout = ({
165
169
  onSuccess: () => {
166
170
  router.replace(router.asPath);
167
171
  pushToast({
168
- message: "Default contract updated successfully",
172
+ message: t("contracts.toasts.setDefaultSuccess"),
169
173
  status: "INFO",
170
174
  });
171
175
  },
@@ -178,8 +182,7 @@ export const ContractInformationLayout = ({
178
182
  // message is not JSON
179
183
  }
180
184
  pushToast({
181
- message:
182
- apiMessage ?? "Failed to set default contract. Please try again.",
185
+ message: apiMessage ?? t("contracts.toasts.setDefaultError"),
183
186
  status: "ERROR",
184
187
  });
185
188
  },
@@ -214,7 +217,7 @@ export const ContractInformationLayout = ({
214
217
  aria-label="Back to contracts"
215
218
  >
216
219
  <Icon name="ArrowPointLeft" width={16} height={16} />
217
- Contracts
220
+ {t("layouts.navigation.contracts")}
218
221
  </Link>
219
222
  )}
220
223
 
@@ -232,7 +235,7 @@ export const ContractInformationLayout = ({
232
235
  disabled
233
236
  >
234
237
  <Icon name="Star" width={16} height={16} />
235
- Set as default
238
+ {t("contracts.actions.setAsDefault")}
236
239
  </DropdownItem>
237
240
  ) : (
238
241
  <DropdownItem
@@ -241,14 +244,14 @@ export const ContractInformationLayout = ({
241
244
  disabled={isSetDefaultContractLoading}
242
245
  >
243
246
  <Icon name="StarOutline" width={16} height={16} />
244
- Set as default
247
+ {t("contracts.actions.setAsDefault")}
245
248
  </DropdownItem>
246
249
  )}
247
250
  <DropdownItem
248
251
  data-fs-bp-contract-information-more-menu-remove
249
252
  onClick={openConfirmDrawer}
250
253
  >
251
- Remove
254
+ {t("shared.buttons.remove")}
252
255
  </DropdownItem>
253
256
  </BasicDropdownMenu>
254
257
  </Dropdown>
@@ -264,24 +267,28 @@ export const ContractInformationLayout = ({
264
267
  <main data-fs-bp-contract-information-content>
265
268
  <section data-fs-bp-contract-information-basic-info>
266
269
  <h2 data-fs-bp-contract-information-section-title>
267
- Basic information
270
+ {t("contracts.nav.basicInformation")}
268
271
  </h2>
269
272
  <div data-fs-bp-contract-information-fields>
270
273
  <div data-fs-bp-contract-information-field>
271
- <span data-fs-bp-contract-information-field-label>Name</span>
274
+ <span data-fs-bp-contract-information-field-label>
275
+ {t("shared.labels.name")}
276
+ </span>
272
277
  <span data-fs-bp-contract-information-field-value>
273
278
  {contract?.name ?? "—"}
274
279
  </span>
275
280
  </div>
276
281
  <div data-fs-bp-contract-information-field>
277
- <span data-fs-bp-contract-information-field-label>Email</span>
282
+ <span data-fs-bp-contract-information-field-label>
283
+ {t("shared.labels.email")}
284
+ </span>
278
285
  <span data-fs-bp-contract-information-field-value>
279
286
  {contract?.email ?? "—"}
280
287
  </span>
281
288
  </div>
282
289
  <div data-fs-bp-contract-information-field>
283
290
  <span data-fs-bp-contract-information-field-label>
284
- Created date
291
+ {t("contracts.details.createdDate")}
285
292
  </span>
286
293
  <span data-fs-bp-contract-information-field-value>
287
294
  {contract?.creationDate ?? "—"}
@@ -21,6 +21,7 @@ import {
21
21
  } from "../../../shared/hooks";
22
22
  import { GlobalLayout } from "../../../shared/layouts";
23
23
  import { SumaSidebar } from "../../../shared/layouts/SumaPageLayout/SumaSidebar";
24
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
24
25
  import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
25
26
  import { AddContractsDrawer } from "../../components/AddContractsDrawer/AddContractsDrawer";
26
27
  import { ConfirmRemoveDrawer } from "../../components/ConfirmRemoveDrawer/ConfirmRemoveDrawer";
@@ -54,24 +55,11 @@ export type ContractListingLayoutProps = {
54
55
  loading?: boolean;
55
56
  };
56
57
 
57
- const headerInfo = (
58
- <Tooltip
59
- content="Contracts define the commercial rules applied during purchasing, such as addresses, payment methods, credit cards and other checkout settings."
60
- placement="right-center"
61
- >
62
- <Icon
63
- name="InfoFilled"
64
- width={20}
65
- height={20}
66
- data-fs-bp-contract-listing-info-icon
67
- />
68
- </Tooltip>
69
- );
70
-
71
58
  export const ContractListingLayout = ({
72
59
  data: { orgUnit, contracts, user },
73
60
  loading = false,
74
61
  }: ContractListingLayoutProps) => {
62
+ const { t } = useLocalization();
75
63
  const isSingleContract = contracts.length <= 1;
76
64
  const router = useRouter();
77
65
  const { pushToast } = useUI();
@@ -145,8 +133,7 @@ export const ContractListingLayout = ({
145
133
  (switched) => {
146
134
  if (!switched) {
147
135
  pushToast({
148
- message:
149
- "Contracts removed, but we couldn't refresh your active contract. Please reload the page.",
136
+ message: t("contracts.toasts.refreshError"),
150
137
  status: "ERROR",
151
138
  });
152
139
  router.replace(router.asPath);
@@ -159,9 +146,10 @@ export const ContractListingLayout = ({
159
146
 
160
147
  router.replace(router.asPath);
161
148
  pushToast({
162
- message: `${pendingRemoveIds.size} contract${
163
- pendingRemoveIds.size === 1 ? "" : "s"
164
- } removed successfully`,
149
+ message: t("contracts.toasts.removeSuccess", [
150
+ pendingRemoveIds.size,
151
+ pendingRemoveIds.size === 1 ? "" : "s",
152
+ ]),
165
153
  status: "INFO",
166
154
  });
167
155
  },
@@ -175,7 +163,7 @@ export const ContractListingLayout = ({
175
163
  }
176
164
  confirmRemoveDrawer.close();
177
165
  pushToast({
178
- message: apiMessage ?? "Failed to remove contracts. Please try again.",
166
+ message: apiMessage ?? t("contracts.toasts.removeError"),
179
167
  status: "ERROR",
180
168
  });
181
169
  },
@@ -206,7 +194,7 @@ export const ContractListingLayout = ({
206
194
  onSuccess: () => {
207
195
  router.replace(router.asPath);
208
196
  pushToast({
209
- message: "Default contract updated successfully",
197
+ message: t("contracts.toasts.setDefaultSuccess"),
210
198
  status: "INFO",
211
199
  });
212
200
  },
@@ -220,8 +208,7 @@ export const ContractListingLayout = ({
220
208
  }
221
209
  void router.replace(router.asPath).catch(() => {});
222
210
  pushToast({
223
- message:
224
- apiMessage ?? "Failed to set default contract. Please try again.",
211
+ message: apiMessage ?? t("contracts.toasts.setDefaultError"),
225
212
  status: "ERROR",
226
213
  });
227
214
  },
@@ -243,7 +230,7 @@ export const ContractListingLayout = ({
243
230
  useEffect(() => {
244
231
  if (router.query.error === "contract-load-failed") {
245
232
  pushToast({
246
- message: "Failed to load contract information.",
233
+ message: t("contracts.toasts.loadError"),
247
234
  status: "ERROR",
248
235
  });
249
236
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -303,6 +290,20 @@ export const ContractListingLayout = ({
303
290
  }
304
291
  };
305
292
 
293
+ const headerInfo = (
294
+ <Tooltip
295
+ content={t("contracts.listing.infoTooltip")}
296
+ placement="right-center"
297
+ >
298
+ <Icon
299
+ name="InfoFilled"
300
+ width={20}
301
+ height={20}
302
+ data-fs-bp-contract-listing-info-icon
303
+ />
304
+ </Tooltip>
305
+ );
306
+
306
307
  return (
307
308
  <GlobalLayout>
308
309
  <OrgUnitDetailsNavbar
@@ -325,7 +326,7 @@ export const ContractListingLayout = ({
325
326
 
326
327
  <section data-fs-bp-contract-listing-section>
327
328
  <HeaderInside
328
- title="Contracts"
329
+ title={t("contracts.titles.contracts")}
329
330
  info={headerInfo}
330
331
  data-fs-bp-contract-listing-header
331
332
  >
@@ -340,7 +341,7 @@ export const ContractListingLayout = ({
340
341
  {isSingleContract ? (
341
342
  <BasicCard
342
343
  data-fs-bp-contracts-settings-card
343
- footerMessage="Manage contract settings"
344
+ footerMessage={t("orgUnitDetails.links.manageContractSettings")}
344
345
  footerLink={buyerPortalRoutes.profileDetails({
345
346
  orgUnitId: orgUnit.id,
346
347
  contractId: defaultContractId,
@@ -369,6 +370,7 @@ export const ContractListingLayout = ({
369
370
  accountingFieldsLoading: listAccountingFieldLoading,
370
371
  onAddAccountingField: openAccountingFieldDrawer,
371
372
  onAccountingFieldChange: listAccountingFieldRefresh,
373
+ t,
372
374
  })}
373
375
  />
374
376
  )}
@@ -6,6 +6,7 @@ import { Icon } from "../../../shared/components";
6
6
  import { buyerPortalRoutes } from "../../../shared/utils/buyerPortalRoutes";
7
7
 
8
8
  import type { AccountingField } from "../../../accounting-fields/types";
9
+ import type { LocalizeFn } from "../../../shared/localization/types";
9
10
  import type { VerticalNavTreeItemProps } from "../../../shared/types";
10
11
 
11
12
  export const ICON_SIZE = 20;
@@ -17,6 +18,7 @@ export type BuildContractSettingsNavItemsParams = {
17
18
  accountingFieldsLoading?: boolean;
18
19
  onAddAccountingField: () => void;
19
20
  onAccountingFieldChange: () => void;
21
+ t: LocalizeFn;
20
22
  };
21
23
 
22
24
  export const buildContractSettingsNavItems = ({
@@ -26,32 +28,33 @@ export const buildContractSettingsNavItems = ({
26
28
  accountingFieldsLoading = false,
27
29
  onAddAccountingField,
28
30
  onAccountingFieldChange,
31
+ t,
29
32
  }: BuildContractSettingsNavItemsParams): VerticalNavTreeItemProps[] => {
30
33
  const linkParams = { orgUnitId, contractId };
31
34
 
32
35
  return [
33
36
  {
34
- name: "Basic information",
37
+ name: t("contracts.nav.basicInformation"),
35
38
  link: buyerPortalRoutes.profileDetails(linkParams),
36
39
  },
37
40
  {
38
- name: "Addresses",
41
+ name: t("layouts.navigation.addresses"),
39
42
  link: buyerPortalRoutes.addresses(linkParams),
40
43
  },
41
44
  {
42
- name: "Payment methods",
45
+ name: t("layouts.navigation.paymentMethods"),
43
46
  link: buyerPortalRoutes.paymentMethods(linkParams),
44
47
  },
45
48
  {
46
- name: "Credit cards",
49
+ name: t("layouts.navigation.creditCards"),
47
50
  link: buyerPortalRoutes.creditCards(linkParams),
48
51
  },
49
52
  {
50
- name: "Assortment",
53
+ name: t("contracts.nav.assortment"),
51
54
  link: buyerPortalRoutes.productAssortment(linkParams),
52
55
  },
53
56
  {
54
- name: "Accounting fields",
57
+ name: t("layouts.navigation.accountingFields"),
55
58
  loading: accountingFieldsLoading,
56
59
  submenu: {
57
60
  items: accountingFields.map((field) => ({
@@ -22,4 +22,4 @@ export const SCOPE_KEYS = {
22
22
  CREDIT_CARDS: "creditCards",
23
23
  } as const;
24
24
 
25
- export const CURRENT_VERSION = "2.0.20";
25
+ export const CURRENT_VERSION = "2.0.21";
@@ -13,6 +13,7 @@ import { Skeleton, Tag as RemovableTag } from "@faststore/ui";
13
13
  import { Tag } from "../../../shared/components";
14
14
  import { useAutocompletePosition } from "../../../shared/components/AutocompleteDropdown/useAutocompletePosition";
15
15
  import { useDropdownFlipPosition } from "../../../shared/components/AutocompleteDropdown/useDropdownFlipPosition";
16
+ import { useLocalization } from "../../../shared/localization/LocalizationContext";
16
17
 
17
18
  import {
18
19
  filterRoleOptions,
@@ -43,6 +44,7 @@ export const RolesMultiSelect = ({
43
44
  isLoading = false,
44
45
  mode,
45
46
  }: RolesMultiSelectProps) => {
47
+ const { t } = useLocalization();
46
48
  const listboxId = useId();
47
49
  const roleOptions = useMemo(() => options ?? [], [options]);
48
50
 
@@ -236,11 +238,9 @@ export const RolesMultiSelect = ({
236
238
  onKeyDown={handleKeyDown}
237
239
  />
238
240
 
239
- {/* No CMS localization key exists yet for this floating label (see
240
- Decision 6 in the spec) — hardcoded for the same reason as "Custom"
241
- and "No matching roles" below: t() has no fallback and would render
242
- the raw key string if missing. */}
243
- <label data-fs-bp-roles-multi-select-label>Roles</label>
241
+ <label data-fs-bp-roles-multi-select-label>
242
+ {t("users.forms.roles")}
243
+ </label>
244
244
  </div>
245
245
 
246
246
  {isOpen && (
@@ -254,10 +254,9 @@ export const RolesMultiSelect = ({
254
254
  style={{ width: positionStyle.width }}
255
255
  >
256
256
  {filteredOptions.length === 0 ? (
257
- // "No matching roles" has no CMS localization key yet (like the
258
- // "Custom" tag below) — hardcoded for the same reason: t() has no
259
- // fallback and would render the raw key string if missing.
260
- <li data-fs-bp-roles-multi-select-empty>No matching roles</li>
257
+ <li data-fs-bp-roles-multi-select-empty>
258
+ {t("users.rolesMultiSelect.noMatchingRoles")}
259
+ </li>
261
260
  ) : (
262
261
  filteredOptions.map((role, index) => {
263
262
  const isChecked = selected.includes(role.roleId);
@@ -297,7 +296,7 @@ export const RolesMultiSelect = ({
297
296
  </span>
298
297
  {role.isCustom && (
299
298
  <Tag data-fs-bp-roles-multi-select-option-custom-tag>
300
- Custom
299
+ {t("users.rolesMultiSelect.customTag")}
301
300
  </Tag>
302
301
  )}
303
302
  </div>