@vtex/faststore-plugin-buyer-portal 1.3.50 → 1.3.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,9 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.3.52] - 2026-01-06
11
+
12
+ ### Fixed
13
+
14
+ - Removed settings object from LoadingTabs
15
+
16
+ ## [1.3.51] - 2026-01-05
17
+
18
+ ### Added
19
+
20
+ - Add Settings Drawers for Credit Cards, Payment Methods and Collections
21
+ - Implement `CreditCardSettingsDrawer` component for credit cards scope configuration
22
+ - Implement `PaymentMethodSettingsDrawer` component for payment methods scope configuration
23
+ - Implement `CollectionsSettingsDrawer` component for product assortment scope configuration
24
+ - Integrate settings drawers with respective layout pages
25
+
10
26
  ## [1.3.50] - 2025-12-19
11
27
 
12
28
  ### Changed
29
+
13
30
  - Introduces several improvements and refactorings to the budget notification drawer, focusing on user experience.
14
31
 
15
32
  ## [1.3.49] - 2025-12-19
@@ -28,7 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
28
45
  - Alternative Login Keys:
29
46
  - Add auth setup drawer
30
47
  - Update AddUserDrawer to support username
31
-
48
+
32
49
  ## [1.3.46] - 2025-12-19
33
50
 
34
51
  ### Fixed
@@ -38,16 +55,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
38
55
  ## [1.3.45] - 2025-12-17
39
56
 
40
57
  ### Fixed
58
+
41
59
  - Remove AwaitedType imports
42
60
 
43
61
  ## [1.3.44] - 2025-12-16
44
62
 
45
63
  ### Fixed
64
+
46
65
  - Remove AwaitedType export that was breaking the application build
47
66
 
48
67
  ## [1.3.43] - 2025-12-16
49
68
 
50
69
  ### Fixed
70
+
51
71
  - Update maskPhoneNumber to include country DDI
52
72
  - Send only phone digits to API in Users and Recipients pages
53
73
  - Show masked phone in layout and forms in Users and Recipients pages
@@ -67,6 +87,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
67
87
 
68
88
  ## [1.3.40] - 2025-12-09
69
89
 
90
+ ### Fixed
91
+
70
92
  - Fix e2e auth flow
71
93
 
72
94
  ## [1.3.39] - 2025-12-09
@@ -84,7 +106,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
84
106
 
85
107
  ## [1.3.37] - 2025-12-04
86
108
 
87
- ## Changed
109
+ ### Changed
88
110
 
89
111
  - Allow Credit Card CVV to accept 4 digits
90
112
 
@@ -102,12 +124,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
102
124
 
103
125
  ## [1.3.34] - 2025-12-01
104
126
 
127
+ ### Added
128
+
105
129
  - Add generic `SettingsDrawer` component with `ListType` subcomponent for scope configuration
106
130
  - Add Scope Config API integration (`useGetScopeConfig`, `useSetScopeConfig` hooks)
107
131
  - Integrate Settings Drawer with Address Settings page
108
-
109
- ### Added
110
-
111
132
  - Add DK Docs
112
133
 
113
134
  ### Fixed
@@ -311,6 +332,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
311
332
 
312
333
  ## [1.3.4] - 2025-10-16
313
334
 
335
+ ### Fixed
336
+
314
337
  - Error boundary:
315
338
  - Updating the authentication loader to provide detailed error context.
316
339
 
@@ -422,21 +445,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
422
445
  - Add CHANGELOG file
423
446
  - Add README file
424
447
 
