220 lines
6.7 KiB
JavaScript
220 lines
6.7 KiB
JavaScript
import React, { useState, useEffect, useContext } from 'react'
|
||
import { useParams, useNavigate } from 'react-router-dom'
|
||
import api from '../api'
|
||
import { CartContext } from '../context/CartContext'
|
||
import { AuthContext } from '../context/AuthContext'
|
||
import '../styles/global.css'
|
||
|
||
export default function ProductDetail() {
|
||
const { id } = useParams()
|
||
const navigate = useNavigate()
|
||
const [product, setProduct] = useState(null)
|
||
const [loading, setLoading] = useState(true)
|
||
const [selectedSize, setSelectedSize] = useState('')
|
||
const [quantity, setQuantity] = useState(1)
|
||
const [inWishlist, setInWishlist] = useState(false)
|
||
const [selectedImageIndex, setSelectedImageIndex] = useState(0)
|
||
const { addToCart } = useContext(CartContext)
|
||
const { token } = useContext(AuthContext)
|
||
|
||
useEffect(() => {
|
||
fetchProduct()
|
||
}, [id])
|
||
|
||
const fetchProduct = async () => {
|
||
try {
|
||
const response = await api.get(`/products/${id}`)
|
||
setProduct(response.data)
|
||
if (response.data.sizes.length > 0) setSelectedSize(response.data.sizes[0])
|
||
} catch (error) {
|
||
console.error('Error fetching product:', error)
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
const handleAddToCart = async () => {
|
||
try {
|
||
if (!token) {
|
||
navigate('/login')
|
||
return
|
||
}
|
||
|
||
await api.post('/cart/add', {
|
||
product_id: product.id,
|
||
quantity,
|
||
size: selectedSize,
|
||
token,
|
||
})
|
||
|
||
addToCart(product, quantity, selectedSize)
|
||
alert('Product added to cart!')
|
||
} catch (error) {
|
||
console.error('Error adding to cart:', error)
|
||
}
|
||
}
|
||
|
||
const handleToggleWishlist = async () => {
|
||
if (!token) {
|
||
navigate('/login')
|
||
return
|
||
}
|
||
|
||
try {
|
||
if (inWishlist) {
|
||
await api.delete(`/wishlist/${product.id}`)
|
||
} else {
|
||
await api.post(`/wishlist/${product.id}`)
|
||
}
|
||
setInWishlist(!inWishlist)
|
||
} catch (error) {
|
||
console.error('Error updating wishlist:', error)
|
||
}
|
||
}
|
||
|
||
if (loading) return <div className="loading">Loading...</div>
|
||
if (!product) return <div className="error">Product not found</div>
|
||
|
||
const price = product.discount_price || product.price
|
||
|
||
return (
|
||
<div className="product-detail">
|
||
<div className="detail-container">
|
||
<div className="detail-images">
|
||
{/* Main Image */}
|
||
<div className="main-image-container">
|
||
<img
|
||
src={product.images[selectedImageIndex] || product.images[0]}
|
||
alt={product.name}
|
||
className="main-image"
|
||
/>
|
||
|
||
{/* Navigation Arrows */}
|
||
{product.images.length > 1 && (
|
||
<>
|
||
<button
|
||
className="nav-arrow prev"
|
||
onClick={() => setSelectedImageIndex((prev) =>
|
||
prev === 0 ? product.images.length - 1 : prev - 1
|
||
)}
|
||
aria-label="Previous image"
|
||
>
|
||
‹
|
||
</button>
|
||
<button
|
||
className="nav-arrow next"
|
||
onClick={() => setSelectedImageIndex((prev) =>
|
||
prev === product.images.length - 1 ? 0 : prev + 1
|
||
)}
|
||
aria-label="Next image"
|
||
>
|
||
›
|
||
</button>
|
||
</>
|
||
)}
|
||
</div>
|
||
|
||
{/* Thumbnail Gallery */}
|
||
{product.images.length > 1 && (
|
||
<div className="thumbnail-gallery">
|
||
{product.images.map((img, idx) => (
|
||
<img
|
||
key={idx}
|
||
src={img}
|
||
alt={`View ${idx + 1}`}
|
||
className={`thumbnail ${selectedImageIndex === idx ? 'active' : ''}`}
|
||
onClick={() => setSelectedImageIndex(idx)}
|
||
/>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<div className="detail-info">
|
||
<h1>{product.name}</h1>
|
||
<p className="brand">{product.brand}</p>
|
||
|
||
<div className="rating">
|
||
<span>⭐⭐⭐⭐⭐ (42 reviews)</span>
|
||
</div>
|
||
|
||
<div className="price">
|
||
{product.discount_price ? (
|
||
<>
|
||
<span className="original">₪{product.price.toFixed(2)}</span>
|
||
<span className="current">₪{product.discount_price.toFixed(2)}</span>
|
||
</>
|
||
) : (
|
||
<span className="current">₪{price.toFixed(2)}</span>
|
||
)}
|
||
</div>
|
||
|
||
<p className="description">{product.description}</p>
|
||
|
||
{product.sizes.length > 0 && (
|
||
<div className="option-group">
|
||
<label>Size:</label>
|
||
<div className="size-options">
|
||
{product.sizes.map((size) => (
|
||
<button
|
||
key={size}
|
||
className={`size-btn ${selectedSize === size ? 'active' : ''}`}
|
||
onClick={() => setSelectedSize(size)}
|
||
>
|
||
{size}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="option-group">
|
||
<label>Quantity:</label>
|
||
<div className="quantity-selector">
|
||
<button onClick={() => setQuantity(Math.max(1, quantity - 1))}>−</button>
|
||
<input type="number" value={quantity} readOnly />
|
||
<button onClick={() => setQuantity(quantity + 1)}>+</button>
|
||
</div>
|
||
</div>
|
||
|
||
{product.stock !== null && (
|
||
<div className="stock-info">
|
||
{product.stock > 0 ? (
|
||
<span className="in-stock">✓ In Stock ({product.stock} available)</span>
|
||
) : (
|
||
<span className="out-of-stock">✗ Out of Stock</span>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
<div className="action-buttons">
|
||
<button
|
||
className="btn btn-large"
|
||
onClick={handleAddToCart}
|
||
disabled={product.stock === 0}
|
||
>
|
||
Add to Cart
|
||
</button>
|
||
<button
|
||
className={`btn btn-icon ${inWishlist ? 'active' : ''}`}
|
||
onClick={handleToggleWishlist}
|
||
>
|
||
{inWishlist ? '❤️' : '🤍'}
|
||
</button>
|
||
</div>
|
||
|
||
<div className="product-details">
|
||
<h3>Product Details</h3>
|
||
<ul>
|
||
<li><strong>Category:</strong> Shoes</li>
|
||
<li><strong>Gender:</strong> {product.gender}</li>
|
||
<li><strong>Brand:</strong> {product.brand}</li>
|
||
<li><strong>SKU:</strong> {product.id}</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|