Todos os componentes

Checkout Forms 02

Checkout Forms 02 é um componente de formulário para React e Tailwind CSS, da biblioteca Bund UI, com licença MIT. Copia e cola no teu projeto.

Bund UI MIT forms

O que é

Checkout Forms 02 é um componente de formulário para React e Tailwind CSS, da biblioteca Bund UI, com licença MIT. Copia e cola no teu projeto.

Como usar

Abre o componente no catálogo, carrega em copiar, e cola no teu projeto. O prompt copiado leva o código e as regras para o teu agente o reproduzir sem alterar nada.

Abrir no catálogo

Cinco cópias grátis por mês. Sem cartão.

Precisa de

npm i lucide-react

Licença

Este componente vem de Bund UI e é redistribuído sob a licença MIT, que permite uso comercial. O aviso de copyright viaja com o código.

Checkout Forms 02

examples/blocks/ecommerce/checkout-forms/02/components/delivery-card.tsx
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;
examples/blocks/ecommerce/checkout-forms/02/components/order-item.tsx
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;
examples/blocks/ecommerce/checkout-forms/02/components/order-summary.tsx
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;
examples/blocks/ecommerce/checkout-forms/02/components/payment-method-card.tsx
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;
examples/blocks/ecommerce/checkout-forms/02/components/payment-stepper.tsx
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;
examples/blocks/ecommerce/checkout-forms/02/data.ts
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"
};
examples/blocks/ecommerce/checkout-forms/02/page.tsx
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>
  );
}

Componentes parecidos