managing shopping carts for e-commerce websites.
import { IconHeartFilled, IconTrashFilled, IconInfoCircleFilled } from '@tabler/icons-react';
import { cva } from 'class-variance-authority';
import Image from '@/components/ui/Image';
import Select from '@/components/ui/Select';
import NumberStepper from '@/components/ui/NumberStepper';
import Segment from '@/components/ui/Segment';
import ToolTip from '@/components/ui/Tooltip';
import { DataList, DataListItem, DataListLabel, DataListValue } from '@/components/ui/DataList';
import Heading from '@/components/ui/Heading';
import Button from '@/components/ui/Button';
const panel = cva('bg-background border border-line rounded-md p-4');
export default function Page() {
return (
<div className="flex h-screen justify-center md:items-center md:gap-4">
<div className="flex flex-col gap-4 p-4 md:flex-row">
<section className={panel({ className: 'flex-1 lg:min-w-xl' })}>
<Heading as="h4" className="pb-2">
Cart
</Heading>
<ul>
<GoodItem name="High-fidelity headphones" stock={24} price={159.99} discount={20} cover="/images/goods/ear-pods.webp" />
<GoodItem name="Casual skate shoes" stock={500} price={59.9} discount={5.2} cover="/images/goods/shoe.webp" />
<GoodItem name="Cotton socks" stock={246} price={1.09} cover="/images/goods/socks.webp" />
</ul>
</section>
<section className={panel({ className: 'h-fit md:w-65' })}>
<div className="border-line border-b border-dashed pb-4">
<Heading as="h5" className="pb-2">
Delivery
</Heading>
<Segment
options={[
{ label: 'Free', value: 'free' },
{ label: 'Express:$2.99', value: 'express' },
]}
/>
<p className="text-description mt-2 text-sm">delivery date: July 12, 2025</p>
</div>
<div className="border-line mt-4 border-b border-dashed pb-4">
<DataList className="[&_[data-slot=datalist-item]]:justify-between">
<DataListItem>
<DataListLabel>Subtotal</DataListLabel>
<DataListValue>$195.78</DataListValue>
</DataListItem>
<DataListItem>
<DataListLabel className="flex items-center gap-1">
Delivery
<ToolTip side="top" align="start" className="origin-top-left" alignOffset={-12} trigger={<IconInfoCircleFilled size={14} className="mt-0.5" />}>
Free shipping on this mode
</ToolTip>
</DataListLabel>
<DataListValue>$0.00</DataListValue>
</DataListItem>
<DataListItem>
<DataListLabel className="flex items-center gap-1">
Tax
<ToolTip side="top" align="start" className="origin-top-left" alignOffset={-12} trigger={<IconInfoCircleFilled size={14} className="mt-0.5" />}>
different tax for each country
</ToolTip>
</DataListLabel>
<DataListValue>+$12.00</DataListValue>
</DataListItem>
</DataList>
</div>
<DataList className="mt-4">
<DataListItem className="justify-between">
<DataListLabel>Total</DataListLabel>
<DataListValue>$207.78</DataListValue>
</DataListItem>
</DataList>
<div className="mt-6 space-y-3">
<Button className="w-full" size="sm">
Proceed to checkout
</Button>
<Button className="w-full" size="sm" colors="neutral" variant="bordered">
Continue shopping
</Button>
</div>
</section>
</div>
</div>
);
}
interface GoodItemProps {
name: string;
cover?: string;
stock: number;
price: number;
discount?: number;
children?: React.ReactNode;
}
function GoodItem({ name, cover, stock, price, discount, children }: GoodItemProps) {
return (
<li className="border-line grid grid-cols-[1fr_auto] gap-2 border-t py-3">
<div className="flex gap-3 overflow-hidden">
<Image src={cover} className="aspect-[3/4] w-20 shrink-0 rounded md:w-26" alt={name} />
<div className="h-full overflow-hidden py-3">
<p className="truncate font-semibold">{name}</p>
<p className="text-description mt-1 text-sm">{stock} in stock</p>
<NumberStepper size="sm" min={1} defaultValue={1} className="mt-6 w-30 [&_input]:text-center" />
</div>
</div>
<div className="relative h-full py-3">
<p className="pr-3 text-end font-semibold">${(price - (discount || 0)).toFixed(2)}</p>
<p className="mt-1 pr-3 text-end text-sm font-semibold text-red-400 line-through">{discount ? `$${price}` : ''}</p>
<div className="absolute right-0 bottom-3 flex justify-end md:bottom-6">
<Button size="sm" colors="neutral" variant="light" className="size-8 gap-1 p-0 text-neutral-500 md:w-auto md:px-3">
<IconHeartFilled size={16} />
<span className="hidden md:block">Like</span>
</Button>
<Button size="sm" colors="neutral" variant="light" className="size-8 gap-1 p-0 text-neutral-500 md:w-auto md:px-3">
<IconTrashFilled size={16} />
<span className="hidden md:block">Delete</span>
</Button>
</div>
</div>
</li>
);
}
import { IconArrowNarrowRight, IconShoppingCart, IconX } from '@tabler/icons-react';
import Button from '@/components/ui/Button';
import Tag from '@/components/ui/Tag';
import { Drawer, DrawerClose } from '@/components/ui/Drawer';
import Image from '@/components/ui/Image';
import Divider from '@/components/ui/Divider';
import Heading from '@/components/ui/Heading';
export default function Page() {
return (
<div>
<Drawer
className="flex w-full flex-col justify-between overflow-hidden px-0 md:w-lg"
trigger={
<Button colors="neutral" variant="bordered" asIcon className="absolute top-6 right-6">
<IconShoppingCart size={18} />
</Button>
}>
<div className="flex items-center justify-between px-4">
<Heading as="h4">Shopping Cart</Heading>
<DrawerClose>
<Button asIcon colors="neutral" size="sm" variant="light" className="">
<IconX size={18} />
</Button>
</DrawerClose>
</div>
<ul className="mt-4 flex-1 overflow-auto">
<GoodItem cover="/images/goods/ear-pods.webp" name="High-fidelity headphones" price={159.99} quantity={1} sku={['red', 'bluetooth']} />
<GoodItem cover="/images/goods/shoe.webp" name="Casual skate shoes" price={59.9} quantity={2} sku={['brown', 'eu 40']} />
<GoodItem cover="/images/goods/socks.webp" name="Cotton socks" price={1.09} quantity={4} sku={['white', 'short']} />
</ul>
<div className="border-line bg-background w-ull bottom-4 border-t px-4 pt-6">
<div className="flex justify-between pb-6">
<p>Subtotal</p>
<p>$284.04</p>
</div>
<div className="space-y-2">
<Button className="w-full">Checkout</Button>
<Button className="w-full gap-1" colors="neutral">
Continue shopping <IconArrowNarrowRight size={18} />
</Button>
</div>
</div>
</Drawer>
</div>
);
}
interface GoodItemProps {
name: string;
cover?: string;
sku?: string[];
price: number;
quantity: number;
}
function GoodItem({ name, cover, sku, price, quantity }: GoodItemProps) {
return (
<li className="border-line relative mx-4 flex h-40 justify-between py-4 not-last:border-b">
<div className="flex h-full gap-3">
<Image src={cover} className="aspect-[3/4] h-full rounded" alt={name} />
<div className="flex h-full flex-col justify-between py-2">
<div>
<p className="text-lg font-semibold">{name}</p>
<div className="mt-2 flex items-center gap-2">
{sku?.map((item, index) => (
<Tag colors="secondary" key={index} size="sm">
{item}
</Tag>
))}
</div>
</div>
<p>Qty:{quantity}</p>
</div>
</div>
<div className="flex h-full flex-col justify-between py-2">
<p className="font-semibold">${price}</p>
<div className="cursor-pointer text-red-500/60 transition-colors hover:text-red-500">Remove</div>
</div>
</li>
);
}