-
Notifications
You must be signed in to change notification settings - Fork 79
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactore(core): checkout summary (#711)
* refactore(core): checkout summary * fix: use reducer in shipping info * fix: messaging * fix: use Promise.all
- Loading branch information
Showing
20 changed files
with
761 additions
and
847 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@bigcommerce/catalyst-core": patch | ||
--- | ||
|
||
Use checkout field from GQL to populate checkout summary. |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
152 changes: 31 additions & 121 deletions
152
apps/core/app/[locale]/(default)/cart/_components/checkout-summary.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,146 +1,56 @@ | ||
'use client'; | ||
import { AlertCircle } from 'lucide-react'; | ||
import { NextIntlClientProvider } from 'next-intl'; | ||
import { getMessages, getTranslations } from 'next-intl/server'; | ||
import { toast } from 'react-hot-toast'; | ||
|
||
import { useTranslations } from 'next-intl'; | ||
import { createContext, Dispatch, SetStateAction, useState } from 'react'; | ||
|
||
import { getCart } from '~/client/queries/get-cart'; | ||
import { ExistingResultType } from '~/client/util'; | ||
import { getCheckout } from '~/client/queries/get-checkout'; | ||
|
||
import { getShippingCountries } from '../_actions/get-shipping-countries'; | ||
|
||
import { ShippingEstimator } from './shipping-estimator'; | ||
|
||
export const createCurrencyFormatter = (currencyCode: string) => | ||
new Intl.NumberFormat('en-US', { | ||
style: 'currency', | ||
currency: currencyCode, | ||
}); | ||
export const CheckoutSummary = async ({ cartId, locale }: { cartId: string; locale: string }) => { | ||
const t = await getTranslations({ locale, namespace: 'Cart.CheckoutSummary' }); | ||
const messages = await getMessages({ locale }); | ||
|
||
type CartSummary = ExistingResultType<typeof getCart>; | ||
export type CheckoutSummary = CartSummary & { | ||
shippingCostTotal: { | ||
currencyCode: string; | ||
value: number; | ||
}; | ||
handlingCostTotal: { | ||
currencyCode: string; | ||
value: number; | ||
}; | ||
consignmentEntityId: string; | ||
}; | ||
export interface ShippingCosts { | ||
shippingCostTotal: number; | ||
handlingCostTotal: number; | ||
selectedShippingOption: string; | ||
} | ||
type ShippingCountries = ExistingResultType<typeof getShippingCountries>; | ||
const [checkout, shippingCountries] = await Promise.all([ | ||
getCheckout(cartId), | ||
getShippingCountries(), | ||
]); | ||
|
||
export const CheckoutContext = createContext<{ | ||
availableShippingCountries: ShippingCountries; | ||
checkoutEntityId: string; | ||
consignmentEntityId: string | null; | ||
shippingCosts: ShippingCosts | null; | ||
currencyCode: string; | ||
isShippingMethodSelected: boolean; | ||
setIsShippingMethodSelected: (newIsShippingMethodSelected: boolean) => void; | ||
updateCheckoutSummary: Dispatch<SetStateAction<CheckoutSummary>>; | ||
}>({ | ||
availableShippingCountries: [], | ||
checkoutEntityId: '', | ||
consignmentEntityId: '', | ||
currencyCode: '', | ||
isShippingMethodSelected: false, | ||
setIsShippingMethodSelected: () => undefined, | ||
shippingCosts: null, | ||
updateCheckoutSummary: () => undefined, | ||
}); | ||
if (!checkout) { | ||
toast.error(t('errorMessage'), { | ||
icon: <AlertCircle className="text-error-secondary" />, | ||
}); | ||
|
||
export const CheckoutSummary = ({ | ||
cart, | ||
shippingCountries, | ||
shippingCosts, | ||
}: { | ||
cart: NonNullable<CartSummary>; | ||
shippingCountries: ShippingCountries; | ||
shippingCosts: ShippingCosts | null; | ||
}) => { | ||
const t = useTranslations('Cart.CheckoutSummary'); | ||
const [isShippingMethodSelected, setIsShippingMethodSelected] = useState(false); | ||
const [checkoutSummary, updateCheckoutSummary] = useState<CheckoutSummary>({ | ||
...cart, | ||
shippingCostTotal: { | ||
currencyCode: cart.currencyCode, | ||
value: shippingCosts?.shippingCostTotal ?? 0, | ||
}, | ||
handlingCostTotal: { | ||
currencyCode: cart.currencyCode, | ||
value: shippingCosts?.handlingCostTotal ?? 0, | ||
}, | ||
consignmentEntityId: '', | ||
}); | ||
return null; | ||
} | ||
|
||
const currencyFormatter = createCurrencyFormatter(checkoutSummary.currencyCode); | ||
const extractCartlineItemsData = ({ | ||
entityId, | ||
productEntityId, | ||
quantity, | ||
variantEntityId, | ||
}: (typeof cart.lineItems.physicalItems)[number]) => ({ | ||
lineItemEntityId: entityId, | ||
productEntityId, | ||
quantity, | ||
variantEntityId, | ||
const currencyFormatter = new Intl.NumberFormat('en-US', { | ||
style: 'currency', | ||
currency: checkout.cart?.currencyCode, | ||
}); | ||
|
||
return ( | ||
<CheckoutContext.Provider | ||
value={{ | ||
availableShippingCountries: shippingCountries, | ||
checkoutEntityId: checkoutSummary.entityId, | ||
consignmentEntityId: checkoutSummary.consignmentEntityId, | ||
currencyCode: checkoutSummary.currencyCode, | ||
isShippingMethodSelected, | ||
setIsShippingMethodSelected, | ||
shippingCosts, | ||
updateCheckoutSummary, | ||
}} | ||
> | ||
<> | ||
<div className="flex justify-between border-t border-t-gray-200 py-4"> | ||
<span className="text-base font-semibold">{t('subTotal')}</span> | ||
<span className="text-base"> | ||
{currencyFormatter.format(cart.totalExtendedListPrice.value)} | ||
</span> | ||
<span className="font-semibold">{t('subTotal')}</span> | ||
<span>{currencyFormatter.format(checkout.subtotal?.value || 0)}</span> | ||
</div> | ||
|
||
<ShippingEstimator | ||
shippingItems={checkoutSummary.lineItems.physicalItems.reduce< | ||
Array<{ quantity: number; lineItemEntityId: string }> | ||
>((items, product) => { | ||
const { lineItemEntityId, quantity } = extractCartlineItemsData(product); | ||
|
||
items.push({ quantity, lineItemEntityId }); | ||
|
||
return items; | ||
}, [])} | ||
/> | ||
<NextIntlClientProvider locale={locale} messages={{ Cart: messages.Cart ?? {} }}> | ||
<ShippingEstimator checkout={checkout} shippingCountries={shippingCountries} /> | ||
</NextIntlClientProvider> | ||
|
||
<div className="flex justify-between border-t border-t-gray-200 py-4"> | ||
<span className="text-base font-semibold">{t('discounts')}</span> | ||
<span className="text-base"> | ||
-{currencyFormatter.format(checkoutSummary.discountedAmount.value)} | ||
</span> | ||
<span className="font-semibold">{t('discounts')}</span> | ||
<span>-{currencyFormatter.format(checkout.cart?.discountedAmount.value || 0)}</span> | ||
</div> | ||
|
||
<div className="flex justify-between border-t border-t-gray-200 py-4 text-xl font-bold lg:text-2xl"> | ||
{t('grandTotal')} | ||
<span> | ||
{currencyFormatter.format( | ||
checkoutSummary.amount.value + | ||
checkoutSummary.shippingCostTotal.value + | ||
checkoutSummary.handlingCostTotal.value, | ||
)} | ||
</span> | ||
<span>{currencyFormatter.format(checkout.grandTotal?.value || 0)}</span> | ||
</div> | ||
</CheckoutContext.Provider> | ||
</> | ||
); | ||
}; |
Oops, something went wrong.