diff --git a/frontend-prep/src/components/ProductCard.tsx b/frontend-prep/src/components/ProductCard.tsx index 5acb9c34..d27c56a6 100644 --- a/frontend-prep/src/components/ProductCard.tsx +++ b/frontend-prep/src/components/ProductCard.tsx @@ -7,7 +7,13 @@ import "./ProductCard.css"; function getTextColor(hex: string): string { const h = hex.replace("#", ""); - const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h; + const full = + h.length === 3 + ? h + .split("") + .map((c) => c + c) + .join("") + : h; const r = parseInt(full.slice(0, 2), 16); const g = parseInt(full.slice(2, 4), 16); const b = parseInt(full.slice(4, 6), 16); @@ -160,7 +166,7 @@ function ProductCard({
SOLD OUT
)} {isComingSoon && ( -
À VENIR
+
COMMING SOON
)} @@ -188,15 +194,18 @@ function ProductCard({ disabled={isOutOfStock || isComingSoon} style={ categoryColor && !isOutOfStock && !isComingSoon - ? { background: categoryColor, color: getTextColor(categoryColor) } + ? { + background: categoryColor, + color: getTextColor(categoryColor), + } : undefined } > {isOutOfStock ? "Rupture de stock" : isComingSoon - ? "Bientôt disponible" - : "Ajouter rapidement"} + ? "Bientôt disponible" + : "Ajouter rapidement"} {showQuantitySelect && prices && prices.length > 0 && ( @@ -217,8 +226,9 @@ function ProductCard({ key={priceOption.quantity} value={priceOption.quantity} > - {priceOption.quantity}{unit} -{" "} - {priceOption.price.toFixed(2)} € + {priceOption.quantity} + {unit} - {priceOption.price.toFixed(2)}{" "} + € ))} @@ -228,32 +238,34 @@ function ProductCard({ {/* ✨ Modal vidéo — rendu via Portal pour éviter le clipping du transform:scale sur .product-card */} - {showVideo && videoUrl && createPortal( -
-
e.stopPropagation()} - > - - -
-
, - document.body - )} + + + + , + document.body, + )} ); } diff --git a/frontend-prep/src/pages/User/ProductDetail.tsx b/frontend-prep/src/pages/User/ProductDetail.tsx index 44962dea..00396572 100644 --- a/frontend-prep/src/pages/User/ProductDetail.tsx +++ b/frontend-prep/src/pages/User/ProductDetail.tsx @@ -1,6 +1,10 @@ import { useParams, useNavigate } from "react-router-dom"; import { useState, useEffect } from "react"; -import { getProductById, getCategories, isUserAuthenticated } from "../../api/api"; +import { + getProductById, + getCategories, + isUserAuthenticated, +} from "../../api/api"; import type { Product } from "../../api/api"; import { useCart } from "../../context/useCart"; import Navbar from "../../components/Navbar"; @@ -87,9 +91,19 @@ function ProductDetail() { ...response.data, prices: response.data.prices - ?.filter((p: { quantity: number; price: number; active_price?: boolean }) => p.active_price !== false) + ?.filter( + (p: { + quantity: number; + price: number; + active_price?: boolean; + }) => p.active_price !== false, + ) .map( - (p: { quantity: number; price: number; active_price?: boolean }) => ({ + (p: { + quantity: number; + price: number; + active_price?: boolean; + }) => ({ quantity: parseFloat(String(p.quantity)), price: parseFloat(String(p.price)), active_price: p.active_price, @@ -107,14 +121,20 @@ function ProductDetail() { // Couleur de la catégorie depuis la DB const matched = categories.find( - (c) => c.name.toLowerCase() === (response.data.category || "").toLowerCase(), + (c) => + c.name.toLowerCase() === + (response.data.category || "").toLowerCase(), ); if (matched?.color) setCatColor(matched.color); } else { setError(response.message || "Produit non trouvé"); } } catch (err: unknown) { - setError(err instanceof Error ? err.message : "Erreur lors du chargement du produit"); + setError( + err instanceof Error + ? err.message + : "Erreur lors du chargement du produit", + ); } finally { setLoading(false); } @@ -217,7 +237,8 @@ function ProductDetail() { const r = parseInt(h.slice(0, 2), 16); const g = parseInt(h.slice(2, 4), 16); const b = parseInt(h.slice(4, 6), 16); - const catTextColor = (r * 299 + g * 587 + b * 114) / 1000 > 128 ? "#000000" : "#ffffff"; + const catTextColor = + (r * 299 + g * 587 + b * 114) / 1000 > 128 ? "#000000" : "#ffffff"; return ( <> @@ -235,21 +256,38 @@ function ProductDetail() {
-
+
m.type === "image")?.url || ""} + src={ + product.media?.find((m) => m.type === "image") + ?.url || "" + } alt={product.name} className="product-detail-image" /> - {isOutOfStock &&
SOLD OUT
} - {isComingSoon &&
À VENIR
} + {isOutOfStock && ( +
SOLD OUT
+ )} + {isComingSoon && ( +
+ COMMING SOON +
+ )}
@@ -258,7 +296,8 @@ function ProductDetail() { {selectedPrice > 0 && (

{selectedPrice.toFixed(2)} €{" "} - {selectedGrams && `pour ${selectedGrams}${product.unit || "g"}`} + {selectedGrams && + `pour ${selectedGrams}${product.unit || "g"}`}

)} @@ -296,7 +335,8 @@ function ProductDetail() { key={p.quantity} value={p.quantity} > - {p.quantity}{product.unit || "g"} -{" "} + {p.quantity} + {product.unit || "g"} -{" "} {p.price.toFixed(2)} € ))} @@ -308,13 +348,17 @@ function ProductDetail() {
diff --git a/mobile/src/components/ProductCard.tsx b/mobile/src/components/ProductCard.tsx index 01a0b06c..61fb9d00 100644 --- a/mobile/src/components/ProductCard.tsx +++ b/mobile/src/components/ProductCard.tsx @@ -13,7 +13,13 @@ import { function getTextColor(hex: string): string { const h = hex.replace("#", ""); - const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h; + const full = + h.length === 3 + ? h + .split("") + .map((c) => c + c) + .join("") + : h; const r = parseInt(full.slice(0, 2), 16); const g = parseInt(full.slice(2, 4), 16); const b = parseInt(full.slice(4, 6), 16); @@ -37,20 +43,29 @@ interface ProductCardProps { stock: number; unit?: string; coming_soon?: boolean; - prices?: Array<{ quantity: number; price: number; active_price?: boolean }>; + prices?: Array<{ + quantity: number; + price: number; + active_price?: boolean; + }>; media?: Array<{ url: string; type: string }>; }; onPress: () => void; categoryColor?: string; } -export default function ProductCard({ product, onPress, categoryColor }: ProductCardProps) { +export default function ProductCard({ + product, + onPress, + categoryColor, +}: ProductCardProps) { const { colors } = useTheme(); const { addToCart } = useCart(); const catColor = categoryColor ?? colors.accent; const isSoldOut = product.stock <= 0; const isComingSoon = product.coming_soon === true; - const activePrices = product.prices?.filter((p) => p.active_price !== false) ?? []; + const activePrices = + product.prices?.filter((p) => p.active_price !== false) ?? []; const firstPrice = activePrices[0]?.price ?? null; const imageMedia = product.media?.find((m) => m.type === "image"); @@ -161,7 +176,7 @@ export default function ProductCard({ product, onPress, categoryColor }: Product )} {isComingSoon && ( - À VENIR + COMMING SOON )} @@ -236,8 +251,8 @@ export default function ProductCard({ product, onPress, categoryColor }: Product {isSoldOut ? "Rupture de stock" : isComingSoon - ? "Bientôt disponible" - : "Ajouter rapidement"} + ? "Bientôt disponible" + : "Ajouter rapidement"} )} @@ -297,7 +312,8 @@ export default function ProductCard({ product, onPress, categoryColor }: Product { color: colors.textWhite }, ]} > - {p.quantity}{product.unit || "g"} + {p.quantity} + {product.unit || "g"} c.name.toLowerCase() === (p.category || "").toLowerCase(), + (c) => + c.name.toLowerCase() === + (p.category || "").toLowerCase(), ); if (matched?.color) setCatColor(matched.color); } else { @@ -111,11 +113,19 @@ export default function ProductDetailScreen() { const catTextColor = (() => { const h = catColor.replace("#", ""); - const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h; + const full = + h.length === 3 + ? h + .split("") + .map((c) => c + c) + .join("") + : h; const r = parseInt(full.slice(0, 2), 16); const g = parseInt(full.slice(2, 4), 16); const b = parseInt(full.slice(4, 6), 16); - return (r * 299 + g * 587 + b * 114) / 1000 > 128 ? "#000000" : "#ffffff"; + return (r * 299 + g * 587 + b * 114) / 1000 > 128 + ? "#000000" + : "#ffffff"; })(); const overlayBg = isDark ? "rgba(255,255,255,0.02)" : "rgba(0,0,0,0.02)"; @@ -546,7 +556,9 @@ export default function ProductDetailScreen() { )} {isComingSoon && ( - À VENIR + + COMMING SOON + )} {videoUri && !isOutOfStock && ( @@ -573,7 +585,8 @@ export default function ProductDetailScreen() { {selectedPrice.toFixed(2)} €{" "} - {selectedGrams && `pour ${selectedGrams}${product.unit || "g"}`} + {selectedGrams && + `pour ${selectedGrams}${product.unit || "g"}`} )} @@ -615,27 +628,34 @@ export default function ProductDetailScreen() { {isOutOfStock ? "Rupture de stock" : isComingSoon - ? "Bientôt disponible" - : "Ajouter au panier"} + ? "Bientôt disponible" + : "Ajouter au panier"} @@ -691,7 +711,8 @@ export default function ProductDetailScreen() { }, ]} > - {p.quantity}{product.unit || "g"} + {p.quantity} + {product.unit || "g"}