@vtex/faststore-plugin-buyer-portal 1.3.49 → 1.3.51

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 (27) hide show
  1. package/CHANGELOG.md +56 -40
  2. package/package.json +1 -1
  3. package/src/features/addresses/components/CreateAddressSettingsDrawer/CreateAddressSettingsDrawer.tsx +2 -13
  4. package/src/features/budgets/components/BudgetEditNotificationDrawer/BudgetEditNotificationDrawer.tsx +1 -0
  5. package/src/features/budgets/components/BudgetNotificationForm/BudgetNotificationForm.tsx +64 -26
  6. package/src/features/budgets/components/BudgetNotificationForm/budget-notification-form.scss +26 -15
  7. package/src/features/budgets/components/BudgetUsersTable/BudgetUsersTable.tsx +2 -13
  8. package/src/features/budgets/components/CreateBudgetDrawer/create-budget-drawer.scss +0 -9
  9. package/src/features/budgets/components/EditBudgetDrawer/edit-budget-drawer.scss +0 -6
  10. package/src/features/credit-cards/components/CreditCardSettingsDrawer/CreditCardSettingsDrawer.tsx +45 -56
  11. package/src/features/credit-cards/layouts/CreditCardsLayout/CreditCardLayout.tsx +18 -0
  12. package/src/features/custom-fields/components/CustomFieldSettingsDrawer/CustomFieldSettingsDrawer.tsx +225 -49
  13. package/src/features/payment-methods/components/PaymentMethodSettingsDrawer/PaymentMethodSettingsDrawer.tsx +78 -0
  14. package/src/features/payment-methods/components/index.ts +4 -0
  15. package/src/features/payment-methods/layouts/PaymentMethodsLayout/PaymentMethodsLayout.tsx +19 -0
  16. package/src/features/product-assortment/components/CollectionsSettingsDrawer/CollectionsSettingsDrawer.tsx +77 -0
  17. package/src/features/product-assortment/components/index.tsx +4 -0
  18. package/src/features/product-assortment/layouts/ProductAssortmentLayout/ProductAssortmentLayout.tsx +22 -1
  19. package/src/features/shared/components/AutocompleteDropdown/AutocompleteDropdown.tsx +23 -6
  20. package/src/features/shared/components/AutocompleteDropdown/autocomplete-dropdown.scss +21 -7
  21. package/src/features/shared/components/BuyerPortalProvider/BuyerPortalProvider.tsx +1 -0
  22. package/src/features/shared/components/QuantitySelectorWithPercentage/QuantitySelectorWithPercentage.tsx +6 -3
  23. package/src/features/shared/components/SettingsDrawer/SettingsDrawerListType.tsx +28 -16
  24. package/src/features/shared/components/SettingsDrawer/listTypeOptions.ts +23 -0
  25. package/src/features/shared/components/Table/TableRow/table-row.scss +4 -0
  26. package/src/features/shared/components/index.ts +5 -0
  27. package/src/features/shared/utils/constants.ts +1 -1
@@ -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
  );
