Checkout Forms 02
Checkout Forms 02 is a React and Tailwind CSS form component from Bund UI, licensed MIT. Copy it and paste it into your project.
Bund UI
MIT
forms
What it is
Checkout Forms 02 is a React and Tailwind CSS form component from Bund UI, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
Needs
npm i lucide-react
Licence
This component comes from Bund UI and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Checkout Forms 02
import { Card, CardContent } from "@/components/ui/card";
import { MapPin, Truck } from "lucide-react";
interface DeliveryCardProps {
address: {
name: string;
street: string;
apt: string;
city: string;
country: string;
};
delivery: {
type: string;
date: string;
time: string;
};
}
const DeliveryCard = ({ address, delivery }: DeliveryCardProps) => {
return (
<div className="grid gap-4 md:grid-cols-2">
<Card className="shadow-none">
<CardContent className="flex gap-4">
<div className="bg-muted flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-lg">
<MapPin className="text-muted-foreground h-6 w-6" />
</div>
<div className="flex-1">
<h3 className="text-foreground mb-3 font-semibold">{address.name}</h3>
<div className="text-foreground space-y-1 text-sm">
<p>{address.street}</p>
<p>{address.apt}</p>
<p>{address.city}</p>
<p>{address.country}</p>
</div>
</div>
</CardContent>
</Card>
<Card className="shadow-none">
<CardContent className="flex gap-4">
<div className="bg-muted flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-lg">
<Truck className="text-muted-foreground h-6 w-6" />
</div>
<div className="flex-1">
<h3 className="text-foreground mb-3 font-semibold">{delivery.type}</h3>
<div className="text-foreground space-y-1 text-sm">
<p>{delivery.date}</p>
<p>{delivery.time}</p>
</div>
</div>
</CardContent>
</Card>
</div>
);
};
export default DeliveryCard;
import { Card } from "@/components/ui/card";
interface OrderItemProps {
image: string;
name: string;
quantity: number;
price: string;
}
const OrderItem = ({ image, name, quantity, price }: OrderItemProps) => {
return (
<div className="bg-muted/50 mb-3 flex flex-row items-center justify-between gap-4 rounded-lg border-none pe-4 shadow-none">
<img src={image} alt={name} className="aspect-square w-20 rounded-md object-cover" />
<div className="min-w-0 flex-1">
<h3 className="font-medium">{name}</h3>
</div>
<div className="flex flex-shrink-0 items-center gap-6">
<span className="text-muted-foreground">{quantity} pc</span>
<span className="font-medium">{price}</span>
</div>
</div>
);
};
export default OrderItem;
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
interface OrderSummaryProps {
subtotal: string;
discount: string;
delivery: string;
total: string;
}
const OrderSummary = ({ subtotal, discount, delivery, total }: OrderSummaryProps) => {
return (
<Card className="shadow-none">
<CardContent className="space-y-6">
<div className="space-y-4">
<div className="font-heading flex items-center justify-between text-2xl">
<div>Total</div>
<div>{total}</div>
</div>
<div className="border-border space-y-3 border-t pt-4 text-sm">
<div className="flex items-center justify-between">
<span className="">Subtotal</span>
<span className="font-medium">{subtotal}</span>
</div>
<div className="flex items-center justify-between">
<span className="">Discount</span>
<span className="text-destructive font-medium">-{discount}</span>
</div>
<div className="flex items-center justify-between">
<span className="">Delivery</span>
<span className="font-medium">{delivery}</span>
</div>
</div>
</div>
<Button size="lg" className="w-full">
Pay {total}
</Button>
<p className="text-muted-foreground text-xs leading-relaxed">
By placing an order, you're agreeing to our{" "}
<a href="#" className="underline">
Privacy Policy
</a>
,{" "}
<a href="#" className="underline">
Terms and Conditions
</a>{" "}
and{" "}
<a href="#" className="underline">
Cancellation policy
</a>
.
</p>
</CardContent>
</Card>
);
};
export default OrderSummary;
import { Card, CardContent } from "@/components/ui/card";
interface PaymentMethodCardProps {
icon: React.ReactNode;
title: string;
description: string;
}
const PaymentMethodCard = ({ icon, title, description }: PaymentMethodCardProps) => {
return (
<Card className="shadow-none">
<CardContent className="flex gap-4">
<div className="flex-shrink-0">{icon}</div>
<div className="flex-1">
<h3 className="mb-1 font-semibold">{title}</h3>
<p className="text-muted-foreground text-sm">{description}</p>
</div>
</CardContent>
</Card>
);
};
export default PaymentMethodCard;
import { Check } from "lucide-react";
import { cn } from "@/lib/utils";
interface Step {
number: number;
label: string;
completed: boolean;
active: boolean;
}
interface PaymentStepperProps {
currentStep: number;
}
const PaymentStepper = ({ currentStep }: PaymentStepperProps) => {
const steps: Step[] = [
{ number: 1, label: "Cart", completed: currentStep > 1, active: currentStep === 1 },
{
number: 2,
label: "Delivery and payment",
completed: currentStep > 2,
active: currentStep === 2
},
{ number: 3, label: "Summary", completed: currentStep > 3, active: currentStep === 3 },
{ number: 4, label: "Done", completed: false, active: currentStep === 4 }
];
return (
<div className="w-full py-8">
<div className="mx-auto flex max-w-5xl items-center justify-between px-4">
{steps.map((step, index) => (
<div key={step.number} className="flex flex-1 items-center">
<div className="flex flex-1 flex-col items-center">
<div className="flex w-full items-center">
<div
className={cn(
"flex h-10 w-10 items-center justify-center rounded-full border-2 font-medium transition-colors",
step.active && "bg-accent border-accent text-accent-foreground",
step.completed && "bg-accent border-accent text-accent-foreground",
!step.active &&
!step.completed &&
"bg-muted border-border text-muted-foreground"
)}>
{step.completed ? <Check className="h-5 w-5" /> : step.number}
</div>
{index < steps.length - 1 && (
<div
className={cn(
"mx-2 h-0.5 flex-1 transition-colors",
step.completed ? "bg-accent" : "bg-border"
)}
/>
)}
</div>
<span
className={cn(
"mt-2 text-center text-sm font-medium whitespace-nowrap",
step.active ? "text-foreground" : "text-muted-foreground"
)}>
{step.label}
</span>
</div>
</div>
))}
</div>
</div>
);
};
export default PaymentStepper;
export const orderItems = [
{
image: "/images/products/list3.png",
name: "Ilia C Beyond Triple Serum SPF 40",
quantity: 1,
price: "$23.99"
},
{
image: "/images/products/list1.png",
name: "Our Place Always Pan",
quantity: 1,
price: "$129.99"
}
];
export const deliveryAddress = {
name: "Emily Anderson",
street: "123 Main Street",
apt: "Apt 4B",
city: "Cityville, State 56789",
country: "United States"
};
export const deliveryInfo = {
type: "Courier delivery",
date: "September 17, 2023",
time: "12:00 - 14:00"
};
import { Button } from "@/components/ui/button";
import OrderItem from "./components/order-item";
import DeliveryCard from "./components/delivery-card";
import PaymentMethodCard from "./components/payment-method-card";
import OrderSummary from "./components/order-summary";
import { orderItems, deliveryAddress, deliveryInfo } from "./data";
import {
Stepper,
StepperIndicator,
StepperItem,
StepperNav,
StepperSeparator,
StepperTrigger
} from "@/components/ui/stepper";
import { Check, LoaderCircleIcon } from "lucide-react";
const steps = [1, 2, 3, 4];
export default function CheckoutPage() {
const ApplePayIcon = () => (
<div className="bg-muted flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-lg">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
className="bi bi-apple size-6"
viewBox="0 0 16 16">
<path d="M11.182.008C11.148-.03 9.923.023 8.857 1.18c-1.066 1.156-.902 2.482-.878 2.516s1.52.087 2.475-1.258.762-2.391.728-2.43m3.314 11.733c-.048-.096-2.325-1.234-2.113-3.422s1.675-2.789 1.698-2.854-.597-.79-1.254-1.157a3.7 3.7 0 0 0-1.563-.434c-.108-.003-.483-.095-1.254.116-.508.139-1.653.589-1.968.607-.316.018-1.256-.522-2.267-.665-.647-.125-1.333.131-1.824.328-.49.196-1.422.754-2.074 2.237-.652 1.482-.311 3.83-.067 4.56s.625 1.924 1.273 2.796c.576.984 1.34 1.667 1.659 1.899s1.219.386 1.843.067c.502-.308 1.408-.485 1.766-.472.357.013 1.061.154 1.782.539.571.197 1.111.115 1.652-.105.541-.221 1.324-1.059 2.238-2.758q.52-1.185.473-1.282" />
<path d="M11.182.008C11.148-.03 9.923.023 8.857 1.18c-1.066 1.156-.902 2.482-.878 2.516s1.52.087 2.475-1.258.762-2.391.728-2.43m3.314 11.733c-.048-.096-2.325-1.234-2.113-3.422s1.675-2.789 1.698-2.854-.597-.79-1.254-1.157a3.7 3.7 0 0 0-1.563-.434c-.108-.003-.483-.095-1.254.116-.508.139-1.653.589-1.968.607-.316.018-1.256-.522-2.267-.665-.647-.125-1.333.131-1.824.328-.49.196-1.422.754-2.074 2.237-.652 1.482-.311 3.83-.067 4.56s.625 1.924 1.273 2.796c.576.984 1.34 1.667 1.659 1.899s1.219.386 1.843.067c.502-.308 1.408-.485 1.766-.472.357.013 1.061.154 1.782.539.571.197 1.111.115 1.652-.105.541-.221 1.324-1.059 2.238-2.758q.52-1.185.473-1.282" />
</svg>
</div>
);
return (
<section className="py-10 lg:py-20">
<div className="px-4 lg:px-6">
<div className="mx-auto max-w-3xl">
<Stepper
defaultValue={2}
indicators={{
completed: <Check className="size-4" />,
loading: <LoaderCircleIcon className="size-4 animate-spin" />
}}
className="mb-14 space-y-8">
<StepperNav>
{steps.map((step) => (
<StepperItem key={step} step={step}>
<StepperTrigger>
<StepperIndicator className="data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=completed]:bg-green-500 data-[state=completed]:text-white data-[state=inactive]:text-gray-500">
{step}
</StepperIndicator>
</StepperTrigger>
{steps.length > step && (
<StepperSeparator className="group-data-[state=completed]/step:bg-primary" />
)}
</StepperItem>
))}
</StepperNav>
</Stepper>
</div>
<div className="grid gap-8 lg:grid-cols-[1fr_400px]">
{/* Left Column */}
<div className="space-y-12">
{/* Order Details */}
<section>
<div className="mb-4 flex items-center justify-between">
<h2 className="font-heading text-xl">Order details</h2>
</div>
<div>
{orderItems.map((item, index) => (
<OrderItem key={index} {...item} />
))}
</div>
</section>
{/* Delivery */}
<section>
<div className="mb-4 flex items-center justify-between">
<h2 className="font-heading text-xl">Delivery</h2>
<Button variant="secondary" size="sm">
Edit
</Button>
</div>
<DeliveryCard address={deliveryAddress} delivery={deliveryInfo} />
</section>
{/* Payment Method */}
<section>
<div className="mb-4 flex items-center justify-between">
<h2 className="font-heading text-xl">Payment method</h2>
<Button variant="secondary" size="sm">
Edit
</Button>
</div>
<PaymentMethodCard
icon={<ApplePayIcon />}
title="Apple Pay"
description="Swiftly make contactless payments with Apple Pay, ensuring a seamless and secure checkout experience."
/>
</section>
</div>
{/* Right Column - Summary */}
<div>
<OrderSummary subtotal="$153.98" discount="$10.00" delivery="Free" total="$143.98" />
</div>
</div>
</div>
</section>
);
}