@vtex/faststore-plugin-buyer-portal 1.3.40 → 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.
Files changed (25) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/package.json +1 -1
  3. package/src/features/addresses/components/AddressForm/AddressForm.tsx +254 -220
  4. package/src/features/addresses/components/CreateAddressSettingsDrawer/CreateAddressSettingsDrawer.tsx +64 -25
  5. package/src/features/addresses/components/CreateAddressSettingsDrawer/create-address-settings-drawer.scss +22 -22
  6. package/src/features/addresses/services/default-values/get-default-address.service.ts +1 -1
  7. package/src/features/addresses/types/AddressData.ts +1 -0
  8. package/src/features/payment-methods/layouts/PaymentMethodsLayout/PaymentMethodsLayout.tsx +8 -6
  9. package/src/features/shared/clients/ScopeClient.ts +38 -2
  10. package/src/features/shared/components/Error/Error.tsx +15 -13
  11. package/src/features/shared/components/SettingsDrawer/SettingsDrawer.tsx +106 -0
  12. package/src/features/shared/components/SettingsDrawer/SettingsDrawerContext.tsx +17 -0
  13. package/src/features/shared/components/SettingsDrawer/SettingsDrawerListType.tsx +100 -0
  14. package/src/features/shared/components/SettingsDrawer/settings-drawer.scss +61 -0
  15. package/src/features/shared/components/index.ts +7 -0
  16. package/src/features/shared/hooks/index.ts +2 -0
  17. package/src/features/shared/hooks/useGetScopeConfig.ts +35 -0
  18. package/src/features/shared/hooks/useSetScopeConfig.ts +30 -0
  19. package/src/features/shared/services/get-scope-config.service.ts +19 -0
  20. package/src/features/shared/services/index.ts +9 -0
  21. package/src/features/shared/services/set-scope-config.service.ts +27 -0
  22. package/src/features/shared/types/index.ts +1 -0
  23. package/src/features/shared/utils/constants.ts +10 -1
  24. package/src/features/shared/utils/index.ts +6 -1
  25. package/src/pages/payment-methods.tsx +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ 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
+
17
+ ## [1.3.41] - 2025-12-09
18
+
19
+ ### Changed
20
+
21
+ - Refactor `<AddressForm>` to use conditional rendering in zipCode field
22
+
10
23
  ## [1.3.40] - 2025-12-09
11
24
 
12
25
  - Fix e2e auth flow
@@ -44,10 +57,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
44
57
 
45
58
  ## [1.3.34] - 2025-12-01
46
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
+
47
64
  ### Added
48
65
 
49
66
  - Add DK Docs
50
67
 
68
+ ### Fixed
69
+
70
+ - Fix address ID mapping in default address service to use correct `id` field
71
+
51
72
  ## [1.3.33] - 2025-11-25
52
73
 
53
74
  ### Changed
@@ -356,7 +377,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
356
377
  - Add CHANGELOG file
357
378
  - Add README file
358
379
 
