151 lines
8.8 KiB
TypeScript
151 lines
8.8 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { Link, useNavigate } from 'react-router-dom';
|
|
import { useStore } from '../src/context/StoreContext';
|
|
|
|
const Checkout: React.FC = () => {
|
|
const { cart } = useStore();
|
|
const navigate = useNavigate();
|
|
const [formData, setFormData] = useState({
|
|
email: '',
|
|
firstName: '',
|
|
lastName: '',
|
|
address: '',
|
|
city: '',
|
|
postalCode: ''
|
|
});
|
|
|
|
const subtotal = cart.reduce((total, item) => total + (item.price * item.quantity), 0);
|
|
const shipping = subtotal > 150 ? 0 : 15;
|
|
const total = subtotal + shipping;
|
|
|
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
setFormData({ ...formData, [e.target.name]: e.target.value });
|
|
};
|
|
|
|
const handleProceed = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
const orderData = {
|
|
customer_email: formData.email,
|
|
customer_name: `${formData.firstName} ${formData.lastName}`,
|
|
shipping_address: {
|
|
address: formData.address,
|
|
city: formData.city,
|
|
postalCode: formData.postalCode
|
|
},
|
|
items: cart.map(item => ({
|
|
id: item.id,
|
|
title: item.title,
|
|
quantity: item.quantity,
|
|
price: item.price
|
|
})),
|
|
total_amount: total
|
|
};
|
|
|
|
navigate('/mock-payment', { state: { orderData } });
|
|
};
|
|
|
|
if (cart.length === 0) {
|
|
return (
|
|
<div className="min-h-screen pt-48 flex flex-col items-center justify-center text-text-main dark:text-white px-6">
|
|
<h2 className="font-display text-4xl mb-8">Your bag is empty</h2>
|
|
<Link to="/collections" className="text-xs uppercase tracking-widest underline underline-offset-8 hover:text-stone-500 transition-colors">
|
|
View Collections
|
|
</Link>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="bg-white dark:bg-stone-950 min-h-screen pt-32 pb-24">
|
|
<div className="max-w-[1400px] mx-auto px-6 md:px-12">
|
|
<motion.h1
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
className="font-display text-5xl md:text-7xl font-light text-text-main dark:text-white mb-16"
|
|
>
|
|
Checkout
|
|
</motion.h1>
|
|
|
|
<form onSubmit={handleProceed} className="grid grid-cols-1 lg:grid-cols-12 gap-16 xl:gap-24">
|
|
{/* Order Summary Form */}
|
|
<div className="lg:col-span-7 space-y-12">
|
|
<section>
|
|
<h3 className="text-xs uppercase tracking-[0.3em] text-stone-400 mb-8 border-b border-stone-100 dark:border-stone-900 pb-4">Contact Information</h3>
|
|
<div className="grid grid-cols-1 gap-6">
|
|
<input
|
|
required
|
|
name="email"
|
|
type="email"
|
|
placeholder="Email Address"
|
|
value={formData.email}
|
|
onChange={handleInputChange}
|
|
className="w-full bg-stone-50 dark:bg-stone-900 border border-stone-200 dark:border-stone-800 p-4 text-sm focus:outline-none focus:border-stone-400 transition-colors"
|
|
/>
|
|
</div>
|
|
</section>
|
|
|
|
<section>
|
|
<h3 className="text-xs uppercase tracking-[0.3em] text-stone-400 mb-8 border-b border-stone-100 dark:border-stone-900 pb-4">Shipping Address</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<input required name="firstName" type="text" placeholder="First Name" value={formData.firstName} onChange={handleInputChange} className="w-full bg-stone-50 dark:bg-stone-900 border border-stone-200 dark:border-stone-800 p-4 text-sm focus:outline-none focus:border-stone-400" />
|
|
<input required name="lastName" type="text" placeholder="Last Name" value={formData.lastName} onChange={handleInputChange} className="w-full bg-stone-50 dark:bg-stone-900 border border-stone-200 dark:border-stone-800 p-4 text-sm focus:outline-none focus:border-stone-400" />
|
|
<div className="md:col-span-2">
|
|
<input required name="address" type="text" placeholder="Address" value={formData.address} onChange={handleInputChange} className="w-full bg-stone-50 dark:bg-stone-900 border border-stone-200 dark:border-stone-800 p-4 text-sm focus:outline-none focus:border-stone-400" />
|
|
</div>
|
|
<input required name="city" type="text" placeholder="City" value={formData.city} onChange={handleInputChange} className="w-full bg-stone-50 dark:bg-stone-900 border border-stone-200 dark:border-stone-800 p-4 text-sm focus:outline-none focus:border-stone-400" />
|
|
<input required name="postalCode" type="text" placeholder="Postal Code" value={formData.postalCode} onChange={handleInputChange} className="w-full bg-stone-50 dark:bg-stone-900 border border-stone-200 dark:border-stone-800 p-4 text-sm focus:outline-none focus:border-stone-400" />
|
|
</div>
|
|
</section>
|
|
|
|
<button type="submit" className="w-full bg-black dark:bg-white text-white dark:text-black py-5 uppercase tracking-[0.3em] text-xs font-bold hover:opacity-90 transition-opacity flex items-center justify-center gap-4 shadow-xl">
|
|
Proceed to Payment
|
|
<span className="material-symbols-outlined text-sm">arrow_forward</span>
|
|
</button>
|
|
</div>
|
|
|
|
{/* Cart Preview Sidebar */}
|
|
<div className="lg:col-span-5">
|
|
<div className="lg:sticky lg:top-32 bg-stone-50 dark:bg-stone-900/40 p-8 md:p-12 rounded-sm border border-stone-100 dark:border-stone-900">
|
|
<h3 className="text-xs uppercase tracking-[0.3em] text-stone-400 mb-8">In your bag</h3>
|
|
|
|
<div className="space-y-8 mb-12 max-h-96 overflow-y-auto pr-4 custom-scrollbar">
|
|
{cart.map((item) => (
|
|
<div key={item.id} className="flex gap-6">
|
|
<div className="w-20 aspect-[4/5] bg-stone-200 dark:bg-stone-800 flex-shrink-0 overflow-hidden">
|
|
<img src={item.image} alt={item.title} className="w-full h-full object-cover" />
|
|
</div>
|
|
<div className="flex-1 space-y-1">
|
|
<h4 className="font-display text-lg text-text-main dark:text-white">{item.title}</h4>
|
|
<p className="text-xs text-stone-500 uppercase tracking-widest">Qty: {item.quantity}</p>
|
|
<p className="text-sm font-light pt-2">${(item.price * item.quantity).toFixed(2)}</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="space-y-4 pt-8 border-t border-stone-200 dark:border-stone-800">
|
|
<div className="flex justify-between text-sm">
|
|
<span className="text-stone-500 font-light">Subtotal</span>
|
|
<span>${subtotal.toFixed(2)}</span>
|
|
</div>
|
|
<div className="flex justify-between text-sm">
|
|
<span className="text-stone-500 font-light">Shipping</span>
|
|
<span>{shipping === 0 ? 'Free' : `$${shipping.toFixed(2)}`}</span>
|
|
</div>
|
|
<div className="flex justify-between text-xl font-display pt-4">
|
|
<span>Total</span>
|
|
<span className="text-text-main dark:text-white">${total.toFixed(2)}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Checkout;
|