@vtex/faststore-plugin-buyer-portal 1.0.34 → 1.0.36
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 +13 -0
- package/README.md +29 -0
- package/package.json +1 -1
- package/public/buyer-portal-icons.svg +22 -1
- package/src/features/addresses/clients/AddressesClient.ts +86 -2
- package/src/features/addresses/clients/PostalCodeClient.ts +19 -0
- package/src/features/addresses/components/AddressDropdownMenu/AddressDropdownMenu.tsx +136 -0
- package/src/features/addresses/components/AddressDropdownMenu/address-dropdown-menu.scss +5 -0
- package/src/features/addresses/components/AddressForm/AddressForm.tsx +280 -0
- package/src/features/addresses/components/AddressLine/AddressLine.tsx +11 -9
- package/src/features/addresses/components/CreateAddressDrawer/CreateAddressDrawer.tsx +187 -0
- package/src/features/addresses/components/CreateAddressDrawer/create-address-drawer.scss +56 -0
- package/src/features/addresses/components/DeleteAddressDrawer/DeleteAddressDrawer.tsx +99 -0
- package/src/features/addresses/components/EditAddressDrawer/EditAddreessDrawer.tsx +100 -0
- package/src/features/addresses/components/EditAddressDrawer/edit-address-drawer.scss +29 -0
- package/src/features/addresses/components/ExistingAddress/ExistingAddress.tsx +35 -0
- package/src/features/addresses/components/ExistingAddress/existing-address.scss +23 -0
- package/src/features/addresses/components/LocationForm/LocationForm.tsx +52 -0
- package/src/features/addresses/components/LocationForm/location-form.scss +29 -0
- package/src/features/addresses/components/LocationsDrawer/LocationsDrawer.tsx +38 -0
- package/src/features/addresses/components/RecipientsDrawer/RecipientsDrawer.tsx +39 -0
- package/src/features/addresses/components/RecipientsForm/RecipientsForm.tsx +73 -0
- package/src/features/addresses/components/RecipientsForm/recipients-form.scss +29 -0
- package/src/features/addresses/components/RemoveAddressDrawer/RemoveAddressDrawer.tsx +80 -0
- package/src/features/addresses/components/RemoveAddressDrawer/remove-address-drawer.scss +15 -0
- package/src/features/addresses/components/index.ts +11 -0
- package/src/features/addresses/data/BRA.ts +29 -0
- package/src/features/addresses/data/CAN.ts +15 -0
- package/src/features/addresses/data/MEX.ts +34 -0
- package/src/features/addresses/data/USA.ts +65 -0
- package/src/features/addresses/data/countries.ts +6 -0
- package/src/features/addresses/data/states.ts +12 -0
- package/src/features/addresses/hooks/index.ts +3 -0
- package/src/features/addresses/hooks/useCreateAddress.ts +28 -0
- package/src/features/addresses/hooks/useDebouncedSearchAddress.ts +20 -0
- package/src/features/addresses/hooks/useDeleteAddress.ts +25 -0
- package/src/features/addresses/hooks/useEditAddress.ts +25 -0
- package/src/features/addresses/hooks/useSearchAddress.ts +20 -0
- package/src/features/addresses/layouts/AddressDetailsLayout/AddressDetailsLayout.tsx +55 -65
- package/src/features/addresses/layouts/AddressDetailsLayout/address-details-layout.scss +46 -15
- package/src/features/addresses/layouts/AddressesLayout/AddressesLayout.tsx +12 -5
- package/src/features/addresses/layouts/AddressesLayout/addresses-layout.scss +3 -0
- package/src/features/addresses/services/auto-complete-address.service.ts +16 -0
- package/src/features/addresses/services/create-new-address.service.ts +14 -0
- package/src/features/addresses/services/delete-address.service.ts +16 -0
- package/src/features/addresses/services/edit-address.service.ts +17 -0
- package/src/features/addresses/services/get-address-details.service.ts +15 -5
- package/src/features/addresses/services/get-addresses.service.ts +3 -3
- package/src/features/addresses/services/index.ts +20 -0
- package/src/features/addresses/services/search-address-by-name.service.ts +30 -0
- package/src/features/addresses/types/AddressData.ts +40 -4
- package/src/features/addresses/types/index.ts +7 -1
- package/src/features/shared/clients/ScopeClient.ts +37 -0
- package/src/features/shared/components/AutocompleteDropdown/autocomplete-dropdown.scss +0 -1
- package/src/features/shared/hooks/index.ts +2 -0
- package/src/features/shared/hooks/useAddToScope.ts +21 -0
- package/src/features/shared/hooks/useRemoveFromScope.ts +24 -0
- package/src/features/shared/services/add-to-scope.service.ts +21 -0
- package/src/features/shared/services/index.ts +8 -0
- package/src/features/shared/services/remove-from-scope.service.ts +21 -0
- package/src/features/shared/types/ScopeInput.d.ts +4 -0
- package/src/features/shared/types/index.ts +1 -0
- package/src/features/shared/utils/addresLabelToPropMapping.ts +7 -7
- package/src/features/shared/utils/api.ts +4 -0
- package/src/features/shared/utils/constants.ts +1 -1
- package/src/features/shared/utils/index.ts +1 -1
- package/src/features/shared/utils/mask.ts +51 -0
- package/src/features/shared/utils/postalCode.ts +79 -0
- package/src/pages/address-details.tsx +9 -2
- package/src/pages/addresses.tsx +2 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Add CHANGELOG file
|
|
13
|
+
- Add README file
|
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# How to Run the project locally
|
|
2
|
+
|
|
3
|
+
- Step 1: Clone this repo, the Buyer Portal
|
|
4
|
+
- Step 2: Clone the [store repo](https://github.com/vtex-sites/b2bfaststoredev.store)
|
|
5
|
+
- Open two terminals, one at the Buyer Portal Repo, one at the Store Repo
|
|
6
|
+
|
|
7
|
+
### On the Buyer Portal Repo:
|
|
8
|
+
|
|
9
|
+
- Run `yarn`
|
|
10
|
+
- Run `yarn link` - You will get a return with `@vtex/faststore-plugin-buyer-portal`
|
|
11
|
+
|
|
12
|
+
### On the Store Repo:
|
|
13
|
+
|
|
14
|
+
- Run `yarn link @vtex/faststore-plugin-buyer-portal`
|
|
15
|
+
- Run `yarn`
|
|
16
|
+
- Run `yarn dev` to start your local server
|
|
17
|
+
- You will acess the buyer-portal via: localhost:3000/buyer-portal
|
|
18
|
+
|
|
19
|
+
### Generating a Cookie
|
|
20
|
+
|
|
21
|
+
- Go to `b2bdev.vtexfaststore.com/login` and log-in as a user that has already been added to an Unit. If you don't have one, contact someone on the B2B - Enabler
|
|
22
|
+
- Go to copy your `VtexIdclientAutCookie_b2bfaststoredev` cookie
|
|
23
|
+
- Create a cookie in your `localhost:3000` with the `VtexIdclientAutCookie_b2bfaststoredev` key and the value you just copied
|
|
24
|
+
- Go to `localhost:3000/buyer-portal`
|
|
25
|
+
|
|
26
|
+
## Developing
|
|
27
|
+
|
|
28
|
+
- Make your changes at the Buyer Portal repo
|
|
29
|
+
- To test your changes, stop your local server and start it again with the `yarn dev` on the Store Repo
|
package/package.json
CHANGED
|
@@ -247,4 +247,25 @@
|
|
|
247
247
|
d="M3 18C2.45 18 1.97917 17.8042 1.5875 17.4125C1.19583 17.0208 1 16.55 1 16V3H0V1H5V0H11V1H16V3H15V16C15 16.55 14.8042 17.0208 14.4125 17.4125C14.0208 17.8042 13.55 18 13 18H3ZM13 3H3V16H13V3ZM5 14H7V5H5V14ZM9 14H11V5H9V14Z"
|
|
248
248
|
fill="currentColor" />
|
|
249
249
|
</symbol>
|
|
250
|
-
|
|
250
|
+
|
|
251
|
+
<symbol
|
|
252
|
+
id="ErrorX"
|
|
253
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
254
|
+
viewBox="0 -960 960 960"
|
|
255
|
+
fill="currentColor">
|
|
256
|
+
<path
|
|
257
|
+
d="m336-280 144-144 144 144 56-56-144-144 144-144-56-56-144 144-144-144-56 56 144 144-144 144 56 56ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Z"/>
|
|
258
|
+
</symbol>
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
<symbol
|
|
262
|
+
id="BookmarkAdd"
|
|
263
|
+
viewBox="0 0 13 15"
|
|
264
|
+
fill="currentColor"
|
|
265
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
266
|
+
<path d="M0 15V2.5C0 2.08181 0.145833 1.72375 0.4375 1.42583C0.729167 1.12806 1.08333 0.986111 1.5 1H6V2.5H1.5V12.7708L5 11.375L8.5 12.7708V7H10V15L5 13L0 15ZM9.25 5.25V3.75H7.75V2.25H9.25V0.75H10.75V2.25H12.25V3.75H10.75V5.25H9.25Z"/>
|
|
267
|
+
</symbol>
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
</svg>
|
|
271
|
+
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Client } from "../../shared/clients/Client";
|
|
2
2
|
import { getApiUrl } from "../../shared/utils";
|
|
3
|
-
import type { AddressData } from "../types";
|
|
3
|
+
import type { AddressData, AddressInput } from "../types";
|
|
4
|
+
|
|
5
|
+
type AddressList = {
|
|
6
|
+
addresses: AddressData[];
|
|
7
|
+
};
|
|
4
8
|
|
|
5
9
|
export default class AddressesClient extends Client {
|
|
6
10
|
constructor() {
|
|
@@ -8,7 +12,87 @@ export default class AddressesClient extends Client {
|
|
|
8
12
|
}
|
|
9
13
|
|
|
10
14
|
getAddressesByCustomerId(customerId: string, cookie: string) {
|
|
11
|
-
return this.get<
|
|
15
|
+
return this.get<AddressList>(`addresses/${customerId}`, {
|
|
16
|
+
headers: {
|
|
17
|
+
Cookie: cookie,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getAddressById(addressId: string, cookie: string) {
|
|
23
|
+
return this.get<AddressData>(`address/${addressId}`, {
|
|
24
|
+
headers: {
|
|
25
|
+
Cookie: cookie,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
searchAddressesByName(customerId: string, name: string, cookie: string) {
|
|
31
|
+
return this.get<AddressList>(
|
|
32
|
+
`search/addresses/${customerId}?name=${name}`,
|
|
33
|
+
{
|
|
34
|
+
headers: {
|
|
35
|
+
Cookie: cookie,
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
createNewAddress(data: AddressInput, customerId: string, cookie: string) {
|
|
42
|
+
const formatedAddress = {
|
|
43
|
+
name: data.name,
|
|
44
|
+
zip: data.zip,
|
|
45
|
+
streetAddress: data.streetAddress,
|
|
46
|
+
neighborhood: data.neighborhood,
|
|
47
|
+
streetAddress2: data.streetAddress2,
|
|
48
|
+
city: data.city,
|
|
49
|
+
state: data.state,
|
|
50
|
+
country: data.countryCode,
|
|
51
|
+
geoCordinates: data.geoCordinates,
|
|
52
|
+
types: data.types,
|
|
53
|
+
userId: customerId,
|
|
54
|
+
isActive: true,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return this.post(
|
|
58
|
+
`addresses/${customerId}`,
|
|
59
|
+
{ ...formatedAddress },
|
|
60
|
+
{
|
|
61
|
+
headers: {
|
|
62
|
+
Cookie: cookie,
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
editAddress(data: AddressInput, addressId: string, cookie: string) {
|
|
69
|
+
const formatedAddress = {
|
|
70
|
+
name: data.name,
|
|
71
|
+
zip: data.zip,
|
|
72
|
+
streetAddress: data.streetAddress,
|
|
73
|
+
neighborhood: data.neighborhood,
|
|
74
|
+
streetAddress2: data.streetAddress2,
|
|
75
|
+
city: data.city,
|
|
76
|
+
state: data.state,
|
|
77
|
+
country: data.countryCode,
|
|
78
|
+
geoCordinates: data.geoCordinates,
|
|
79
|
+
types: data.types,
|
|
80
|
+
isActive: true,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return this.patch(
|
|
84
|
+
`address/${addressId}`,
|
|
85
|
+
{ ...formatedAddress },
|
|
86
|
+
{
|
|
87
|
+
headers: {
|
|
88
|
+
Cookie: cookie,
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
deleteAddress(addressId: string, cookie: string) {
|
|
95
|
+
return this.delete(`address/${addressId}`, null, {
|
|
12
96
|
headers: {
|
|
13
97
|
Cookie: cookie,
|
|
14
98
|
},
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Client } from "../../shared/clients/Client";
|
|
2
|
+
import { getPostalCodeApiUrl } from "../../shared/utils";
|
|
3
|
+
import type { CheckoutAddress } from "../types";
|
|
4
|
+
|
|
5
|
+
export default class PostalCodeClient extends Client {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(getPostalCodeApiUrl());
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getAutoCompleteAddress(countryCode: string, postalCode: string) {
|
|
11
|
+
return this.get<CheckoutAddress>(`${countryCode}/${postalCode}`, {
|
|
12
|
+
ignoreContentType: true,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const postalCodeClient = new PostalCodeClient();
|
|
18
|
+
|
|
19
|
+
export { postalCodeClient };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { DropdownItem, Icon as UIIcon } from "@faststore/ui";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
EditAddressDrawer,
|
|
5
|
+
LocationDrawer,
|
|
6
|
+
RecipientsDrawer,
|
|
7
|
+
RemoveAddressDrawer,
|
|
8
|
+
DeleteAddressDrawer,
|
|
9
|
+
} from "..";
|
|
10
|
+
import { useDrawerProps, useBuyerPortal } from "../../../shared/hooks";
|
|
11
|
+
import { BasicDropdownMenu, Icon } from "../../../shared/components";
|
|
12
|
+
import type { AddressData } from "../../types";
|
|
13
|
+
|
|
14
|
+
export type AddressDropdownMenuProps = {
|
|
15
|
+
currentAddress: AddressData;
|
|
16
|
+
onUpdate?: () => void;
|
|
17
|
+
onCreate?: () => void;
|
|
18
|
+
isComplete?: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const AddressDropdownMenu = ({
|
|
22
|
+
currentAddress,
|
|
23
|
+
}: AddressDropdownMenuProps) => {
|
|
24
|
+
const { currentOrgUnit } = useBuyerPortal();
|
|
25
|
+
|
|
26
|
+
const idsPath = currentOrgUnit?.path?.ids?.split("/") ?? [];
|
|
27
|
+
|
|
28
|
+
const isRootLevelOrgUnit = idsPath.length === 2 ?? false;
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
open: openEditDrawerProps,
|
|
32
|
+
isOpen: isEditDrawerOpen,
|
|
33
|
+
...editDrawerProps
|
|
34
|
+
} = useDrawerProps();
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
open: openLocationDrawer,
|
|
38
|
+
isOpen: isOpenLocationDrawer,
|
|
39
|
+
...locationDrawerProps
|
|
40
|
+
} = useDrawerProps();
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
open: openRecipientsDrawer,
|
|
44
|
+
isOpen: isOpenRecipientsDrawer,
|
|
45
|
+
...recipientDrawerProps
|
|
46
|
+
} = useDrawerProps();
|
|
47
|
+
|
|
48
|
+
const {
|
|
49
|
+
open: openRemoveAddressDrawer,
|
|
50
|
+
isOpen: isOpenRemoveAddressDrawer,
|
|
51
|
+
...removeAddressDrawerProps
|
|
52
|
+
} = useDrawerProps();
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
open: openDeleteAddressDrawer,
|
|
56
|
+
isOpen: isOpenDeleteAddressDrawer,
|
|
57
|
+
...deleteAddressDrawerProps
|
|
58
|
+
} = useDrawerProps();
|
|
59
|
+
|
|
60
|
+
const sizeProps = { width: 20, height: 20 };
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<>
|
|
64
|
+
<BasicDropdownMenu data-fs-bp-dropdown-menu>
|
|
65
|
+
<DropdownItem onClick={openEditDrawerProps}>
|
|
66
|
+
<Icon name="Edit" {...sizeProps} />
|
|
67
|
+
Edit details
|
|
68
|
+
</DropdownItem>
|
|
69
|
+
<DropdownItem onClick={openLocationDrawer}>
|
|
70
|
+
<UIIcon name="PlusCircle" {...sizeProps} />
|
|
71
|
+
Add Locations
|
|
72
|
+
</DropdownItem>
|
|
73
|
+
<DropdownItem onClick={openRecipientsDrawer}>
|
|
74
|
+
<UIIcon name="PlusCircle" {...sizeProps} />
|
|
75
|
+
Add Recipients
|
|
76
|
+
</DropdownItem>
|
|
77
|
+
<DropdownItem onClick={() => {}}>
|
|
78
|
+
<Icon name="BookmarkAdd" {...sizeProps} />
|
|
79
|
+
Set as Default
|
|
80
|
+
</DropdownItem>
|
|
81
|
+
|
|
82
|
+
<BasicDropdownMenu.Separator />
|
|
83
|
+
<DropdownItem onClick={openRemoveAddressDrawer}>
|
|
84
|
+
<UIIcon name="MinusCircle" {...sizeProps} />
|
|
85
|
+
Remove from Unit
|
|
86
|
+
</DropdownItem>
|
|
87
|
+
{isRootLevelOrgUnit && (
|
|
88
|
+
<DropdownItem onClick={openDeleteAddressDrawer}>
|
|
89
|
+
<Icon name="Trash" {...sizeProps} data-fs-bp-delete-address />
|
|
90
|
+
<span data-fs-bp-delete-address>Delete</span>
|
|
91
|
+
</DropdownItem>
|
|
92
|
+
)}
|
|
93
|
+
</BasicDropdownMenu>
|
|
94
|
+
{isEditDrawerOpen && (
|
|
95
|
+
<EditAddressDrawer
|
|
96
|
+
readonly
|
|
97
|
+
{...editDrawerProps}
|
|
98
|
+
isOpen={isEditDrawerOpen}
|
|
99
|
+
currentAddress={currentAddress}
|
|
100
|
+
/>
|
|
101
|
+
)}
|
|
102
|
+
{isOpenLocationDrawer && (
|
|
103
|
+
<LocationDrawer
|
|
104
|
+
readonly
|
|
105
|
+
{...locationDrawerProps}
|
|
106
|
+
isOpen={isOpenLocationDrawer}
|
|
107
|
+
/>
|
|
108
|
+
)}
|
|
109
|
+
{isOpenRecipientsDrawer && (
|
|
110
|
+
<RecipientsDrawer
|
|
111
|
+
readonly
|
|
112
|
+
{...recipientDrawerProps}
|
|
113
|
+
isOpen={isOpenRecipientsDrawer}
|
|
114
|
+
/>
|
|
115
|
+
)}
|
|
116
|
+
{isOpenRemoveAddressDrawer && (
|
|
117
|
+
<RemoveAddressDrawer
|
|
118
|
+
readonly
|
|
119
|
+
addressName={currentAddress.name}
|
|
120
|
+
addressId={currentAddress.id}
|
|
121
|
+
{...removeAddressDrawerProps}
|
|
122
|
+
isOpen={isOpenRemoveAddressDrawer}
|
|
123
|
+
/>
|
|
124
|
+
)}
|
|
125
|
+
{isOpenDeleteAddressDrawer && (
|
|
126
|
+
<DeleteAddressDrawer
|
|
127
|
+
readonly
|
|
128
|
+
addressName={currentAddress.name}
|
|
129
|
+
addressId={currentAddress.id}
|
|
130
|
+
isOpen={isOpenDeleteAddressDrawer}
|
|
131
|
+
{...deleteAddressDrawerProps}
|
|
132
|
+
/>
|
|
133
|
+
)}
|
|
134
|
+
</>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InputText,
|
|
3
|
+
ErrorMessage,
|
|
4
|
+
AutocompleteDropdown,
|
|
5
|
+
Icon,
|
|
6
|
+
} from "../../../shared/components";
|
|
7
|
+
import { CountryOptions } from "../../data/countries";
|
|
8
|
+
import { states as StateOptions } from "../../data/states";
|
|
9
|
+
import {
|
|
10
|
+
maskPostalCode,
|
|
11
|
+
validatePostalCode,
|
|
12
|
+
} from "../../../shared/utils/postalCode";
|
|
13
|
+
import { AddressData, AddressInput } from "../../types";
|
|
14
|
+
import { useState } from "react";
|
|
15
|
+
import { useDebouncedSearchAddress } from "../../hooks/useDebouncedSearchAddress";
|
|
16
|
+
import { ExistingAddress } from "../";
|
|
17
|
+
|
|
18
|
+
export type AddressFormProps = {
|
|
19
|
+
address: AddressInput;
|
|
20
|
+
isTouched: boolean;
|
|
21
|
+
setAddress: (address: AddressInput) => void;
|
|
22
|
+
setInvalidAddress: (hasError: boolean) => void;
|
|
23
|
+
useExistingAddress?: boolean;
|
|
24
|
+
setUseExistingAddress?: (useExistingAddress: boolean) => void;
|
|
25
|
+
completedAddress?: AddressData;
|
|
26
|
+
setCompletedAddress?: (completedAddress: AddressData) => void;
|
|
27
|
+
isEdit?: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const AddressForm = ({
|
|
31
|
+
address,
|
|
32
|
+
isTouched,
|
|
33
|
+
setAddress,
|
|
34
|
+
setInvalidAddress,
|
|
35
|
+
isEdit = false,
|
|
36
|
+
useExistingAddress,
|
|
37
|
+
setUseExistingAddress,
|
|
38
|
+
completedAddress,
|
|
39
|
+
setCompletedAddress,
|
|
40
|
+
}: AddressFormProps) => {
|
|
41
|
+
const addressTypeOptions = ["Shipping", "Billing", "Sold To"];
|
|
42
|
+
|
|
43
|
+
const [autoCompleteAddressName, setAutoCompleteAddressName] = useState("");
|
|
44
|
+
|
|
45
|
+
const { searchedAddresses } = useDebouncedSearchAddress(
|
|
46
|
+
autoCompleteAddressName
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const cleanCompletedAddress = () => {
|
|
50
|
+
if (setCompletedAddress && setUseExistingAddress) {
|
|
51
|
+
setCompletedAddress({} as AddressData);
|
|
52
|
+
setUseExistingAddress(false);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const handleChangePostalCode = (postalCode: string) => {
|
|
57
|
+
setAddress({
|
|
58
|
+
...address,
|
|
59
|
+
zip: maskPostalCode(postalCode, address.countryCode),
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
{!useExistingAddress ? (
|
|
66
|
+
<div>
|
|
67
|
+
{!isEdit && <span>Fill in the address details</span>}
|
|
68
|
+
|
|
69
|
+
<AutocompleteDropdown
|
|
70
|
+
data-fs-bp-create-address-country-selector
|
|
71
|
+
label="Country"
|
|
72
|
+
value={address.country}
|
|
73
|
+
options={CountryOptions}
|
|
74
|
+
onConfirmKeyPress={(option) =>
|
|
75
|
+
setAddress({
|
|
76
|
+
...address,
|
|
77
|
+
country: option.name,
|
|
78
|
+
countryCode: option.id,
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
renderOption={(option, index) => (
|
|
82
|
+
<AutocompleteDropdown.Item
|
|
83
|
+
key={option.id}
|
|
84
|
+
closeOnClick={true}
|
|
85
|
+
index={index}
|
|
86
|
+
isSelected={address.country === option.name}
|
|
87
|
+
onClick={() =>
|
|
88
|
+
setAddress({
|
|
89
|
+
...address,
|
|
90
|
+
country: option.name,
|
|
91
|
+
countryCode: option.id,
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
>
|
|
95
|
+
{option?.name}
|
|
96
|
+
{address.country === option?.name && (
|
|
97
|
+
<Icon name="Check" width={12} height={12} />
|
|
98
|
+
)}
|
|
99
|
+
</AutocompleteDropdown.Item>
|
|
100
|
+
)}
|
|
101
|
+
/>
|
|
102
|
+
|
|
103
|
+
{isEdit ? (
|
|
104
|
+
<InputText
|
|
105
|
+
label="Address Name"
|
|
106
|
+
value={address.name}
|
|
107
|
+
wrapperProps={{ style: { marginTop: 16 } }}
|
|
108
|
+
hasError={isTouched && !address.name?.trim()}
|
|
109
|
+
onChange={(event) =>
|
|
110
|
+
setAddress({ ...address, name: event.target.value })
|
|
111
|
+
}
|
|
112
|
+
/>
|
|
113
|
+
) : (
|
|
114
|
+
<AutocompleteDropdown
|
|
115
|
+
label="Address Name"
|
|
116
|
+
value={autoCompleteAddressName}
|
|
117
|
+
options={searchedAddresses}
|
|
118
|
+
onChange={(event) => {
|
|
119
|
+
setAutoCompleteAddressName(event.currentTarget.value);
|
|
120
|
+
setAddress({ ...address, name: event.target.value });
|
|
121
|
+
}}
|
|
122
|
+
renderOption={(option, index) => (
|
|
123
|
+
<AutocompleteDropdown.Item
|
|
124
|
+
key={option.id}
|
|
125
|
+
closeOnClick
|
|
126
|
+
index={index}
|
|
127
|
+
isSelected={completedAddress?.id === option?.id}
|
|
128
|
+
onClick={() => {
|
|
129
|
+
if (setCompletedAddress && setUseExistingAddress) {
|
|
130
|
+
setCompletedAddress(option);
|
|
131
|
+
setUseExistingAddress(true);
|
|
132
|
+
}
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
{option?.name}
|
|
136
|
+
<br />
|
|
137
|
+
{option?.streetAddress}
|
|
138
|
+
{completedAddress?.id === option?.id && (
|
|
139
|
+
<Icon name="Check" width={12} height={12} />
|
|
140
|
+
)}
|
|
141
|
+
</AutocompleteDropdown.Item>
|
|
142
|
+
)}
|
|
143
|
+
/>
|
|
144
|
+
)}
|
|
145
|
+
|
|
146
|
+
<ErrorMessage
|
|
147
|
+
show={isTouched && !address.name?.trim()}
|
|
148
|
+
message="Address Name is required"
|
|
149
|
+
/>
|
|
150
|
+
|
|
151
|
+
<InputText
|
|
152
|
+
label="Street Address"
|
|
153
|
+
value={address.streetAddress}
|
|
154
|
+
wrapperProps={{ style: { marginTop: 16 } }}
|
|
155
|
+
hasError={isTouched && !address.streetAddress?.trim()}
|
|
156
|
+
onChange={(event) =>
|
|
157
|
+
setAddress({ ...address, streetAddress: event.target.value })
|
|
158
|
+
}
|
|
159
|
+
/>
|
|
160
|
+
|
|
161
|
+
<ErrorMessage
|
|
162
|
+
show={isTouched && !address.streetAddress?.trim()}
|
|
163
|
+
message="Street Address is required"
|
|
164
|
+
/>
|
|
165
|
+
|
|
166
|
+
<InputText
|
|
167
|
+
label="Apt, Suite, Building (optional)"
|
|
168
|
+
value={address.streetAddress2}
|
|
169
|
+
wrapperProps={{ style: { marginTop: 16, marginBottom: 16 } }}
|
|
170
|
+
onChange={(event) =>
|
|
171
|
+
setAddress({ ...address, streetAddress2: event.target.value })
|
|
172
|
+
}
|
|
173
|
+
/>
|
|
174
|
+
|
|
175
|
+
<InputText
|
|
176
|
+
label="City"
|
|
177
|
+
value={address.city}
|
|
178
|
+
wrapperProps={{ style: { marginTop: 16 } }}
|
|
179
|
+
hasError={isTouched && !address.city?.trim()}
|
|
180
|
+
onChange={(event) =>
|
|
181
|
+
setAddress({ ...address, city: event.target.value })
|
|
182
|
+
}
|
|
183
|
+
/>
|
|
184
|
+
|
|
185
|
+
<AutocompleteDropdown
|
|
186
|
+
data-fs-bp-create-address-state-selector
|
|
187
|
+
label="State"
|
|
188
|
+
value={address.state}
|
|
189
|
+
options={
|
|
190
|
+
address.countryCode ? StateOptions[address.countryCode] : []
|
|
191
|
+
}
|
|
192
|
+
onConfirmKeyPress={(option) =>
|
|
193
|
+
setAddress({ ...address, state: option })
|
|
194
|
+
}
|
|
195
|
+
disabled={!address.countryCode}
|
|
196
|
+
renderOption={(option, index) => (
|
|
197
|
+
<AutocompleteDropdown.Item
|
|
198
|
+
key={`${option}-${index}`}
|
|
199
|
+
closeOnClick={true}
|
|
200
|
+
index={index}
|
|
201
|
+
isSelected={
|
|
202
|
+
address.state.toLocaleLowerCase() ===
|
|
203
|
+
option.toLocaleLowerCase()
|
|
204
|
+
}
|
|
205
|
+
onClick={() => setAddress({ ...address, state: option })}
|
|
206
|
+
>
|
|
207
|
+
{option}
|
|
208
|
+
{address.state.toLowerCase() === option.toLocaleLowerCase() && (
|
|
209
|
+
<Icon name="Check" width={12} height={12} />
|
|
210
|
+
)}
|
|
211
|
+
</AutocompleteDropdown.Item>
|
|
212
|
+
)}
|
|
213
|
+
/>
|
|
214
|
+
|
|
215
|
+
<InputText
|
|
216
|
+
label="Postal Code"
|
|
217
|
+
value={address.zip}
|
|
218
|
+
wrapperProps={{ style: { marginTop: 16, marginBottom: 16 } }}
|
|
219
|
+
hasError={isTouched && !address.zip?.trim()}
|
|
220
|
+
onChange={(event) => handleChangePostalCode(event.target.value)}
|
|
221
|
+
disabled={!address.countryCode}
|
|
222
|
+
/>
|
|
223
|
+
|
|
224
|
+
<ErrorMessage
|
|
225
|
+
show={isTouched && !address.zip?.trim()}
|
|
226
|
+
message="Postal Code is required"
|
|
227
|
+
/>
|
|
228
|
+
|
|
229
|
+
<ErrorMessage
|
|
230
|
+
show={isTouched && !address.city?.trim()}
|
|
231
|
+
message="City is required"
|
|
232
|
+
/>
|
|
233
|
+
|
|
234
|
+
{isEdit && (
|
|
235
|
+
<InputText
|
|
236
|
+
label="Geo coordinates (Optional)"
|
|
237
|
+
value={address.geoCordinates}
|
|
238
|
+
wrapperProps={{ style: { marginTop: 16, marginBottom: 16 } }}
|
|
239
|
+
onChange={(event) =>
|
|
240
|
+
setAddress({ ...address, geoCordinates: event.target.value })
|
|
241
|
+
}
|
|
242
|
+
/>
|
|
243
|
+
)}
|
|
244
|
+
|
|
245
|
+
<AutocompleteDropdown
|
|
246
|
+
label="Address Type"
|
|
247
|
+
value={address.types}
|
|
248
|
+
options={addressTypeOptions}
|
|
249
|
+
onConfirmKeyPress={(option) =>
|
|
250
|
+
setAddress({ ...address, types: [option] })
|
|
251
|
+
}
|
|
252
|
+
renderOption={(option, index) => (
|
|
253
|
+
<AutocompleteDropdown.Item
|
|
254
|
+
key={`${option}-${index}`}
|
|
255
|
+
closeOnClick={true}
|
|
256
|
+
index={index}
|
|
257
|
+
isSelected={
|
|
258
|
+
address?.types[0]?.toLocaleLowerCase() ===
|
|
259
|
+
option.toLocaleLowerCase()
|
|
260
|
+
}
|
|
261
|
+
onClick={() => setAddress({ ...address, types: [option] })}
|
|
262
|
+
>
|
|
263
|
+
{option}
|
|
264
|
+
{address?.types[0]?.toLowerCase() ===
|
|
265
|
+
option.toLocaleLowerCase() && (
|
|
266
|
+
<Icon name="Check" width={12} height={12} />
|
|
267
|
+
)}
|
|
268
|
+
</AutocompleteDropdown.Item>
|
|
269
|
+
)}
|
|
270
|
+
/>
|
|
271
|
+
</div>
|
|
272
|
+
) : (
|
|
273
|
+
<ExistingAddress
|
|
274
|
+
addressName={completedAddress?.name ?? ""}
|
|
275
|
+
removeExistingAddress={cleanCompletedAddress}
|
|
276
|
+
/>
|
|
277
|
+
)}
|
|
278
|
+
</>
|
|
279
|
+
);
|
|
280
|
+
};
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { Toggle } from "@faststore/ui";
|
|
1
|
+
import { Toggle, Dropdown, DropdownButton } from "@faststore/ui";
|
|
2
2
|
import Link from "next/link";
|
|
3
3
|
import { Icon, Tag } from "../../../shared/components";
|
|
4
|
+
import type { AddressData } from "../../types";
|
|
5
|
+
import { AddressDropdownMenu } from "..";
|
|
4
6
|
|
|
5
7
|
export type AddressLineProps = {
|
|
6
8
|
id: string;
|
|
7
9
|
name: string;
|
|
8
10
|
types: string[];
|
|
9
11
|
isActive: boolean;
|
|
12
|
+
currentAddress: AddressData;
|
|
10
13
|
href: string;
|
|
11
14
|
};
|
|
12
15
|
|
|
@@ -16,6 +19,7 @@ export const AddressLine = ({
|
|
|
16
19
|
id,
|
|
17
20
|
href,
|
|
18
21
|
isActive,
|
|
22
|
+
currentAddress,
|
|
19
23
|
}: AddressLineProps) => (
|
|
20
24
|
<li data-fs-addresses-line>
|
|
21
25
|
<Link href={href} data-fs-addresses-line-link>
|
|
@@ -35,13 +39,11 @@ export const AddressLine = ({
|
|
|
35
39
|
</Link>
|
|
36
40
|
|
|
37
41
|
<Toggle id={id} defaultChecked={isActive} />
|
|
38
|
-
<
|
|
39
|
-
<
|
|
40
|
-
name="MoreVert"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
/>
|
|
45
|
-
</button>
|
|
42
|
+
<Dropdown>
|
|
43
|
+
<DropdownButton data-fs-addresses-dropdown-trigger>
|
|
44
|
+
<Icon name="MoreVert" data-fs-addresses-dropdown-trigger-icon />
|
|
45
|
+
</DropdownButton>
|
|
46
|
+
<AddressDropdownMenu currentAddress={currentAddress} />
|
|
47
|
+
</Dropdown>
|
|
46
48
|
</li>
|
|
47
49
|
);
|