Chloe 1a0d183681
feat: core charge shouldn't be treated as a separate product
Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
2024-05-31 15:32:00 +07:00

116 lines
5.1 KiB
TypeScript

'use client';
import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react';
import { ShoppingCartIcon } from '@heroicons/react/24/outline';
import Price from 'components/price';
import type { Cart } from 'lib/shopify/types';
import { Fragment, useEffect, useRef, useState } from 'react';
import CloseCart from './close-cart';
import LineItem from './line-item';
import OpenCart from './open-cart';
export default function CartModal({ cart }: { cart: Cart | undefined }) {
const [isOpen, setIsOpen] = useState(false);
const quantityRef = useRef(cart?.totalQuantity);
const openCart = () => setIsOpen(true);
const closeCart = () => setIsOpen(false);
useEffect(() => {
// Open cart modal when quantity changes.
if (cart?.totalQuantity !== quantityRef.current) {
// But only if it's not already open (quantity also changes when editing items in cart).
if (!isOpen) {
setIsOpen(true);
}
// Always update the quantity reference
quantityRef.current = cart?.totalQuantity;
}
}, [isOpen, cart?.totalQuantity, quantityRef]);
return (
<>
<button aria-label="Open cart" onClick={openCart}>
<OpenCart quantity={cart?.totalQuantity} />
</button>
<Transition show={isOpen} as={Fragment}>
<Dialog onClose={closeCart} className="relative z-50">
<TransitionChild
as={Fragment}
enter="transition-all ease-in-out duration-300"
enterFrom="opacity-0 backdrop-blur-none"
enterTo="opacity-100 backdrop-blur-[.5px]"
leave="transition-all ease-in-out duration-200"
leaveFrom="opacity-100 backdrop-blur-[.5px]"
leaveTo="opacity-0 backdrop-blur-none"
>
<div className="fixed inset-0 bg-black/30" aria-hidden="true" />
</TransitionChild>
<TransitionChild
as={Fragment}
enter="transition-all ease-in-out duration-300"
enterFrom="translate-x-full"
enterTo="translate-x-0"
leave="transition-all ease-in-out duration-200"
leaveFrom="translate-x-0"
leaveTo="translate-x-full"
>
<DialogPanel className="fixed bottom-0 right-0 top-0 flex h-full w-full flex-col border-l border-neutral-200 bg-white/80 p-6 text-black backdrop-blur-xl dark:border-neutral-700 dark:bg-black/80 dark:text-white md:w-[390px]">
<div className="flex items-center justify-between">
<p className="text-lg font-semibold">My Cart</p>
<button aria-label="Close cart" onClick={closeCart}>
<CloseCart />
</button>
</div>
{!cart || cart.lines.length === 0 ? (
<div className="mt-20 flex w-full flex-col items-center justify-center overflow-hidden">
<ShoppingCartIcon className="h-16" />
<p className="mt-6 text-center text-2xl font-bold">Your cart is empty.</p>
</div>
) : (
<div className="flex h-full flex-col justify-between overflow-hidden p-1">
<ul className="flex-grow overflow-auto py-4">
{cart.lines.map((item) => {
return <LineItem item={item} closeCart={closeCart} key={item.id} />;
})}
</ul>
<div className="py-4 text-sm text-neutral-500 dark:text-neutral-400">
<div className="mb-3 flex items-center justify-between border-b border-neutral-200 pb-1 dark:border-neutral-700">
<p>Taxes</p>
<Price
className="text-right text-base text-black dark:text-white"
amount={cart.cost.totalTaxAmount.amount}
currencyCode={cart.cost.totalTaxAmount.currencyCode}
/>
</div>
<div className="mb-3 flex items-center justify-between border-b border-neutral-200 pb-1 pt-1 dark:border-neutral-700">
<p>Shipping</p>
<p className="text-right">Calculated at checkout</p>
</div>
<div className="mb-3 flex items-center justify-between border-b border-neutral-200 pb-1 pt-1 dark:border-neutral-700">
<p>Total</p>
<Price
className="text-right text-base text-black dark:text-white"
amount={cart.cost.totalAmount.amount}
currencyCode={cart.cost.totalAmount.currencyCode}
/>
</div>
</div>
<a
href={cart.checkoutUrl}
className="block w-full rounded-full bg-secondary p-3 text-center text-sm font-medium text-white opacity-90 hover:opacity-100"
>
Proceed to Checkout
</a>
</div>
)}
</DialogPanel>
</TransitionChild>
</Dialog>
</Transition>
</>
);
}