Skip to content

Commit 49a48fa

Browse files
@W-19743729 remove shipment dropdown from cart, put orphan bonus products in the end (#3358)
* remove shipment dropdown from cart, put orphan bonus products in the end * lint, tests
1 parent 03b8ab4 commit 49a48fa

File tree

8 files changed

+687
-147
lines changed

8 files changed

+687
-147
lines changed

packages/template-retail-react-app/app/pages/cart/index.jsx

Lines changed: 222 additions & 78 deletions
Large diffs are not rendered by default.

packages/template-retail-react-app/app/pages/cart/index.test.js

Lines changed: 398 additions & 6 deletions
Large diffs are not rendered by default.

packages/template-retail-react-app/app/pages/cart/partials/bonus-products-title.jsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,11 @@
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77
import React from 'react'
8+
import PropTypes from 'prop-types'
89
import {FormattedMessage} from 'react-intl'
910
import {Box, Text} from '@salesforce/retail-react-app/app/components/shared/ui'
10-
import {useCurrentBasket} from '@salesforce/retail-react-app/app/hooks/use-current-basket'
11-
12-
const BonusProductsTitle = () => {
13-
const {data: basket} = useCurrentBasket()
14-
const bonusItemsCount =
15-
basket?.productItems?.filter((item) => item.bonusProductLineItem)?.length || 0
1611

12+
const BonusProductsTitle = ({bonusItemsCount = 0}) => {
1713
return (
1814
<Box layerStyle="cardBordered" p={3}>
1915
<Text fontWeight="bold">
@@ -27,4 +23,8 @@ const BonusProductsTitle = () => {
2723
)
2824
}
2925

26+
BonusProductsTitle.propTypes = {
27+
bonusItemsCount: PropTypes.number
28+
}
29+
3030
export default BonusProductsTitle

packages/template-retail-react-app/app/pages/cart/partials/bonus-products-title.test.js

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,51 +8,25 @@ import React from 'react'
88
import {screen} from '@testing-library/react'
99
import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
1010
import BonusProductsTitle from '@salesforce/retail-react-app/app/pages/cart/partials/bonus-products-title'
11-
import {useCurrentBasket} from '@salesforce/retail-react-app/app/hooks/use-current-basket'
12-
13-
// Mock the useCurrentBasket hook
14-
jest.mock('@salesforce/retail-react-app/app/hooks/use-current-basket')
1511

1612
describe('BonusProductsTitle', () => {
17-
beforeEach(() => {
18-
jest.clearAllMocks()
19-
// Provide a default mock that includes derivedData to prevent AddToCartModal errors
20-
useCurrentBasket.mockReturnValue({
21-
data: {},
22-
derivedData: {totalItems: 0}
23-
})
24-
})
25-
2613
it('renders title with 1 item when one bonus product', () => {
27-
const basketData = {
28-
productItems: [
29-
{id: '1', bonusProductLineItem: true},
30-
{id: '2', bonusProductLineItem: false}
31-
]
32-
}
33-
useCurrentBasket.mockReturnValue({
34-
data: basketData,
35-
derivedData: {totalItems: 2}
36-
})
37-
38-
renderWithProviders(<BonusProductsTitle />)
14+
renderWithProviders(<BonusProductsTitle bonusItemsCount={1} />)
3915
expect(screen.getByText('Bonus Products (1 item)')).toBeInTheDocument()
4016
})
4117

4218
it('renders title with multiple items when multiple bonus products', () => {
43-
const basketData = {
44-
productItems: [
45-
{id: '1', bonusProductLineItem: true},
46-
{id: '2', bonusProductLineItem: true},
47-
{id: '3', bonusProductLineItem: false}
48-
]
49-
}
50-
useCurrentBasket.mockReturnValue({
51-
data: basketData,
52-
derivedData: {totalItems: 3}
53-
})
19+
renderWithProviders(<BonusProductsTitle bonusItemsCount={2} />)
20+
expect(screen.getByText('Bonus Products (2 items)')).toBeInTheDocument()
21+
})
5422

23+
it('renders title with 0 items when no bonus products', () => {
24+
renderWithProviders(<BonusProductsTitle bonusItemsCount={0} />)
25+
expect(screen.getByText('Bonus Products (0 items)')).toBeInTheDocument()
26+
})
27+
28+
it('renders title with default count when no prop provided', () => {
5529
renderWithProviders(<BonusProductsTitle />)
56-
expect(screen.getByText('Bonus Products (2 items)')).toBeInTheDocument()
30+
expect(screen.getByText('Bonus Products (0 items)')).toBeInTheDocument()
5731
})
5832
})

packages/template-retail-react-app/app/pages/cart/partials/cart-product-list-with-grouped-bonus-products.jsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,6 @@ const CartProductListWithGroupedBonusProducts = ({
185185
)
186186
}
187187
})}
188-
189-
{/* Temporarily disabled orphan bonus products for debugging */}
190188
</Stack>
191189
)
192190
}

