@vtex/faststore-plugin-buyer-portal 2.0.29 → 2.0.31

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 (63) hide show
  1. package/package.json +3 -1
  2. package/src/features/addresses/components/AddressDropdownMenu/AddressDropdownMenu.tsx +1 -1
  3. package/src/features/addresses/components/AddressesCard/AddressesCard.tsx +1 -1
  4. package/src/features/buying-policies/components/AccountingFieldCriteriaSelector/AccountingFieldCriteriaSelector.tsx +5 -2
  5. package/src/features/buying-policies/components/AccountingFieldCriteriaSelector/AccountingValueCriteriaSelector.tsx +8 -3
  6. package/src/features/buying-policies/components/BudgetCriteriaSelector/BudgetCriteriaSelector.tsx +5 -2
  7. package/src/features/contracts/components/AddContractsDrawer/AddContractsDrawer.tsx +12 -1
  8. package/src/features/contracts/components/ContractListItem/ContractListItem.tsx +19 -11
  9. package/src/features/contracts/components/ContractListItem/__tests__/contractListItemMenu.test.ts +32 -0
  10. package/src/features/contracts/components/ContractListItem/contractListItemMenu.ts +17 -0
  11. package/src/features/contracts/components/ContractSelectionList/ContractSelectionListSkeleton.tsx +35 -0
  12. package/src/features/contracts/components/ContractSwitchOverlay/ContractSwitchOverlay.tsx +9 -1
  13. package/src/features/contracts/components/UnableToRemoveContractsDrawer/UnableToRemoveContractsDrawer.tsx +1 -0
  14. package/src/features/contracts/components/index.ts +2 -0
  15. package/src/features/contracts/hooks/useSwitchContract.ts +6 -4
  16. package/src/features/contracts/layouts/ContractInformationLayout/ContractInformationLayout.tsx +9 -12
  17. package/src/features/contracts/layouts/ContractListingLayout/ContractListingLayout.tsx +90 -41
  18. package/src/features/contracts/layouts/ContractListingLayout/__tests__/utils.test.ts +40 -1
  19. package/src/features/contracts/layouts/ContractListingLayout/contract-listing-layout.scss +10 -2
  20. package/src/features/contracts/layouts/ContractListingLayout/utils.ts +25 -0
  21. package/src/features/contracts/utils/__tests__/clearPersistedSessionState.test.ts +32 -0
  22. package/src/features/contracts/utils/__tests__/resolveSwitchRedirect.test.ts +21 -0
  23. package/src/features/contracts/utils/clearPersistedSessionState.ts +43 -5
  24. package/src/features/contracts/utils/resolveSwitchRedirect.ts +15 -0
  25. package/src/features/org-units/components/OrgUnitBreadcrumb/OrgUnitBreadcrumb.tsx +21 -31
  26. package/src/features/org-units/components/OrgUnitBreadcrumb/__tests__/buildBreadcrumbItems.test.ts +64 -0
  27. package/src/features/org-units/components/OrgUnitBreadcrumb/buildBreadcrumbItems.ts +36 -0
  28. package/src/features/org-units/components/OrgUnitDetailsNavbar/OrgUnitDetailsNavbar.tsx +67 -27
  29. package/src/features/org-units/components/OrgUnitDetailsNavbar/org-unit-details-navbar.scss +22 -1
  30. package/src/features/org-units/components/OrgUnitHeaderNav/OrgUnitHeaderNav.tsx +112 -0
  31. package/src/features/org-units/components/OrgUnitHeaderNav/__tests__/getOrgUnitHeaderNavItems.test.ts +83 -0
  32. package/src/features/org-units/components/OrgUnitHeaderNav/getOrgUnitHeaderNavItems.ts +36 -0
  33. package/src/features/org-units/components/OrgUnitHeaderNav/org-unit-header-nav.scss +129 -0
  34. package/src/features/org-units/components/index.ts +4 -0
  35. package/src/features/org-units/layouts/OrgUnitDetailsLayout/org-units-details.scss +1 -0
  36. package/src/features/org-units/types/OrgUnitBreadcrumbTypes.ts +8 -0
  37. package/src/features/profile/layouts/ProfileLayout/ProfileLayout.tsx +8 -31
  38. package/src/features/shared/components/LocalizationEslintIgnore/LocalizationEslintIgnore.tsx +12 -0
  39. package/src/features/shared/components/index.ts +4 -0
  40. package/src/features/shared/hooks/index.ts +1 -0
  41. package/src/features/shared/hooks/useRefreshPageData.ts +35 -0
  42. package/src/features/shared/layouts/BaseTabsLayout/about-drawer/__tests__/aboutQueryParam.test.ts +23 -0
  43. package/src/features/shared/layouts/BaseTabsLayout/about-drawer/aboutQueryParam.ts +13 -0
  44. package/src/features/shared/layouts/ContractTabsLayout/ContractTabsLayout.tsx +2 -2
  45. package/src/features/shared/layouts/ErrorTabsLayout/ErrorTabsLayout.tsx +6 -1
  46. package/src/features/shared/layouts/LoadingTabsLayout/LoadingTabsLayout.tsx +35 -2
  47. package/src/features/shared/layouts/SumaPageLayout/FullSidebarNav.tsx +33 -12
  48. package/src/features/shared/layouts/SumaPageLayout/SumaPageLayout.tsx +4 -2
  49. package/src/features/shared/layouts/SumaPageLayout/SumaSidebar.tsx +27 -18
  50. package/src/features/shared/layouts/SumaPageLayout/__tests__/resolveOrgLinkContractId.test.ts +16 -0
  51. package/src/features/shared/layouts/SumaPageLayout/__tests__/sumaSidebarConfig.test.ts +27 -0
  52. package/src/features/shared/layouts/SumaPageLayout/resolveOrgLinkContractId.ts +13 -0
  53. package/src/features/shared/layouts/SumaPageLayout/sumaSidebarConfig.ts +37 -0
  54. package/src/features/shared/utils/__tests__/getContractSettingsLinks.test.ts +18 -0
  55. package/src/features/shared/utils/__tests__/getSumaListingSidebarLinks.test.ts +43 -0
  56. package/src/features/shared/utils/__tests__/routeLayoutMapping.test.ts +34 -0
  57. package/src/features/shared/utils/constants.ts +1 -1
  58. package/src/features/shared/utils/getContractSettingsLinks.ts +6 -1
  59. package/src/features/shared/utils/routeLayoutMapping.ts +9 -1
  60. package/src/pages/contract-information.tsx +9 -1
  61. package/src/pages/contracts.tsx +5 -0
  62. package/src/pages/home.tsx +25 -19
  63. package/src/pages/org-unit-details.tsx +10 -24
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "2.0.29",
3
+ "version": "2.0.31",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -16,6 +16,8 @@
16
16
  "test": "vitest run",
