diff --git a/app/components/CartLineItem.module.css b/app/components/CartLineItem.module.css new file mode 100644 index 0000000..47440c9 --- /dev/null +++ b/app/components/CartLineItem.module.css @@ -0,0 +1,11 @@ +.freeRewardBadge { + display: inline-flex; + align-items: center; + border-radius: 0.375rem; + background-color: #ecfdf5; + padding: 0.25rem 0.5rem; + font-size: 0.75rem; + font-weight: 500; + color: #047857; + box-shadow: inset 0 0 0 1px rgba(5, 150, 105, 0.2); +} diff --git a/app/components/CartLineItem.tsx b/app/components/CartLineItem.tsx index 80e34be..cf906d6 100644 --- a/app/components/CartLineItem.tsx +++ b/app/components/CartLineItem.tsx @@ -6,6 +6,7 @@ import {Link} from 'react-router'; import {ProductPrice} from './ProductPrice'; import {useAside} from './Aside'; import type {CartApiQueryFragment} from 'storefrontapi.generated'; +import styles from './CartLineItem.module.css'; type CartLine = OptimisticCartLine; @@ -25,6 +26,10 @@ export function CartLineItem({ const lineItemUrl = useVariantUrl(product.handle, selectedOptions); const {close} = useAside(); + const isFreeReward = (line?.attributes ?? []).some( + (x) => x.key === '__lion_sfp_id', + ); + return (
  • {image && ( @@ -52,7 +57,11 @@ export function CartLineItem({ {product.title}

    - + {isFreeReward ? ( + FREE REWARD + ) : ( + + )}
      {selectedOptions.map((option) => (
    • @@ -62,7 +71,7 @@ export function CartLineItem({
    • ))}
    - +
  • ); @@ -73,12 +82,26 @@ export function CartLineItem({ * These controls are disabled when the line item is new, and the server * hasn't yet responded that it was successfully added to the cart. */ -function CartLineQuantity({line}: {line: CartLine}) { +function CartLineQuantity({ + line, + isFreeReward, +}: { + line: CartLine; + isFreeReward: boolean; +}) { if (!line || typeof line?.quantity === 'undefined') return null; const {id: lineId, quantity, isOptimistic} = line; const prevQuantity = Number(Math.max(0, quantity - 1).toFixed(0)); const nextQuantity = Number((quantity + 1).toFixed(0)); + if (isFreeReward) { + return ( +
    + +
    + ); + } + return (
    Quantity: {quantity}    diff --git a/app/components/CartMain.tsx b/app/components/CartMain.tsx index 1117b68..bad680a 100644 --- a/app/components/CartMain.tsx +++ b/app/components/CartMain.tsx @@ -4,22 +4,31 @@ import type {CartApiQueryFragment} from 'storefrontapi.generated'; import {useAside} from '~/components/Aside'; import {CartLineItem} from '~/components/CartLineItem'; import {CartSummary} from './CartSummary'; +import {LoyaltylionData} from '~/lib/loyaltylion/fetchLoyaltylionData'; +import {useLoyaltylionCartUpdates} from '~/lib/loyaltylion/useLoyaltylionCartUpdates'; +import {AvailableProductRewards} from './loyaltylion/AvailableProductRewards'; export type CartLayout = 'page' | 'aside'; export type CartMainProps = { cart: CartApiQueryFragment | null; layout: CartLayout; + loyaltylion: Promise; }; /** * The main cart component that displays the cart items and summary. * It is used by both the /cart route and the cart aside dialog. */ -export function CartMain({layout, cart: originalCart}: CartMainProps) { +export function CartMain({ + layout, + cart: originalCart, + loyaltylion, +}: CartMainProps) { // The useOptimisticCart hook applies pending actions to the cart // so the user immediately sees feedback when they modify the cart. const cart = useOptimisticCart(originalCart); + useLoyaltylionCartUpdates(loyaltylion); const linesCount = Boolean(cart?.lines?.nodes?.length || 0); const withDiscount = @@ -41,6 +50,7 @@ export function CartMain({layout, cart: originalCart}: CartMainProps) {
    {cartHasItems && } + ); } diff --git a/app/components/Header.tsx b/app/components/Header.tsx index 45b620b..3df9f15 100644 --- a/app/components/Header.tsx +++ b/app/components/Header.tsx @@ -7,12 +7,14 @@ import { } from '@shopify/hydrogen'; import type {HeaderQuery, CartApiQueryFragment} from 'storefrontapi.generated'; import {useAside} from '~/components/Aside'; +import {LoyaltylionData} from '~/lib/loyaltylion/fetchLoyaltylionData'; interface HeaderProps { header: HeaderQuery; cart: Promise; isLoggedIn: Promise; publicStoreDomain: string; + loyaltylion: Promise; } type Viewport = 'desktop' | 'mobile'; @@ -22,6 +24,7 @@ export function Header({ isLoggedIn, cart, publicStoreDomain, + loyaltylion, }: HeaderProps) { const {shop, menu} = header; return ( @@ -35,7 +38,11 @@ export function Header({ primaryDomainUrl={header.shop.primaryDomain.url} publicStoreDomain={publicStoreDomain} /> - + ); } @@ -98,10 +105,39 @@ export function HeaderMenu({ function HeaderCtas({ isLoggedIn, cart, -}: Pick) { + loyaltylion, +}: Pick) { return (