Skip to content

Commit

Permalink
fix(new-price-calculator): stop deriving purchase summary state from …
Browse files Browse the repository at this point in the history
…selected offer (#4882)

## Describe your changes

* We need to keep an active query for `PriceIntent` for all price calculator context. So I'm moving `useSyncPriceIntentState` call from `PurchaseFormV2` to `PriceCalculatorCmsPageContent`
* Stop getting offer data used by `PurchaseSummary` form `selectedOfferAtom`. That one should be in sync with price intent changes to avoid unexpected behaviors.
* It's hard to know which offer was just added to the cart while reseting price intent whenever something get's added. We do need to reset price intent whenever something get's added to the cart so I'm replacing _show purchase summary_ state with an _added offer_ state which `PurchaseSummary` can use for its UI. Showing/hiding `PurchaseSummary` now is about changing that state. 

With that said this is how it works now:

* Price calculator get's initialized with price intent (P1)
* User adds it to the cart
* We store the offer that got added
* We create a new price intent P2 and synchronize all atoms with it
* We use the stored offer to render `PurchaseSummary`

## Justify why they are needed

The issue

https://github.com/user-attachments/assets/10a149f6-ced8-495d-8238-75d5d27412a2

`PurchaseHero` relies on `useIsPriceIntentStateReady()` which check the presence of a price intent. Since `useSyncPriceIntentState` was being called inside `PurchaseFormV2` we keep an active query for Price Intent until `PurchaseFormV2` is mounted. When something get's added to the cart we reset Price Intent and swap what's gets rendered at the right section: `PurchaseFormV2` --> `PurchaseSummary` marking the end of the flow. However `useIsPriceIntentStateReady()` will never return `true` because we don't sync priceIntentAtoms with the new Price Intent because `PurchaseFormV2` was unmounted. This causes the `PurchaseHero` never being rendered.
  • Loading branch information
guilhermespopolin authored Oct 23, 2024
2 parents 67beab0 + ad66c40 commit 9c4e2a9
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use client'

import { useAtomValue } from 'jotai'
import { priceCalculatorShowPurchaseSummaryAtom } from '@/features/priceCalculator/priceCalculatorAtoms'
import { useSyncPriceIntentState } from '@/components/ProductPage/PurchaseForm/priceIntentAtoms'
import { priceCalculatorAddedOffer } from '@/features/priceCalculator/priceCalculatorAtoms'
import { PurchaseSummary } from '@/features/priceCalculator/PurchaseFormV2/PurchaseSummary/PurchaseSummary'
import { useResponsiveVariant } from '@/utils/useResponsiveVariant'
import { ProductHeroV2 } from '../../ProductHeroV2/ProductHeroV2'
Expand All @@ -17,9 +18,11 @@ import {

export function PriceCalculatorCmsPageContent() {
const variant = useResponsiveVariant('lg')
const showPurchaseSummary = useAtomValue(priceCalculatorShowPurchaseSummaryAtom)
const addedOfferToCart = useAtomValue(priceCalculatorAddedOffer)

const showProductHero = variant === 'desktop' || !showPurchaseSummary
useSyncPriceIntentState({ replacePriceIntentWhenCurrentIsAddedToCart: true })

const showProductHero = variant === 'desktop' || !addedOfferToCart

return (
<div className={pageGrid}>
Expand All @@ -29,7 +32,7 @@ export function PriceCalculatorCmsPageContent() {
</section>
)}
<section className={priceCalculatorSection}>
{showPurchaseSummary ? (
{addedOfferToCart ? (
<div className={purchaseSummaryWrapper}>
<PurchaseSummary className={purchaseSummary} />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { useCartEntryToReplace } from '@/components/ProductPage/useCartEntryToRe
import { DiscountFieldContainer } from '@/components/ShopBreakdown/DiscountFieldContainer'
import {
priceCalculatorStepAtom,
priceCalculatorShowPurchaseSummaryAtom,
priceCalculatorAddedOffer,
} from '@/features/priceCalculator/priceCalculatorAtoms'
import { DeductibleSelectorV2 } from '@/features/priceCalculator/PurchaseFormV2/OfferPresenterV2/DeductibleSelectorV2/DeductibleSelectorV2'
import { ProductCardSmall } from '@/features/priceCalculator/PurchaseFormV2/OfferPresenterV2/ProductCardSmall/ProductCardSmall'
Expand Down Expand Up @@ -163,7 +163,7 @@ function OfferSummary() {
const shopSessionId = useShopSessionIdOrThrow()
const selectedOffer = useSelectedOfferValueOrThrow()
const priceIntent = usePriceIntent()
const setShowPurchaseSummary = useSetAtom(priceCalculatorShowPurchaseSummaryAtom)
const setAddedOffer = useSetAtom(priceCalculatorAddedOffer)

const entryToReplace = useCartEntryToReplace()
const tracking = useTracking()
Expand Down Expand Up @@ -195,7 +195,7 @@ function OfferSummary() {
await addToCart(selectedOffer.id)
// Make sure user views "added to cart" notification and/or bundle discount banner
window.scrollTo({ top: 0, behavior: 'instant' })
setShowPurchaseSummary(true)
setAddedOffer(selectedOffer)
}

const productData = useProductData()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
import { useAtom } from 'jotai'
import { useEffect } from 'react'
import { PriceLoader } from '@/components/PriceLoader'
import {
useIsPriceIntentStateReady,
useSyncPriceIntentState,
} from '@/components/ProductPage/PurchaseForm/priceIntentAtoms'
import { useIsPriceIntentStateReady } from '@/components/ProductPage/PurchaseForm/priceIntentAtoms'
import {
PRELOADED_PRICE_INTENT_QUERY_PARAM,
usePreloadedPriceIntentId,
Expand All @@ -21,7 +18,6 @@ import { InsuranceDataForm } from './InsuranceDataForm/InsuranceDataForm'
import { centered, priceLoaderWrapper, viewOffersWrapper } from './PurchaseFormV2.css'

export function PurchaseFormV2() {
useSyncPriceIntentState({ replacePriceIntentWhenCurrentIsAddedToCart: true })
useWarnOnPreloadedPriceIntentId()

const isReady = useIsPriceIntentStateReady()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import clsx from 'clsx'
import { useSetAtom } from 'jotai'
import { useAtom } from 'jotai'
import { useTranslation } from 'next-i18next'
import { useEffect } from 'react'
import { Text, Card, Divider, Button, yStack } from 'ui'
Expand All @@ -8,15 +8,14 @@ import { ButtonNextLink } from '@/components/ButtonNextLink'
import { useSetGlobalBanner, useDismissBanner } from '@/components/GlobalBanner/globalBannerState'
import { Pillow } from '@/components/Pillow/Pillow'
import { usePriceTemplate } from '@/components/ProductPage/PurchaseForm/priceTemplateAtom'
import { useSelectedOfferValueOrThrow } from '@/components/ProductPage/PurchaseForm/useSelectedOffer'
import { TextWithLink } from '@/components/TextWithLink'
import { TotalPrice } from '@/components/TotalPrice/TotalPrice'
import { BUNDLE_DISCOUNT_PERCENTAGE } from '@/features/bundleDiscount/bundleDiscount.constants'
import {
hasBundleDiscount,
hasCartItemsEligibleForBundleDiscount,
} from '@/features/bundleDiscount/bundleDiscount.utils'
import { priceCalculatorShowPurchaseSummaryAtom } from '@/features/priceCalculator/priceCalculatorAtoms'
import { priceCalculatorAddedOffer } from '@/features/priceCalculator/priceCalculatorAtoms'
import type { TemplateV2 } from '@/services/PriceCalculator/PriceCalculator.types'
import { useShopSessionValueOrThrow } from '@/services/shopSession/ShopSessionContext'
import { getOfferPrice } from '@/utils/getOfferPrice'
Expand All @@ -27,22 +26,22 @@ import { actions } from './PurchaseSummary.css'
export function PurchaseSummary({ className }: { className?: string }) {
const { t } = useTranslation('purchase-form')
const locale = useRoutingLocale()
// Selected offer is not price intent dependent but defined at page level. So even
// though at this point a new price intent is loaded the selected offer will remain
// referencing to an offer created by the previous price intent
const offer = useSelectedOfferValueOrThrow()
const [addedOffer, setAddedOffer] = useAtom(priceCalculatorAddedOffer)
const priceTemplate = usePriceTemplate() as TemplateV2
const setShowPurchaseSummary = useSetAtom(priceCalculatorShowPurchaseSummaryAtom)

useNotifyAboutBundleDiscounts()

if (!addedOffer) {
return null
}

const handleAddMore = () => {
// Price intent is reset whenever a product is added to the cart so the only thing
// we need to do in order to add another product is to close the purchase summary
setShowPurchaseSummary(false)
setAddedOffer(null)
}

const showAddMoreButton = offer.product.multiple && priceTemplate.addMultiple
const showAddMoreButton = addedOffer.product.multiple && priceTemplate.addMultiple

return (
<div className={clsx(yStack({ gap: 'xl' }), className)}>
Expand All @@ -51,22 +50,22 @@ export function PurchaseSummary({ className }: { className?: string }) {
<Card.Root>
<Card.Header>
<Card.Media>
<Pillow size="small" {...offer.product.pillowImage} />
<Pillow size="small" {...addedOffer.product.pillowImage} />
</Card.Media>
<Card.Heading>
<Card.Title>{offer.product.displayNameFull}</Card.Title>
<Card.Subtitle>{offer.exposure.displayNameShort}</Card.Subtitle>
<Card.Title>{addedOffer.product.displayNameFull}</Card.Title>
<Card.Subtitle>{addedOffer.exposure.displayNameShort}</Card.Subtitle>
</Card.Heading>
</Card.Header>
<Divider />
<TotalPrice label={t('YOUR_PRICE', { ns: 'common' })} {...getOfferPrice(offer.cost)} />
<TotalPrice label={t('YOUR_PRICE', { ns: 'common' })} {...getOfferPrice(addedOffer.cost)} />
</Card.Root>

<div className={actions}>
{showAddMoreButton && (
<Button onClick={handleAddMore} variant="secondary">
{t('ADD_ANOTHER_INSURANCE_LABEL', {
productName: offer.product.displayNameShort.toLowerCase(),
productName: addedOffer.product.displayNameShort.toLowerCase(),
})}
</Button>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
currentPriceIntentIdAtom,
priceIntentAtom,
} from '@/components/ProductPage/PurchaseForm/priceIntentAtoms'
import { type ProductOfferFragment } from '@/services/graphql/generated'
import { getAtomValueOrThrow } from '@/utils/jotaiUtils'

type PriceCalculatorStep = 'loadingForm' | 'fillForm' | 'calculatingPrice' | 'viewOffers'
Expand Down Expand Up @@ -53,4 +54,4 @@ export const usePriceCalculatorDeductibleInfo = () => {

export const priceCalculatorShowEditSsnWarningAtom = atom(false)

export const priceCalculatorShowPurchaseSummaryAtom = atom(false)
export const priceCalculatorAddedOffer = atom<ProductOfferFragment | null>(null)

0 comments on commit 9c4e2a9

Please sign in to comment.