@vtex/faststore-plugin-buyer-portal 1.3.41 → 1.3.42
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 +18 -1
- package/package.json +1 -1
- package/src/features/addresses/components/CreateAddressSettingsDrawer/CreateAddressSettingsDrawer.tsx +64 -25
- package/src/features/addresses/components/CreateAddressSettingsDrawer/create-address-settings-drawer.scss +22 -22
- package/src/features/addresses/services/default-values/get-default-address.service.ts +1 -1
- package/src/features/addresses/types/AddressData.ts +1 -0
- package/src/features/payment-methods/layouts/PaymentMethodsLayout/PaymentMethodsLayout.tsx +8 -6
- package/src/features/shared/clients/ScopeClient.ts +38 -2
- package/src/features/shared/components/Error/Error.tsx +15 -13
- package/src/features/shared/components/SettingsDrawer/SettingsDrawer.tsx +106 -0
- package/src/features/shared/components/SettingsDrawer/SettingsDrawerContext.tsx +17 -0
- package/src/features/shared/components/SettingsDrawer/SettingsDrawerListType.tsx +100 -0
- package/src/features/shared/components/SettingsDrawer/settings-drawer.scss +61 -0
- package/src/features/shared/components/index.ts +7 -0
- package/src/features/shared/hooks/index.ts +2 -0
- package/src/features/shared/hooks/useGetScopeConfig.ts +35 -0
- package/src/features/shared/hooks/useSetScopeConfig.ts +30 -0
- package/src/features/shared/services/get-scope-config.service.ts +19 -0
- package/src/features/shared/services/index.ts +9 -0
- package/src/features/shared/services/set-scope-config.service.ts +27 -0
- package/src/features/shared/types/index.ts +1 -0
- package/src/features/shared/utils/constants.ts +10 -1
- package/src/features/shared/utils/index.ts +6 -1
- package/src/pages/payment-methods.tsx +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.42] - 2025-12-11
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Improve error handling in the payment methods
|
|
15
|
+
- Show error details on error boundary only in development environment
|
|
16
|
+
|
|
10
17
|
## [1.3.41] - 2025-12-09
|
|
11
18
|
|
|
12
19
|
### Changed
|
|
@@ -50,10 +57,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
50
57
|
|
|
51
58
|
## [1.3.34] - 2025-12-01
|
|
52
59
|
|
|
60
|
+
- Add generic `SettingsDrawer` component with `ListType` subcomponent for scope configuration
|
|
61
|
+
- Add Scope Config API integration (`useGetScopeConfig`, `useSetScopeConfig` hooks)
|
|
62
|
+
- Integrate Settings Drawer with Address Settings page
|
|
63
|
+
|
|
53
64
|
### Added
|
|
54
65
|
|
|
55
66
|
- Add DK Docs
|
|
56
67
|
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- Fix address ID mapping in default address service to use correct `id` field
|
|
71
|
+
|
|
57
72
|
## [1.3.33] - 2025-11-25
|
|
58
73
|
|
|
59
74
|
### Changed
|
|
@@ -362,7 +377,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
362
377
|
- Add CHANGELOG file
|
|
363
378
|
- Add README file
|
|
364
379
|
|
|
365
|
-
[unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/
|
|
380
|
+
[unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/1.3.42...HEAD
|
|
366
381
|
[1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.2.2...1.2.3
|
|
367
382
|
[1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.3
|
|
368
383
|
[1.2.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.4
|
|
@@ -411,3 +426,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
411
426
|
[1.3.37]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.36...v1.3.37
|
|
412
427
|
[1.3.36]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.35...v1.3.36
|
|
413
428
|
[1.3.35]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.35
|
|
429
|
+
|
|
430
|
+
[1.3.42]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.42
|
package/package.json
CHANGED
|
@@ -7,11 +7,14 @@ import { useUI, Skeleton } from "@faststore/ui";
|
|
|
7
7
|
import {
|
|
8
8
|
type BasicDrawerProps,
|
|
9
9
|
AutocompleteDropdown,
|
|
10
|
-
|
|
10
|
+
DEFAULT_LIST_TYPE_OPTIONS,
|
|
11
11
|
Icon,
|
|
12
|
+
ListTypeOption,
|
|
13
|
+
SettingsDrawer,
|
|
12
14
|
} from "../../../shared/components";
|
|
13
15
|
import { OptionSelected } from "../../../shared/components/OptionSelected/OptionSelected";
|
|
14
16
|
import { SearchHighlight } from "../../../shared/components/SearchHighlight/SearchHighlight";
|
|
17
|
+
import { useSetScopeConfig, SCOPE_KEYS } from "../../../shared/hooks";
|
|
15
18
|
import { ADDRESS_MESSAGES } from "../../constants/messages";
|
|
16
19
|
import { useDebouncedSearchAddressByUnitId } from "../../hooks/useDebouncedSearchAddressByUnitId";
|
|
17
20
|
import { useGetDefaultAddress } from "../../hooks/useGetDefaultAddresses";
|
|
@@ -28,14 +31,27 @@ export type CreateAddressSettingsDrawerProps = Omit<
|
|
|
28
31
|
onUpdate?: () => void;
|
|
29
32
|
};
|
|
30
33
|
|
|
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
|
+
];
|
|
45
|
+
|
|
31
46
|
export const CreateAddressSettingsDrawer = ({
|
|
32
47
|
close,
|
|
33
48
|
onUpdate,
|
|
34
|
-
...
|
|
49
|
+
...otherProps
|
|
35
50
|
}: CreateAddressSettingsDrawerProps) => {
|
|
36
51
|
const { pushToast } = useUI();
|
|
37
52
|
const router = useRouter();
|
|
38
53
|
|
|
54
|
+
const [listType, setListType] = useState<"sync" | "custom">("custom");
|
|
39
55
|
const [searchValue, setSearchValue] = useState("");
|
|
40
56
|
const [activeField, setActiveField] = useState<"Shipping" | "Billing" | null>(
|
|
41
57
|
"Shipping"
|
|
@@ -103,6 +119,21 @@ export const CreateAddressSettingsDrawer = ({
|
|
|
103
119
|
},
|
|
104
120
|
});
|
|
105
121
|
|
|
122
|
+
const { setScopeConfig, isSetScopeConfigLoading } = useSetScopeConfig({
|
|
123
|
+
onSuccess: () => {
|
|
124
|
+
pushToast({
|
|
125
|
+
message: "Scope configuration updated successfully",
|
|
126
|
+
status: "INFO",
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
onError: () => {
|
|
130
|
+
pushToast({
|
|
131
|
+
message: "Failed to update scope configuration",
|
|
132
|
+
status: "ERROR",
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
106
137
|
const isConfirmButtonEnabled = Boolean(
|
|
107
138
|
shippingAddress?.id || billingAddress?.id
|
|
108
139
|
);
|
|
@@ -125,6 +156,15 @@ export const CreateAddressSettingsDrawer = ({
|
|
|
125
156
|
}
|
|
126
157
|
|
|
127
158
|
const handleConfirmClick = () => {
|
|
159
|
+
// Update scope config if listType changed
|
|
160
|
+
setScopeConfig({
|
|
161
|
+
customerId: router.query.contractId as string,
|
|
162
|
+
unitId: router.query.orgUnitId as string,
|
|
163
|
+
scopeName: SCOPE_KEYS.ADDRESSES,
|
|
164
|
+
type: listType,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Set default addresses
|
|
128
168
|
setDefaultAddresses({
|
|
129
169
|
orgUnitId: router.query.orgUnitId as string,
|
|
130
170
|
customerId: router.query.contractId as string,
|
|
@@ -133,16 +173,29 @@ export const CreateAddressSettingsDrawer = ({
|
|
|
133
173
|
};
|
|
134
174
|
|
|
135
175
|
return (
|
|
136
|
-
<
|
|
137
|
-
|
|
176
|
+
<SettingsDrawer
|
|
177
|
+
title="Address settings"
|
|
178
|
+
{...otherProps}
|
|
138
179
|
close={close}
|
|
139
|
-
{
|
|
180
|
+
onPrimaryAction={handleConfirmClick}
|
|
181
|
+
isPrimaryButtonLoading={
|
|
182
|
+
isSetDefaultAddressesLoading || isSetScopeConfigLoading
|
|
183
|
+
}
|
|
184
|
+
isPrimaryButtonDisabled={!isConfirmButtonEnabled}
|
|
185
|
+
scopeName={SCOPE_KEYS.ADDRESSES}
|
|
186
|
+
onDismiss={close}
|
|
187
|
+
data-fs-bp-create-address-settings-drawer
|
|
140
188
|
>
|
|
141
|
-
<
|
|
189
|
+
<SettingsDrawer.ListType
|
|
190
|
+
title="List type"
|
|
191
|
+
name="listType"
|
|
192
|
+
value={listType}
|
|
193
|
+
onChange={setListType}
|
|
194
|
+
options={ADDRESS_LIST_TYPE_OPTIONS}
|
|
195
|
+
/>
|
|
142
196
|
|
|
143
|
-
<
|
|
144
|
-
<
|
|
145
|
-
<h4>Select the default addresses for this unit</h4>
|
|
197
|
+
<div data-fs-bp-default-addresses-section>
|
|
198
|
+
<h4 data-fs-bp-default-addresses-title>Default addresses</h4>
|
|
146
199
|
|
|
147
200
|
<p data-fs-bp-default-address-label>
|
|
148
201
|
Default shipping address (optional)
|
|
@@ -248,21 +301,7 @@ export const CreateAddressSettingsDrawer = ({
|
|
|
248
301
|
)}
|
|
249
302
|
/>
|
|
250
303
|
)}
|
|
251
|
-
</
|
|
252
|
-
|
|
253
|
-
<BasicDrawer.Footer>
|
|
254
|
-
<BasicDrawer.Button variant="ghost" onClick={close}>
|
|
255
|
-
Cancel
|
|
256
|
-
</BasicDrawer.Button>
|
|
257
|
-
<BasicDrawer.Button
|
|
258
|
-
variant="confirm"
|
|
259
|
-
disabled={!isConfirmButtonEnabled}
|
|
260
|
-
onClick={handleConfirmClick}
|
|
261
|
-
isLoading={isSetDefaultAddressesLoading}
|
|
262
|
-
>
|
|
263
|
-
Save
|
|
264
|
-
</BasicDrawer.Button>
|
|
265
|
-
</BasicDrawer.Footer>
|
|
266
|
-
</BasicDrawer>
|
|
304
|
+
</div>
|
|
305
|
+
</SettingsDrawer>
|
|
267
306
|
);
|
|
268
307
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
@import "../../../shared/components/
|
|
1
|
+
@import "../../../shared/components/SettingsDrawer/settings-drawer.scss";
|
|
2
2
|
|
|
3
3
|
[data-fs-bp-create-address-settings-drawer] {
|
|
4
4
|
@import "../../../shared/components/InputText/input-text.scss";
|
|
5
5
|
@import "../../../shared/components/ErrorMessage/error-message.scss";
|
|
6
6
|
@import "../../../shared/components/SearchHighlight/search-highlight.scss";
|
|
7
|
+
|
|
7
8
|
@import "../ExistingAddress/existing-address.scss";
|
|
8
9
|
@import "../LocationForm/location-form.scss";
|
|
9
10
|
@import "../RecipientsForm/recipients-form.scss";
|
|
@@ -12,33 +13,32 @@
|
|
|
12
13
|
@import "@faststore/ui/src/components/atoms/Button/styles.scss";
|
|
13
14
|
@import "@faststore/ui/src/components/molecules/Alert/styles.scss";
|
|
14
15
|
|
|
15
|
-
[data-fs-bp-
|
|
16
|
+
[data-fs-bp-settings-drawer-body] {
|
|
16
17
|
padding-bottom: 90px;
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
[data-fs-bp-default-addresses-section] {
|
|
20
|
+
[data-fs-bp-default-addresses-title] {
|
|
21
|
+
font-weight: var(--fs-text-weight-semibold);
|
|
22
|
+
font-size: var(--fs-text-size-1);
|
|
23
|
+
line-height: 1.25rem;
|
|
24
|
+
letter-spacing: -0.01em;
|
|
25
|
+
margin-bottom: var(--fs-spacing-4);
|
|
25
26
|
margin-top: 0;
|
|
27
|
+
color: #000000;
|
|
26
28
|
}
|
|
27
|
-
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
[data-fs-bp-default-address-label] {
|
|
31
|
+
font-weight: var(--fs-text-weight-regular);
|
|
32
|
+
font-size: var(--fs-text-size-1);
|
|
33
|
+
line-height: 1.5;
|
|
34
|
+
color: #1f1f1f;
|
|
35
|
+
margin-bottom: var(--fs-spacing-1);
|
|
36
|
+
margin-top: var(--fs-spacing-4);
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
color: #1f1f1f;
|
|
41
|
-
margin-bottom: var(--fs-spacing-0);
|
|
38
|
+
&:first-of-type {
|
|
39
|
+
margin-top: 0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -45,7 +45,7 @@ export const PaymentMethodsLayout = ({
|
|
|
45
45
|
setSelectedMethod(undefined);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
const isLastPage = data.paging
|
|
48
|
+
const isLastPage = data.paging?.pages === page || data.paging?.pages === 0;
|
|
49
49
|
|
|
50
50
|
const {
|
|
51
51
|
isLoading,
|
|
@@ -171,9 +171,9 @@ export const PaymentMethodsLayout = ({
|
|
|
171
171
|
textSearch={setSearchTerm}
|
|
172
172
|
/>
|
|
173
173
|
<Paginator.Counter
|
|
174
|
-
total={data.paging
|
|
174
|
+
total={data.paging?.total ?? 0}
|
|
175
175
|
itemsLength={
|
|
176
|
-
isLastPage ? data.paging
|
|
176
|
+
isLastPage ? data.paging?.total : page * data.paging?.perPage
|
|
177
177
|
}
|
|
178
178
|
/>
|
|
179
179
|
</div>
|
|
@@ -182,7 +182,7 @@ export const PaymentMethodsLayout = ({
|
|
|
182
182
|
|
|
183
183
|
{!isLoading && paymentMethods.length > 0 && (
|
|
184
184
|
<div data-fs-bp-payment-methods-paginator>
|
|
185
|
-
{data.paging
|
|
185
|
+
{(data.paging?.page ?? 1) > 1 ? (
|
|
186
186
|
<Paginator.NextPageButton
|
|
187
187
|
onClick={decreasePage}
|
|
188
188
|
disabled={isLoading}
|
|
@@ -204,9 +204,11 @@ export const PaymentMethodsLayout = ({
|
|
|
204
204
|
)}
|
|
205
205
|
|
|
206
206
|
<Paginator.Counter
|
|
207
|
-
total={data.paging
|
|
207
|
+
total={data.paging?.total ?? 0}
|
|
208
208
|
itemsLength={
|
|
209
|
-
isLastPage
|
|
209
|
+
isLastPage
|
|
210
|
+
? data.paging?.total ?? 0
|
|
211
|
+
: page * (data.paging?.perPage ?? 0)
|
|
210
212
|
}
|
|
211
213
|
/>
|
|
212
214
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getApiUrl } from "../../shared/utils";
|
|
1
|
+
import { getApiUrl, SCOPE_KEYS } from "../../shared/utils";
|
|
2
2
|
|
|
3
3
|
import { Client } from "./Client";
|
|
4
4
|
|
|
@@ -32,8 +32,44 @@ export default class ScopeClient extends Client {
|
|
|
32
32
|
}
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
getScopeConfig(
|
|
37
|
+
customerId: string,
|
|
38
|
+
unitId: string,
|
|
39
|
+
scopeName: string,
|
|
40
|
+
cookie: string
|
|
41
|
+
) {
|
|
42
|
+
return this.get<{ type: "sync" | "custom" }>(
|
|
43
|
+
`customers/${customerId}/units/${unitId}/scopes/configs`,
|
|
44
|
+
{
|
|
45
|
+
params: { scopeName },
|
|
46
|
+
headers: {
|
|
47
|
+
Cookie: cookie,
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setScopeConfig(
|
|
54
|
+
customerId: string,
|
|
55
|
+
unitId: string,
|
|
56
|
+
scopeName: string,
|
|
57
|
+
type: "sync" | "custom",
|
|
58
|
+
cookie: string
|
|
59
|
+
) {
|
|
60
|
+
return this.post<{ message: string }, { type: "sync" | "custom" }>(
|
|
61
|
+
`customers/${customerId}/units/${unitId}/scopes/configs`,
|
|
62
|
+
{ type },
|
|
63
|
+
{
|
|
64
|
+
params: { scopeName },
|
|
65
|
+
headers: {
|
|
66
|
+
Cookie: cookie,
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}
|
|
35
71
|
}
|
|
36
72
|
|
|
37
73
|
const scopesClient = new ScopeClient();
|
|
38
74
|
|
|
39
|
-
export { scopesClient };
|
|
75
|
+
export { scopesClient, SCOPE_KEYS };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { isDevelopment } from "../../utils/environment";
|
|
2
2
|
import { Icon } from "../Icon";
|
|
3
3
|
|
|
4
4
|
export type ErrorProps = {
|
|
@@ -20,18 +20,20 @@ export default function Error({ error }: ErrorProps) {
|
|
|
20
20
|
<button data-fs-bp-error-button onClick={() => window.location.reload()}>
|
|
21
21
|
Try again
|
|
22
22
|
</button>
|
|
23
|
-
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
23
|
+
{isDevelopment() && (
|
|
24
|
+
<div data-fs-bp-error-details>
|
|
25
|
+
<span data-fs-bp-error-details-type>{error?.tags?.errorType}</span>
|
|
26
|
+
<h2 data-fs-bp-error-details-title>Error Details</h2>
|
|
27
|
+
<p data-fs-bp-error-details-message>{error?.error.message}</p>
|
|
28
|
+
<p data-fs-bp-error-details-stack>Stack: {error?.error.stack}</p>
|
|
29
|
+
<p data-fs-bp-error-details-component>
|
|
30
|
+
Component: {error?.tags?.component}
|
|
31
|
+
</p>
|
|
32
|
+
<p data-fs-bp-error-details-query>
|
|
33
|
+
Query: {JSON.stringify(error?.query)}
|
|
34
|
+
</p>
|
|
35
|
+
</div>
|
|
36
|
+
)}
|
|
35
37
|
</div>
|
|
36
38
|
);
|
|
37
39
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { useBuyerPortal } from "../../hooks";
|
|
4
|
+
import { BasicDrawer, type BasicDrawerProps } from "../BasicDrawer/BasicDrawer";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
SettingsDrawerContext,
|
|
8
|
+
type SettingsDrawerContextType,
|
|
9
|
+
} from "./SettingsDrawerContext";
|
|
10
|
+
import {
|
|
11
|
+
SettingsDrawerListType,
|
|
12
|
+
type ListTypeOption,
|
|
13
|
+
type SettingsDrawerListTypeProps,
|
|
14
|
+
DEFAULT_LIST_TYPE_OPTIONS,
|
|
15
|
+
} from "./SettingsDrawerListType";
|
|
16
|
+
|
|
17
|
+
export type { ListTypeOption, SettingsDrawerListTypeProps };
|
|
18
|
+
export { DEFAULT_LIST_TYPE_OPTIONS };
|
|
19
|
+
|
|
20
|
+
export type SettingsDrawerProps = Omit<BasicDrawerProps, "children"> & {
|
|
21
|
+
title: string;
|
|
22
|
+
subtitle?: string;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
primaryButtonLabel?: string;
|
|
25
|
+
secondaryButtonLabel?: string;
|
|
26
|
+
onPrimaryAction?: () => void;
|
|
27
|
+
onSecondaryAction?: () => void;
|
|
28
|
+
isPrimaryButtonLoading?: boolean;
|
|
29
|
+
isPrimaryButtonDisabled?: boolean;
|
|
30
|
+
isSecondaryButtonDisabled?: boolean;
|
|
31
|
+
scopeName?: string;
|
|
32
|
+
customerId?: string;
|
|
33
|
+
unitId?: string;
|
|
34
|
+
onScopeConfigChange?: (type: "sync" | "custom") => void;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const SettingsDrawer = ({
|
|
38
|
+
title,
|
|
39
|
+
subtitle,
|
|
40
|
+
children,
|
|
41
|
+
primaryButtonLabel = "Save",
|
|
42
|
+
secondaryButtonLabel = "Cancel",
|
|
43
|
+
onPrimaryAction,
|
|
44
|
+
onSecondaryAction,
|
|
45
|
+
isPrimaryButtonLoading = false,
|
|
46
|
+
isPrimaryButtonDisabled = false,
|
|
47
|
+
isSecondaryButtonDisabled = false,
|
|
48
|
+
scopeName,
|
|
49
|
+
customerId,
|
|
50
|
+
unitId,
|
|
51
|
+
onScopeConfigChange,
|
|
52
|
+
onDismiss,
|
|
53
|
+
...otherProps
|
|
54
|
+
}: SettingsDrawerProps) => {
|
|
55
|
+
const { clientContext, currentOrgUnit, currentContract } = useBuyerPortal();
|
|
56
|
+
|
|
57
|
+
const resolvedCustomerId =
|
|
58
|
+
customerId || currentContract?.id || clientContext.customerId;
|
|
59
|
+
const resolvedUnitId = unitId || currentOrgUnit?.id || "";
|
|
60
|
+
|
|
61
|
+
const handleClose = () => {
|
|
62
|
+
onDismiss?.();
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const contextValue: SettingsDrawerContextType = {
|
|
66
|
+
scopeName,
|
|
67
|
+
customerId: resolvedCustomerId,
|
|
68
|
+
unitId: resolvedUnitId,
|
|
69
|
+
onScopeConfigChange,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<SettingsDrawerContext.Provider value={contextValue}>
|
|
74
|
+
<BasicDrawer
|
|
75
|
+
data-fs-bp-settings-drawer
|
|
76
|
+
onDismiss={handleClose}
|
|
77
|
+
{...otherProps}
|
|
78
|
+
>
|
|
79
|
+
<BasicDrawer.Heading title={title} onClose={handleClose} />
|
|
80
|
+
<BasicDrawer.Body data-fs-bp-settings-drawer-body>
|
|
81
|
+
{subtitle && <p data-fs-bp-settings-drawer-subtitle>{subtitle}</p>}
|
|
82
|
+
{children}
|
|
83
|
+
</BasicDrawer.Body>
|
|
84
|
+
<BasicDrawer.Footer>
|
|
85
|
+
<BasicDrawer.Button
|
|
86
|
+
variant="ghost"
|
|
87
|
+
onClick={onSecondaryAction || handleClose}
|
|
88
|
+
disabled={isSecondaryButtonDisabled}
|
|
89
|
+
>
|
|
90
|
+
{secondaryButtonLabel}
|
|
91
|
+
</BasicDrawer.Button>
|
|
92
|
+
<BasicDrawer.Button
|
|
93
|
+
variant="confirm"
|
|
94
|
+
onClick={onPrimaryAction}
|
|
95
|
+
isLoading={isPrimaryButtonLoading}
|
|
96
|
+
disabled={isPrimaryButtonDisabled}
|
|
97
|
+
>
|
|
98
|
+
{primaryButtonLabel}
|
|
99
|
+
</BasicDrawer.Button>
|
|
100
|
+
</BasicDrawer.Footer>
|
|
101
|
+
</BasicDrawer>
|
|
102
|
+
</SettingsDrawerContext.Provider>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
SettingsDrawer.ListType = SettingsDrawerListType;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
|
|
3
|
+
export type SettingsDrawerContextType = {
|
|
4
|
+
scopeName?: string;
|
|
5
|
+
customerId?: string;
|
|
6
|
+
unitId?: string;
|
|
7
|
+
onScopeConfigChange?: (type: "sync" | "custom") => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const SettingsDrawerContext = createContext<
|
|
11
|
+
SettingsDrawerContextType | undefined
|
|
12
|
+
>(undefined);
|
|
13
|
+
|
|
14
|
+
export const useSettingsDrawerContext = (): SettingsDrawerContextType => {
|
|
15
|
+
const context = useContext(SettingsDrawerContext);
|
|
16
|
+
return context || {};
|
|
17
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { RadioGroup, RadioOption } from "@faststore/ui";
|
|
4
|
+
|
|
5
|
+
import { useGetScopeConfig } from "../../hooks";
|
|
6
|
+
|
|
7
|
+
import { useSettingsDrawerContext } from "./SettingsDrawerContext";
|
|
8
|
+
|
|
9
|
+
export type ListTypeOption = {
|
|
10
|
+
value: string;
|
|
11
|
+
label: string;
|
|
12
|
+
description: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_LIST_TYPE_OPTIONS: ListTypeOption[] = [
|
|
16
|
+
{
|
|
17
|
+
value: "custom",
|
|
18
|
+
label: "Custom list",
|
|
19
|
+
description: "Manage a unique for this organization.",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
value: "sync",
|
|
23
|
+
label: "Synchronized List",
|
|
24
|
+
description:
|
|
25
|
+
"Use the shared list defined by the contract. Updates are automatic.",
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export type SettingsDrawerListTypeProps = {
|
|
30
|
+
title: string;
|
|
31
|
+
name: string;
|
|
32
|
+
value?: "sync" | "custom";
|
|
33
|
+
options?: ListTypeOption[];
|
|
34
|
+
onChange?: (value: "sync" | "custom") => void;
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const SettingsDrawerListType = ({
|
|
39
|
+
title,
|
|
40
|
+
name,
|
|
41
|
+
value: controlledValue,
|
|
42
|
+
options = DEFAULT_LIST_TYPE_OPTIONS,
|
|
43
|
+
onChange: controlledOnChange,
|
|
44
|
+
disabled = false,
|
|
45
|
+
}: SettingsDrawerListTypeProps) => {
|
|
46
|
+
const { scopeName, customerId, unitId } = useSettingsDrawerContext();
|
|
47
|
+
const [internalValue, setInternalValue] = useState<"sync" | "custom">(
|
|
48
|
+
"custom"
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const { isScopeConfigLoading } = useGetScopeConfig(
|
|
52
|
+
{
|
|
53
|
+
customerId: customerId ?? "",
|
|
54
|
+
unitId: unitId ?? "",
|
|
55
|
+
scopeName: scopeName ?? "",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
lazy: !scopeName || !customerId || !unitId,
|
|
59
|
+
onSuccess: (data) => {
|
|
60
|
+
if (data?.type) {
|
|
61
|
+
setInternalValue(data.type);
|
|
62
|
+
controlledOnChange?.(data.type);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const value = controlledValue ?? internalValue;
|
|
69
|
+
const isLoading = isScopeConfigLoading;
|
|
70
|
+
|
|
71
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
72
|
+
const newValue = e.target.value as "sync" | "custom";
|
|
73
|
+
setInternalValue(newValue);
|
|
74
|
+
|
|
75
|
+
if (controlledOnChange) {
|
|
76
|
+
controlledOnChange(newValue);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div data-fs-bp-settings-drawer-list-type>
|
|
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>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[data-fs-bp-settings-drawer] {
|
|
2
|
+
@import "@faststore/ui/src/components/atoms/Radio/styles.scss";
|
|
3
|
+
|
|
4
|
+
[data-fs-bp-settings-drawer-body] {
|
|
5
|
+
color: #1f1f1f;
|
|
6
|
+
font-size: var(--fs-text-size-1);
|
|
7
|
+
|
|
8
|
+
[data-fs-bp-settings-drawer-subtitle] {
|
|
9
|
+
margin-bottom: var(--fs-spacing-5);
|
|
10
|
+
color: #1f1f1f;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
[data-fs-bp-settings-drawer-list-type] {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
margin-bottom: var(--fs-spacing-4);
|
|
17
|
+
|
|
18
|
+
h4[data-fs-bp-settings-drawer-list-type-title] {
|
|
19
|
+
color: #000000;
|
|
20
|
+
font-weight: var(--fs-text-weight-semibold);
|
|
21
|
+
font-size: var(--fs-text-size-1);
|
|
22
|
+
line-height: 1.25rem;
|
|
23
|
+
letter-spacing: -0.01em;
|
|
24
|
+
margin-bottom: var(--fs-spacing-4);
|
|
25
|
+
margin-top: 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
[data-fs-bp-settings-drawer-list-type-option] {
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
margin-bottom: var(--fs-spacing-4);
|
|
32
|
+
|
|
33
|
+
&:last-child {
|
|
34
|
+
margin-bottom: 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[data-fs-radio-group-option] {
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: flex-start;
|
|
40
|
+
gap: var(--fs-spacing-2);
|
|
41
|
+
|
|
42
|
+
label {
|
|
43
|
+
font-weight: var(--fs-text-weight-regular);
|
|
44
|
+
font-size: var(--fs-text-size-1);
|
|
45
|
+
color: #1f1f1f;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[data-fs-bp-settings-drawer-list-type-description] {
|
|
50
|
+
margin-left: var(--fs-spacing-5);
|
|
51
|
+
margin-bottom: 0;
|
|
52
|
+
color: #707070;
|
|
53
|
+
font-weight: var(--fs-text-weight-regular);
|
|
54
|
+
font-size: var(--fs-bp-text-size-0);
|
|
55
|
+
line-height: var(--fs-bp-text-size-2);
|
|
56
|
+
letter-spacing: 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -7,6 +7,13 @@ export {
|
|
|
7
7
|
export { useAutocompletePosition } from "./AutocompleteDropdown/useAutocompletePosition";
|
|
8
8
|
export { BasicCard, type BasicCardProps } from "./BasicCard/BasicCard";
|
|
9
9
|
export { BasicDrawer, type BasicDrawerProps } from "./BasicDrawer/BasicDrawer";
|
|
10
|
+
export {
|
|
11
|
+
SettingsDrawer,
|
|
12
|
+
type SettingsDrawerProps,
|
|
13
|
+
type ListTypeOption,
|
|
14
|
+
type SettingsDrawerListTypeProps,
|
|
15
|
+
DEFAULT_LIST_TYPE_OPTIONS,
|
|
16
|
+
} from "./SettingsDrawer/SettingsDrawer";
|
|
10
17
|
export {
|
|
11
18
|
BasicDropdownMenu,
|
|
12
19
|
type BasicDropdownMenuProps,
|
|
@@ -15,3 +15,5 @@ export { useRouterLoading } from "./useRouterLoading";
|
|
|
15
15
|
export { useLogger } from "./useLogger";
|
|
16
16
|
export { useGetDependenciesVersion } from "./useGetDependenciesVersion";
|
|
17
17
|
export { useAnalytics } from "./analytics/useAnalytics";
|
|
18
|
+
export { useGetScopeConfig, SCOPE_KEYS } from "./useGetScopeConfig";
|
|
19
|
+
export { useSetScopeConfig } from "./useSetScopeConfig";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getScopeConfigService,
|
|
3
|
+
type GetScopeConfigServiceProps,
|
|
4
|
+
SCOPE_KEYS,
|
|
5
|
+
} from "../services";
|
|
6
|
+
|
|
7
|
+
import { useQuery, type QueryOptions } from "./useQuery";
|
|
8
|
+
|
|
9
|
+
import type { AwaitedType } from "../types";
|
|
10
|
+
|
|
11
|
+
export const useGetScopeConfig = (
|
|
12
|
+
props: Omit<GetScopeConfigServiceProps, "cookie">,
|
|
13
|
+
options?: QueryOptions<AwaitedType<typeof getScopeConfigService>>
|
|
14
|
+
) => {
|
|
15
|
+
const { data, error, isLoading, refetch } = useQuery<
|
|
16
|
+
AwaitedType<typeof getScopeConfigService>
|
|
17
|
+
>(
|
|
18
|
+
`scope-config-${props.customerId}-${props.unitId}-${props.scopeName}`,
|
|
19
|
+
(clientContext) =>
|
|
20
|
+
getScopeConfigService({
|
|
21
|
+
...props,
|
|
22
|
+
cookie: clientContext.cookie,
|
|
23
|
+
}),
|
|
24
|
+
options
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
scopeConfig: data,
|
|
29
|
+
isScopeConfigLoading: isLoading,
|
|
30
|
+
hasScopeConfigError: error,
|
|
31
|
+
refetchScopeConfig: refetch,
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export { SCOPE_KEYS };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
setScopeConfigService,
|
|
3
|
+
type SetScopeConfigServiceProps,
|
|
4
|
+
SCOPE_KEYS,
|
|
5
|
+
} from "../services";
|
|
6
|
+
|
|
7
|
+
import { type MutationOptions, useMutation } from "./useMutation";
|
|
8
|
+
|
|
9
|
+
import type { AwaitedType } from "../types";
|
|
10
|
+
|
|
11
|
+
export const useSetScopeConfig = (
|
|
12
|
+
options?: MutationOptions<AwaitedType<typeof setScopeConfigService>>
|
|
13
|
+
) => {
|
|
14
|
+
const { mutate, isLoading, error } = useMutation<
|
|
15
|
+
AwaitedType<typeof setScopeConfigService>,
|
|
16
|
+
Omit<SetScopeConfigServiceProps, "cookie">
|
|
17
|
+
>(
|
|
18
|
+
(variables, clientContext) =>
|
|
19
|
+
setScopeConfigService({ ...variables, cookie: clientContext.cookie }),
|
|
20
|
+
options
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
setScopeConfig: mutate,
|
|
25
|
+
isSetScopeConfigLoading: isLoading,
|
|
26
|
+
hasSetScopeConfigError: error,
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { SCOPE_KEYS };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { scopesClient, SCOPE_KEYS } from "../clients/ScopeClient";
|
|
2
|
+
|
|
3
|
+
export type GetScopeConfigServiceProps = {
|
|
4
|
+
customerId: string;
|
|
5
|
+
unitId: string;
|
|
6
|
+
scopeName: string;
|
|
7
|
+
cookie: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const getScopeConfigService = async ({
|
|
11
|
+
customerId,
|
|
12
|
+
unitId,
|
|
13
|
+
scopeName,
|
|
14
|
+
cookie,
|
|
15
|
+
}: GetScopeConfigServiceProps) => {
|
|
16
|
+
return scopesClient.getScopeConfig(customerId, unitId, scopeName, cookie);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export { SCOPE_KEYS };
|
|
@@ -15,3 +15,12 @@ export {
|
|
|
15
15
|
getDependenciesVersionService,
|
|
16
16
|
type GetDependenciesVersionProps,
|
|
17
17
|
} from "./get-dependencies-version.service";
|
|
18
|
+
export {
|
|
19
|
+
getScopeConfigService,
|
|
20
|
+
type GetScopeConfigServiceProps,
|
|
21
|
+
SCOPE_KEYS,
|
|
22
|
+
} from "./get-scope-config.service";
|
|
23
|
+
export {
|
|
24
|
+
setScopeConfigService,
|
|
25
|
+
type SetScopeConfigServiceProps,
|
|
26
|
+
} from "./set-scope-config.service";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { scopesClient, SCOPE_KEYS } from "../clients/ScopeClient";
|
|
2
|
+
|
|
3
|
+
export type SetScopeConfigServiceProps = {
|
|
4
|
+
customerId: string;
|
|
5
|
+
unitId: string;
|
|
6
|
+
scopeName: string;
|
|
7
|
+
type: "sync" | "custom";
|
|
8
|
+
cookie: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const setScopeConfigService = async ({
|
|
12
|
+
customerId,
|
|
13
|
+
unitId,
|
|
14
|
+
scopeName,
|
|
15
|
+
type,
|
|
16
|
+
cookie,
|
|
17
|
+
}: SetScopeConfigServiceProps) => {
|
|
18
|
+
return scopesClient.setScopeConfig(
|
|
19
|
+
customerId,
|
|
20
|
+
unitId,
|
|
21
|
+
scopeName,
|
|
22
|
+
type,
|
|
23
|
+
cookie
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export { SCOPE_KEYS };
|
|
@@ -13,4 +13,13 @@ export const LOCAL_STORAGE_LOCATION_EDIT_KEY = "bp_hide_edit_location_confirm";
|
|
|
13
13
|
export const LOCAL_STORAGE_RECIPIENT_EDIT_KEY =
|
|
14
14
|
"bp_hide_edit_recipient_confirm";
|
|
15
15
|
|
|
16
|
-
export const
|
|
16
|
+
export const SCOPE_KEYS = {
|
|
17
|
+
CONTRACTS: "contractIds",
|
|
18
|
+
ADDRESSES: "addresses",
|
|
19
|
+
CUSTOM_FIELDS: "customFields",
|
|
20
|
+
COLLECTIONS: "collectionIds",
|
|
21
|
+
PAYMENT_SYSTEMS: "paymentSystemIds",
|
|
22
|
+
CREDIT_CARDS: "creditCards",
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
export const CURRENT_VERSION = "1.3.42";
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export { addressLabelToPropMapping } from "./addresLabelToPropMapping";
|
|
2
2
|
export { getApiUrl, getPostalCodeApiUrl, getTokenizationUrl } from "./api";
|
|
3
3
|
export { compareItems } from "./compareItems";
|
|
4
|
-
export {
|
|
4
|
+
export {
|
|
5
|
+
API_URL,
|
|
6
|
+
AUT_COOKIE_KEY,
|
|
7
|
+
DEBOUNCE_TIMEOUT,
|
|
8
|
+
SCOPE_KEYS,
|
|
9
|
+
} from "./constants";
|
|
5
10
|
export {
|
|
6
11
|
getAuthCookie,
|
|
7
12
|
getCookieAsString,
|
|
@@ -82,7 +82,7 @@ const loaderFunction = async (
|
|
|
82
82
|
return {
|
|
83
83
|
data: paymentMethods,
|
|
84
84
|
search: search ?? "",
|
|
85
|
-
totalPaymentMethods: contractPaymentMethods
|
|
85
|
+
totalPaymentMethods: contractPaymentMethods?.paging?.total ?? 0,
|
|
86
86
|
context: {
|
|
87
87
|
clientContext: { cookie, userId, ...clientContext },
|
|
88
88
|
currentOrgUnit,
|