425
- [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.50...HEAD
426
- [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.2.2...1.2.3
427
- [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.3
428
- [1.2.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.4
429
- [1.3.2]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.2
430
- [1.3.3]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.3
431
- [1.3.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.4
432
- [1.3.5]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.5
433
- [1.3.6]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.6
434
- [1.3.9]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.8...v1.3.9
435
- [1.3.8]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.7...v1.3.8
436
- [1.3.7]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.7
437
-
438
- # <<<<<<< HEAD
439
-
448
+ [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.52...HEAD
449
+ [1.3.52]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.51...v1.3.52
450
+ [1.3.51]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.50...v1.3.51
451
+ [1.3.50]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.49...v1.3.50
452
+ [1.3.49]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.48...v1.3.49
453
+ [1.3.48]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.47...v1.3.48
454
+ [1.3.47]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.46...v1.3.47
455
+ [1.3.46]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.45...v1.3.46
456
+ [1.3.45]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.44...v1.3.45
457
+ [1.3.44]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.43...v1.3.44
458
+ [1.3.43]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.42...v1.3.43
459
+ [1.3.42]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.42
460
+ [1.3.41]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.40...v1.3.41
461
+ [1.3.40]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.39...v1.3.40
462
+ [1.3.39]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.38...v1.3.39
463
+ [1.3.38]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.37...v1.3.38
464
+ [1.3.37]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.36...v1.3.37
465
+ [1.3.36]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.35...v1.3.36
466
+ [1.3.35]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.35
440
467
  [1.3.34]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.33...v1.3.34
441
468
  [1.3.33]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.32...v1.3.33
442
469
  [1.3.32]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.31...v1.3.32
@@ -460,24 +487,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
460
487
  [1.3.14]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.13...v1.3.14
461
488
  [1.3.13]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.12...v1.3.13
462
489
  [1.3.12]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.11...v1.3.12
463
-
464
- > > > > > > > main
465
- > > > > > > > [1.3.11]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.11
466
-
467
- [1.3.41]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.40...v1.3.41
468
- [1.3.40]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.39...v1.3.40
469
- [1.3.39]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.38...v1.3.39
470
- [1.3.38]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.37...v1.3.38
471
- [1.3.37]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.36...v1.3.37
472
- [1.3.36]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.35...v1.3.36
473
- [1.3.35]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.35
474
-
475
- [1.3.50]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.49...v1.3.50
476
- [1.3.49]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.48...v1.3.49
477
- [1.3.48]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.47...v1.3.48
478
- [1.3.47]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.46...v1.3.47
479
- [1.3.46]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.45...v1.3.46
480
- [1.3.45]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.44...v1.3.45
481
- [1.3.44]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.43...v1.3.44
482
- [1.3.43]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.42...v1.3.43
483
- [1.3.42]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.42
490
+ [1.3.11]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.11
491
+ [1.3.9]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.8...v1.3.9
492
+ [1.3.8]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.7...v1.3.8
493
+ [1.3.7]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.7
494
+ [1.3.6]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.6
495
+ [1.3.5]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.5
496
+ [1.3.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.4
497
+ [1.3.3]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.3
498
+ [1.3.2]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.2
499
+ [1.2.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.4
500
+ [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "1.3.50",
3
+ "version": "1.3.52",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -7,10 +7,9 @@ import { useUI, Skeleton } from "@faststore/ui";
7
7
  import {
8
8
  type BasicDrawerProps,
9
9
  AutocompleteDropdown,
10
- DEFAULT_LIST_TYPE_OPTIONS,
11
10
  Icon,
12
- ListTypeOption,
13
11
  SettingsDrawer,
12
+ createListTypeOptions,
14
13
  } from "../../../shared/components";
15
14
  import { OptionSelected } from "../../../shared/components/OptionSelected/OptionSelected";
16
15
  import { SearchHighlight } from "../../../shared/components/SearchHighlight/SearchHighlight";
@@ -31,17 +30,7 @@ export type CreateAddressSettingsDrawerProps = Omit<
31
30
  onUpdate?: () => void;
32
31
  };
33
32
 
34
- export const ADDRESS_LIST_TYPE_OPTIONS: ListTypeOption[] = [
35
- {
36
- ...DEFAULT_LIST_TYPE_OPTIONS[0],
37
- description: "Manage a unique list of addresses for this organization.",
38
- },
39
- {
40
- ...DEFAULT_LIST_TYPE_OPTIONS[1],
41
- description:
42
- "Use the shared list of addresses defined by the contract. Updates are automatic.",
43
- },
44
- ];
33
+ export const ADDRESS_LIST_TYPE_OPTIONS = createListTypeOptions("addresses");
45
34
 
46
35
  export const CreateAddressSettingsDrawer = ({
47
36
  close,
@@ -6,11 +6,10 @@ import { useUI } from "@faststore/ui";
6
6
 
7
7
  import {
8
8
  type BasicDrawerProps,
9
- BasicDrawer,
10
- InputText,
9
+ SettingsDrawer,
10
+ createListTypeOptions,
11
11
  } from "../../../shared/components";
12
-
13
- import type { CreditCard } from "../../types";
12
+ import { useSetScopeConfig, SCOPE_KEYS } from "../../../shared/hooks";
14
13
 
15
14
  export type CreditCardSettingsDrawerProps = Omit<
16
15
  BasicDrawerProps,
@@ -19,70 +18,60 @@ export type CreditCardSettingsDrawerProps = Omit<
19
18
  readonly?: boolean;
20
19
  };
21
20
 
21
+ const CREDIT_CARD_LIST_TYPE_OPTIONS = createListTypeOptions("credit cards");
22
+
22
23
  export const CreditCardSettingsDrawer = ({
23
24
  close,
24
- ...props
25
+ ...otherProps
25
26
  }: CreditCardSettingsDrawerProps) => {
26
27
  const { pushToast } = useUI();
27
-
28
28
  const router = useRouter();
29
- const [newCard, setNewCard] = useState<CreditCard>({} as CreditCard);
29
+
30
+ const [listType, setListType] = useState<"sync" | "custom">("custom");
31
+
32
+ const { setScopeConfig, isSetScopeConfigLoading } = useSetScopeConfig({
33
+ onSuccess: () => {
34
+ pushToast({
35
+ message: "Scope configuration updated successfully",
36
+ status: "INFO",
37
+ });
38
+ close();
39
+ },
40
+ onError: () => {
41
+ pushToast({
42
+ message: "Failed to update scope configuration",
43
+ status: "ERROR",
44
+ });
45
+ },
46
+ });
30
47
 
31
48
  const handleConfirmClick = () => {
32
- pushToast({
33
- message: "Credit card added successfully",
34
- status: "INFO",
49
+ setScopeConfig({
50
+ customerId: router.query.contractId as string,
51
+ unitId: router.query.orgUnitId as string,
52
+ scopeName: SCOPE_KEYS.CREDIT_CARDS,
53
+ type: listType,
35
54
  });
36
- router.reload();
37
- close();
38
55
  };
39
56
 
40
- const hasCardInformation = () => {
41
- return (
42
- newCard.cardholder &&
43
- newCard.cardLabel &&
44
- newCard.cvv &&
45
- newCard.expirationDate &&
46
- newCard.number
47
- );
48
- };
49
-
50
- const isConfirmButtonEnabled = hasCardInformation();
51
-
52
57
  return (
53
- <BasicDrawer
54
- data-fs-bp-edit-credit-card-settings-drawer
58
+ <SettingsDrawer
59
+ title="Credit cards settings"
60
+ {...otherProps}
55
61
  close={close}
56
- {...props}
62
+ onPrimaryAction={handleConfirmClick}
63
+ isPrimaryButtonLoading={isSetScopeConfigLoading}
64
+ scopeName={SCOPE_KEYS.CREDIT_CARDS}
65
+ onDismiss={close}
66
+ data-fs-bp-edit-credit-card-settings-drawer
57
67
  >
58
- <BasicDrawer.Heading title="Credit card settings" onClose={close} />
59
- <BasicDrawer.Body>
60
- <div data-fs-bp-edit-credit-card-settings-drawer-section-label>
61
- <span>Default credit card</span>
62
- </div>
63
- <InputText
64
- label="Search Card nickname"
65
- value={newCard.cardLabel}
66
- wrapperProps={{ style: { marginTop: 16 } }}
67
- onChange={(event) =>
68
- setNewCard({ ...newCard, cardLabel: event.target.value })
69
- }
70
- />
71
- </BasicDrawer.Body>
72
-
73
- <BasicDrawer.Footer>
74
- <BasicDrawer.Button variant="ghost" onClick={close}>
75
- Cancel
76
- </BasicDrawer.Button>
77
- <BasicDrawer.Button
78
- variant="confirm"
79
- disabled={!isConfirmButtonEnabled}
80
- onClick={handleConfirmClick}
81
- isLoading={false}
82
- >
83
- Save
84
- </BasicDrawer.Button>
85
- </BasicDrawer.Footer>
86
- </BasicDrawer>
68
+ <SettingsDrawer.ListType
69
+ title="List type"
70
+ name="credit-card-list-type"
71
+ value={listType}
72
+ onChange={setListType}
73
+ options={CREDIT_CARD_LIST_TYPE_OPTIONS}
74
+ />
75
+ </SettingsDrawer>
87
76
  );
88
77
  };
@@ -19,6 +19,7 @@ import {
19
19
  CreateCreditCardDrawer,
20
20
  CreditCardDropdownMenu,
21
21
  } from "../../components";
22
+ import { CreditCardSettingsDrawer } from "../../components/CreditCardSettingsDrawer/CreditCardSettingsDrawer";
22
23
  import { useChangeCardScope } from "../../hooks";
23
24
  import { useGetCreditCards } from "../../hooks/useGetCreditCards";
24
25
 
@@ -35,6 +36,12 @@ export const CreditCardLayout = ({ data }: CreditCardsLayoutProps) => {
35
36
  ...createDrawerProps
36
37
  } = useDrawerProps();
37
38
 
39
+ const {
40
+ open: openCreateCreditCardSettingsDrawer,
41
+ isOpen: isCreateCreditCardSettingsDrawerOpen,
42
+ ...createCreditCardSettingsDrawerProps
43
+ } = useDrawerProps();
44
+
38
45
  const {
39
46
  currentOrgUnit: orgUnit,
40
47
  currentUser: user,
@@ -137,6 +144,11 @@ export const CreditCardLayout = ({ data }: CreditCardsLayoutProps) => {
137
144
  >
138
145
  <section data-fs-credit-card-section>
139
146
  <HeaderInside title="Credit cards">
147
+ <HeaderInside.Button
148
+ iconName="EditSettings"
149
+ style={{ backgroundColor: "transparent", color: "#1f1f1f" }}
150
+ onClick={openCreateCreditCardSettingsDrawer}
151
+ />
140
152
  <HeaderInside.Button onClick={openCreateDrawer} />
141
153
  </HeaderInside>
142
154
  {data.length === 0 ? (
@@ -173,6 +185,12 @@ export const CreditCardLayout = ({ data }: CreditCardsLayoutProps) => {
173
185
  isOpen={isCreateCreditCardDrawerOpen}
174
186
  />
175
187
  )}
188
+ {isCreateCreditCardSettingsDrawerOpen && (
189
+ <CreditCardSettingsDrawer
190
+ isOpen={isCreateCreditCardSettingsDrawerOpen}
191
+ {...createCreditCardSettingsDrawerProps}
192
+ />
193
+ )}
176
194
  </section>
177
195
  </ContractTabsLayout>
178
196
  </GlobalLayout>
@@ -1,10 +1,19 @@
1
- import { useState } from "react";
1
+ import { useEffect, useMemo, useState } from "react";
2
2
 
3
- import { useUI } from "@faststore/ui";
3
+ import { RadioGroup, RadioOption, useUI } from "@faststore/ui";
4
4
 
5
- import { BasicDrawerProps } from "../../../shared/components";
6
- import { CustomFieldSettingsDrawer as CustomFieldSettingsDrawerComponent } from "../../../shared/components/CustomField";
7
- import { useBuyerPortal, useDebounce } from "../../../shared/hooks";
5
+ import {
6
+ AutocompleteDropdown,
7
+ SettingsDrawer,
8
+ type BasicDrawerProps,
9
+ createListTypeOptions,
10
+ } from "../../../shared/components";
11
+ import { CustomFieldSelectedCardItem } from "../../../shared/components/CustomField/selected-card-item/CustomFieldSelectedCardItem";
12
+ import {
13
+ useBuyerPortal,
14
+ useDebounce,
15
+ useSetScopeConfig,
16
+ } from "../../../shared/hooks";
8
17
  import {
9
18
  useAddDefaultValue,
10
19
  useCustomFieldValues,
@@ -15,6 +24,12 @@ import {
15
24
  import { useCustomFieldSettings } from "../../../shared/hooks/custom-field/useCustomFieldSettings";
16
25
  import { CustomFieldData } from "../../types";
17
26
 
27
+ type FormState = {
28
+ level: string;
29
+ required: boolean;
30
+ defaultValue: CustomFieldData | null;
31
+ };
32
+
18
33
  export interface CustomFieldSettingsDrawerProps
19
34
  extends Omit<BasicDrawerProps, "children"> {
20
35
  contractId: string;
@@ -30,6 +45,8 @@ export function CustomFieldSettingsDrawer({
30
45
  unitId,
31
46
  close,
32
47
  refetch,
48
+ loading = false,
49
+ onDismiss,
33
50
  ...props
34
51
  }: CustomFieldSettingsDrawerProps) {
35
52
  const {
@@ -40,8 +57,23 @@ export function CustomFieldSettingsDrawer({
40
57
  CustomFieldData[]
41
58
  >([]);
42
59
  const [search, setSearch] = useState<string>("");
60
+ const [formData, setFormData] = useState<FormState>({
61
+ level: "",
62
+ required: false,
63
+ defaultValue: null,
64
+ });
65
+ const [autoCompleteValue, setAutocompleteValue] = useState<string>("");
66
+ const [listType, setListType] = useState<"sync" | "custom">("custom");
43
67
 
44
68
  const debouncedSearchValue = useDebounce(search, 200);
69
+ const customFieldScopeName = useMemo(
70
+ () => `custom-fields/${customField}`,
71
+ [customField]
72
+ );
73
+ const customFieldPlural = useMemo(
74
+ () => `${customField.toLowerCase()}s`,
75
+ [customField]
76
+ );
45
77
 
46
78
  const {
47
79
  data: customFieldSettings,
@@ -72,7 +104,7 @@ export function CustomFieldSettingsDrawer({
72
104
 
73
105
  const {
74
106
  mutate: updateCustomFieldSettings,
75
- isLoading: isLoadginUpdateCustomFieldSettings,
107
+ isLoading: isLoadingUpdateCustomFieldSettings,
76
108
  } = useUpdateCustomFieldSettings({
77
109
  options: {
78
110
  onSuccess: () =>
@@ -114,6 +146,14 @@ export function CustomFieldSettingsDrawer({
114
146
  },
115
147
  });
116
148
 
149
+ const { setScopeConfig, isSetScopeConfigLoading } = useSetScopeConfig({
150
+ onError: () =>
151
+ pushToast({
152
+ status: "ERROR",
153
+ message: "Failed to update scope configuration",
154
+ }),
155
+ });
156
+
117
157
  useCustomFieldValues({
118
158
  keys: `default-values/options/${debouncedSearchValue}`,
119
159
  data: {
@@ -140,63 +180,199 @@ export function CustomFieldSettingsDrawer({
140
180
  },
141
181
  });
142
182
 
143
- async function onSave(
144
- level: string,
145
- required: boolean,
146
- defaulValue: CustomFieldData | null
147
- ) {
148
- if (defaulValue) {
183
+ const listTypeOptions = useMemo(
184
+ () => createListTypeOptions(customFieldPlural),
185
+ [customFieldPlural]
186
+ );
187
+
188
+ useEffect(() => {
189
+ setFormData({
190
+ level: customFieldSettings?.level ?? "",
191
+ required: !!customFieldSettings?.required,
192
+ defaultValue: defaultValueData
193
+ ? { ...defaultValueData, isDefault: true }
194
+ : null,
195
+ });
196
+ }, [customFieldSettings, defaultValueData]);
197
+
198
+ const handleClose = () => {
199
+ onDismiss?.();
200
+ close();
201
+ setSearch("");
202
+ setAutocompleteValue("");
203
+ };
204
+
205
+ const handleSave = async () => {
206
+ if (formData.defaultValue) {
149
207
  await updateDefaultValue({
150
208
  contractId,
151
209
  customField,
152
- customFieldValueId: defaulValue.id,
210
+ customFieldValueId: formData.defaultValue.id,
153
211
  unitId,
154
212
  });
155
213
  } else {
156
214
  await removeDefaultValue({ contractId, customField, unitId });
157
215
  }
158
216
 
159
- updateCustomFieldSettings({
160
- cookie,
161
- data: {
162
- contractId,
217
+ await Promise.all([
218
+ updateCustomFieldSettings({
219
+ cookie,
220
+ data: {
221
+ contractId,
222
+ unitId,
223
+ customField,
224
+ level: formData.level,
225
+ required: formData.required,
226
+ },
227
+ }),
228
+ setScopeConfig({
229
+ customerId: contractId,
163
230
  unitId,
164
- customField,
165
- level,
166
- required,
167
- },
168
- });
169
- }
231
+ scopeName: customFieldScopeName,
232
+ type: listType,
233
+ }),
234
+ ]);
235
+
236
+ handleClose();
237
+ refetch();
238
+ };
239
+
240
+ const isSavingSettings =
241
+ isLoadingUpdateDefaultValue ||
242
+ isLoadingRemoveDefaultValue ||
243
+ isLoadingUpdateCustomFieldSettings ||
244
+ isSetScopeConfigLoading ||
245
+ loading;
246
+
247
+ const isPrimaryButtonDisabled =
248
+ (!formData.defaultValue?.id && !!autoCompleteValue) || isSavingSettings;
170
249
 
171
250
  if (isLoading || isLoadingDefaultValue || error) return null;
172
251
 
173
252
  return (
174
- <CustomFieldSettingsDrawerComponent
175
- initialValue={{
176
- level: customFieldSettings?.level ?? "",
177
- required: !!customFieldSettings?.required,
178
- defaultValue: defaultValueData
179
- ? { ...defaultValueData, isDefault: true }
180
- : null,
181
- }}
182
- close={() => {
183
- close();
184
- setSearch("");
185
- }}
186
- onSave={async ({ level, required, defaultValue }) => {
187
- await onSave(level, required, defaultValue);
188
- close();
189
- refetch();
190
- }}
191
- loading={
192
- isLoadingUpdateDefaultValue ||
193
- isLoadingRemoveDefaultValue ||
194
- isLoadginUpdateCustomFieldSettings
195
- }
196
- onSearch={setSearch}
197
- customFieldOptions={customFieldOptions}
198
- customFieldLabel={customField}
253
+ <SettingsDrawer
254
+ title={`${customField} settings`}
255
+ close={close}
256
+ onPrimaryAction={handleSave}
257
+ onSecondaryAction={handleClose}
258
+ onDismiss={handleClose}
259
+ isPrimaryButtonLoading={isSavingSettings}
260
+ isPrimaryButtonDisabled={isPrimaryButtonDisabled}
261
+ scopeName={customFieldScopeName}
262
+ customerId={contractId}
263
+ unitId={unitId}
264
+ data-bp-custom-fields-settings-drawer
199
265
  {...props}
200
- />
266
+ >
267
+ <div data-bp-settings-drawer-body>
268
+ <p>
269
+ Set how buyers in this unit should enter {customField}s during
270
+ checkout
271
+ </p>
272
+
273
+ <SettingsDrawer.ListType
274
+ title="List type"
275
+ name={`${customField}-list-type`}
276
+ value={listType}
277
+ onChange={setListType}
278
+ options={listTypeOptions}
279
+ />
280
+
281
+ <form data-bp-custom-fields-settings-drawer-form>
282
+ <div>
283
+ <h4>Input level</h4>
284
+ <RadioGroup
285
+ name="inputLevel"
286
+ selectedValue={formData.level}
287
+ onChange={(e) => {
288
+ setFormData((curr) => ({
289
+ ...curr,
290
+ level: e.target.value,
291
+ }));
292
+ }}
293
+ >
294
+ <RadioOption value="order" label="Order level" name="inputLevel">
295
+ Order level
296
+ </RadioOption>
297
+
298
+ <RadioOption value="item" label="Item level" name="inputLevel">
299
+ Item level
300
+ </RadioOption>
301
+ </RadioGroup>
302
+ </div>
303
+
304
+ <div>
305
+ <h4>Input requirement</h4>
306
+ <RadioGroup
307
+ name="requirement"
308
+ selectedValue={formData.required.toString()}
309
+ onChange={(e) => {
310
+ setFormData((curr) => ({
311
+ ...curr,
312
+ required: e.target.value === "true",
313
+ }));
314
+ }}
315
+ >
316
+ <RadioOption value="true" label="Required" name="requirement">
317
+ Required
318
+ </RadioOption>
319
+
320
+ <RadioOption value="false" label="Optional" name="requirement">
321
+ Optional
322
+ </RadioOption>
323
+ </RadioGroup>
324
+ </div>
325
+
326
+ <div data-bp-settings-drawer-autocomplete-dropdown-container>
327
+ <h4>Default {customField}</h4>
328
+ <span>Select the default {customField} for this unit.</span>
329
+
330
+ {formData.defaultValue?.id ? (
331
+ <CustomFieldSelectedCardItem
332
+ text={formData.defaultValue.value}
333
+ onCancel={() => {
334
+ setFormData((curr) => ({
335
+ ...curr,
336
+ defaultValue: null,
337
+ }));
338
+ setAutocompleteValue("");
339
+ setSearch("");
340
+ }}
341
+ />
342
+ ) : (
343
+ <AutocompleteDropdown
344
+ label=""
345
+ value={autoCompleteValue}
346
+ options={customFieldOptions}
347
+ shouldOpenOnFocus={false}
348
+ autoComplete="off"
349
+ shouldShowArrowDown={false}
350
+ onChange={(v) => {
351
+ setAutocompleteValue(v.target.value);
352
+ setSearch(v.target.value);
353
+ }}
354
+ placeholder={`Search by ${customField} name`}
355
+ renderOption={(option, index) => (
356
+ <AutocompleteDropdown.Item
357
+ key={option.id}
358
+ closeOnClick
359
+ index={index}
360
+ isSelected={formData.defaultValue?.id === option?.id}
361
+ onClick={() => {
362
+ setFormData((curr) => ({
363
+ ...curr,
364
+ defaultValue: option,
365
+ }));
366
+ }}
367
+ >
368
+ {option?.value}
369
+ </AutocompleteDropdown.Item>
370
+ )}
371
+ />
372
+ )}
373
+ </div>
374
+ </form>
375
+ </div>
376
+ </SettingsDrawer>
201
377
  );
202
378
  }
@@ -0,0 +1,78 @@
1
+ import { useState } from "react";
2
+
3
+ import { useRouter } from "next/router";
4
+
5
+ import { useUI } from "@faststore/ui";
6
+
7
+ import {
8
+ type BasicDrawerProps,
9
+ SettingsDrawer,
10
+ createListTypeOptions,
11
+ } from "../../../shared/components";
12
+ import { useSetScopeConfig, SCOPE_KEYS } from "../../../shared/hooks";
13
+
14
+ export type PaymentMethodSettingsDrawerProps = Omit<
15
+ BasicDrawerProps,
16
+ "children"
17
+ > & {
18
+ readonly?: boolean;
19
+ };
20
+
21
+ const PAYMENT_METHOD_LIST_TYPE_OPTIONS =
22
+ createListTypeOptions("payment methods");
23
+
24
+ export const PaymentMethodSettingsDrawer = ({
25
+ close,
26
+ ...otherProps
27
+ }: PaymentMethodSettingsDrawerProps) => {
28
+ const { pushToast } = useUI();
29
+ const router = useRouter();
30
+
31
+ const [listType, setListType] = useState<"sync" | "custom">("custom");
32
+
33
+ const { setScopeConfig, isSetScopeConfigLoading } = useSetScopeConfig({
34
+ onSuccess: () => {
35
+ pushToast({
36
+ message: "Scope configuration updated successfully",
37
+ status: "INFO",
38
+ });
39
+ close();
40
+ },
41
+ onError: () => {
42
+ pushToast({
43
+ message: "Failed to update scope configuration",
44
+ status: "ERROR",
45
+ });
46
+ },
47
+ });
48
+
49
+ const handleConfirmClick = () => {
50
+ setScopeConfig({
51
+ customerId: router.query.contractId as string,
52
+ unitId: router.query.orgUnitId as string,
53
+ scopeName: SCOPE_KEYS.PAYMENT_SYSTEMS,
54
+ type: listType,
55
+ });
56
+ };
57
+
58
+ return (
59
+ <SettingsDrawer
60
+ title="Payment method settings"
61
+ {...otherProps}
62
+ close={close}
63
+ onPrimaryAction={handleConfirmClick}
64
+ isPrimaryButtonLoading={isSetScopeConfigLoading}
65
+ scopeName={SCOPE_KEYS.PAYMENT_SYSTEMS}
66
+ onDismiss={close}
67
+ data-fs-bp-payment-method-settings-drawer
68
+ >
69
+ <SettingsDrawer.ListType
70
+ title="List type"
71
+ name="payment-method-list-type"
72
+ value={listType}
73
+ onChange={setListType}
74
+ options={PAYMENT_METHOD_LIST_TYPE_OPTIONS}
75
+ />
76
+ </SettingsDrawer>
77
+ );
78
+ };
@@ -2,3 +2,7 @@ export { AddPaymentMethodsDrawer } from "./AddPaymentMethodsDrawer/AddPaymentMet
2
2
  export { RemovePaymentMethodsDrawer } from "./RemovePaymentMethodsDrawer/RemovePaymentMethodsDrawer";
3
3
  export { RemoveMethodButton } from "./RemoveMethodButton/RemoveMethodButton";
4
4
  export { SearchPaymentMethods } from "./SearchPaymentMethods/SearchPaymentMethods";
5
+ export {
6
+ PaymentMethodSettingsDrawer,
7
+ type PaymentMethodSettingsDrawerProps,
8
+ } from "./PaymentMethodSettingsDrawer/PaymentMethodSettingsDrawer";
@@ -19,6 +19,7 @@ import {
19
19
  AddPaymentMethodsDrawer,
20
20
  RemoveMethodButton,
21
21
  RemovePaymentMethodsDrawer,
22
+ PaymentMethodSettingsDrawer,
22
23
  } from "../../components";
23
24
  import { useRemovePaymentMethod } from "../../hooks/useRemovePaymentMethodSubmit";
24
25
 
@@ -66,6 +67,12 @@ export const PaymentMethodsLayout = ({
66
67
  ...addDrawerProps
67
68
  } = useDrawerProps();
68
69
 
70
+ const {
71
+ open: openPaymentMethodSettingsDrawer,
72
+ isOpen: isPaymentMethodSettingsDrawerOpen,
73
+ ...paymentMethodSettingsDrawerProps
74
+ } = useDrawerProps();
75
+
69
76
  const {
70
77
  open: openRemoveDrawer,
71
78
  isOpen: isRemovePaymentMethodDrawerOpen,
@@ -153,6 +160,11 @@ export const PaymentMethodsLayout = ({
153
160
  >
154
161
  <section data-fs-payment-methods-section>
155
162
  <HeaderInside title="Payment methods">
163
+ <HeaderInside.Button
164
+ iconName="EditSettings"
165
+ style={{ backgroundColor: "transparent", color: "#1f1f1f" }}
166
+ onClick={openPaymentMethodSettingsDrawer}
167
+ />
156
168
  <ConditionalTooltip
157
169
  condition={allPaymentMethodsSelected}
158
170
  placement="left-center"
@@ -227,6 +239,13 @@ export const PaymentMethodsLayout = ({
227
239
  />
228
240
  )}
229
241
 
242
+ {isPaymentMethodSettingsDrawerOpen && (
243
+ <PaymentMethodSettingsDrawer
244
+ isOpen={isPaymentMethodSettingsDrawerOpen}
245
+ {...paymentMethodSettingsDrawerProps}
246
+ />
247
+ )}
248
+
230
249
  {isRemovePaymentMethodDrawerOpen && selectedMethod && (
231
250
  <RemovePaymentMethodsDrawer
232
251
  {...removeDrawerProps}
@@ -0,0 +1,77 @@
1
+ import { useState } from "react";
2
+
3
+ import { useRouter } from "next/router";
4
+
5
+ import { useUI } from "@faststore/ui";
6
+
7
+ import {
8
+ type BasicDrawerProps,
9
+ SettingsDrawer,
10
+ createListTypeOptions,
11
+ } from "../../../shared/components";
12
+ import { useSetScopeConfig, SCOPE_KEYS } from "../../../shared/hooks";
13
+
14
+ export type CollectionsSettingsDrawerProps = Omit<
15
+ BasicDrawerProps,
16
+ "children"
17
+ > & {
18
+ readonly?: boolean;
19
+ };
20
+
21
+ const COLLECTIONS_LIST_TYPE_OPTIONS = createListTypeOptions("collections");
22
+
23
+ export const CollectionsSettingsDrawer = ({
24
+ close,
25
+ ...otherProps
26
+ }: CollectionsSettingsDrawerProps) => {
27
+ const { pushToast } = useUI();
28
+ const router = useRouter();
29
+
30
+ const [listType, setListType] = useState<"sync" | "custom">("custom");
31
+
32
+ const { setScopeConfig, isSetScopeConfigLoading } = useSetScopeConfig({
33
+ onSuccess: () => {
34
+ pushToast({
35
+ message: "Scope configuration updated successfully",
36
+ status: "INFO",
37
+ });
38
+ close();
39
+ },
40
+ onError: () => {
41
+ pushToast({
42
+ message: "Failed to update scope configuration",
43
+ status: "ERROR",
44
+ });
45
+ },
46
+ });
47
+
48
+ const handleConfirmClick = () => {
49
+ setScopeConfig({
50
+ customerId: router.query.contractId as string,
51
+ unitId: router.query.orgUnitId as string,
52
+ scopeName: SCOPE_KEYS.COLLECTIONS,
53
+ type: listType,
54
+ });
55
+ };
56
+
57
+ return (
58
+ <SettingsDrawer
59
+ title="Collections settings"
60
+ {...otherProps}
61
+ close={close}
62
+ onPrimaryAction={handleConfirmClick}
63
+ isPrimaryButtonLoading={isSetScopeConfigLoading}
64
+ scopeName={SCOPE_KEYS.COLLECTIONS}
65
+ onDismiss={close}
66
+ data-fs-bp-collections-settings-drawer
67
+ >
68
+ <SettingsDrawer.ListType
69
+ title="List type"
70
+ name="collections-list-type"
71
+ value={listType}
72
+ onChange={setListType}
73
+ options={COLLECTIONS_LIST_TYPE_OPTIONS}
74
+ />
75
+ </SettingsDrawer>
76
+ );
77
+ };
@@ -1,2 +1,6 @@
1
1
  export { AddProductAssortmentDrawer } from "./AddProductAssortmentDrawer/AddProductAssortmentDrawer";
2
2
  export { AddProductAssortmentDrawerTable } from "./table/AddProductAssortmentDrawerTable";
3
+ export {
4
+ CollectionsSettingsDrawer,
5
+ type CollectionsSettingsDrawerProps,
6
+ } from "./CollectionsSettingsDrawer/CollectionsSettingsDrawer";
@@ -14,7 +14,10 @@ import {
14
14
  usePageItems,
15
15
  } from "../../../shared/hooks";
16
16
  import { ContractTabsLayout, GlobalLayout } from "../../../shared/layouts";
17
- import { AddProductAssortmentDrawer } from "../../components";
17
+ import {
18
+ AddProductAssortmentDrawer,
19
+ CollectionsSettingsDrawer,
20
+ } from "../../components";
18
21
  import { ProductAssortmentTable } from "../../components/ProductAssortmentTable/ProductAssortmentTable";
19
22
  import { useGetProductAssortment } from "../../hooks/useGetProductAssortment";
20
23
  import { useGetProductAssortmentFromContract } from "../../hooks/useGetProductAssortmentFromContract";
@@ -92,6 +95,12 @@ export const ProductAssortmentLayout = ({
92
95
  ...addProductAssortmentDrawerProps
93
96
  } = useDrawerProps();
94
97
 
98
+ const {
99
+ open: openCollectionsSettingsDrawer,
100
+ isOpen: isCollectionsSettingsDrawerOpen,
101
+ ...collectionsSettingsDrawerProps
102
+ } = useDrawerProps();
103
+
95
104
  const hasAllAssortment = drawerProductAssortment.items.length === 0;
96
105
  const isEmpty = productAssortment.length === 0;
97
106
 
@@ -123,6 +132,11 @@ export const ProductAssortmentLayout = ({
123
132
  >
124
133
  <section data-fs-bp-product-assortment-container>
125
134
  <HeaderInside title="Product assortment">
135
+ <HeaderInside.Button
136
+ iconName="EditSettings"
137
+ style={{ backgroundColor: "transparent", color: "#1f1f1f" }}
138
+ onClick={openCollectionsSettingsDrawer}
139
+ />
126
140
  {!isContractEmpty &&
127
141
  (hasAllAssortment ? (
128
142
  <Tooltip
@@ -222,6 +236,13 @@ export const ProductAssortmentLayout = ({
222
236
  {...addProductAssortmentDrawerProps}
223
237
  />
224
238
  )}
239
+
240
+ {isCollectionsSettingsDrawerOpen && (
241
+ <CollectionsSettingsDrawer
242
+ isOpen={isCollectionsSettingsDrawerOpen}
243
+ {...collectionsSettingsDrawerProps}
244
+ />
245
+ )}
225
246
  </ContractTabsLayout>
226
247
  </GlobalLayout>
227
248
  );
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from "react";
2
2
 
3
- import { RadioGroup, RadioOption } from "@faststore/ui";
3
+ import { RadioGroup, RadioOption, Skeleton } from "@faststore/ui";
4
4
 
5
5
  import { useGetScopeConfig } from "../../hooks";
6
6
 
@@ -80,21 +80,33 @@ export const SettingsDrawerListType = ({
80
80
  return (
81
81
  <div data-fs-bp-settings-drawer-list-type>
82
82
  <h4 data-fs-bp-settings-drawer-list-type-title>{title}</h4>
83
- <RadioGroup name={name} selectedValue={value} onChange={handleChange}>
84
- {options.map((option) => (
85
- <div key={option.value} data-fs-bp-settings-drawer-list-type-option>
86
- <RadioOption
87
- value={option.value}
88
- label={option.label}
89
- name={name}
90
- disabled={disabled || isLoading}
91
- />
92
- <p data-fs-bp-settings-drawer-list-type-description>
93
- {option.description}
94
- </p>
95
- </div>
96
- ))}
97
- </RadioGroup>
83
+ {isLoading ? (
84
+ <div data-fs-bp-settings-drawer-list-type-skeleton>
85
+ {options.map((option) => (
86
+ <div key={option.value} data-fs-bp-settings-drawer-list-type-option>
87
+ <Skeleton size={{ width: "100%", height: "3.5rem" }} />
88
+ </div>
89
+ ))}
90
+ </div>
91
+ ) : (
92
+ <RadioGroup name={name} selectedValue={value} onChange={handleChange}>
93
+ {options.map((option) => (
94
+ <div key={option.value} data-fs-bp-settings-drawer-list-type-option>
95
+ <RadioOption
96
+ value={option.value}
97
+ label={option.label}
98
+ name={name}
99
+ disabled={disabled || isLoading}
100
+ >
101
+ {option.label}
102
+ </RadioOption>
103
+ <p data-fs-bp-settings-drawer-list-type-description>
104
+ {option.description}
105
+ </p>
106
+ </div>
107
+ ))}
108
+ </RadioGroup>
109
+ )}
98
110
  </div>
99
111
  );
100
112
  };
@@ -0,0 +1,23 @@
1
+ import {
2
+ DEFAULT_LIST_TYPE_OPTIONS,
3
+ type ListTypeOption,
4
+ } from "./SettingsDrawerListType";
5
+
6
+ export const getCustomListDescription = (entityPlural: string) =>
7
+ `Manage a unique list of ${entityPlural} for this organization.`;
8
+
9
+ export const getSharedListDescription = (entityPlural: string) =>
10
+ `Use the shared list of ${entityPlural} defined by the contract. Updates are automatic.`;
11
+
12
+ export const createListTypeOptions = (
13
+ entityPlural: string
14
+ ): ListTypeOption[] => [
15
+ {
16
+ ...DEFAULT_LIST_TYPE_OPTIONS[0],
17
+ description: getCustomListDescription(entityPlural),
18
+ },
19
+ {
20
+ ...DEFAULT_LIST_TYPE_OPTIONS[1],
21
+ description: getSharedListDescription(entityPlural),
22
+ },
23
+ ];
@@ -14,6 +14,11 @@ export {
14
14
  type SettingsDrawerListTypeProps,
15
15
  DEFAULT_LIST_TYPE_OPTIONS,
16
16
  } from "./SettingsDrawer/SettingsDrawer";
17
+ export {
18
+ createListTypeOptions,
19
+ getCustomListDescription,
20
+ getSharedListDescription,
21
+ } from "./SettingsDrawer/listTypeOptions";
17
22
  export {
18
23
  BasicDropdownMenu,
19
24
  type BasicDropdownMenuProps,
@@ -124,19 +124,6 @@ export const LoadingTabsLayout = ({ children }: LoadingTabsLayoutProps) => {
124
124
  name: "",
125
125
  },
126
126
  },
127
- settings: {
128
- userIdentification: {
129
- userName: false,
130
- email: false,
131
- phone: false,
132
- },
133
- authenticationMethods: {
134
- method: "PASSWORD",
135
- },
136
- "2FA": {
137
- verificationCode: false,
138
- },
139
- },
140
127
  }}
141
128
  />
142
129
  );
@@ -22,4 +22,4 @@ export const SCOPE_KEYS = {
22
22
  CREDIT_CARDS: "creditCards",
23
23
  } as const;
24
24
 
25
- export const CURRENT_VERSION = "1.3.50";
25
+ export const CURRENT_VERSION = "1.3.52";