Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
13 changes: 8 additions & 5 deletions packages/template-retail-react-app/app/components/_app/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ import {
THEME_COLOR,
CAT_MENU_DEFAULT_NAV_SSR_DEPTH,
CAT_MENU_DEFAULT_ROOT_CATEGORY,
DEFAULT_LOCALE
DEFAULT_LOCALE,
STORE_LOCATOR_IS_ENABLED
} from '@salesforce/retail-react-app/app/constants'

import Seo from '@salesforce/retail-react-app/app/components/seo'
Expand Down Expand Up @@ -354,10 +355,12 @@ const App = (props) => {

<Box id="app" display="flex" flexDirection="column" flex={1}>
<SkipNavLink zIndex="skipLink">Skip to Content</SkipNavLink>
<StoreLocatorModal
isOpen={isOpenStoreLocator}
onClose={onCloseStoreLocator}
/>
{STORE_LOCATOR_IS_ENABLED && (
<StoreLocatorModal
isOpen={isOpenStoreLocator}
onClose={onCloseStoreLocator}
/>
)}
<Box {...styles.headerWrapper}>
{!isCheckout ? (
<>
Expand Down
197 changes: 102 additions & 95 deletions packages/template-retail-react-app/app/components/product-view/index.jsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import React, {useState, useEffect, createContext} from 'react'
import PropTypes from 'prop-types'
import useMultiSite from '@salesforce/retail-react-app/app/hooks/use-multi-site'
import {STORE_LOCATOR_IS_ENABLED} from '@salesforce/retail-react-app/app/constants'

const onClient = typeof window !== 'undefined'

Expand All @@ -21,11 +22,14 @@ const readValue = (key) => {
export const StoreLocatorContext = createContext(null)

export const StoreLocatorProvider = ({config, children}) => {
// Only enable BOPIS functionality if the feature toggle is on
const isBopisEnabled = STORE_LOCATOR_IS_ENABLED

// remember the shopper's preferred store for the current site
// TODO: Change this to `useLocalStorage` hook when localStorage detection is more robust
const {site} = useMultiSite()
const selectedStoreBySiteId = `selectedStore_${site?.id}`
const selectedStoreId = readValue(selectedStoreBySiteId)
const selectedStoreId = isBopisEnabled ? readValue(selectedStoreBySiteId) : null

const [state, setState] = useState({
mode: 'input',
Expand All @@ -42,10 +46,10 @@ export const StoreLocatorProvider = ({config, children}) => {
})

useEffect(() => {
if (onClient && state.selectedStoreId) {
if (isBopisEnabled && onClient && state.selectedStoreId) {
window.localStorage.setItem(selectedStoreBySiteId, state.selectedStoreId)
}
}, [state.selectedStoreId])
}, [state.selectedStoreId, isBopisEnabled])

const value = {
state,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {useIntl} from 'react-intl'
import {useVariationParams} from '@salesforce/retail-react-app/app/hooks/use-variation-params'
import {useVariationAttributes} from '@salesforce/retail-react-app/app/hooks/use-variation-attributes'
import {useSelectedStore} from '@salesforce/retail-react-app/app/hooks/use-selected-store'
import {STORE_LOCATOR_IS_ENABLED} from '@salesforce/retail-react-app/app/constants'

const OUT_OF_STOCK = 'OUT_OF_STOCK'
const UNFULFILLABLE = 'UNFULFILLABLE'
Expand Down Expand Up @@ -48,7 +49,11 @@ export const useDerivedProduct = (
const [quantity, setQuantity] = useState(initialQuantity)
const {selectedStore} = useSelectedStore()

const selectedStoreInventory = getInventoryById(product, selectedStore?.inventoryId)
// Only enable BOPIS functionality if the feature toggle is on
const isBopisEnabled = STORE_LOCATOR_IS_ENABLED
const selectedInventoryId = isBopisEnabled ? selectedStore?.inventoryId || null : null

const selectedStoreInventory = getInventoryById(product, selectedInventoryId)
const selectedStoreStockLevel = selectedStoreInventory?.stockLevel || 0
// selectedStoreStockLevel and selectedStoreInventory are already variant specific,
// so we don't need to check for variation attributes
Expand Down Expand Up @@ -116,7 +121,7 @@ export const useDerivedProduct = (
stockLevel,
isOutOfStock,
unfulfillable,
isSelectedStoreOutOfStock,
selectedStore
isSelectedStoreOutOfStock: isBopisEnabled ? isSelectedStoreOutOfStock : false,
selectedStore: isBopisEnabled ? selectedStore : null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ import {
useShopperBasketsMutation,
useShippingMethodsForShipment
} from '@salesforce/commerce-sdk-react'
import {STORE_LOCATOR_IS_ENABLED} from '@salesforce/retail-react-app/app/constants'

/**
* Custom hook to handle pickup in store shipment configuration
* @returns {Object} Object containing helper functions for pickup shipment management
*/
export const usePickupShipment = (basket) => {
// Only enable BOPIS functionality if the feature toggle is on
const isBopisEnabled = STORE_LOCATOR_IS_ENABLED

const updateShipmentForBasketMutation = useShopperBasketsMutation('updateShipmentForBasket')

// Hook for shipping methods - we'll use refetch when needed
Expand All @@ -36,7 +40,7 @@ export const usePickupShipment = (basket) => {
* @returns {string|null} The shipping method ID for pickup in store, or null if not found
*/
const getPickupShippingMethodId = (shippingMethods) => {
if (!shippingMethods?.applicableShippingMethods) {
if (!isBopisEnabled || !shippingMethods?.applicableShippingMethods) {
return null
}

Expand All @@ -62,7 +66,7 @@ export const usePickupShipment = (basket) => {
* @returns {boolean} True if the current shipping method is a pickup method
*/
const isCurrentShippingMethodPickup = (currentShippingMethod) => {
return currentShippingMethod?.c_storePickupEnabled === true
return isBopisEnabled && currentShippingMethod?.c_storePickupEnabled === true
}

/**
Expand All @@ -75,6 +79,8 @@ export const usePickupShipment = (basket) => {
* @param {boolean} options.throwOnError - Whether to throw on error (default: false)
*/
const updatePickupShipment = async (basketId, productItems, storeInfo, options = {}) => {
if (!isBopisEnabled) return

const defaultPickupShippingMethodId = '005'
const {pickupShippingMethodId = defaultPickupShippingMethodId, throwOnError = false} =
options
Expand Down Expand Up @@ -157,6 +163,8 @@ export const usePickupShipment = (basket) => {
* @returns {boolean} True if any items are pickup items
*/
const hasPickupItems = (productSelectionValues, pickupInStoreMap, mainProduct) => {
if (!isBopisEnabled) return false

return productSelectionValues.some((item) => {
const prodKey =
(item.variant || item.product || mainProduct).productId ||
Expand All @@ -173,7 +181,7 @@ export const usePickupShipment = (basket) => {
* @returns {Array} Updated product items with inventory IDs
*/
const addInventoryIdsToPickupItems = (productItems, pickupInStoreMap, storeInfo) => {
if (!storeInfo?.inventoryId) return productItems
if (!isBopisEnabled || !storeInfo?.inventoryId) return productItems

return productItems.map((item) => {
const prodKey = item.productId || item.id
Expand Down Expand Up @@ -201,7 +209,7 @@ export const usePickupShipment = (basket) => {
hasAnyPickupSelected,
selectedStore
) => {
if (!basketResponse?.basketId || !basketResponse.shipments.length) {
if (!isBopisEnabled || !basketResponse?.basketId || !basketResponse.shipments.length) {
return
}

Expand Down
62 changes: 36 additions & 26 deletions packages/template-retail-react-app/app/pages/cart/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ import {
TOAST_ACTION_VIEW_WISHLIST,
TOAST_MESSAGE_ADDED_TO_WISHLIST,
TOAST_MESSAGE_REMOVED_ITEM_FROM_CART,
TOAST_MESSAGE_ALREADY_IN_WISHLIST
TOAST_MESSAGE_ALREADY_IN_WISHLIST,
STORE_LOCATOR_IS_ENABLED
} from '@salesforce/retail-react-app/app/constants'
import {REMOVE_CART_ITEM_CONFIRMATION_DIALOG_CONFIG} from '@salesforce/retail-react-app/app/pages/cart/partials/cart-secondary-button-group'

Expand All @@ -68,23 +69,26 @@ const DEBOUNCE_WAIT = 750
const Cart = () => {
const {data: basket, isLoading} = useCurrentBasket()

// Pickup in Store
const isPickupOrder = basket?.shipments[0]?.shippingMethod?.c_storePickupEnabled === true
const storeId = basket?.shipments?.[0]?.c_fromStoreId
// Pickup in Store - only enabled if feature toggle is on
const isBopisEnabled = STORE_LOCATOR_IS_ENABLED
const isPickupOrder = isBopisEnabled
? basket?.shipments[0]?.shippingMethod?.c_storePickupEnabled === true
: false
const storeId = isBopisEnabled ? basket?.shipments?.[0]?.c_fromStoreId : null
const {data: storeData} = useStores(
{
parameters: {
ids: storeId
}
},
{
enabled: !!storeId
enabled: !!storeId && isBopisEnabled
}
)
const storeName = storeData?.data?.[0]?.name

const {selectedStore} = useSelectedStore()
const selectedInventoryId = selectedStore?.inventoryId || null
const selectedInventoryId = isBopisEnabled ? selectedStore?.inventoryId || null : null
const productIds = basket?.productItems?.map(({productId}) => productId).join(',') ?? ''
const {data: products, isLoading: isProductsLoading} = useProducts(
{
Expand Down Expand Up @@ -591,6 +595,9 @@ const Cart = () => {
}
return (
<Box background="gray.50" flex="1" data-testid="sf-cart-container">
{/* <Helmet>
<title>Cart</title>
</Helmet> */}
<Container
maxWidth="container.xl"
px={[4, 6, 6, 4]}
Expand All @@ -606,26 +613,29 @@ const Cart = () => {
>
<GridItem>
<Stack spacing={4}>
<Box layerStyle="cardBordered" p={3}>
{isPickupOrder ? (
<Text fontWeight="bold">
<FormattedMessage
defaultMessage="Pickup in Store ({storeName})"
id="cart.order_type.pickup_in_store"
values={{
storeName
}}
/>
</Text>
) : (
<Text fontWeight="bold">
<FormattedMessage
defaultMessage="Delivery"
id="cart.order_type.delivery"
/>
</Text>
)}
</Box>
{/* Order Type Display */}
{isBopisEnabled && (
<Box layerStyle="cardBordered" p={3}>
{isPickupOrder ? (
<Text fontWeight="bold">
<FormattedMessage
defaultMessage="Pickup in Store ({storeName})"
id="cart.order_type.pickup_in_store"
values={{
storeName
}}
/>
</Text>
) : (
<Text fontWeight="bold">
<FormattedMessage
defaultMessage="Delivery"
id="cart.order_type.delivery"
/>
</Text>
)}
</Box>
)}
{basket.productItems?.map((productItem, idx) => {
return (
<ProductItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ import CartItemVariantName from '@salesforce/retail-react-app/app/components/ite
import CartItemVariantAttributes from '@salesforce/retail-react-app/app/components/item-variant/item-attributes'
import CartItemVariantPrice from '@salesforce/retail-react-app/app/components/item-variant/item-price'
import {useCurrentCustomer} from '@salesforce/retail-react-app/app/hooks/use-current-customer'
import {API_ERROR_MESSAGE} from '@salesforce/retail-react-app/app/constants'
import {
API_ERROR_MESSAGE,
STORE_LOCATOR_IS_ENABLED
} from '@salesforce/retail-react-app/app/constants'
import {useCurrency} from '@salesforce/retail-react-app/app/hooks'

const onClient = typeof window !== 'undefined'
Expand All @@ -67,7 +70,10 @@ const CheckoutConfirmation = () => {
const form = useForm()

// Check if this is a pickup order and get store details
const isPickupOrder = order?.shipments?.[0]?.shippingMethod?.c_storePickupEnabled === true
const isBopisEnabled = STORE_LOCATOR_IS_ENABLED
const isPickupOrder = isBopisEnabled
? order?.shipments?.[0]?.shippingMethod?.c_storePickupEnabled === true
: false
const storeId = order?.shipments?.[0]?.c_fromStoreId
const {data: storeData} = useStores(
{
Expand All @@ -76,7 +82,7 @@ const CheckoutConfirmation = () => {
}
},
{
enabled: !!storeId && isPickupOrder && onClient
enabled: !!storeId && isPickupOrder && onClient && isBopisEnabled
}
)
const store = storeData?.data?.[0]
Expand Down Expand Up @@ -252,8 +258,8 @@ const CheckoutConfirmation = () => {
/>
</Heading>

<Stack spacing={1}>
<Heading as="h3" fontSize="sm">
<Stack spacing={2}>
<Heading as="h3" fontSize="md">
<FormattedMessage
defaultMessage="Pickup Address"
id="checkout_confirmation.heading.pickup_address"
Expand Down
15 changes: 12 additions & 3 deletions packages/template-retail-react-app/app/pages/checkout/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ import {useShopperOrdersMutation, useShopperBasketsMutation} from '@salesforce/c
import UnavailableProductConfirmationModal from '@salesforce/retail-react-app/app/components/unavailable-product-confirmation-modal'
import {
API_ERROR_MESSAGE,
TOAST_MESSAGE_REMOVED_ITEM_FROM_CART
TOAST_MESSAGE_REMOVED_ITEM_FROM_CART,
STORE_LOCATOR_IS_ENABLED
} from '@salesforce/retail-react-app/app/constants'
import {useToast} from '@salesforce/retail-react-app/app/hooks/use-toast'
import LoadingSpinner from '@salesforce/retail-react-app/app/components/loading-spinner'
Expand All @@ -53,7 +54,11 @@ const Checkout = () => {
const isSocialEnabled = !!social?.enabled
const isPasswordlessEnabled = !!passwordless?.enabled

const isPickupOrder = basket?.shipments[0]?.shippingMethod?.c_storePickupEnabled === true
// Only enable BOPIS functionality if the feature toggle is on
const isBopisEnabled = STORE_LOCATOR_IS_ENABLED
const isPickupOrder = isBopisEnabled
? basket?.shipments[0]?.shippingMethod?.c_storePickupEnabled === true
: false

useEffect(() => {
if (error || step === 4) {
Expand Down Expand Up @@ -102,7 +107,11 @@ const Checkout = () => {
isPasswordlessEnabled={isPasswordlessEnabled}
idps={idps}
/>
{isPickupOrder ? <PickupAddress /> : <ShippingAddress />}
{isBopisEnabled && isPickupOrder ? (
<PickupAddress />
) : (
<ShippingAddress />
)}
{!isPickupOrder && <ShippingOptions />}
<Payment />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const Payment = () => {
const appliedPayment = basket?.paymentInstruments && basket?.paymentInstruments[0]

const isPickupOrder = basket?.shipments[0]?.shippingMethod?.c_storePickupEnabled === true
const [billingSameAsShipping, setBillingSameAsShipping] = useState(!isPickupOrder) // By default, have billing addr to be the same as shipping
const [billingSameAsShipping, setBillingSameAsShipping] = useState(!isPickupOrder)
const {mutateAsync: addPaymentInstrumentToBasket} = useShopperBasketsMutation(
'addPaymentInstrumentToBasket'
)
Expand Down
Loading
Loading