17
17
  "test:watch": "vitest",
18
18
  "test:coverage": "vitest run --coverage",
19
+ "i18n:check": "node scripts/check-localization-keys.mjs check --base=main",
20
+ "i18n:refresh": "node scripts/check-localization-keys.mjs refresh",
19
21
  "prepare": "husky",
20
22
  "release": "yarn release:stable",
21
23
  "release:stable": "yarn check --integrity && yarn release-it -c .release-it.json --ci --no-npm",
@@ -192,7 +192,7 @@ ${t("shared.messages.changesTimeout")}`,
192
192
  <BasicDropdownMenu data-fs-bp-dropdown-menu>
193
193
  <DropdownItem onClick={handleOpenNewTab}>
194
194
  <Icon name="OpenNewTab" {...sizeProps} />
195
- Open
195
+ {t("shared.buttons.open")}
196
196
  </DropdownItem>
197
197
  {!isAddressEditingRestricted && (
198
198
  <>
@@ -67,7 +67,7 @@ export default function AddressesCard({
67
67
  <DropdownMenu>
68
68
  <DropdownItem data-fs-card-dropdown-item>
69
69
  <Icon name="ArrowSquareOut" data-fs-dropdown-icon />
70
- Open
70
+ {t("shared.buttons.open")}
71
71
  </DropdownItem>
72
72
  <DropdownItem>
73
73
  <Icon
@@ -4,6 +4,7 @@ import { useUI } from "@faststore/ui";
4
4
 
5
5
  import { useListAccountingFields } from "../../../accounting-fields/hooks";
6
6
  import { CustomDropdown } from "../../../shared/components/CustomDropdown/CustomDropdown";
7
+ import { LocalizationEslintIgnore } from "../../../shared/components/LocalizationEslintIgnore/LocalizationEslintIgnore";
7
8
  import { useBuyerPortal } from "../../../shared/hooks";
8
9
  import { useLocalization } from "../../../shared/localization/LocalizationContext";
9
10
  import {
@@ -91,7 +92,9 @@ export const AccountingFieldCriteriaSelector = ({
91
92
  <div data-fs-bp-accounting-field-criteria-selector>
92
93
  <div data-fs-bp-accounting-fields-criteria-string>
93
94
  <span onClick={handleEditablePartClick} data-fs-bp-editable-part>
94
- $exists(customData.customFields.fields[customFieldId="
95
+ <LocalizationEslintIgnore>
96
+ $exists(customData.customFields.fields[customFieldId="
97
+ </LocalizationEslintIgnore>
95
98
  </span>
96
99
 
97
100
  <div onClick={handleFieldClick} style={{ display: "inline-block" }}>
@@ -115,7 +118,7 @@ export const AccountingFieldCriteriaSelector = ({
115
118
  </div>
116
119
 
117
120
  <span onClick={handleEditablePartClick} data-fs-bp-editable-part>
118
- "].value)
121
+ <LocalizationEslintIgnore>"].value)</LocalizationEslintIgnore>
119
122
  </span>
120
123
  </div>
121
124
  </div>
@@ -8,6 +8,7 @@ import {
8
8
  useListAccountingValues,
9
9
  } from "../../../accounting-fields/hooks";
10
10
  import { CustomDropdown } from "../../../shared/components/CustomDropdown/CustomDropdown";
11
+ import { LocalizationEslintIgnore } from "../../../shared/components/LocalizationEslintIgnore/LocalizationEslintIgnore";
11
12
  import { useBuyerPortal, useDebounce } from "../../../shared/hooks";
12
13
  import { useLocalization } from "../../../shared/localization/LocalizationContext";
13
14
  import {
@@ -216,7 +217,9 @@ export const AccountingValueCriteriaSelector = ({
216
217
  <div data-fs-bp-accounting-field-criteria-selector>
217
218
  <div data-fs-bp-accounting-fields-criteria-string>
218
219
  <span onClick={handleEditablePartClick} data-fs-bp-editable-part>
219
- customData.customFields.fields[customFieldId="
220
+ <LocalizationEslintIgnore>
221
+ customData.customFields.fields[customFieldId="
222
+ </LocalizationEslintIgnore>
220
223
  </span>
221
224
 
222
225
  <div onClick={handleFieldClick} style={{ display: "inline-block" }}>
@@ -244,7 +247,9 @@ export const AccountingValueCriteriaSelector = ({
244
247
  </div>
245
248
 
246
249
  <span onClick={handleEditablePartClick} data-fs-bp-editable-part>
247
- "].customFieldValueId="
250
+ <LocalizationEslintIgnore>
251
+ "].customFieldValueId="
252
+ </LocalizationEslintIgnore>
248
253
  </span>
249
254
 
250
255
  <div onClick={handleValueClick} style={{ display: "inline-block" }}>
@@ -275,7 +280,7 @@ export const AccountingValueCriteriaSelector = ({
275
280
  </div>
276
281
 
277
282
  <span onClick={handleEditablePartClick} data-fs-bp-editable-part>
278
- "
283
+ <LocalizationEslintIgnore>"</LocalizationEslintIgnore>
279
284
  </span>
280
285
  </div>
281
286
  </div>
@@ -1,6 +1,7 @@
1
1
  import { useState, useEffect } from "react";
2
2
 
3
3
  import { CustomDropdown } from "../../../shared/components/CustomDropdown/CustomDropdown";
4
+ import { LocalizationEslintIgnore } from "../../../shared/components/LocalizationEslintIgnore/LocalizationEslintIgnore";
4
5
  import { useBuyerPortal } from "../../../shared/hooks";
5
6
  import { useLocalization } from "../../../shared/localization/LocalizationContext";
6
7
  import { useGetBudgets } from "../../hooks/useGetBudgets";
@@ -90,7 +91,9 @@ export const BudgetCriteriaSelector = ({
90
91
  <div data-fs-bp-budget-criteria-selector>
91
92
  <div data-fs-bp-budget-criteria-string>
92
93
  <span onClick={handleEditablePartClick} data-fs-bp-editable-part>
93
- $exists(budgetData.budgets[id="
94
+ <LocalizationEslintIgnore>
95
+ $exists(budgetData.budgets[id="
96
+ </LocalizationEslintIgnore>
94
97
  </span>
95
98
 
96
99
  <div onClick={handleBudgetValueClick}>
@@ -115,7 +118,7 @@ export const BudgetCriteriaSelector = ({
115
118
  </div>
116
119
 
117
120
  <span onClick={handleEditablePartClick} data-fs-bp-editable-part>
118
- "])
121
+ <LocalizationEslintIgnore>"])</LocalizationEslintIgnore>
119
122
  </span>
120
123
  </div>
121
124
  </div>
@@ -1,4 +1,4 @@
1
- import { useEffect, useMemo, useState } from "react";
1
+ import { useEffect, useMemo, useRef, useState } from "react";
2
2
 
3
3
  import { useUI } from "@faststore/ui";
4
4
 
@@ -15,6 +15,7 @@ import {
15
15
  buildRangeSet,
16
16
  buildToggleSet,
17
17
  } from "../../hooks/contractSelectionHelpers";
18
+ import { pluralSuffixArgs } from "../../layouts/ContractListingLayout/utils";
18
19
 
19
20
  export type AddContractsDrawerProps = Omit<BasicDrawerProps, "children"> & {
20
21
  orgUnitId: string;
@@ -43,10 +44,19 @@ export const AddContractsDrawer = ({
43
44
  fetchAvailableContracts,
44
45
  } = useListAvailableContracts(orgUnitId);
45
46
 
47
+ const pendingAddCountRef = useRef(0);
48
+
46
49
  const { addContracts, isAddContractsLoading } = useAddContracts({
47
50
  onSuccess: () => {
48
51
  onSuccess();
49
52
  drawerProps.onDismiss?.();
53
+ pushToast({
54
+ message: t(
55
+ "contracts.toasts.addSuccess",
56
+ pluralSuffixArgs(pendingAddCountRef.current)
57
+ ),
58
+ status: "INFO",
59
+ });
50
60
  },
51
61
  onError: () => {
52
62
  drawerProps.onDismiss?.();
@@ -129,6 +139,7 @@ export const AddContractsDrawer = ({
129
139
  };
130
140
 
131
141
  const handleConfirm = () => {
142
+ pendingAddCountRef.current = selectedIds.size;
132
143
  addContracts({
133
144
  orgUnitId,
134
145
  ids: Array.from(selectedIds),
@@ -6,6 +6,8 @@ import { useLocalization } from "../../../shared/localization/LocalizationContex
6
6
  import { useSwitchContract } from "../../hooks";
7
7
  import { ContractSwitchOverlay } from "../ContractSwitchOverlay/ContractSwitchOverlay";
8
8
 
9
+ import { getContractMenuState } from "./contractListItemMenu";
10
+
9
11
  import type { ContractData } from "../../types";
10
12
 
11
13
  export type ContractListItemProps = {
@@ -30,6 +32,10 @@ export const ContractListItem = ({
30
32
  const { pushToast } = useUI();
31
33
  const { switchContract, loading } = useSwitchContract();
32
34
  const isRemovable = contract.isAttached !== false;
35
+ const menuState = getContractMenuState({
36
+ isDefault: contract.isDefault,
37
+ isRemovable,
38
+ });
33
39
 
34
40
  const handleCheckboxClick = (event: React.MouseEvent) => {
35
41
  event.stopPropagation();
@@ -97,18 +103,20 @@ export const ContractListItem = ({
97
103
  <Dropdown>
98
104
  <BasicDropdownMenu.Trigger />
99
105
  <BasicDropdownMenu>
100
- {contract.isDefault || !isRemovable ? (
101
- <DropdownItem disabled>
102
- <Icon name="Star" width={16} height={16} />
103
- {t("contracts.actions.setAsDefault")}
104
- </DropdownItem>
105
- ) : (
106
- <DropdownItem onClick={onSetDefault}>
107
- <Icon name="StarOutline" width={16} height={16} />
108
- {t("contracts.actions.setAsDefault")}
109
- </DropdownItem>
106
+ {menuState.showSetDefault && (
107
+ <>
108
+ <DropdownItem
109
+ onClick={
110
+ menuState.setDefaultEnabled ? onSetDefault : undefined
111
+ }
112
+ disabled={!menuState.setDefaultEnabled}
113
+ >
114
+ <Icon name="StarOutline" width={16} height={16} />
115
+ {t("contracts.actions.setAsDefault")}
116
+ </DropdownItem>
117
+ <BasicDropdownMenu.Separator />
118
+ </>
110
119
  )}
111
- <BasicDropdownMenu.Separator />
112
120
  <DropdownItem
113
121
  data-fs-bp-dropdown-menu-item-mode="danger"
114
122
  disabled={!isRemovable}
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import { getContractMenuState } from "../contractListItemMenu";
4
+
5
+ describe("getContractMenuState", () => {
6
+ it("hides 'Set as default' when the contract is already the default", () => {
7
+ expect(
8
+ getContractMenuState({ isDefault: true, isRemovable: true })
9
+ ).toEqual({
10
+ showSetDefault: false,
11
+ setDefaultEnabled: false,
12
+ });
13
+ });
14
+
15
+ it("shows and enables 'Set as default' for a removable non-default contract", () => {
16
+ expect(
17
+ getContractMenuState({ isDefault: false, isRemovable: true })
18
+ ).toEqual({
19
+ showSetDefault: true,
20
+ setDefaultEnabled: true,
21
+ });
22
+ });
23
+
24
+ it("shows but disables 'Set as default' for a legacy (non-attached) contract", () => {
25
+ expect(
26
+ getContractMenuState({ isDefault: undefined, isRemovable: false })
27
+ ).toEqual({
28
+ showSetDefault: true,
29
+ setDefaultEnabled: false,
30
+ });
31
+ });
32
+ });
@@ -0,0 +1,17 @@
1
+ export type ContractMenuState = {
2
+ /** The default contract never offers "Set as default" (AS-8). */
3
+ showSetDefault: boolean;
4
+ /** Legacy non-attached contracts keep the item visible but disabled. */
5
+ setDefaultEnabled: boolean;
6
+ };
7
+
8
+ export const getContractMenuState = ({
9
+ isDefault,
10
+ isRemovable,
11
+ }: {
12
+ isDefault?: boolean;
13
+ isRemovable: boolean;
14
+ }): ContractMenuState => ({
15
+ showSetDefault: !isDefault,
16
+ setDefaultEnabled: !isDefault && isRemovable,
17
+ });
@@ -0,0 +1,35 @@
1
+ import { Skeleton } from "@faststore/ui";
2
+
3
+ export type ContractSelectionListSkeletonProps = {
4
+ /** Number of placeholder rows; defaults to a typical page. */
5
+ rows?: number;
6
+ };
7
+
8
+ /**
9
+ * Placeholder rows shaped like `ContractListItem`, shown while the listing
10
+ * loads (route transition) or refreshes after adding/removing contracts.
11
+ */
12
+ export const ContractSelectionListSkeleton = ({
13
+ rows = 4,
14
+ }: ContractSelectionListSkeletonProps) => (
15
+ <div
16
+ data-fs-bp-contract-selection-list
17
+ data-fs-bp-contract-selection-list-skeleton
18
+ aria-busy="true"
19
+ aria-live="polite"
20
+ >
21
+ <div data-fs-bp-contract-selection-list-rows>
22
+ {Array.from({ length: Math.max(rows, 1) }, (_, index) => (
23
+ <div
24
+ data-fs-bp-contract-list-item
25
+ data-fs-bp-contract-list-item-skeleton
26
+ key={`contract-skeleton-${index}`}
27
+ >
28
+ <Skeleton size={{ width: "1.25rem", height: "1.25rem" }} />
29
+ <Skeleton size={{ width: "2rem", height: "2rem" }} />
30
+ <Skeleton size={{ width: "14rem", height: "1.25rem" }} />
31
+ </div>
32
+ ))}
33
+ </div>
34
+ </div>
35
+ );
@@ -1,3 +1,4 @@
1
+ import { useBuyerPortal } from "../../../shared/hooks";
1
2
  import { ContractTabsLayout } from "../../../shared/layouts/ContractTabsLayout/ContractTabsLayout";
2
3
  import { LoadingTabsLayoutContent } from "../../../shared/layouts/LoadingTabsLayout/LoadingTabsLayout";
3
4
  import { useLocalization } from "../../../shared/localization/LocalizationContext";
@@ -21,6 +22,7 @@ export const ContractSwitchOverlay = ({
21
22
  contractId,
22
23
  }: ContractSwitchOverlayProps) => {
23
24
  const { t } = useLocalization();
25
+ const { featureFlags } = useBuyerPortal();
24
26
 
25
27
  return (
26
28
  <div data-fs-bp-contract-switch-overlay role="alert" aria-busy="true">
@@ -32,7 +34,13 @@ export const ContractSwitchOverlay = ({
32
34
  pageName={t("layouts.tabs.contract")}
33
35
  loading
34
36
  >
35
- <LoadingTabsLayoutContent title={t("layouts.titles.profile")} />
37
+ <LoadingTabsLayoutContent
38
+ title={t(
39
+ featureFlags?.suma
40
+ ? "layouts.navigation.basicInformation"
41
+ : "layouts.titles.profile"
42
+ )}
43
+ />
36
44
  </ContractTabsLayout>
37
45
  </div>
38
46
  );
@@ -37,6 +37,7 @@ export const UnableToRemoveContractsDrawer = ({
37
37
  </p>
38
38
  <p>
39
39
  {t("contracts.removeDrawer.unableDeselectHint", [
40
+ // eslint-disable-next-line no-restricted-syntax -- legacy hardcoded string, pending migration (see specs/custom-localization-system.md#out-of-scope)
40
41
  <strong key="organizationalUnit">organizational unit</strong>,
41
42
  ])}
42
43
  </p>
@@ -3,6 +3,8 @@ export { ContractListItem } from "./ContractListItem/ContractListItem";
3
3
  export type { ContractListItemProps } from "./ContractListItem/ContractListItem";
4
4
  export { ContractSelectionList } from "./ContractSelectionList/ContractSelectionList";
5
5
  export type { ContractSelectionListProps } from "./ContractSelectionList/ContractSelectionList";
6
+ export { ContractSelectionListSkeleton } from "./ContractSelectionList/ContractSelectionListSkeleton";
7
+ export type { ContractSelectionListSkeletonProps } from "./ContractSelectionList/ContractSelectionListSkeleton";
6
8
  export { ConfirmRemoveDrawer } from "./ConfirmRemoveDrawer/ConfirmRemoveDrawer";
7
9
  export type { ConfirmRemoveDrawerProps } from "./ConfirmRemoveDrawer/ConfirmRemoveDrawer";
8
10
  export { ErrorRemoveDrawer } from "./ErrorRemoveDrawer/ErrorRemoveDrawer";
@@ -1,16 +1,18 @@
1
1
  import { useState } from "react";
2
2
 
3
- import { buyerPortalRoutes } from "../../shared/utils/buyerPortalRoutes";
4
3
  import {
5
4
  ContractSwitchError,
6
5
  changeContractToken,
7
6
  } from "../utils/changeContractToken";
8
7
  import { clearPersistedSessionState } from "../utils/clearPersistedSessionState";
8
+ import { resolveSwitchRedirect } from "../utils/resolveSwitchRedirect";
9
9
 
10
10
  import { useContractSwitchChannel } from "./useContractSwitchChannel";
11
11
 
12
12
  export type SwitchContractParams = {
13
13
  orgUnitId: string;
14
+ /** Where to land after the switch. Defaults to the contract's Profile page. */
15
+ redirectTo?: string;
14
16
  };
15
17
 
16
18
  export type UseSwitchContractReturn = {
@@ -28,7 +30,7 @@ export type UseSwitchContractReturn = {
28
30
  * 1. POST switch-properties with the target contract id and apply the returned auth cookie.
29
31
  * 2. Clear persisted host session state.
30
32
  * 3. Broadcast to other tabs so they reload under the new context.
31
- * 4. Full-navigate to the contract's profile page so SSR re-runs with the new cookie.
33
+ * 4. Full-navigate to `redirectTo` (default: the contract's profile page) so SSR re-runs with the new cookie.
32
34
  *
33
35
  * On failure the previous contract stays active and `switchContract` resolves to `false`.
34
36
  */
@@ -39,7 +41,7 @@ export function useSwitchContract(): UseSwitchContractReturn {
39
41
 
40
42
  const switchContract = async (
41
43
  contractId: string,
42
- { orgUnitId }: SwitchContractParams
44
+ { orgUnitId, redirectTo }: SwitchContractParams
43
45
  ): Promise<boolean> => {
44
46
  setLoading(true);
45
47
  setError(null);
@@ -52,7 +54,7 @@ export function useSwitchContract(): UseSwitchContractReturn {
52
54
 
53
55
  if (typeof window !== "undefined") {
54
56
  window.location.assign(
55
- buyerPortalRoutes.profileDetails({ orgUnitId, contractId })
57
+ resolveSwitchRedirect({ orgUnitId, contractId, redirectTo })
56
58
  );
57
59
  }
58
60
 
@@ -21,6 +21,7 @@ import { ConfirmRemoveDrawer } from "../../components/ConfirmRemoveDrawer/Confir
21
21
  import { ErrorRemoveDrawer } from "../../components/ErrorRemoveDrawer/ErrorRemoveDrawer";
22
22
  import { useContractSwitchChannel, useSetDefaultContract } from "../../hooks";
23
23
  import { removeContractService } from "../../services";
24
+ import { pluralSuffixArgs } from "../ContractListingLayout/utils";
24
25
 
25
26
  import type { OrgUnitBasicData } from "../../../org-units/types";
26
27
  import type { FeatureFlags } from "../../../shared/components/BuyerPortalProvider/BuyerPortalProvider";
@@ -125,6 +126,7 @@ export const ContractInformationLayout = ({
125
126
  const router = useRouter();
126
127
  const { broadcast } = useContractSwitchChannel();
127
128
  const { featureFlags } = useBuyerPortal();
129
+ const { pushToast } = useUI();
128
130
 
129
131
  const contract = contracts.find((c) => c.id === contractId);
130
132
  const isSingleContract = contracts.length <= 1;
@@ -144,8 +146,12 @@ export const ContractInformationLayout = ({
144
146
 
145
147
  const handleRemoveSuccess = useCallback(() => {
146
148
  broadcast({ type: "contract-removed", contractId });
149
+ pushToast({
150
+ message: t("contracts.toasts.removeSuccess", pluralSuffixArgs(1)),
151
+ status: "INFO",
152
+ });
147
153
  router.push(buyerPortalRoutes.contracts({ orgUnitId: orgUnit.id }));
148
- }, [broadcast, contractId, orgUnit.id, router]);
154
+ }, [broadcast, contractId, orgUnit.id, router, pushToast, t]);
149
155
 
150
156
  const handleRemoveError = useCallback(() => {
151
157
  confirmDrawerProps.close();
@@ -173,7 +179,6 @@ export const ContractInformationLayout = ({
173
179
  openConfirmDrawer();
174
180
  };
175
181
 
176
- const { pushToast } = useUI();
177
182
  const { setDefaultContract, isSetDefaultContractLoading } =
178
183
  useSetDefaultContract({
179
184
  onSuccess: () => {
@@ -224,7 +229,7 @@ export const ContractInformationLayout = ({
224
229
  <Link
225
230
  href={contractsUrl}
226
231
  data-fs-bp-contract-information-back-link
227
- aria-label="Back to contracts"
232
+ aria-label={t("layouts.navigation.contracts")}
228
233
  >
229
234
  <Icon name="ArrowPointLeft" width={16} height={16} />
230
235
  {t("layouts.navigation.contracts")}
@@ -239,15 +244,7 @@ export const ContractInformationLayout = ({
239
244
  <Dropdown>
240
245
  <BasicDropdownMenu.Trigger />
241
246
  <BasicDropdownMenu>
242
- {contract?.isDefault ? (
243
- <DropdownItem
244
- data-fs-bp-contract-information-more-menu-set-default
245
- disabled
246
- >
247
- <Icon name="Star" width={16} height={16} />
248
- {t("contracts.actions.setAsDefault")}
249
- </DropdownItem>
250
- ) : (
247
+ {contract && !contract.isDefault && (
251
248
  <DropdownItem
252
249
  data-fs-bp-contract-information-more-menu-set-default
253
250
  onClick={handleSetDefault}