packages/template-retail-react-app/app/utils/bonus-product/business-logic.js

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
import {
99
isProductEligibleForBonusProducts,
10-
isProductAvailableAsBonus
10+
isProductAvailableAsBonus,
11+
getPromotionIdsForProduct
1112
} from '@salesforce/retail-react-app/app/utils/bonus-product/common'
1213

1314
/**
@@ -24,11 +25,43 @@ import {
2425
* - UI behavior determination
2526
*/
2627

28+
/**
29+
* Determines if a product's promotions are automatic (no choice) or manual (choice of bonus products).
30+
* Automatic promotions add bonus products directly to cart without user selection.
31+
* Choice promotions allow users to select which bonus products they want.
32+
*
33+
* @param {Object} basket - The current basket data
34+
* @param {string} productId - The product ID to check
35+
* @param {Object} productsWithPromotions - Object mapping productId to product data with promotions
36+
* @returns {boolean} True if product has automatic promotions only
37+
*/
38+
export const isAutomaticPromotion = (basket, productId, productsWithPromotions) => {
39+
if (!basket || !productId || !productsWithPromotions) {
40+
return false
41+
}
42+
43+
// Get promotion IDs for this product
44+
const promotionIds = getPromotionIdsForProduct(basket, productId, productsWithPromotions)
45+
46+
if (promotionIds.length === 0) {
47+
return false
48+
}
49+
50+
// Check if ANY of this product's promotions have bonusDiscountLineItems (choice promotions)
51+
const hasChoicePromotions =
52+
basket.bonusDiscountLineItems?.some((item) => promotionIds.includes(item.promotionId)) ||
53+
false
54+
55+
// Automatic if no choice promotions found
56+
return !hasChoicePromotions
57+
}
58+
2759
/**
2860
* Enhanced check if a product should show bonus product selection.
2961
* A product is eligible if:
3062
* 1. It has promotions that can trigger bonus products
3163
* 2. It is NOT itself available as a bonus product in the current basket
64+
* 3. It is NOT an automatic promotion (which doesn't need selection UI)
3265
* @param {Object} basket - The current basket data
3366
* @param {string} productId - The product ID to check
3467
* @param {Object} productsWithPromotions - Object mapping productId to product data with promotions
@@ -44,6 +77,16 @@ export const shouldShowBonusProductSelection = (basket, productId, productsWithP
4477
// Then check if this product is itself available as a bonus product
4578
// If it is, it shouldn't show bonus product selection when added as a regular item
4679
const isAvailableAsBonus = isProductAvailableAsBonus(basket, productId)
80+
if (isAvailableAsBonus) {
81+
return false
82+
}
83+
84+
// Finally check if this is an automatic promotion
85+
// Automatic promotions don't need selection UI since products are added automatically
86+
const isAutomatic = isAutomaticPromotion(basket, productId, productsWithPromotions)
87+
if (isAutomatic) {
88+
return false
89+
}
4790

48-
return !isAvailableAsBonus
91+
return true
4992
}

packages/template-retail-react-app/app/utils/bonus-product/business-logic.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ describe('Bonus Product Business Logic', () => {
1313
const basket = {
1414
bonusDiscountLineItems: [
1515
{
16+
promotionId: 'promo-1', // Include the promotion ID to make it a choice promotion
1617
bonusProducts: [{productId: 'different-product'}]
1718
}
1819
]

packages/template-retail-react-app/app/utils/bonus-product/utils.js

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ export {
4141
export {getBonusProductCountsForPromotion} from '@salesforce/retail-react-app/app/utils/bonus-product/calculations'
4242

4343
// Re-export business logic utilities
44-
export {shouldShowBonusProductSelection} from '@salesforce/retail-react-app/app/utils/bonus-product/business-logic'
44+
export {
45+
shouldShowBonusProductSelection,
46+
isAutomaticPromotion
47+
} from '@salesforce/retail-react-app/app/utils/bonus-product/business-logic'
4548

4649
// Re-export React hooks
4750
export {
@@ -50,18 +53,3 @@ export {
5053
useAvailableBonusItemsForProduct,
5154
useRemainingAvailableBonusProductsForProduct
5255
} from '@salesforce/retail-react-app/app/utils/bonus-product/hooks'
53-
54-
// Shipment management utilities
55-
export const getBonusProductsForQualifyingItems = (basket, qualifyingItems) => {
56-
if (!basket?.productItems || !qualifyingItems?.length) return []
57-
58-
return basket.productItems.filter((productItem) => {
59-
if (!productItem.bonusProductLineItem) return false
60-
61-
return qualifyingItems.some(
62-
(qualifyingItem) =>
63-
productItem.qualifyingProductItemId === qualifyingItem.itemId ||
64-
productItem.shipmentId === qualifyingItem.shipmentId
65-
)
66-
})
67-
}

0 commit comments

Comments
 (0)