Complete frontend implementation with: - Next.js 16 with App Router and TypeScript - Tailwind CSS v4 with custom violet theme - shadcn/ui components with Lucide React icons - Landing page with hero, services, pricing, testimonials, FAQ - Service selection page with toggle - Login/Register pages with social auth UI - Multi-step checkout flow - Client dashboard with stats, projects, support tickets - Billing page with subscription, payment methods, invoices - All mock data and TypeScript types 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
286 lines
12 KiB
TypeScript
286 lines
12 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { useSearchParams } from 'next/navigation'
|
|
import { Card } from '@/components/ui/card'
|
|
import { CheckoutSteps } from '@/components/checkout/checkout-steps'
|
|
import { CheckoutSummary } from '@/components/checkout/checkout-summary'
|
|
import { PaymentMethodCard } from '@/components/checkout/payment-method-card'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Label } from '@/components/ui/label'
|
|
import { getPricingPlanById } from '@/lib/mock-data'
|
|
import Link from 'next/link'
|
|
import { Rocket, ChevronRight } from 'lucide-react'
|
|
|
|
export function CheckoutContent() {
|
|
const searchParams = useSearchParams()
|
|
const planId = searchParams.get('plan') || 'growth-web'
|
|
const step = (searchParams.get('step') as 'account' | 'billing' | 'payment') || 'account'
|
|
|
|
const [currentStep, setCurrentStep] = useState<'account' | 'billing' | 'payment'>(step)
|
|
const [selectedPaymentMethod, setSelectedPaymentMethod] = useState('card')
|
|
|
|
const plan = getPricingPlanById(planId)
|
|
|
|
if (!plan) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center">
|
|
<div className="text-center">
|
|
<h1 className="text-2xl font-bold mb-4">Plan not found</h1>
|
|
<Button asChild>
|
|
<Link href="/services">Back to Services</Link>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const serviceName = plan.serviceType === 'web-design' ? 'Web Design' : 'AI Automation'
|
|
|
|
return (
|
|
<div className="min-h-screen bg-background">
|
|
{/* Header */}
|
|
<header className="border-b border-border py-4">
|
|
<div className="max-w-[1280px] mx-auto px-4 flex items-center gap-3">
|
|
<Link href="/" className="flex items-center gap-3">
|
|
<div className="size-8 flex items-center justify-center rounded bg-gradient-to-br from-primary to-primary-glow text-white">
|
|
<Rocket className="w-5 h-5" />
|
|
</div>
|
|
<h2 className="text-foreground text-lg font-bold">ScaleSite</h2>
|
|
</Link>
|
|
</div>
|
|
</header>
|
|
|
|
<div className="max-w-6xl mx-auto px-4 py-12">
|
|
{/* Steps */}
|
|
<CheckoutSteps currentStep={currentStep} />
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
{/* Left side - Forms */}
|
|
<div className="lg:col-span-2">
|
|
{/* Account Step */}
|
|
{currentStep === 'account' && (
|
|
<Card className="bg-card border-border p-6">
|
|
<h2 className="text-xl font-bold text-foreground mb-6">Account Information</h2>
|
|
<form className="flex flex-col gap-4">
|
|
<div className="flex flex-col gap-2">
|
|
<Label htmlFor="email">Email Address</Label>
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
placeholder="you@example.com"
|
|
className="bg-background border-border"
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<Label htmlFor="confirm-email">Confirm Email</Label>
|
|
<Input
|
|
id="confirm-email"
|
|
type="email"
|
|
placeholder="you@example.com"
|
|
className="bg-background border-border"
|
|
/>
|
|
</div>
|
|
<div className="flex items-start gap-2">
|
|
<input type="checkbox" id="newsletter" className="mt-1" />
|
|
<Label htmlFor="newsletter" className="text-sm text-muted-foreground">
|
|
I want to receive updates about my order and promotional offers
|
|
</Label>
|
|
</div>
|
|
<Button
|
|
type="button"
|
|
onClick={() => setCurrentStep('billing')}
|
|
className="glow-button bg-primary hover:bg-primary-glow text-white w-full"
|
|
>
|
|
Continue to Billing
|
|
</Button>
|
|
</form>
|
|
</Card>
|
|
)}
|
|
|
|
{/* Billing Step */}
|
|
{currentStep === 'billing' && (
|
|
<Card className="bg-card border-border p-6">
|
|
<h2 className="text-xl font-bold text-foreground mb-6">Billing Address</h2>
|
|
<form className="flex flex-col gap-4">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div className="flex flex-col gap-2">
|
|
<Label htmlFor="firstName">First Name</Label>
|
|
<Input
|
|
id="firstName"
|
|
type="text"
|
|
placeholder="John"
|
|
className="bg-background border-border"
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<Label htmlFor="lastName">Last Name</Label>
|
|
<Input
|
|
id="lastName"
|
|
type="text"
|
|
placeholder="Doe"
|
|
className="bg-background border-border"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<Label htmlFor="address">Street Address</Label>
|
|
<Input
|
|
id="address"
|
|
type="text"
|
|
placeholder="123 Main St"
|
|
className="bg-background border-border"
|
|
/>
|
|
</div>
|
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
|
<div className="flex flex-col gap-2">
|
|
<Label htmlFor="city">City</Label>
|
|
<Input
|
|
id="city"
|
|
type="text"
|
|
placeholder="Berlin"
|
|
className="bg-background border-border"
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<Label htmlFor="postalCode">Postal Code</Label>
|
|
<Input
|
|
id="postalCode"
|
|
type="text"
|
|
placeholder="10115"
|
|
className="bg-background border-border"
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col gap-2 col-span-2 md:col-span-1">
|
|
<Label htmlFor="country">Country</Label>
|
|
<Input
|
|
id="country"
|
|
type="text"
|
|
placeholder="Germany"
|
|
className="bg-background border-border"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="flex gap-3">
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={() => setCurrentStep('account')}
|
|
className="bg-card border-border"
|
|
>
|
|
Back
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
onClick={() => setCurrentStep('payment')}
|
|
className="glow-button bg-primary hover:bg-primary-glow text-white flex-1"
|
|
>
|
|
Continue to Payment
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</Card>
|
|
)}
|
|
|
|
{/* Payment Step */}
|
|
{currentStep === 'payment' && (
|
|
<Card className="bg-card border-border p-6">
|
|
<h2 className="text-xl font-bold text-foreground mb-6">Payment Method</h2>
|
|
<div className="flex flex-col gap-3 mb-6">
|
|
<PaymentMethodCard
|
|
id="card"
|
|
icon="card"
|
|
label="Credit Card"
|
|
description="Pay with Visa, Mastercard, or Amex"
|
|
selected={selectedPaymentMethod === 'card'}
|
|
onSelect={() => setSelectedPaymentMethod('card')}
|
|
/>
|
|
<PaymentMethodCard
|
|
id="paypal"
|
|
icon="paypal"
|
|
label="PayPal"
|
|
description="Pay with your PayPal account"
|
|
selected={selectedPaymentMethod === 'paypal'}
|
|
onSelect={() => setSelectedPaymentMethod('paypal')}
|
|
/>
|
|
<PaymentMethodCard
|
|
id="stripe-link"
|
|
icon="link"
|
|
label="Stripe Link"
|
|
description="Quick payment with saved cards"
|
|
selected={selectedPaymentMethod === 'stripe-link'}
|
|
onSelect={() => setSelectedPaymentMethod('stripe-link')}
|
|
/>
|
|
</div>
|
|
|
|
{selectedPaymentMethod === 'card' && (
|
|
<form className="flex flex-col gap-4 border-t border-border pt-6">
|
|
<div className="flex flex-col gap-2">
|
|
<Label htmlFor="cardNumber">Card Number</Label>
|
|
<Input
|
|
id="cardNumber"
|
|
type="text"
|
|
placeholder="4242 4242 4242 4242"
|
|
className="bg-background border-border"
|
|
/>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="flex flex-col gap-2">
|
|
<Label htmlFor="expiry">Expiry Date</Label>
|
|
<Input
|
|
id="expiry"
|
|
type="text"
|
|
placeholder="MM/YY"
|
|
className="bg-background border-border"
|
|
/>
|
|
</div>
|
|
<div className="flex flex-col gap-2">
|
|
<Label htmlFor="cvc">CVC</Label>
|
|
<Input
|
|
id="cvc"
|
|
type="text"
|
|
placeholder="123"
|
|
className="bg-background border-border"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="flex gap-3">
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
onClick={() => setCurrentStep('billing')}
|
|
className="bg-card border-border"
|
|
>
|
|
Back
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
className="glow-button bg-primary hover:bg-primary-glow text-white flex-1"
|
|
>
|
|
Pay Now
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
)}
|
|
</Card>
|
|
)}
|
|
</div>
|
|
|
|
{/* Right side - Summary */}
|
|
<div className="lg:col-span-1">
|
|
<div className="sticky top-24">
|
|
<CheckoutSummary
|
|
serviceName={serviceName}
|
|
tier={plan.tier}
|
|
price={plan.price}
|
|
currency={plan.currency}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|