359
- [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.40...HEAD
380
+ [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/1.3.42...HEAD
360
381
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.2.2...1.2.3
361
382
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.3
362
383
  [1.2.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.4
@@ -398,9 +419,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
398
419
  > > > > > > > main
399
420
  > > > > > > > [1.3.11]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.3.11
400
421
 
422
+ [1.3.41]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.40...v1.3.41
401
423
  [1.3.40]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.39...v1.3.40
402
424
  [1.3.39]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.38...v1.3.39
403
425
  [1.3.38]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.37...v1.3.38
404
426
  [1.3.37]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.36...v1.3.37
405
427
  [1.3.36]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.35...v1.3.36
406
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "1.3.40",
3
+ "version": "1.3.42",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -93,268 +93,302 @@ export const AddressForm = ({
93
93
  <div data-fs-create-address-form>
94
94
  {!hideLabel && <span>Fill in the address details</span>}
95
95
 
96
- <AutocompleteDropdown
97
- data-fs-bp-create-address-country-selector
98
- label="Country"
99
- className="address-country"
100
- value={address.country}
101
- options={CountryOptions}
102
- shouldCloseOnSelect
103
- onConfirmKeyPress={(option) => {
104
- handleCountrySelect(option);
105
- }}
106
- renderOption={(option, index) => (
107
- <AutocompleteDropdown.Item
108
- key={option.id}
109
- closeOnClick
110
- index={index}
111
- isSelected={address.country === option.name}
112
- onClick={() => {
113
- handleCountrySelect(option);
114
- }}
115
- >
116
- {option?.name}
117
- {address.country === option?.name && (
118
- <Icon name="Check" width={12} height={12} />
119
- )}
120
- </AutocompleteDropdown.Item>
121
- )}
122
- />
123
-
124
- {isEdit ? (
125
- <InputText
126
- label="Address Name"
127
- value={address.name}
128
- className="address-name"
129
- hasError={isTouched && !address.name?.trim()}
130
- onChange={(event) =>
131
- setAddress({ ...address, name: event.target.value })
132
- }
133
- />
134
- ) : (
96
+ <div key="country">
135
97
  <AutocompleteDropdown
136
- label="Address Name"
137
- value={autoCompleteAddressName}
138
- className="address-name"
139
- options={searchedAddresses}
98
+ data-fs-bp-create-address-country-selector
99
+ label="Country"
100
+ className="address-country"
101
+ value={address.country}
102
+ options={CountryOptions}
140
103
  shouldCloseOnSelect
141
- shouldShowArrowDown={false}
142
- placeholder="Already in the contract — select to add"
143
- onChange={(event) => {
144
- setAutoCompleteAddressName(event.currentTarget.value);
145
- setAddress({ ...address, name: event.target.value });
104
+ onConfirmKeyPress={(option) => {
105
+ handleCountrySelect(option);
146
106
  }}
147
107
  renderOption={(option, index) => (
148
108
  <AutocompleteDropdown.Item
149
109
  key={option.id}
150
110
  closeOnClick
151
111
  index={index}
152
- isSelected={completedAddress?.id === option?.id}
112
+ isSelected={address.country === option.name}
153
113
  onClick={() => {
154
- if (setCompletedAddress && setUseExistingAddress) {
155
- setCompletedAddress(option);
156
- setUseExistingAddress(true);
157
- }
114
+ handleCountrySelect(option);
158
115
  }}
159
116
  >
160
- <div data-fs-bp-autocomplete-dropdown-option-content>
161
- <p data-fs-bp-autocomplete-dropdown-option-title>
162
- {option?.name}
163
- </p>
164
- <p data-fs-bp-autocomplete-dropdown-option-subtitle>
165
- {option?.streetAddress}
166
- </p>
167
- </div>
168
- {completedAddress?.id === option?.id && (
117
+ {option?.name}
118
+ {address.country === option?.name && (
169
119
  <Icon name="Check" width={12} height={12} />
170
120
  )}
171
121
  </AutocompleteDropdown.Item>
172
122
  )}
173
123
  />
174
- )}
175
-
176
- <ErrorMessage
177
- show={isTouched && !address.name?.trim()}
178
- message="Address Name is required"
179
- />
180
-
181
- <InputText
182
- label="Street Address"
183
- value={address.streetAddress}
184
- className="street-address"
185
- hasError={isTouched && !address.streetAddress?.trim()}
186
- onChange={(event) =>
187
- setAddress({ ...address, streetAddress: event.target.value })
188
- }
189
- />
190
-
191
- <ErrorMessage
192
- show={isTouched && !address.streetAddress?.trim()}
193
- message="Street Address is required"
194
- />
124
+ </div>
195
125
 
196
126
  {address.countryCode === "BRA" && (
197
- <>
127
+ <div key="zipCode">
198
128
  <InputText
199
- type="number"
200
- label="Number"
201
- value={address.streetNumber || ""}
202
- className="address-number"
203
- hasError={isTouched && !address.streetNumber?.trim()}
204
- onChange={(event) =>
205
- setAddress({ ...address, streetNumber: event.target.value })
206
- }
129
+ label="Postal Code"
130
+ value={address.zip}
131
+ className="address-postal-code"
132
+ hasError={isTouched && !address.zip?.trim()}
133
+ onChange={(event) => handleChangePostalCode(event.target.value)}
134
+ disabled={!address.countryCode}
207
135
  />
208
-
209
136
  <ErrorMessage
210
- show={isTouched && !address.streetNumber?.trim()}
211
- message="Number is required"
137
+ show={isTouched && !address.zip?.trim()}
138
+ message="Postal Code is required"
212
139
  />
140
+ </div>
141
+ )}
213
142
 
143
+ <div key="addressName">
144
+ {isEdit ? (
214
145
  <InputText
215
- label="Neighborhood"
216
- value={address.neighborhood || ""}
217
- className="address-neighborhood"
218
- hasError={isTouched && !address.neighborhood?.trim()}
146
+ label="Address Name"
147
+ value={address.name}
148
+ className="address-name"
149
+ hasError={isTouched && !address.name?.trim()}
219
150
  onChange={(event) =>
220
- setAddress({ ...address, neighborhood: event.target.value })
151
+ setAddress({ ...address, name: event.target.value })
221
152
  }
222
153
  />
223
-
224
- <ErrorMessage
225
- show={isTouched && !address.neighborhood?.trim()}
226
- message="Neighborhood is required"
154
+ ) : (
155
+ <AutocompleteDropdown
156
+ label="Address Name"
157
+ value={autoCompleteAddressName}
158
+ className="address-name"
159
+ options={searchedAddresses}
160
+ shouldCloseOnSelect
161
+ shouldShowArrowDown={false}
162
+ placeholder="Already in the contract — select to add"
163
+ onChange={(event) => {
164
+ setAutoCompleteAddressName(event.currentTarget.value);
165
+ setAddress({ ...address, name: event.target.value });
166
+ }}
167
+ renderOption={(option, index) => (
168
+ <AutocompleteDropdown.Item
169
+ key={option.id}
170
+ closeOnClick
171
+ index={index}
172
+ isSelected={completedAddress?.id === option?.id}
173
+ onClick={() => {
174
+ if (setCompletedAddress && setUseExistingAddress) {
175
+ setCompletedAddress(option);
176
+ setUseExistingAddress(true);
177
+ }
178
+ }}
179
+ >
180
+ <div data-fs-bp-autocomplete-dropdown-option-content>
181
+ <p data-fs-bp-autocomplete-dropdown-option-title>
182
+ {option?.name}
183
+ </p>
184
+ <p data-fs-bp-autocomplete-dropdown-option-subtitle>
185
+ {option?.streetAddress}
186
+ </p>
187
+ </div>
188
+ {completedAddress?.id === option?.id && (
189
+ <Icon name="Check" width={12} height={12} />
190
+ )}
191
+ </AutocompleteDropdown.Item>
192
+ )}
227
193
  />
228
- </>
229
- )}
230
-
231
- <InputText
232
- label="Apt, Suite, Building (optional)"
233
- className="street-address2"
234
- value={address.streetAddress2}
235
- onChange={(event) =>
236
- setAddress({ ...address, streetAddress2: event.target.value })
237
- }
238
- />
239
-
240
- <InputText
241
- label="City"
242
- className="address-city"
243
- value={address.city}
244
- hasError={isTouched && !address.city?.trim()}
245
- onChange={(event) =>
246
- setAddress({ ...address, city: event.target.value })
247
- }
248
- />
194
+ )}
195
+ </div>
249
196
 
250
- <AutocompleteDropdown
251
- data-fs-bp-create-address-state-selector
252
- label="State"
253
- className="address-state"
254
- value={selectedState}
255
- options={getStateDisplayOptions(address.countryCode || "")}
256
- shouldCloseOnSelect
257
- onConfirmKeyPress={(option) => {
258
- const stateData = findStateByDisplayValue(
259
- address.countryCode || "",
260
- option
261
- );
262
- if (stateData) {
263
- setSelectedState(option);
264
- setAddress({ ...address, state: getStateSaveValue(stateData) });
197
+ <div key="streetAddress">
198
+ <InputText
199
+ label="Street Address"
200
+ value={address.streetAddress}
201
+ className="street-address"
202
+ hasError={isTouched && !address.streetAddress?.trim()}
203
+ onChange={(event) =>
204
+ setAddress({ ...address, streetAddress: event.target.value })
265
205
  }
266
- }}
267
- disabled={!address.countryCode}
268
- renderOption={(option, index) => {
269
- const stateData = findStateByDisplayValue(
270
- address.countryCode || "",
271
- option
272
- );
273
- const isSelected = stateData
274
- ? isStateSelected(address.state, stateData)
275
- : false;
276
-
277
- return (
278
- <AutocompleteDropdown.Item
279
- key={`${option}-${index}`}
280
- closeOnClick
281
- index={index}
282
- isSelected={isSelected}
283
- onClick={() => {
284
- if (stateData) {
285
- setSelectedState(getStateDisplayValue(stateData));
286
- setAddress({
287
- ...address,
288
- state: getStateSaveValue(stateData),
289
- });
290
- }
291
- }}
292
- >
293
- {option}
294
- {isSelected && <Icon name="Check" width={12} height={12} />}
295
- </AutocompleteDropdown.Item>
296
- );
297
- }}
298
- />
206
+ />
207
+ <ErrorMessage
208
+ show={isTouched && !address.streetAddress?.trim()}
209
+ message="Street Address is required"
210
+ />
211
+ </div>
299
212
 
300
- <InputText
301
- label="Postal Code"
302
- value={address.zip}
303
- className="address-postal-code"
304
- hasError={isTouched && !address.zip?.trim()}
305
- onChange={(event) => handleChangePostalCode(event.target.value)}
306
- disabled={!address.countryCode}
307
- />
213
+ {address.countryCode === "BRA" && (
214
+ <>
215
+ <div key="streetNumber">
216
+ <InputText
217
+ type="number"
218
+ label="Number"
219
+ value={address.streetNumber || ""}
220
+ className="address-number"
221
+ hasError={isTouched && !address.streetNumber?.trim()}
222
+ onChange={(event) =>
223
+ setAddress({ ...address, streetNumber: event.target.value })
224
+ }
225
+ />
226
+ <ErrorMessage
227
+ show={isTouched && !address.streetNumber?.trim()}
228
+ message="Number is required"
229
+ />
230
+ </div>
308
231
 
309
- <ErrorMessage
310
- show={isTouched && !address.zip?.trim()}
311
- message="Postal Code is required"
312
- />
232
+ <div key="neighborhood">
233
+ <InputText
234
+ label="Neighborhood"
235
+ value={address.neighborhood || ""}
236
+ className="address-neighborhood"
237
+ hasError={isTouched && !address.neighborhood?.trim()}
238
+ onChange={(event) =>
239
+ setAddress({ ...address, neighborhood: event.target.value })
240
+ }
241
+ />
242
+ <ErrorMessage
243
+ show={isTouched && !address.neighborhood?.trim()}
244
+ message="Neighborhood is required"
245
+ />
246
+ </div>
247
+ </>
248
+ )}
313
249
 
314
- <ErrorMessage
315
- show={isTouched && !address.city?.trim()}
316
- message="City is required"
317
- />
250
+ <div key="streetAddress2">
251
+ <InputText
252
+ label="Apt, Suite, Building (optional)"
253
+ className="street-address2"
254
+ value={address.streetAddress2}
255
+ onChange={(event) =>
256
+ setAddress({ ...address, streetAddress2: event.target.value })
257
+ }
258
+ />
259
+ </div>
318
260
 
319
- {isEdit && (
261
+ <div key="city">
320
262
  <InputText
321
- label="Geo coordinates (Optional)"
322
- value={address.geoCoordinates}
263
+ label="City"
264
+ className="address-city"
265
+ value={address.city}
266
+ hasError={isTouched && !address.city?.trim()}
323
267
  onChange={(event) =>
324
- setAddress({ ...address, geoCoordinates: event.target.value })
268
+ setAddress({ ...address, city: event.target.value })
325
269
  }
326
270
  />
327
- )}
271
+ <ErrorMessage
272
+ show={isTouched && !address.city?.trim()}
273
+ message="City is required"
274
+ />
275
+ </div>
328
276
 
329
- {!fixedType && (
277
+ <div key="state">
330
278
  <AutocompleteDropdown
331
- label="Address Type"
332
- value={address?.types.length > 0 ? address.types : ""}
333
- className="address-type"
334
- options={addressTypeOptions}
279
+ data-fs-bp-create-address-state-selector
280
+ label="State"
281
+ className="address-state"
282
+ value={selectedState}
283
+ options={getStateDisplayOptions(address.countryCode || "")}
335
284
  shouldCloseOnSelect
336
- onConfirmKeyPress={(option) =>
337
- setAddress({ ...address, types: [option] })
338
- }
339
- renderOption={(option, index) => (
340
- <AutocompleteDropdown.Item
341
- key={`${option}-${index}`}
342
- closeOnClick
343
- index={index}
344
- isSelected={
345
- address?.types[0]?.toLocaleLowerCase() ===
346
- option.toLocaleLowerCase()
347
- }
348
- onClick={() => setAddress({ ...address, types: [option] })}
349
- >
350
- {option}
351
- {address?.types[0]?.toLowerCase() ===
352
- option.toLocaleLowerCase() && (
353
- <Icon name="Check" width={12} height={12} />
354
- )}
355
- </AutocompleteDropdown.Item>
356
- )}
285
+ onConfirmKeyPress={(option) => {
286
+ const stateData = findStateByDisplayValue(
287
+ address.countryCode || "",
288
+ option
289
+ );
290
+ if (stateData) {
291
+ setSelectedState(option);
292
+ setAddress({
293
+ ...address,
294
+ state: getStateSaveValue(stateData),
295
+ });
296
+ }
297
+ }}
298
+ disabled={!address.countryCode}
299
+ renderOption={(option, index) => {
300
+ const stateData = findStateByDisplayValue(
301
+ address.countryCode || "",
302
+ option
303
+ );
304
+ const isSelected = stateData
305
+ ? isStateSelected(address.state, stateData)
306
+ : false;
307
+
308
+ return (
309
+ <AutocompleteDropdown.Item
310
+ key={`${option}-${index}`}
311
+ closeOnClick
312
+ index={index}
313
+ isSelected={isSelected}
314
+ onClick={() => {
315
+ if (stateData) {
316
+ setSelectedState(getStateDisplayValue(stateData));
317
+ setAddress({
318
+ ...address,
319
+ state: getStateSaveValue(stateData),
320
+ });
321
+ }
322
+ }}
323
+ >
324
+ {option}
325
+ {isSelected && <Icon name="Check" width={12} height={12} />}
326
+ </AutocompleteDropdown.Item>
327
+ );
328
+ }}
357
329
  />
330
+ </div>
331
+
332
+ {address.countryCode !== "BRA" && (
333
+ <div key="zipCode">
334
+ <InputText
335
+ label="Postal Code"
336
+ value={address.zip}
337
+ className="address-postal-code"
338
+ hasError={isTouched && !address.zip?.trim()}
339
+ onChange={(event) => handleChangePostalCode(event.target.value)}
340
+ disabled={!address.countryCode}
341
+ />
342
+ <ErrorMessage
343
+ show={isTouched && !address.zip?.trim()}
344
+ message="Postal Code is required"
345
+ />
346
+ </div>
347
+ )}
348
+
349
+ {isEdit && (
350
+ <div key="geoCoordinates">
351
+ <InputText
352
+ label="Geo coordinates (Optional)"
353
+ value={address.geoCoordinates}
354
+ onChange={(event) =>
355
+ setAddress({ ...address, geoCoordinates: event.target.value })
356
+ }
357
+ />
358
+ </div>
359
+ )}
360
+
361
+ {!fixedType && (
362
+ <div key="addressType">
363
+ <AutocompleteDropdown
364
+ label="Address Type"
365
+ value={address?.types.length > 0 ? address.types : ""}
366
+ className="address-type"
367
+ options={addressTypeOptions}
368
+ shouldCloseOnSelect
369
+ onConfirmKeyPress={(option) =>
370
+ setAddress({ ...address, types: [option] })
371
+ }
372
+ renderOption={(option, index) => (
373
+ <AutocompleteDropdown.Item
374
+ key={`${option}-${index}`}
375
+ closeOnClick
376
+ index={index}
377
+ isSelected={
378
+ address?.types[0]?.toLocaleLowerCase() ===
379
+ option.toLocaleLowerCase()
380
+ }
381
+ onClick={() => setAddress({ ...address, types: [option] })}
382
+ >
383
+ {option}
384
+ {address?.types[0]?.toLowerCase() ===
385
+ option.toLocaleLowerCase() && (
386
+ <Icon name="Check" width={12} height={12} />
387
+ )}
388
+ </AutocompleteDropdown.Item>
389
+ )}
390
+ />
391
+ </div>
358
392
  )}
359
393
  </div>
360
394
  ) : (