Update src/app/product/[id]/page.tsx

This commit is contained in:
2026-03-12 08:14:45 +00:00
parent 167f7f3de8
commit 8fc224dbfd

View File

@@ -20,30 +20,28 @@ const TikTok = (props: React.SVGProps<SVGSVGElement>) => (
</svg>
);
const products = [
{
id: "1", name: "Classic Black Hoodie", price: 89.99,
description: "Premium oversized hoodie crafted from 100% organic cotton. Perfect for layering or wearing solo. Features reinforced seams and a comfortable fit that works with any style.", image: "http://img.b2bpic.net/free-photo/medium-shot-man-posing-with-hoodie-indoors_23-2149359859.jpg", sizes: ["XS", "S", "M", "L", "XL", "XXL"],
const products: Record<string, any> = {
"1": {
id: "1", name: "Classic Black Hoodie", price: "$89.99", originalPrice: "$119.99", description: "Our signature black hoodie - a timeless essential for any streetwear collection. Premium 100% cotton blend with soft interior lining. Features a kangaroo pocket, drawstring with metal tips, and embroidered Umbra logo on the chest.", images: [
"http://img.b2bpic.net/free-photo/medium-shot-man-posing-with-hoodie-indoors_23-2149359859.jpg", "http://img.b2bpic.net/free-photo/medium-shot-man-posing-with-hoodie-indoors_23-2149359859.jpg"],
sizes: ["XS", "S", "M", "L", "XL", "2XL"],
colors: ["Black", "Charcoal", "Navy"],
rating: 4.8,
reviews: 342,
inStock: true,
},
{
id: "2", name: "Vintage Denim Jacket", price: 129.99,
description: "Timeless denim jacket with authentic vintage wash. Perfect for any occasion. Heavy-duty construction with durable hardware and reinforced stitching.", image: "http://img.b2bpic.net/free-photo/fashionable-woman-wearing-denim-jacket_23-2148859621.jpg", sizes: ["XS", "S", "M", "L", "XL"],
colors: ["Indigo", "Light Wash", "Medium Wash"],
"2": {
id: "2", name: "Vintage Denim Jacket", price: "$129.99", description: "Classic denim jacket with a vintage washed finish. Perfect layering piece for any outfit. Features button closure, flap pockets, and adjustable waist tabs.", images: [
"http://img.b2bpic.net/free-photo/fashionable-woman-wearing-denim-jacket_23-2148859621.jpg"],
sizes: ["XS", "S", "M", "L", "XL", "2XL"],
colors: ["Indigo", "Light Wash", "Dark Wash"],
rating: 4.6,
reviews: 218,
inStock: true,
},
{
id: "3", name: "Cargo Pants - Khaki", price: 79.99,
description: "Versatile cargo pants with multiple pockets. Made from durable cotton blend. Perfect for everyday wear and outdoor adventures.", image: "http://img.b2bpic.net/free-photo/young-woman-wearing-trucker-hat_23-2149432334.jpg", sizes: ["28", "30", "32", "34", "36", "38"],
colors: ["Khaki", "Olive", "Black"],
},
];
};
export default function ProductPage({ params }: { params: { id: string } }) {
const product = products.find((p) => p.id === params.id);
const [quantity, setQuantity] = useState(1);
const [selectedSize, setSelectedSize] = useState(product?.sizes[0]);
const [selectedColor, setSelectedColor] = useState(product?.colors[0]);
const navItems = [
{ name: "Shop", id: "products" },
{ name: "Collections", id: "collections" },
@@ -52,6 +50,12 @@ export default function ProductPage({ params }: { params: { id: string } }) {
{ name: "Contact", id: "contact" },
];
const product = products[params.id] || products["1"];
const [quantity, setQuantity] = useState(1);
const [selectedSize, setSelectedSize] = useState(product.sizes[2]);
const [selectedColor, setSelectedColor] = useState(product.colors[0]);
const [selectedImage, setSelectedImage] = useState(0);
const socialLinks = [
{ icon: Instagram, href: "https://instagram.com/umbra", ariaLabel: "Instagram" },
{ icon: Twitter, href: "https://twitter.com/umbra", ariaLabel: "Twitter" },
@@ -59,46 +63,6 @@ export default function ProductPage({ params }: { params: { id: string } }) {
{ icon: Youtube, href: "https://youtube.com/@umbra", ariaLabel: "YouTube" },
];
if (!product) {
return (
<ThemeProvider
defaultButtonVariant="shift-hover"
defaultTextAnimation="reveal-blur"
borderRadius="soft"
contentWidth="smallMedium"
sizing="medium"
background="circleGradient"
cardStyle="glass-elevated"
primaryButtonStyle="gradient"
secondaryButtonStyle="glass"
headingFontWeight="normal"
>
<div id="nav" data-section="nav">
<NavbarStyleCentered
navItems={navItems}
button={{ text: "View Cart", href: "/cart" }}
brandName="Umbra"
/>
</div>
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<h1 className="text-2xl font-semibold text-foreground mb-4">Product not found</h1>
<Link href="/shop" className="text-accent hover:text-background-accent">
Back to Shop
</Link>
</div>
</div>
<div id="footer" data-section="footer">
<FooterCard
logoText="Umbra"
copyrightText="© 2025 | Umbra Streetwear. All rights reserved."
socialLinks={socialLinks}
/>
</div>
</ThemeProvider>
);
}
return (
<ThemeProvider
defaultButtonVariant="shift-hover"
@@ -120,104 +84,141 @@ export default function ProductPage({ params }: { params: { id: string } }) {
/>
</div>
<div id="product" data-section="product" className="mx-auto px-4 md:px-6 py-20">
<div id="product-detail" data-section="product-detail" className="mx-auto px-4 md:px-6 py-20">
<div className="max-w-6xl mx-auto">
<Link href="/shop" className="text-accent hover:text-background-accent mb-8 inline-block">
Back to Shop
</Link>
<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
<div className="flex items-center justify-center bg-card/50 border border-accent/20 rounded-soft p-8">
<img
src={product.image}
alt={product.name}
className="w-full h-auto rounded-soft object-cover"
/>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
{/* Product Images */}
<div className="flex flex-col gap-4">
<div className="w-full aspect-square bg-card rounded-soft overflow-hidden">
<img
src={product.images[selectedImage]}
alt={product.name}
className="w-full h-full object-cover"
/>
</div>
<div className="flex gap-2">
{product.images.map((img: string, idx: number) => (
<button
key={idx}
onClick={() => setSelectedImage(idx)}
className={`w-20 h-20 rounded-soft overflow-hidden border-2 transition-colors ${
selectedImage === idx
? "border-background-accent"
: "border-accent/20 hover:border-accent/50"
}`}
>
<img src={img} alt={`${product.name} ${idx}`} className="w-full h-full object-cover" />
</button>
))}
</div>
</div>
<div className="flex flex-col justify-center">
<h1 className="text-4xl font-light text-foreground mb-4">{product.name}</h1>
<p className="text-3xl font-semibold text-background-accent mb-6">
${product.price.toFixed(2)}
</p>
<p className="text-foreground/70 mb-8 leading-relaxed">{product.description}</p>
{/* Product Details */}
<div>
<h1 className="text-4xl font-light text-foreground mb-2">{product.name}</h1>
<div className="space-y-6 mb-8">
<div>
<label className="block text-foreground font-semibold mb-3">Size</label>
<div className="flex flex-wrap gap-3">
{product.sizes.map((size) => (
<button
key={size}
onClick={() => setSelectedSize(size)}
className={`px-4 py-2 rounded-soft border transition-colors ${
selectedSize === size
? "bg-background-accent text-primary-cta-text border-background-accent"
: "border-accent/30 text-foreground hover:border-accent"
}`}
>
{size}
</button>
))}
</div>
</div>
<div>
<label className="block text-foreground font-semibold mb-3">Color</label>
<div className="flex flex-wrap gap-3">
{product.colors.map((color) => (
<button
key={color}
onClick={() => setSelectedColor(color)}
className={`px-4 py-2 rounded-soft border transition-colors ${
selectedColor === color
? "bg-background-accent text-primary-cta-text border-background-accent"
: "border-accent/30 text-foreground hover:border-accent"
}`}
>
{color}
</button>
))}
</div>
</div>
<div>
<label className="block text-foreground font-semibold mb-3">Quantity</label>
<div className="flex items-center gap-3 w-fit bg-accent/10 rounded-soft p-2">
<button
onClick={() => setQuantity(Math.max(1, quantity - 1))}
className="px-3 py-1 hover:bg-accent/20 rounded transition-colors"
<div className="flex items-center gap-4 mb-6">
<div className="flex items-center gap-1">
{[...Array(5)].map((_, i) => (
<span
key={i}
className={i < Math.floor(product.rating) ? "text-background-accent" : "text-accent/30"}
>
</button>
<span className="w-8 text-center font-semibold text-foreground">{quantity}</span>
</span>
))}
</div>
<span className="text-sm text-foreground/70">({product.reviews} reviews)</span>
</div>
<div className="flex items-baseline gap-4 mb-6">
<span className="text-3xl font-bold text-background-accent">{product.price}</span>
{product.originalPrice && (
<span className="text-lg text-foreground/50 line-through">{product.originalPrice}</span>
)}
</div>
<p className="text-foreground/70 mb-8">{product.description}</p>
{/* Color Selection */}
<div className="mb-8">
<label className="block text-sm font-semibold text-foreground mb-3">Color</label>
<div className="flex gap-3">
{product.colors.map((color: string) => (
<button
onClick={() => setQuantity(quantity + 1)}
className="px-3 py-1 hover:bg-accent/20 rounded transition-colors"
key={color}
onClick={() => setSelectedColor(color)}
className={`px-4 py-2 rounded-soft border-2 transition-colors ${
selectedColor === color
? "border-background-accent bg-background-accent/10"
: "border-accent/20 hover:border-accent/50"
}`}
>
+
{color}
</button>
</div>
))}
</div>
</div>
<div className="flex gap-4">
<button className="flex-1 px-8 py-3 bg-background-accent text-primary-cta-text font-semibold rounded-soft hover:bg-primary-cta hover:text-primary-cta-text transition-colors">
Add to Cart
</button>
<button className="flex-1 px-8 py-3 border border-accent/50 text-foreground font-semibold rounded-soft hover:bg-accent/10 transition-colors">
Save for Later
</button>
{/* Size Selection */}
<div className="mb-8">
<label className="block text-sm font-semibold text-foreground mb-3">Size</label>
<div className="grid grid-cols-3 gap-2">
{product.sizes.map((size: string) => (
<button
key={size}
onClick={() => setSelectedSize(size)}
className={`py-2 rounded-soft border-2 transition-colors ${
selectedSize === size
? "border-background-accent bg-background-accent/10"
: "border-accent/20 hover:border-accent/50"
}`}
>
{size}
</button>
))}
</div>
</div>
<div className="mt-8 pt-8 border-t border-accent/20">
<p className="text-foreground/70 text-sm">
<span className="font-semibold text-foreground">Free Shipping:</span> On orders over $150
</p>
<p className="text-foreground/70 text-sm mt-2">
<span className="font-semibold text-foreground">Easy Returns:</span> 30-day money-back guarantee
</p>
{/* Quantity */}
<div className="mb-8">
<label className="block text-sm font-semibold text-foreground mb-3">Quantity</label>
<div className="flex items-center gap-4 w-fit bg-accent/10 rounded-soft p-2">
<button
onClick={() => setQuantity(Math.max(1, quantity - 1))}
className="px-3 py-1 hover:bg-accent/20 rounded transition-colors"
>
</button>
<span className="w-8 text-center font-semibold">{quantity}</span>
<button
onClick={() => setQuantity(quantity + 1)}
className="px-3 py-1 hover:bg-accent/20 rounded transition-colors"
>
+
</button>
</div>
</div>
{/* Add to Cart Button */}
<button className="w-full px-6 py-4 bg-background-accent text-primary-cta-text font-semibold rounded-soft hover:bg-primary-cta hover:text-primary-cta-text transition-colors mb-4">
Add to Cart
</button>
<button className="w-full px-6 py-3 border border-accent/50 text-foreground font-semibold rounded-soft hover:bg-accent/10 transition-colors">
Save for Later
</button>
{/* Stock Status */}
{product.inStock ? (
<p className="text-sm text-background-accent font-semibold mt-4"> In Stock - Ships within 24 hours</p>
) : (
<p className="text-sm text-foreground/50 mt-4">Out of Stock - Notify me when available</p>
)}
</div>
</div>
</div>