@vtex/faststore-plugin-buyer-portal 2.0.19 → 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 (28) hide show
  1. package/CHANGELOG.md +14 -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/specs/analytics-actor-type.md +243 -0
  6. package/src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx +28 -15
  7. package/src/features/contracts/components/ConfirmRemoveDrawer/ConfirmRemoveDrawer.tsx +15 -12
  8. package/src/features/contracts/components/ContractListItem/ContractListItem.tsx +6 -4
  9. package/src/features/contracts/components/ContractSelectAllRow/ContractSelectAllRow.tsx +5 -1
  10. package/src/features/contracts/components/ContractSelectionBar/ContractSelectionBar.tsx +5 -2
  11. package/src/features/contracts/components/ContractSelectionList/ContractSelectionList.tsx +4 -1
  12. package/src/features/contracts/components/ContractSwitchOverlay/ContractSwitchOverlay.tsx +5 -2
  13. package/src/features/contracts/components/ContractsListHeader/ContractsListHeader.tsx +4 -1
  14. package/src/features/contracts/components/ErrorRemoveDrawer/ErrorRemoveDrawer.tsx +12 -15
  15. package/src/features/contracts/components/UnableToRemoveContractsDrawer/UnableToRemoveContractsDrawer.tsx +11 -8
  16. package/src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx +27 -20
  17. package/src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx +28 -26
  18. package/src/features/contracts/layouts/ContractListingLayout/ContractSettingsNav.tsx +9 -6
  19. package/src/features/shared/hooks/analytics/useAnalytics.ts +1 -1
  20. package/src/features/shared/services/logger/analytics/__tests__/deriveActorType.test.ts +30 -0
  21. package/src/features/shared/services/logger/analytics/__tests__/mergeActorTypeIntoEvent.test.ts +83 -0
  22. package/src/features/shared/services/logger/analytics/analytics.ts +2 -24
  23. package/src/features/shared/services/logger/analytics/deriveActorType.ts +24 -0
  24. package/src/features/shared/services/logger/analytics/mergeActorTypeIntoEvent.ts +29 -0
  25. package/src/features/shared/services/logger/analytics/types.ts +5 -0
  26. package/src/features/shared/services/logger/analytics/useDataIngestionApi.ts +32 -0
  27. package/src/features/shared/utils/constants.ts +1 -1
  28. package/src/features/users/components/RolesMultiSelect/RolesMultiSelect.tsx +9 -10
@@ -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) => ({