@@ -43,6 +43,7 @@ export type AutocompleteDropdownProps<T> = ComponentProps<typeof InputText> & {
43
43
  renderOption?: (option: T, index: number) => ReactNode;
44
44
  onConfirmKeyPress?: (option: T) => void;
45
45
  hasError?: boolean;
46
+ isLoading?: boolean;
46
47
  shouldOpenOnFocus?: boolean;
47
48
  shouldShowArrowDown?: boolean;
48
49
  shouldCloseOnSelect?: boolean;
@@ -57,6 +58,7 @@ export const AutocompleteDropdown = <T,>({
57
58
  disabled,
58
59
  onConfirmKeyPress,
59
60
  hasError,
61
+ isLoading = false,
60
62
  value,
61
63
  helperLabel,
62
64
  shouldOpenOnFocus = true,
@@ -138,6 +140,25 @@ export const AutocompleteDropdown = <T,>({
138
140
  setIsOpened(false);
139
141
  }, []);
140
142
 
143
+ const getIconElement = () => {
144
+ if (shouldShowArrowDown) {
145
+ return <Icon name="ArrowDropDown" width={20} height={20} />;
146
+ }
147
+
148
+ if (isLoading) {
149
+ return (
150
+ <Icon
151
+ name="LoadingIndicator"
152
+ width={20}
153
+ height={20}
154
+ data-fs-icon-loading="true"
155
+ />
156
+ );
157
+ }
158
+
159
+ return undefined;
160
+ };
161
+
141
162
  useEffect(() => {
142
163
  const event = (e: MouseEvent) => {
143
164
  const target = e.target as Node;
@@ -215,11 +236,7 @@ export const AutocompleteDropdown = <T,>({
215
236
  <InputText
216
237
  label={label}
217
238
  onKeyDown={handleBackdropKeyDown}
218
- icon={
219
- shouldShowArrowDown ? (
220
- <Icon name="ArrowDropDown" width={20} height={20} />
221
- ) : null
222
- }
239
+ icon={getIconElement()}
223
240
  onChange={onChange}
224
241
  disabled={disabled}
225
242
  hasError={hasError}
@@ -229,7 +246,7 @@ export const AutocompleteDropdown = <T,>({
229
246
  {...props}
230
247
  />
231
248
 
232
- {isOpened && options.length > 0 ? (
249
+ {!isLoading && isOpened && options.length > 0 ? (
233
250
  <div
234
251
  style={{ width: positionStyle.width }}
235
252
  data-fs-bp-autocomplete-dropdown-menu={position}
@@ -1,9 +1,9 @@
1
- @import "@faststore/ui/src/components/molecules/Dropdown/styles.scss";
1
+ @import '@faststore/ui/src/components/molecules/Dropdown/styles.scss';
2
2
 
3
3
  [data-fs-bp-autocomplete-dropdown] {
4
4
  position: relative;
5
-
6
- &[data-fs-bp-autocomplete-dropdown-only-select="true"] {
5
+
6
+ &[data-fs-bp-autocomplete-dropdown-only-select='true'] {
7
7
  [data-fs-bp-input-text-input] {
8
8
  caret-color: transparent;
9
9
  background-color: #ffffff;
@@ -11,6 +11,20 @@
11
11
  }
12
12
  }
13
13
 
14
+ [data-fs-icon-loading] {
15
+ &[data-fs-icon-loading='true'] {
16
+ @keyframes rotate {
17
+ from {
18
+ transform: rotate(0deg);
19
+ }
20
+ to {
21
+ transform: rotate(360deg);
22
+ }
23
+ }
24
+ animation: rotate 2s linear infinite;
25
+ }
26
+ }
27
+
14
28
  [data-fs-bp-autocomplete-dropdown-menu] {
15
29
  padding: var(--fs-spacing-1) 0;
16
30
  background-color: #fff;
@@ -22,11 +36,11 @@
22
36
 
23
37
  border-radius: calc(var(--fs-border-radius) * 2);
24
38
 
25
- &[data-fs-bp-autocomplete-dropdown-menu="bottom"] {
39
+ &[data-fs-bp-autocomplete-dropdown-menu='bottom'] {
26
40
  top: calc(100% + var(--fs-spacing-0));
27
41
  }
28
42
 
29
- &[data-fs-bp-autocomplete-dropdown-menu="top"] {
43
+ &[data-fs-bp-autocomplete-dropdown-menu='top'] {
30
44
  bottom: calc(100% + var(--fs-spacing-0));
31
45
  }
32
46
 
@@ -59,12 +73,12 @@
59
73
  color: var(--fs-color-neutral-7);
60
74
  }
61
75
 
62
- &[data-fs-bp-autocomplete-dropdown-option-focused="true"] {
76
+ &[data-fs-bp-autocomplete-dropdown-option-focused='true'] {
63
77
  background-color: #f5f5f5;
64
78
  color: #3d3d3d;
65
79
  }
66
80
 
67
- &[data-fs-bp-autocomplete-dropdown-option-selected="true"] {
81
+ &[data-fs-bp-autocomplete-dropdown-option-selected='true'] {
68
82
  color: #0366dd;
69
83
  }
70
84
 
@@ -19,6 +19,7 @@ export type BuyerPortalContextType = {
19
19
  id: string;
20
20
  name: string;
21
21
  role?: string;
22
+ email?: string;
22
23
  } | null;
23
24
  featureFlags?: FeatureFlags;
24
25
  };
@@ -12,10 +12,11 @@ interface QuantitySelectorWithPercentageParams {
12
12
  disabled?: boolean;
13
13
  onChange?: (value: number) => void;
14
14
  onValidateBlur?: (min: number, maxValue: number, quantity: number) => void;
15
-
16
15
  formatAsPercent?: boolean;
17
16
  allowPercentToggle?: boolean;
18
17
  onFormatToggle?: (formatAsPercent: boolean) => void;
18
+ increaseQuantityBy?: number;
19
+ decreaseQuantityBy?: number;
19
20
  }
20
21
 
21
22
  const QuantitySelectorWithPercentage = ({
@@ -29,6 +30,8 @@ const QuantitySelectorWithPercentage = ({
29
30
  onValidateBlur,
30
31
  testId = "fs-quantity-selector",
31
32
  formatAsPercent = true,
33
+ increaseQuantityBy = 1,
34
+ decreaseQuantityBy = 1,
32
35
  ...otherProps
33
36
  }: QuantitySelectorWithPercentageParams) => {
34
37
  const [quantity, setQuantity] = useState<number>(initial ?? min);
@@ -61,8 +64,8 @@ const QuantitySelectorWithPercentage = ({
61
64
  : maxValue;
62
65
  };
63
66
 
64
- const increase = () => changeQuantity(1);
65
- const decrease = () => changeQuantity(-1);
67
+ const increase = () => changeQuantity(increaseQuantityBy);
68
+ const decrease = () => changeQuantity(-decreaseQuantityBy);
66
69
 
67
70
  const changeQuantity = (delta: number) => {
68
71
  const next = validateQuantityBounds(quantity + delta);
@@ -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
+ ];
@@ -98,6 +98,10 @@
98
98
  align-items: center;
99
99
  gap: var(--fs-spacing-1);
100
100
  justify-content: flex-end;
101
+
102
+ svg {
103
+ cursor: pointer;
104
+ }
101
105
 
102
106
  &:empty {
103
107
  padding: 0;
@@ -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,
@@ -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.49";
25
+ export const CURRENT_VERSION = "1.3.51";