const ProductPage = ({ product, setPage, onAddToCart, lang }) => { const [selectedColor, setSelectedColor] = React.useState(product?.colors?.[0] || null); const [mainImg, setMainImg] = React.useState(null); const [selectedSize, setSelectedSize] = React.useState(product?.sizes?.[0] || null); const [variantName, setVariantName] = React.useState(product?.variants?.[0]?.name || null); const [qty, setQty] = React.useState(1); const [added, setAdded] = React.useState(false); const [reviews, setReviews] = React.useState([]); const [reviewStats, setReviewStats] = React.useState({ total:0, avg:0 }); const [canReview, setCanReview] = React.useState(false); const [alreadyReviewed, setAlreadyReviewed] = React.useState(false); const [myRating, setMyRating] = React.useState(5); const [myReview, setMyReview] = React.useState(""); const [submitting, setSubmitting] = React.useState(false); const [reviewMsg, setReviewMsg] = React.useState(""); const t = (en, id) => lang === "id" ? id : en; React.useEffect(() => { setSelectedColor(null); // jangan paksa pilih warna saat load setSelectedSize(product?.sizes?.[0] || null); const _vs = Array.isArray(product?.variants) ? product.variants : []; // Default varian = yang termurah biar harga awal = "mulai dari"; cover foto TIDAK ikut warna let _cheap = null; _vs.forEach(v => { if (!_cheap || (v.price || 0) < (_cheap.price || 0)) _cheap = v; }); setVariantName(_cheap ? _cheap.name : null); // Cover awal = foto utama produk (pilihan admin), BUKAN foto warna. Ganti hanya saat user klik warna. setMainImg( (product?.images && product.images[0]) || product?.image || (_cheap && _cheap.image) || null ); setQty(1); setAdded(false); window.scrollTo({ top: 0, behavior: "smooth" }); if (product?.id && typeof fbq === 'function') { fbq('track', 'ViewContent', { content_ids: [String(product.id)], content_name: product.name, content_category: product.category || '', content_type: 'product', value: product.price, currency: 'IDR' }); } // Load reviews if (product?.id) { fetch("api/reviews.php?product_id=" + product.id, { credentials:"include" }) .then(r => r.json()) .then(d => { setReviews(d.reviews || []); setReviewStats({ total: d.total || 0, avg: d.avg_rating || 0 }); setCanReview(d.can_review || false); setAlreadyReviewed(d.already_reviewed || false); }).catch(() => {}); setMyRating(5); setMyReview(""); setReviewMsg(""); } }, [product]); const submitReview = async () => { if (myReview.length < 10) { setReviewMsg("❌ Ulasan minimal 10 karakter"); return; } setSubmitting(true); try { const res = await fetch("api/reviews.php", { method:"POST", credentials:"include", headers:{"Content-Type":"application/json"}, body: JSON.stringify({ product_id: product.id, rating: myRating, review: myReview }) }); const data = await res.json(); if (data.success) { setReviewMsg("✓ Ulasan berhasil dikirim!"); setCanReview(false); setAlreadyReviewed(true); // Reload reviews fetch("api/reviews.php?product_id=" + product.id, { credentials:"include" }) .then(r => r.json()).then(d => { setReviews(d.reviews||[]); setReviewStats({ total:d.total||0, avg:d.avg_rating||0 }); }); } else { setReviewMsg("❌ " + (data.error||"Gagal mengirim")); } } catch { setReviewMsg("❌ Gagal terhubung"); } finally { setSubmitting(false); } }; const StarRating = ({ value, onChange, size=20 }) => (
{[1,2,3,4,5].map(n => ( onChange && onChange(n)} style={{ fontSize:size, cursor:onChange?"pointer":"default", color: n<=value ? "#f59e0b" : "#ddd", transition:"color 0.1s" }}>★ ))}
); if (!product) return null; // Varian harga: tiap varian punya harga & stok sendiri const variants = Array.isArray(product.variants) ? product.variants : []; const hasVariants = variants.length > 0; const curVariant = hasVariants ? (variants.find(v => v.name === variantName) || variants[0]) : null; const curPrice = curVariant ? curVariant.price : product.price; const curStock = curVariant ? curVariant.stock : product.stock; // Kalau tiap varian punya Warna bernama sama → pemilihan cukup lewat swatch Warna, // sembunyikan baris Varian biar gak dobel (warna jadi "wajah", varian kasih harga/stok). const _colorsLower = Array.isArray(product.colors) ? product.colors.map(c => String(c).toLowerCase().trim()) : []; const variantsCovered = hasVariants && variants.every(v => _colorsLower.includes(String(v.name).toLowerCase().trim())); // Produk berwarna WAJIB pilih warna dulu (biar keranjang selalu tau warnanya) const needColor = Array.isArray(product.colors) && product.colors.length > 0; const mustPick = needColor && !selectedColor; const handleAdd = () => { if (mustPick) { alert(t("Please choose a color first 🙏", "Pilih warna dulu ya 🙏")); return; } if (curStock <= 0) return; onAddToCart({ ...product, price: curPrice, stock: curStock, selectedColor, selectedSize, selectedVariant: curVariant ? curVariant.name : null, }, qty); setAdded(true); setTimeout(() => setAdded(false), 2000); }; const related = PRODUCTS.filter(p => p.category === product.category && p.id !== product.id).slice(0, 4); return (
{/* Breadcrumb */}
{[ { label: t("Home", "Beranda"), page: "home" }, { label: t("Products", "Produk"), page: "catalog" }, { label: product.name, page: null } ].map((crumb, i) => ( {i > 0 && /} crumb.page && setPage(crumb.page)} style={{ fontFamily: "'DM Sans', sans-serif", fontSize: 11, letterSpacing: "0.08em", color: crumb.page ? "#888" : "#111", cursor: crumb.page ? "pointer" : "default", textDecoration: crumb.page ? "underline" : "none", textUnderlineOffset: 3 }} >{crumb.label} ))}
{/* Main content */}
{/* Image Gallery */}
{(() => { const baseImgs = (product.images && product.images.length > 0) ? product.images : (product.image ? [product.image] : []); const ci = product.colorImages || {}; const extra = []; Object.keys(ci).forEach(k => { const u = ci[k]; if (u && !baseImgs.includes(u) && !extra.includes(u)) extra.push(u); }); variants.forEach(v => { if (!v.image) return; // skip kalau varian ini sudah punya Foto per Warna bernama sama (hindari foto dobel) const ck = Object.keys(ci).find(k => String(k).toLowerCase().trim() === String(v.name).toLowerCase().trim()); if (ck && ci[ck]) return; if (!baseImgs.includes(v.image) && !extra.includes(v.image)) extra.push(v.image); }); const imgs = [...baseImgs, ...extra]; const cur = mainImg || imgs[0]; return ( <> {/* Main image */}
{product.name}
{/* Thumbnails */} {imgs.length > 1 && (
{imgs.map((img, i) => (
setMainImg(img)} style={{ width: 64, height: 64, borderRadius: 8, overflow: "hidden", cursor: "pointer", border: cur===img ? "2px solid #111" : "2px solid transparent", opacity: cur===img ? 1 : 0.6, transition: "all 0.2s" }}> {`foto
))}
)} {product.badge && ( {product.badge} )} ); })()}
{/* Info */}

{product.categoryLabel}

{product.name}

{/* Price */}
{formatPrice(curPrice)} {product.originalPrice && ( {formatPrice(product.originalPrice)} )} {product.originalPrice && ( SALE )}
{/* Trust badges — selalu tampil (social proof / kepercayaan toko baru) */}
{[ t("Handmade", "Buatan Tangan"), product.isPreorder ? t("Pre-Order", "Pre-Order") : t("Ready Stock", "Ready Stock"), t("Ships from Bogor", "Dikirim dari Bogor"), t("COD available", "Bisa COD"), ].map((b, i) => ( {b} ))}
{/* Varian Harga — disembunyikan kalau tiap varian sudah diwakili swatch Warna */} {hasVariants && !variantsCovered && (

{t("Variant", "Varian")}: {curVariant ? curVariant.name : ""}

{variants.map(v => { const active = curVariant && curVariant.name === v.name; const out = (v.stock || 0) <= 0; return ( ); })}
)} {/* PO Notice */} {product.isPreorder && (

⏳ Pre-Order — {product.preorderDays || 14} Hari

{t( `This product is made-to-order. It will be processed and shipped approximately ${product.preorderDays || 14} days after payment.`, `Produk ini dibuat berdasarkan pesanan. Akan diproses & dikirim ±${product.preorderDays || 14} hari setelah pembayaran lunas.` )}

)}
{/* Rating */} {reviewStats.total > 0 && (
{[1,2,3,4,5].map(n => ( ))}
{reviewStats.avg} ({reviewStats.total} ulasan)
)} {/* Color */} {product.colors && (

{t("Color", "Warna")}: {selectedColor || t("— Choose", "— Pilih")}

{product.colors.map(c => { const mvc = variants.find(v => String(v.name).toLowerCase().trim() === String(c).toLowerCase().trim()); const cOut = mvc ? (mvc.stock || 0) <= 0 : false; const active = selectedColor === c; return ( ); })}
)} {/* Size */} {product.sizes && (

{t("Size", "Ukuran")}: {selectedSize}

{product.sizes.map(s => ( ))}
)} {/* Qty */}

{t("Quantity", "Jumlah")}

{qty}
{/* Urgensi stok asli — cuma tampil kalau stok beneran tinggal sedikit */} {curStock > 0 && curStock <= 8 && (

🔥 {t(`Only ${curStock} left — order soon`, `Sisa ${curStock} stok — buruan sebelum kehabisan`)}

)} {/* Add to cart — inline */} {/* Sticky bottom CTA — mobile only */}

{product.name}

{formatPrice(curPrice)}

{/* Description */}

{product.description}

{/* Details */}

{t("Details", "Detail")}

    {product.details.map((d, i) => (
  • {d}
  • ))}
{/* Related products */} {related.length > 0 && (

{t("You May Also Like", "Mungkin Kamu Suka")}

{related.map(p => ( { window.scrollTo({ top: 0, behavior: "smooth" }); setTimeout(() => { setSelectedProduct(prod); }, 100); }} onAddToCart={onAddToCart} /> ))}
)}
{/* Reviews Section */}
{/* Header */}

Ulasan Pembeli

{reviewStats.total > 0 && (
{reviewStats.avg} dari {reviewStats.total} ulasan
)}
{/* Form ulasan */} {canReview && (

Tulis Ulasanmu

Rating