Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/template-retail-react-app/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Removed the "Edit" button from product cards of Standard Products [#2581](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2581)
- Support standard product as a child item in bundles [#2574](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2574)
- Support standard product as a child item in sets [#2636](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2636)
- Refactor Add to Cart in PDP [#2664](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2664)

## v6.1.0 (May 22, 2025)

Expand Down
201 changes: 49 additions & 152 deletions packages/template-retail-react-app/app/pages/product-detail/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ import {rebuildPathWithParams} from '@salesforce/retail-react-app/app/utils/url'
import {useHistory, useLocation, useParams} from 'react-router-dom'
import {useToast} from '@salesforce/retail-react-app/app/hooks/use-toast'
import {useWishList} from '@salesforce/retail-react-app/app/hooks/use-wish-list'
import {
handleAddToCart,
handleChildProductValidation,
handleProductBundleAddToCart,
handleProductSetAddToCart
} from '@salesforce/retail-react-app/app/utils/cart-utils'

const ProductDetail = () => {
const {formatMessage} = useIntl()
Expand Down Expand Up @@ -298,147 +304,6 @@ const ProductDetail = () => {
})
}

const handleAddToCart = async (productSelectionValues) => {
try {
const productItems = productSelectionValues.map(({variant, product, quantity}) => ({
productId: variant?.productId || product?.id,
price: variant?.price || product?.price,
quantity
}))

await addItemToNewOrExistingBasket(productItems)

const productItemsForEinstein = productSelectionValues.map(
({product, variant, quantity}) => ({
product,
productId: variant?.productId || product?.id,
price: variant?.price || product?.price,
quantity
})
)
einstein.sendAddToCart(productItemsForEinstein)

// If the items were successfully added, set the return value to be used
// by the add to cart modal.
return productSelectionValues
} catch (error) {
console.log('error', error)
showError(error)
}
}

/**************** Product Set/Bundles Handlers ****************/
const handleChildProductValidation = useCallback(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to move this as well ? If Yes, this can go in some util file.
Others can go in some AddToCart hook/or something, as they are all Adding diff products to cart.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleChildProductValidation - Moved it back to product-detail since it didn't make sense to extract this one to the util

// Run validation for all child products. This will ensure the error
// messages are shown.
Object.values(childProductRefs.current).forEach(({validateOrderability}) => {
validateOrderability({scrollErrorIntoView: false})
})

// Using ot state for which child products are selected, scroll to the first
// one that isn't selected and requires a variant selection.
const selectedProductIds = Object.keys(childProductSelection)
const firstUnselectedProduct = comboProduct.childProducts.find(
({product: childProduct}) => {
// Skip validation for standard products (no variations)
if (childProduct.type?.item) {
return false
}
return !selectedProductIds.includes(childProduct.id)
}
)?.product

if (firstUnselectedProduct) {
// Get the reference to the product view and scroll to it.
const {ref} = childProductRefs.current[firstUnselectedProduct.id]

if (ref.scrollIntoView) {
ref.scrollIntoView({
behavior: 'smooth',
block: 'end'
})
}

return false
}

return true
}, [product, childProductSelection])

/**************** Product Set Handlers ****************/
const handleProductSetAddToCart = () => {
// Get all the selected products, and pass them to the addToCart handler which
// accepts an array.
const productSelectionValues = Object.values(childProductSelection)
return handleAddToCart(productSelectionValues)
}

/**************** Product Bundle Handlers ****************/
// Top level bundle does not have variants
const handleProductBundleAddToCart = async (variant, selectedQuantity) => {
try {
const childProductSelections = Object.values(childProductSelection)

const productItems = [
{
productId: product.id,
price: product.price,
quantity: selectedQuantity,
// The add item endpoint in the shopper baskets API does not respect variant selections
// for bundle children, so we have to make a follow up call to update the basket
// with the chosen variant selections
bundledProductItems: childProductSelections.map((child) => {
return {
productId: child.variant?.productId || child.product?.id,
quantity: child.quantity
}
})
}
]

const res = await addItemToNewOrExistingBasket(productItems)

const bundleChildMasterIds = childProductSelections.map((child) => {
return child.product.id
})

// since the returned data includes all products in basket
// here we compare list of productIds in bundleProductItems of each productItem to filter out the
// current bundle that was last added into cart
const currentBundle = res.productItems.find((productItem) => {
if (!productItem.bundledProductItems?.length) return
const bundleChildIds = productItem.bundledProductItems?.map((item) => {
// seek out the bundle child that still uses masterId as product id
return item.productId
})
return bundleChildIds.every((id) => bundleChildMasterIds.includes(id))
})

const itemsToBeUpdated = getUpdateBundleChildArray(
currentBundle,
childProductSelections
)

if (itemsToBeUpdated.length) {
// make a follow up call to update child variant selection for product bundle
// since add item endpoint doesn't currently consider product bundle child variants
await updateItemsInBasketMutation.mutateAsync({
method: 'PATCH',
parameters: {
basketId: res.basketId
},
body: itemsToBeUpdated
})
}

einstein.sendAddToCart(productItems)

return childProductSelections
} catch (error) {
showError(error)
}
}

/**************** Einstein ****************/
useEffect(() => {
if (product && product.type.set) {
Expand Down Expand Up @@ -498,14 +363,36 @@ const ProductDetail = () => {
category={primaryCategory?.parentCategoryTree || []}
addToCart={
isProductASet
? handleProductSetAddToCart
: handleProductBundleAddToCart
? () =>
handleProductSetAddToCart(
childProductSelection,
addItemToNewOrExistingBasket,
einstein,
showError
)
: (variant, selectedQuantity) =>
handleProductBundleAddToCart(
product,
childProductSelection,
selectedQuantity,
addItemToNewOrExistingBasket,
updateItemsInBasketMutation,
einstein,
showError,
getUpdateBundleChildArray
)
}
addToWishlist={handleAddToWishlist}
isProductLoading={isProductLoading}
isBasketLoading={isBasketLoading}
isWishlistLoading={isWishlistLoading}
validateOrderability={handleChildProductValidation}
validateOrderability={() =>
handleChildProductValidation(
childProductRefs,
comboProduct,
childProductSelection
)
}
childProductOrderability={childProductOrderability}
setSelectedBundleQuantity={setSelectedBundleQuantity}
/>
Expand Down Expand Up @@ -536,13 +423,18 @@ const ProductDetail = () => {
addToCart={
isProductASet
? (variant, quantity) =>
handleAddToCart([
{
product: childProduct,
variant,
quantity
}
])
handleAddToCart(
[
{
product: childProduct,
variant,
quantity
}
],
addItemToNewOrExistingBasket,
einstein,
showError
)
: null
}
addToWishlist={
Expand Down Expand Up @@ -589,7 +481,12 @@ const ProductDetail = () => {
product={product}
category={primaryCategory?.parentCategoryTree || []}
addToCart={(variant, quantity) =>
handleAddToCart([{product, variant, quantity}])
handleAddToCart(
[{product, variant, quantity}],
addItemToNewOrExistingBasket,
einstein,
showError
)
}
addToWishlist={handleAddToWishlist}
isProductLoading={isProductLoading}
Expand Down
Loading
Loading