@salesforce/retail-react-app 7.1.0-preview.0 → 8.0.0-dev
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 +8 -4
- package/app/components/_app/index.jsx +9 -7
- package/app/components/_app/index.test.js +2 -2
- package/app/components/_app-config/index.jsx +9 -3
- package/app/components/drawer-menu/drawer-menu.jsx +3 -1
- package/app/components/footer/index.jsx +3 -1
- package/app/components/header/index.jsx +3 -1
- package/app/components/header/index.test.js +2 -2
- package/app/components/island/README.md +1 -1
- package/app/components/island/index.jsx +3 -1
- package/app/components/island/index.test.js +94 -5
- package/app/components/item-variant/item-attributes.jsx +12 -3
- package/app/components/multiship/multiship-order-summary.jsx +137 -0
- package/app/components/multiship/multiship-order-summary.test.js +121 -0
- package/app/components/order-summary/index.jsx +2 -4
- package/app/components/pickup-or-delivery/index.jsx +80 -0
- package/app/components/pickup-or-delivery/index.test.jsx +182 -0
- package/app/components/product-item/index.jsx +26 -16
- package/app/components/product-item/index.test.js +29 -2
- package/app/components/product-item-list/index.jsx +10 -0
- package/app/components/product-item-list/index.test.jsx +14 -0
- package/app/components/product-view/index.jsx +9 -6
- package/app/components/product-view/index.test.js +25 -21
- package/app/components/quantity-picker/index.test.jsx +12 -12
- package/app/components/reset-password/index.test.js +1 -1
- package/app/components/shared/ui/AlertDescription/index.jsx +8 -0
- package/app/components/shared/ui/index.jsx +1 -0
- package/app/components/store-display/index.jsx +28 -4
- package/app/components/store-display/index.test.js +71 -0
- package/app/components/store-locator/form.test.jsx +16 -4
- package/app/components/store-locator/list.jsx +9 -4
- package/app/components/toggle-card/index.jsx +14 -0
- package/app/components/unavailable-product-confirmation-modal/index.jsx +19 -5
- package/app/components/unavailable-product-confirmation-modal/index.test.js +122 -1
- package/app/constants.js +20 -6
- package/app/contexts/store-locator-provider.jsx +7 -1
- package/app/contexts/store-locator-provider.test.jsx +36 -1
- package/app/hooks/use-address-form.js +155 -0
- package/app/hooks/use-address-form.test.js +501 -0
- package/app/hooks/use-auth-modal.js +2 -6
- package/app/hooks/use-current-basket.js +71 -2
- package/app/hooks/use-current-basket.test.js +37 -1
- package/app/hooks/use-dnt-notification.js +4 -4
- package/app/hooks/use-dnt-notification.test.js +5 -5
- package/app/hooks/use-item-shipment-management.js +233 -0
- package/app/hooks/use-item-shipment-management.test.js +696 -0
- package/app/hooks/use-multiship.js +589 -0
- package/app/hooks/use-multiship.test.js +776 -0
- package/app/hooks/use-pickup-shipment.js +70 -106
- package/app/hooks/use-pickup-shipment.test.js +345 -209
- package/app/hooks/use-product-address-assignment.js +280 -0
- package/app/hooks/use-product-address-assignment.test.js +414 -0
- package/app/hooks/use-product-inventory.js +100 -0
- package/app/hooks/use-product-inventory.test.js +254 -0
- package/app/hooks/use-shipment-operations.js +168 -0
- package/app/hooks/use-shipment-operations.test.js +385 -0
- package/app/hooks/use-store-locator.js +24 -2
- package/app/hooks/use-store-locator.test.jsx +109 -1
- package/app/pages/account/index.test.js +1 -1
- package/app/pages/account/profile.test.js +0 -2
- package/app/pages/cart/index.jsx +397 -157
- package/app/pages/cart/index.test.js +353 -2
- package/app/pages/cart/partials/bonus-products-title.jsx +10 -8
- package/app/pages/cart/partials/cart-secondary-button-group.test.js +1 -1
- package/app/pages/cart/partials/order-type-display.jsx +68 -0
- package/app/pages/cart/partials/order-type-display.test.js +241 -0
- package/app/pages/checkout/confirmation.jsx +79 -158
- package/app/pages/checkout/index.jsx +34 -9
- package/app/pages/checkout/index.test.js +245 -118
- package/app/pages/checkout/partials/contact-info.jsx +2 -6
- package/app/pages/checkout/partials/contact-info.test.js +93 -7
- package/app/pages/checkout/partials/payment.jsx +19 -5
- package/app/pages/checkout/partials/pickup-address.jsx +340 -70
- package/app/pages/checkout/partials/pickup-address.test.js +1075 -82
- package/app/pages/checkout/partials/product-shipping-address-card.jsx +382 -0
- package/app/pages/checkout/partials/shipment-details.jsx +209 -0
- package/app/pages/checkout/partials/shipment-details.test.js +246 -0
- package/app/pages/checkout/partials/shipping-address.jsx +156 -68
- package/app/pages/checkout/partials/shipping-address.test.js +673 -0
- package/app/pages/checkout/partials/shipping-method-options.jsx +180 -0
- package/app/pages/checkout/partials/shipping-methods.jsx +403 -0
- package/app/pages/checkout/partials/shipping-methods.test.js +472 -0
- package/app/pages/checkout/partials/shipping-multi-address.jsx +259 -0
- package/app/pages/checkout/partials/shipping-multi-address.test.js +2088 -0
- package/app/pages/checkout/partials/shipping-product-cards.jsx +101 -0
- package/app/pages/checkout/util/checkout-context.js +25 -18
- package/app/pages/login/index.jsx +2 -6
- package/app/pages/product-detail/index.jsx +96 -81
- package/app/pages/product-detail/index.test.js +103 -19
- package/app/pages/product-list/index.jsx +3 -1
- package/app/pages/product-list/partials/inventory-filter.jsx +18 -21
- package/app/pages/product-list/partials/inventory-filter.test.js +15 -17
- package/app/pages/product-list/partials/selected-refinements.jsx +3 -1
- package/app/ssr.js +1 -5
- package/app/static/translations/compiled/en-GB.json +316 -30
- package/app/static/translations/compiled/en-US.json +316 -30
- package/app/static/translations/compiled/en-XA.json +673 -75
- package/app/utils/address-utils.js +112 -0
- package/app/utils/address-utils.test.js +484 -0
- package/app/utils/product-utils.js +17 -5
- package/app/utils/product-utils.test.js +17 -8
- package/app/utils/sfdc-user-agent-utils.js +32 -0
- package/app/utils/sfdc-user-agent-utils.test.js +82 -0
- package/app/utils/shipment-utils.js +196 -0
- package/app/utils/shipment-utils.test.js +458 -0
- package/app/utils/test-utils.js +4 -4
- package/app/utils/utils.js +6 -1
- package/config/default.js +4 -1
- package/config/mocks/default.js +3 -1
- package/package.json +9 -9
- package/translations/en-GB.json +127 -10
- package/translations/en-US.json +127 -10
- package/app/pages/checkout/partials/shipping-options.jsx +0 -269
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
import React, {useState} from 'react'
|
|
8
|
+
import {useIntl} from 'react-intl'
|
|
9
|
+
import PropTypes from 'prop-types'
|
|
10
|
+
import {useProducts} from '@salesforce/commerce-sdk-react'
|
|
11
|
+
import {findImageGroupBy} from '@salesforce/retail-react-app/app/utils/image-groups-utils'
|
|
12
|
+
import {useCurrentCustomer} from '@salesforce/retail-react-app/app/hooks/use-current-customer'
|
|
13
|
+
import {useToast} from '@salesforce/retail-react-app/app/hooks/use-toast'
|
|
14
|
+
import {
|
|
15
|
+
Text,
|
|
16
|
+
Button,
|
|
17
|
+
Box,
|
|
18
|
+
VStack,
|
|
19
|
+
Alert,
|
|
20
|
+
AlertIcon,
|
|
21
|
+
AlertTitle,
|
|
22
|
+
AlertDescription,
|
|
23
|
+
Center
|
|
24
|
+
} from '@salesforce/retail-react-app/app/components/shared/ui'
|
|
25
|
+
import {useCheckout} from '@salesforce/retail-react-app/app/pages/checkout/util/checkout-context'
|
|
26
|
+
import {useProductAddressAssignment} from '@salesforce/retail-react-app/app/hooks/use-product-address-assignment'
|
|
27
|
+
import {useAddressForm} from '@salesforce/retail-react-app/app/hooks/use-address-form'
|
|
28
|
+
import {useMultiship} from '@salesforce/retail-react-app/app/hooks/use-multiship'
|
|
29
|
+
import ProductShippingAddressCard from '@salesforce/retail-react-app/app/pages/checkout/partials/product-shipping-address-card.jsx'
|
|
30
|
+
|
|
31
|
+
const ShippingMultiAddress = ({basket, submitButtonLabel, noItemsInBasketMessage}) => {
|
|
32
|
+
const {formatMessage} = useIntl()
|
|
33
|
+
const {STEPS, goToStep} = useCheckout()
|
|
34
|
+
const showToast = useToast()
|
|
35
|
+
const productAddressAssignment = useProductAddressAssignment(basket)
|
|
36
|
+
const productIds = productAddressAssignment.deliveryItems
|
|
37
|
+
.map((item) => item.productId)
|
|
38
|
+
.join(',')
|
|
39
|
+
|
|
40
|
+
const {
|
|
41
|
+
data: productsMap,
|
|
42
|
+
isLoading: productsLoading,
|
|
43
|
+
error: productsError
|
|
44
|
+
} = useProducts(
|
|
45
|
+
{parameters: {ids: productIds, allImages: true}},
|
|
46
|
+
{
|
|
47
|
+
enabled: Boolean(productIds),
|
|
48
|
+
select: (data) => {
|
|
49
|
+
return (
|
|
50
|
+
data?.data?.reduce((acc, p) => {
|
|
51
|
+
acc[p.id] = p
|
|
52
|
+
return acc
|
|
53
|
+
}, {}) || {}
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
const {data: customer, isLoading: customerLoading} = useCurrentCustomer()
|
|
59
|
+
|
|
60
|
+
const {
|
|
61
|
+
form: addressForm,
|
|
62
|
+
formStateByItemId: showAddAddressForm,
|
|
63
|
+
isSubmitting: isFormSubmitting,
|
|
64
|
+
openForm,
|
|
65
|
+
closeForm,
|
|
66
|
+
handleCreateAddress,
|
|
67
|
+
isAddressFormOpen
|
|
68
|
+
} = useAddressForm(
|
|
69
|
+
productAddressAssignment.addGuestAddress,
|
|
70
|
+
customer?.isGuest,
|
|
71
|
+
productAddressAssignment.setAddressesForItems,
|
|
72
|
+
productAddressAssignment.availableAddresses,
|
|
73
|
+
productAddressAssignment.deliveryItems
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const {orchestrateShipmentOperations} = useMultiship(basket)
|
|
77
|
+
|
|
78
|
+
const addresses = productAddressAssignment.availableAddresses
|
|
79
|
+
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
80
|
+
|
|
81
|
+
// guests only products loading since they may not have addresses yet
|
|
82
|
+
const isLoading = (customer?.isGuest ? false : customerLoading) || productsLoading
|
|
83
|
+
|
|
84
|
+
const allShipmentsHaveAddress = productAddressAssignment.allItemsHaveAddresses
|
|
85
|
+
|
|
86
|
+
if (!productAddressAssignment.deliveryItems.length) {
|
|
87
|
+
return (
|
|
88
|
+
<Center
|
|
89
|
+
p={8}
|
|
90
|
+
textAlign="center"
|
|
91
|
+
color="gray.500"
|
|
92
|
+
role="status"
|
|
93
|
+
aria-live="polite"
|
|
94
|
+
aria-label={formatMessage(noItemsInBasketMessage)}
|
|
95
|
+
>
|
|
96
|
+
<VStack spacing={4}>
|
|
97
|
+
<Text fontSize="lg" fontWeight="medium">
|
|
98
|
+
{formatMessage(noItemsInBasketMessage)}
|
|
99
|
+
</Text>
|
|
100
|
+
</VStack>
|
|
101
|
+
</Center>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (productsError) {
|
|
106
|
+
return (
|
|
107
|
+
<Alert
|
|
108
|
+
status="error"
|
|
109
|
+
variant="subtle"
|
|
110
|
+
flexDirection="column"
|
|
111
|
+
alignItems="center"
|
|
112
|
+
justifyContent="center"
|
|
113
|
+
textAlign="center"
|
|
114
|
+
height="200px"
|
|
115
|
+
aria-live="assertive"
|
|
116
|
+
>
|
|
117
|
+
<AlertIcon boxSize={5} mr={0} />
|
|
118
|
+
<AlertTitle mr={2}>
|
|
119
|
+
{formatMessage({
|
|
120
|
+
id: 'shipping_multi_address.error.label',
|
|
121
|
+
defaultMessage: 'Something went wrong while loading products.'
|
|
122
|
+
})}
|
|
123
|
+
</AlertTitle>
|
|
124
|
+
<AlertDescription>
|
|
125
|
+
{formatMessage({
|
|
126
|
+
id: 'shipping_multi_address.error.message',
|
|
127
|
+
defaultMessage: 'Something went wrong while loading products. Try again.'
|
|
128
|
+
})}
|
|
129
|
+
</AlertDescription>
|
|
130
|
+
</Alert>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (isLoading) {
|
|
135
|
+
return (
|
|
136
|
+
<Center p={8} textAlign="center" color="gray.500">
|
|
137
|
+
<VStack spacing={4}>
|
|
138
|
+
<Text fontSize="lg" fontWeight="medium">
|
|
139
|
+
{formatMessage({
|
|
140
|
+
id: 'shipping_multi_address.loading.message',
|
|
141
|
+
defaultMessage: 'Loading...'
|
|
142
|
+
})}
|
|
143
|
+
</Text>
|
|
144
|
+
</VStack>
|
|
145
|
+
</Center>
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const handleSubmit = async () => {
|
|
150
|
+
setIsSubmitting(true)
|
|
151
|
+
try {
|
|
152
|
+
await orchestrateShipmentOperations(
|
|
153
|
+
productAddressAssignment.deliveryItems,
|
|
154
|
+
productAddressAssignment.selectedAddresses,
|
|
155
|
+
addresses,
|
|
156
|
+
productsMap
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
goToStep(STEPS.SHIPPING_OPTIONS)
|
|
160
|
+
} catch (error) {
|
|
161
|
+
showToast({
|
|
162
|
+
title: formatMessage({
|
|
163
|
+
defaultMessage: 'Something went wrong while setting up shipments. Try again.',
|
|
164
|
+
id: 'shipping_multi_address.error.submit_failed'
|
|
165
|
+
}),
|
|
166
|
+
status: 'error'
|
|
167
|
+
})
|
|
168
|
+
} finally {
|
|
169
|
+
setIsSubmitting(false)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<Box>
|
|
175
|
+
<VStack spacing={0}>
|
|
176
|
+
<Box
|
|
177
|
+
border="1px solid"
|
|
178
|
+
borderColor="gray.200"
|
|
179
|
+
borderRadius="md"
|
|
180
|
+
bg="white"
|
|
181
|
+
p={2}
|
|
182
|
+
w="100%"
|
|
183
|
+
>
|
|
184
|
+
<VStack spacing={2} w="100%" h="100%">
|
|
185
|
+
{productAddressAssignment.deliveryItems.map((item) => {
|
|
186
|
+
const productDetail = productsMap?.[item.productId] || {}
|
|
187
|
+
const variant = {...item, ...productDetail}
|
|
188
|
+
const image = findImageGroupBy(productDetail.imageGroups, {
|
|
189
|
+
viewType: 'small',
|
|
190
|
+
selectedVariationAttributes: variant.variationValues
|
|
191
|
+
})?.images?.[0]
|
|
192
|
+
const imageUrl = image?.disBaseLink || image?.link || ''
|
|
193
|
+
const addressKey = item.itemId
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<ProductShippingAddressCard
|
|
197
|
+
key={addressKey}
|
|
198
|
+
item={item}
|
|
199
|
+
variant={variant}
|
|
200
|
+
imageUrl={imageUrl}
|
|
201
|
+
addressKey={addressKey}
|
|
202
|
+
selectedAddressId={
|
|
203
|
+
productAddressAssignment.selectedAddresses[addressKey]
|
|
204
|
+
}
|
|
205
|
+
availableAddresses={addresses}
|
|
206
|
+
isGuestUser={customer?.isGuest}
|
|
207
|
+
customerLoading={customerLoading}
|
|
208
|
+
onAddressSelect={productAddressAssignment.setAddressesForItems}
|
|
209
|
+
onAddNewAddress={openForm}
|
|
210
|
+
showAddAddressForm={showAddAddressForm}
|
|
211
|
+
addressForm={addressForm}
|
|
212
|
+
handleCreateAddress={handleCreateAddress}
|
|
213
|
+
closeForm={closeForm}
|
|
214
|
+
/>
|
|
215
|
+
)
|
|
216
|
+
})}
|
|
217
|
+
</VStack>
|
|
218
|
+
</Box>
|
|
219
|
+
<Button
|
|
220
|
+
type="button"
|
|
221
|
+
width="full"
|
|
222
|
+
mt={2}
|
|
223
|
+
opacity={!allShipmentsHaveAddress || isAddressFormOpen ? 0.8 : 1}
|
|
224
|
+
cursor={
|
|
225
|
+
!allShipmentsHaveAddress || isAddressFormOpen ? 'not-allowed' : 'pointer'
|
|
226
|
+
}
|
|
227
|
+
isLoading={
|
|
228
|
+
addressForm.formState.isSubmitting || isFormSubmitting || isSubmitting
|
|
229
|
+
}
|
|
230
|
+
isDisabled={!allShipmentsHaveAddress || isAddressFormOpen}
|
|
231
|
+
data-testid="continue-to-shipping-button"
|
|
232
|
+
loadingText={formatMessage({
|
|
233
|
+
id: 'shipping_multi_address.submit.loading',
|
|
234
|
+
defaultMessage: 'Setting up shipments...'
|
|
235
|
+
})}
|
|
236
|
+
aria-label={formatMessage({
|
|
237
|
+
id: 'shipping_multi_address.submit.description',
|
|
238
|
+
defaultMessage: 'Continue to next step with selected delivery addresses'
|
|
239
|
+
})}
|
|
240
|
+
onClick={() => {
|
|
241
|
+
if (!isAddressFormOpen && allShipmentsHaveAddress) {
|
|
242
|
+
handleSubmit()
|
|
243
|
+
}
|
|
244
|
+
}}
|
|
245
|
+
>
|
|
246
|
+
{formatMessage(submitButtonLabel)}
|
|
247
|
+
</Button>
|
|
248
|
+
</VStack>
|
|
249
|
+
</Box>
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
ShippingMultiAddress.propTypes = {
|
|
254
|
+
basket: PropTypes.object.isRequired,
|
|
255
|
+
submitButtonLabel: PropTypes.object.isRequired,
|
|
256
|
+
noItemsInBasketMessage: PropTypes.object.isRequired
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export default ShippingMultiAddress
|