225 lines
7.8 KiB
TypeScript
225 lines
7.8 KiB
TypeScript
import { useState } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { useCart } from "../context/CartContext";
|
|
import "./ProductCard.css";
|
|
|
|
interface ProductCardProps {
|
|
id: number;
|
|
name: string;
|
|
price: number;
|
|
unit: string;
|
|
image: string;
|
|
stock: number;
|
|
category: string;
|
|
prices?: Array<{ quantity: number; price: number }>;
|
|
hasVideo?: boolean;
|
|
videoUrl?: string; // ✨ Nouveau prop pour l'URL de la vidéo
|
|
}
|
|
|
|
function ProductCard({
|
|
id,
|
|
name,
|
|
price,
|
|
unit = "g",
|
|
image,
|
|
stock,
|
|
category = "autre",
|
|
prices,
|
|
hasVideo = false,
|
|
videoUrl,
|
|
}: ProductCardProps) {
|
|
const navigate = useNavigate();
|
|
const { addToCart } = useCart();
|
|
|
|
const [showQuantitySelect, setShowQuantitySelect] = useState(false);
|
|
const [selectedQuantity, setSelectedQuantity] = useState<number | null>(
|
|
null,
|
|
);
|
|
const [showSuccess, setShowSuccess] = useState(false);
|
|
const [showVideo, setShowVideo] = useState(false); // ✨ État pour afficher/masquer la vidéo
|
|
|
|
const isOutOfStock = stock === 0;
|
|
const normalizedCategory = (category || "autre").toLowerCase().trim();
|
|
|
|
const handleDetailsClick = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
navigate(`/user/product/${id}`);
|
|
};
|
|
|
|
// ✨ Handler pour afficher/masquer la vidéo
|
|
const handleVideoToggle = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
setShowVideo(!showVideo);
|
|
};
|
|
|
|
// ✨ Handler pour fermer la vidéo
|
|
const handleCloseVideo = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
setShowVideo(false);
|
|
};
|
|
|
|
const handleQuickAddClick = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
console.log("🔘 Bouton cliqué", {
|
|
id,
|
|
name,
|
|
category: normalizedCategory,
|
|
isOutOfStock,
|
|
prices: prices?.length || 0,
|
|
showQuantitySelect,
|
|
});
|
|
|
|
if (!isOutOfStock && prices && prices.length > 0) {
|
|
setShowQuantitySelect(!showQuantitySelect);
|
|
}
|
|
};
|
|
|
|
const handleQuantitySelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
const selectedGrams = Number(e.target.value);
|
|
setSelectedQuantity(selectedGrams);
|
|
|
|
const priceOption = prices?.find((p) => p.quantity === selectedGrams);
|
|
if (priceOption) {
|
|
console.log("💾 [ProductCard] Ajout au panier:", {
|
|
id,
|
|
name,
|
|
category: normalizedCategory,
|
|
quantity: selectedGrams,
|
|
price: priceOption.price,
|
|
});
|
|
|
|
addToCart({
|
|
product_id: id,
|
|
name_product: name,
|
|
price: priceOption.price,
|
|
quantity: selectedGrams,
|
|
category: normalizedCategory,
|
|
image: image,
|
|
});
|
|
|
|
setShowSuccess(true);
|
|
setTimeout(() => {
|
|
setShowSuccess(false);
|
|
setShowQuantitySelect(false);
|
|
setSelectedQuantity(null);
|
|
}, 1500);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className={`product-card ${isOutOfStock ? "out-of-stock" : ""}`}>
|
|
<div className="product-card-content">
|
|
<div className="product-image-container">
|
|
<img
|
|
src={image}
|
|
alt={name}
|
|
className="product-image3"
|
|
loading="lazy"
|
|
/>
|
|
{/* ✨ Icône caméra cliquable */}
|
|
{hasVideo && !isOutOfStock && videoUrl && (
|
|
<button
|
|
className="media-indicator"
|
|
onClick={handleVideoToggle}
|
|
aria-label="Voir la vidéo du produit"
|
|
>
|
|
<i className="fas fa-camera"></i>
|
|
</button>
|
|
)}
|
|
{/* ✨ Bouton "Détails" */}
|
|
<button
|
|
className="details-button"
|
|
onClick={handleDetailsClick}
|
|
aria-label="Voir les détails du produit"
|
|
>
|
|
<i className="fas fa-info-circle"></i>
|
|
<span>Détails</span>
|
|
</button>
|
|
{isOutOfStock && (
|
|
<div className="sold-out-overlay">SOLD OUT</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="product-info">
|
|
<h3 className="product-name">{name}</h3>
|
|
<p className="product-price">
|
|
{price > 0
|
|
? `${price.toFixed(2)} €`
|
|
: "Prix non disponible"}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className="quick-add-section"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
{showSuccess ? (
|
|
<div className="success-message">✓ Ajouté !</div>
|
|
) : (
|
|
<>
|
|
<button
|
|
className={`quick-add-btn ${isOutOfStock ? "disabled" : ""}`}
|
|
onClick={handleQuickAddClick}
|
|
disabled={isOutOfStock}
|
|
>
|
|
{isOutOfStock
|
|
? "Rupture de stock"
|
|
: "Ajouter rapidement"}
|
|
</button>
|
|
|
|
{showQuantitySelect && prices && prices.length > 0 && (
|
|
<select
|
|
className="quantity-select"
|
|
value={selectedQuantity ?? ""}
|
|
onChange={handleQuantitySelect}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<option value="">Choisir une quantité</option>
|
|
{prices.map((priceOption) => (
|
|
<option
|
|
key={priceOption.quantity}
|
|
value={priceOption.quantity}
|
|
>
|
|
{priceOption.quantity}{unit} -{" "}
|
|
{priceOption.price.toFixed(2)} €
|
|
</option>
|
|
))}
|
|
</select>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
|
|
{/* ✨ Modal vidéo */}
|
|
{showVideo && videoUrl && (
|
|
<div className="video-modal" onClick={handleCloseVideo}>
|
|
<div
|
|
className="video-modal-content"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<button
|
|
className="video-close-btn"
|
|
onClick={handleCloseVideo}
|
|
aria-label="Fermer la vidéo"
|
|
>
|
|
<i className="fas fa-times"></i>
|
|
</button>
|
|
<video
|
|
src={videoUrl}
|
|
controls
|
|
autoPlay
|
|
className="video-player"
|
|
>
|
|
Votre navigateur ne supporte pas la lecture de
|
|
vidéos.
|
|
</video>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default ProductCard;
|