|
|
|
|
@@ -3,7 +3,7 @@
|
|
|
|
|
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
|
|
|
|
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
|
|
|
|
import FooterCard from "@/components/sections/footer/FooterCard";
|
|
|
|
|
import { Instagram, Twitter, Youtube, Heart, Minus, Plus } from "lucide-react";
|
|
|
|
|
import { Instagram, Twitter, Youtube, Heart, ShoppingCart, ChevronLeft, ChevronRight, Star } from "lucide-react";
|
|
|
|
|
import Link from "next/link";
|
|
|
|
|
import { useState } from "react";
|
|
|
|
|
|
|
|
|
|
@@ -30,27 +30,39 @@ export default function ProductPage({ params }: { params: { id: string } }) {
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const [quantity, setQuantity] = useState(1);
|
|
|
|
|
const [isFavorited, setIsFavorited] = useState(false);
|
|
|
|
|
const [selectedSize, setSelectedSize] = useState("M");
|
|
|
|
|
const [selectedColor, setSelectedColor] = useState("Black");
|
|
|
|
|
const [isFavorited, setIsFavorited] = useState(false);
|
|
|
|
|
const [currentImageIndex, setCurrentImageIndex] = useState(0);
|
|
|
|
|
|
|
|
|
|
const product = {
|
|
|
|
|
id: params.id,
|
|
|
|
|
name: "Classic Black Hoodie", price: 89.99,
|
|
|
|
|
rating: 4.8,
|
|
|
|
|
reviews: 142,
|
|
|
|
|
imageSrc: "http://img.b2bpic.net/free-photo/medium-shot-man-posing-with-hoodie-indoors_23-2149359859.jpg", imageAlt: "Classic black oversized hoodie", description: "Premium quality hoodie crafted from sustainable cotton blend. Features a comfortable oversized fit, kangaroo pocket, and adjustable drawstring hood. Perfect for casual wear or streetwear styling.", sizes: ["XS", "S", "M", "L", "XL", "XXL"],
|
|
|
|
|
colors: ["Black", "Navy", "Heather Gray", "White"],
|
|
|
|
|
inStock: true,
|
|
|
|
|
reviews: 245,
|
|
|
|
|
description: "Premium oversized hoodie crafted from 100% organic cotton. Perfect for casual style with a streetwear edge. Features a relaxed fit and reinforced drawstrings.", images: [
|
|
|
|
|
"http://img.b2bpic.net/free-photo/medium-shot-man-posing-with-hoodie-indoors_23-2149359859.jpg", "http://img.b2bpic.net/free-photo/teenager-boy-stylish-clothes-posing_23-2149085228.jpg", "http://img.b2bpic.net/free-photo/brunette-girl-sitting-bench-central-part-old-city_1153-5758.jpg"],
|
|
|
|
|
sizes: ["XS", "S", "M", "L", "XL", "XXL"],
|
|
|
|
|
colors: ["Black", "Gray", "Navy", "Olive"],
|
|
|
|
|
details: [
|
|
|
|
|
"Material: 100% Organic Cotton", "Weight: 450gsm", "Care: Machine wash cold, air dry", "Fit: Oversized relaxed fit"],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const socialLinks = [
|
|
|
|
|
{ icon: Instagram, href: "https://instagram.com/umbra", ariaLabel: "Instagram" },
|
|
|
|
|
{ icon: Twitter, href: "https://twitter.com/umbra", ariaLabel: "Twitter" },
|
|
|
|
|
{ icon: TikTok, href: "https://tiktok.com/@umbra", ariaLabel: "TikTok" },
|
|
|
|
|
{ icon: TikTok as any, href: "https://tiktok.com/@umbra", ariaLabel: "TikTok" },
|
|
|
|
|
{ icon: Youtube, href: "https://youtube.com/@umbra", ariaLabel: "YouTube" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const handleNextImage = () => {
|
|
|
|
|
setCurrentImageIndex((prev) => (prev + 1) % product.images.length);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handlePrevImage = () => {
|
|
|
|
|
setCurrentImageIndex((prev) => (prev - 1 + product.images.length) % product.images.length);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<ThemeProvider
|
|
|
|
|
defaultButtonVariant="shift-hover"
|
|
|
|
|
@@ -72,144 +84,160 @@ export default function ProductPage({ params }: { params: { id: string } }) {
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="product" data-section="product" className="mx-auto px-4 md:px-6">
|
|
|
|
|
<div className="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 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">
|
|
|
|
|
{/* Product Image */}
|
|
|
|
|
<div className="flex items-center justify-center">
|
|
|
|
|
<div className="w-full aspect-square bg-card rounded-soft overflow-hidden">
|
|
|
|
|
<img
|
|
|
|
|
src={product.imageSrc}
|
|
|
|
|
alt={product.imageAlt}
|
|
|
|
|
className="w-full h-full object-cover"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
|
|
|
|
|
{/* Image Section */}
|
|
|
|
|
<div className="relative">
|
|
|
|
|
<div className="bg-card/50 border border-accent/20 rounded-soft aspect-square overflow-hidden relative">
|
|
|
|
|
<img
|
|
|
|
|
src={product.images[currentImageIndex]}
|
|
|
|
|
alt={product.name}
|
|
|
|
|
className="w-full h-full object-cover"
|
|
|
|
|
/>
|
|
|
|
|
<button
|
|
|
|
|
onClick={handlePrevImage}
|
|
|
|
|
className="absolute left-4 top-1/2 -translate-y-1/2 bg-background/70 hover:bg-background p-2 rounded-full transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<ChevronLeft size={24} className="text-foreground" />
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={handleNextImage}
|
|
|
|
|
className="absolute right-4 top-1/2 -translate-y-1/2 bg-background/70 hover:bg-background p-2 rounded-full transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<ChevronRight size={24} className="text-foreground" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex gap-3 mt-4">
|
|
|
|
|
{product.images.map((image, index) => (
|
|
|
|
|
<button
|
|
|
|
|
key={index}
|
|
|
|
|
onClick={() => setCurrentImageIndex(index)}
|
|
|
|
|
className={`w-16 h-16 rounded-soft overflow-hidden border ${
|
|
|
|
|
index === currentImageIndex
|
|
|
|
|
? "border-background-accent"
|
|
|
|
|
: "border-accent/20"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<img src={image} alt={`View ${index + 1}`} className="w-full h-full object-cover" />
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Product Details */}
|
|
|
|
|
<div className="flex flex-col justify-center">
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<h1 className="text-4xl font-bold text-foreground mb-2">{product.name}</h1>
|
|
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
|
<span className="text-yellow-400">★</span>
|
|
|
|
|
<span className="text-foreground font-semibold">{product.rating}</span>
|
|
|
|
|
{/* Product Info Section */}
|
|
|
|
|
<div className="flex flex-col">
|
|
|
|
|
<div className="mb-6">
|
|
|
|
|
<div className="flex items-start justify-between mb-4">
|
|
|
|
|
<div>
|
|
|
|
|
<h1 className="text-4xl font-bold text-foreground mb-2">{product.name}</h1>
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
|
{[...Array(5)].map((_, i) => (
|
|
|
|
|
<Star
|
|
|
|
|
key={i}
|
|
|
|
|
size={18}
|
|
|
|
|
className={i < Math.floor(product.rating) ? "fill-background-accent text-background-accent" : "text-foreground/30"}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<span className="text-foreground/70 text-sm">({product.reviews} reviews)</span>
|
|
|
|
|
</div>
|
|
|
|
|
{product.inStock ? (
|
|
|
|
|
<span className="text-background-accent font-semibold">In Stock</span>
|
|
|
|
|
) : (
|
|
|
|
|
<span className="text-red-500 font-semibold">Out of Stock</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<p className="text-2xl font-bold text-background-accent mb-6">
|
|
|
|
|
${product.price.toFixed(2)}
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
<p className="text-foreground/80 mb-8 leading-relaxed">
|
|
|
|
|
{product.description}
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
{/* Size Selection */}
|
|
|
|
|
<div className="mb-6">
|
|
|
|
|
<label className="block text-foreground font-semibold mb-3">Size</label>
|
|
|
|
|
<div className="flex gap-2 flex-wrap">
|
|
|
|
|
{product.sizes.map((size) => (
|
|
|
|
|
<button
|
|
|
|
|
key={size}
|
|
|
|
|
onClick={() => setSelectedSize(size)}
|
|
|
|
|
className={`px-4 py-2 rounded-soft font-semibold transition-colors ${
|
|
|
|
|
selectedSize === size
|
|
|
|
|
? "bg-background-accent text-primary-cta-text"
|
|
|
|
|
: "border border-accent/50 text-foreground hover:bg-accent/10"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
{size}
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Color Selection */}
|
|
|
|
|
<div className="mb-6">
|
|
|
|
|
<label className="block text-foreground font-semibold mb-3">Color</label>
|
|
|
|
|
<div className="flex gap-2 flex-wrap">
|
|
|
|
|
{product.colors.map((color) => (
|
|
|
|
|
<button
|
|
|
|
|
key={color}
|
|
|
|
|
onClick={() => setSelectedColor(color)}
|
|
|
|
|
className={`px-4 py-2 rounded-soft font-semibold transition-colors ${
|
|
|
|
|
selectedColor === color
|
|
|
|
|
? "bg-background-accent text-primary-cta-text"
|
|
|
|
|
: "border border-accent/50 text-foreground hover:bg-accent/10"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
{color}
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Quantity Selection */}
|
|
|
|
|
<div className="mb-8">
|
|
|
|
|
<label className="block text-foreground font-semibold mb-3">Quantity</label>
|
|
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
|
<div className="flex items-center gap-3 bg-accent/10 rounded-soft p-3">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setQuantity(Math.max(1, quantity - 1))}
|
|
|
|
|
className="p-1 hover:bg-accent/20 rounded transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<Minus size={18} className="text-foreground" />
|
|
|
|
|
</button>
|
|
|
|
|
<span className="w-8 text-center text-foreground font-semibold text-lg">
|
|
|
|
|
{quantity}
|
|
|
|
|
</span>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setQuantity(quantity + 1)}
|
|
|
|
|
className="p-1 hover:bg-accent/20 rounded transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<Plus size={18} className="text-foreground" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Add to Cart */}
|
|
|
|
|
<div className="flex gap-4 mb-8">
|
|
|
|
|
<button className="flex-1 px-8 py-4 bg-background-accent text-primary-cta-text font-bold rounded-soft hover:bg-primary-cta transition-colors">
|
|
|
|
|
Add to Cart
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setIsFavorited(!isFavorited)}
|
|
|
|
|
className="px-6 py-4 border border-accent/50 rounded-soft hover:bg-accent/10 transition-colors"
|
|
|
|
|
className="p-3 rounded-full hover:bg-accent/10 transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<Heart
|
|
|
|
|
size={24}
|
|
|
|
|
className={isFavorited ? "fill-red-500 text-red-500" : "text-foreground"}
|
|
|
|
|
className={isFavorited ? "fill-background-accent text-background-accent" : "text-foreground/50"}
|
|
|
|
|
/>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Additional Info */}
|
|
|
|
|
<div className="border-t border-accent/20 pt-6 space-y-4">
|
|
|
|
|
<div className="flex justify-between text-foreground/70">
|
|
|
|
|
<span>Free shipping on orders over $150</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex justify-between text-foreground/70">
|
|
|
|
|
<span>30-day returns and exchanges</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex justify-between text-foreground/70">
|
|
|
|
|
<span>Secure checkout</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-3xl font-bold text-background-accent mb-6">${product.price}</div>
|
|
|
|
|
|
|
|
|
|
<p className="text-foreground/70 mb-8">{product.description}</p>
|
|
|
|
|
|
|
|
|
|
{/* Size Selection */}
|
|
|
|
|
<div className="mb-6">
|
|
|
|
|
<label className="block text-foreground font-semibold mb-3">Size</label>
|
|
|
|
|
<div className="flex gap-3 flex-wrap">
|
|
|
|
|
{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/20 text-foreground hover:border-accent/50"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
{size}
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Color Selection */}
|
|
|
|
|
<div className="mb-8">
|
|
|
|
|
<label className="block text-foreground font-semibold mb-3">Color</label>
|
|
|
|
|
<div className="flex gap-3 flex-wrap">
|
|
|
|
|
{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/20 text-foreground hover:border-accent/50"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
{color}
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Quantity and Add to Cart */}
|
|
|
|
|
<div className="flex gap-4 mb-8">
|
|
|
|
|
<div className="flex items-center gap-3 bg-accent/10 rounded-soft p-3 flex-shrink-0">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setQuantity(Math.max(1, quantity - 1))}
|
|
|
|
|
className="p-1 hover:bg-accent/20 rounded transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<ChevronLeft size={20} className="text-foreground" />
|
|
|
|
|
</button>
|
|
|
|
|
<span className="w-8 text-center text-foreground font-semibold">{quantity}</span>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setQuantity(quantity + 1)}
|
|
|
|
|
className="p-1 hover:bg-accent/20 rounded transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<ChevronRight size={20} className="text-foreground" />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<button className="flex-grow px-6 py-3 bg-background-accent text-primary-cta-text font-semibold rounded-soft hover:bg-primary-cta hover:text-primary-cta-text transition-colors flex items-center justify-center gap-2">
|
|
|
|
|
<ShoppingCart size={20} />
|
|
|
|
|
Add to Cart
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Product Details */}
|
|
|
|
|
<div className="bg-card/50 border border-accent/20 rounded-soft p-6">
|
|
|
|
|
<h3 className="text-foreground font-semibold mb-4">Product Details</h3>
|
|
|
|
|
<ul className="space-y-3">
|
|
|
|
|
{product.details.map((detail, index) => (
|
|
|
|
|
<li key={index} className="text-foreground/70 text-sm">
|
|
|
|
|
{detail}
|
|
|
|
|
</li>
|
|
|
|
|
))}
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|