feat: add active modal image state and thumbnail update logic to product display
This commit is contained in:
parent
530d2cbbcb
commit
b7f3953a56
@ -10,6 +10,13 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
|||||||
const [allProducts, setAllProducts] = useState<any[]>(bestSellers.data);
|
const [allProducts, setAllProducts] = useState<any[]>(bestSellers.data);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [selectedCategorySlug, setSelectedCategorySlug] = useState('');
|
const [selectedCategorySlug, setSelectedCategorySlug] = useState('');
|
||||||
|
const [activeModalImage, setActiveModalImage] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedProduct) {
|
||||||
|
setActiveModalImage(selectedProduct?.thumbnail_url);
|
||||||
|
}
|
||||||
|
}, [selectedProduct]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
@ -154,20 +161,18 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
|||||||
<div className="relative mb-8">
|
<div className="relative mb-8">
|
||||||
<div className="custom-scrollbar flex max-h-[300px] flex-col overflow-y-auto border-y border-[#1e4a6d]/20">
|
<div className="custom-scrollbar flex max-h-[300px] flex-col overflow-y-auto border-y border-[#1e4a6d]/20">
|
||||||
{categories.map((cat: any) => (
|
{categories.map((cat: any) => (
|
||||||
<button
|
<button
|
||||||
key={cat.id}
|
key={cat.id}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
setSelectedCategorySlug(cat.slug);
|
setSelectedCategorySlug(cat.slug);
|
||||||
scrollToSection(e, '#product');
|
scrollToSection(e, '#product');
|
||||||
}}
|
}}
|
||||||
className={`group flex items-center justify-between border-b border-[#1e4a6d]/10 px-4 py-4 text-left text-xs font-bold uppercase tracking-wider transition-all hover:bg-[#1e4a6d] hover:text-white last:border-0 ${
|
className={`group flex items-center justify-between border-b border-[#1e4a6d]/10 px-4 py-4 text-left text-xs font-bold uppercase tracking-wider transition-all hover:bg-[#1e4a6d] hover:text-white last:border-0 ${selectedCategorySlug === cat.slug ? 'bg-[#1e4a6d] text-white' : ''
|
||||||
selectedCategorySlug === cat.slug ? 'bg-[#1e4a6d] text-white' : ''
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{cat.name}
|
{cat.name}
|
||||||
<ArrowRight size={14} className={`transition-all group-hover:translate-x-1 group-hover:opacity-100 ${
|
<ArrowRight size={14} className={`transition-all group-hover:translate-x-1 group-hover:opacity-100 ${selectedCategorySlug === cat.slug ? 'opacity-100 translate-x-1' : 'opacity-0'
|
||||||
selectedCategorySlug === cat.slug ? 'opacity-100 translate-x-1' : 'opacity-0'
|
}`} />
|
||||||
}`} />
|
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
{categories.length === 0 && (
|
{categories.length === 0 && (
|
||||||
@ -219,7 +224,7 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
|||||||
onClick={() => setSelectedProduct(product)}
|
onClick={() => setSelectedProduct(product)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={product.thumbnail_url || 'https://images.unsplash.com/photo-1719947348940-7aa339028cbc?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDgzfHx8ZW58MHx8fHx8'}
|
src={product?.thumbnail_url || 'https://images.unsplash.com/photo-1719947348940-7aa339028cbc?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDgzfHx8ZW58MHx8fHx8'}
|
||||||
alt={product.name}
|
alt={product.name}
|
||||||
className="h-full w-full object-cover"
|
className="h-full w-full object-cover"
|
||||||
/>
|
/>
|
||||||
@ -247,7 +252,7 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
|||||||
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
|
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
|
||||||
{/* Category Filter */}
|
{/* Category Filter */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<select
|
<select
|
||||||
value={selectedCategorySlug}
|
value={selectedCategorySlug}
|
||||||
onChange={(e) => setSelectedCategorySlug(e.target.value)}
|
onChange={(e) => setSelectedCategorySlug(e.target.value)}
|
||||||
className="appearance-none border border-[#1e4a6d]/20 bg-transparent py-2 pl-4 pr-10 text-[10px] font-bold uppercase tracking-widest outline-none transition-all focus:border-[#1e4a6d] cursor-pointer"
|
className="appearance-none border border-[#1e4a6d]/20 bg-transparent py-2 pl-4 pr-10 text-[10px] font-bold uppercase tracking-widest outline-none transition-all focus:border-[#1e4a6d] cursor-pointer"
|
||||||
@ -261,13 +266,13 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
|||||||
<div className="h-1 w-1 rotate-45 border-b border-r border-[#1e4a6d]"></div>
|
<div className="h-1 w-1 rotate-45 border-b border-r border-[#1e4a6d]"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Search Input */}
|
{/* Search Input */}
|
||||||
<div className="relative flex items-center">
|
<div className="relative flex items-center">
|
||||||
<Search size={14} className="absolute left-3 opacity-40" />
|
<Search size={14} className="absolute left-3 opacity-40" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Cari produk..."
|
placeholder="Cari produk..."
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
className="border border-[#1e4a6d]/20 bg-transparent py-2 pl-10 pr-4 text-[10px] font-bold uppercase tracking-widest outline-none transition-all focus:border-[#1e4a6d] w-full sm:w-[200px]"
|
className="border border-[#1e4a6d]/20 bg-transparent py-2 pl-10 pr-4 text-[10px] font-bold uppercase tracking-widest outline-none transition-all focus:border-[#1e4a6d] w-full sm:w-[200px]"
|
||||||
@ -288,7 +293,7 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
|||||||
onClick={() => setSelectedProduct(item)}
|
onClick={() => setSelectedProduct(item)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={item.thumbnail_url || 'https://images.unsplash.com/photo-1719947348940-7aa339028cbc?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDgzfHx8ZW58MHx8fHx8'}
|
src={item?.thumbnail_url || 'https://images.unsplash.com/photo-1719947348940-7aa339028cbc?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDgzfHx8ZW58MHx8fHx8'}
|
||||||
alt={item.name}
|
alt={item.name}
|
||||||
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
|
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
|
||||||
/>
|
/>
|
||||||
@ -305,8 +310,8 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
|||||||
{item.name}
|
{item.name}
|
||||||
</p>
|
</p>
|
||||||
<p className="mb-4 text-sm font-medium opacity-60">{formatCurrency(item.retail_price)}</p>
|
<p className="mb-4 text-sm font-medium opacity-60">{formatCurrency(item.retail_price)}</p>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href={`https://wa.me/628123456789?text=${encodeURIComponent(`Halo VN Grup, saya ingin memesan ${item.name} seharga ${formatCurrency(item.retail_price)}. Mohon info detail pembayarannya.`)}`}
|
href={`https://wa.me/628123456789?text=${encodeURIComponent(`Halo VN Grup, saya ingin memesan ${item.name} seharga ${formatCurrency(item.retail_price)}. Mohon info detail pembayarannya.`)}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@ -324,7 +329,7 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
|||||||
</div>
|
</div>
|
||||||
<h3 className="mb-2 text-xl font-medium">Produk tidak ditemukan</h3>
|
<h3 className="mb-2 text-xl font-medium">Produk tidak ditemukan</h3>
|
||||||
<p className="mb-8 text-sm opacity-60">Coba gunakan kata kunci lain atau periksa ejaan Anda.</p>
|
<p className="mb-8 text-sm opacity-60">Coba gunakan kata kunci lain atau periksa ejaan Anda.</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSearch('');
|
setSearch('');
|
||||||
setSelectedCategorySlug('');
|
setSelectedCategorySlug('');
|
||||||
@ -501,24 +506,47 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
|||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2">
|
<div className="grid grid-cols-1 md:grid-cols-2">
|
||||||
{/* Product Images */}
|
{/* Product Images */}
|
||||||
<div className="border-b border-[#1e4a6d]/10 md:border-b-0 md:border-r overflow-hidden bg-white">
|
<div className="border-b border-[#1e4a6d]/10 md:border-b-0 md:border-r overflow-hidden bg-[#fdfcf9]">
|
||||||
<div className="aspect-[4/5] overflow-hidden">
|
<div className="aspect-[4/5] overflow-hidden relative group/hero">
|
||||||
<img
|
<img
|
||||||
src={selectedProduct?.thumbnail_url || 'https://images.unsplash.com/photo-1719947348940-7aa339028cbc?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDgzfHx8ZW58MHx8fHx8'}
|
src={activeModalImage || selectedProduct?.thumbnail_url || 'https://images.unsplash.com/photo-1719947348940-7aa339028cbc?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDgzfHx8ZW58MHx8fHx8'}
|
||||||
alt={selectedProduct?.name}
|
alt={selectedProduct?.name}
|
||||||
className="h-full w-full object-cover"
|
className="h-full w-full object-cover transition-transform duration-700 hover:scale-105"
|
||||||
/>
|
/>
|
||||||
|
{/* Blueprint overlay decoration */}
|
||||||
|
<div className="absolute top-4 left-4 text-[8px] font-mono opacity-20 pointer-events-none">DET_V.1.0</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Image Gallery if exists */}
|
|
||||||
{selectedProduct?.images_data?.length > 0 && (
|
{/* Refined Image Gallery List */}
|
||||||
<div className="flex border-t border-[#1e4a6d]/10">
|
<div className="p-4 border-t border-[#1e4a6d]/10">
|
||||||
{selectedProduct.images_data.map((img: any, i: number) => (
|
<p className="text-[8px] font-bold uppercase tracking-[0.2em] mb-3 opacity-40">Galeri Foto</p>
|
||||||
<div key={i} className="aspect-square w-20 border-r border-[#1e4a6d]/10 overflow-hidden">
|
<div className="flex flex-wrap gap-3">
|
||||||
<img src={img.url} alt="detail" className="h-full w-full object-cover opacity-60 hover:opacity-100 transition-opacity cursor-pointer" />
|
{/* Thumbnail (Main Image) */}
|
||||||
|
<div
|
||||||
|
onClick={() => setActiveModalImage(selectedProduct?.thumbnail_url)}
|
||||||
|
className={`relative aspect-square w-14 cursor-pointer overflow-hidden transition-all duration-300 ${activeModalImage === selectedProduct?.thumbnail_url
|
||||||
|
? 'ring-1 ring-[#1e4a6d] ring-offset-2'
|
||||||
|
: 'opacity-40 hover:opacity-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<img src={selectedProduct?.thumbnail_url} className="h-full w-full object-cover" alt="main" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Additional Images */}
|
||||||
|
{selectedProduct?.images_data?.map((img: any, i: number) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
onClick={() => setActiveModalImage(img.url)}
|
||||||
|
className={`relative aspect-square w-14 cursor-pointer overflow-hidden transition-all duration-300 ${activeModalImage === img.url
|
||||||
|
? 'ring-1 ring-[#1e4a6d] ring-offset-2'
|
||||||
|
: 'opacity-40 hover:opacity-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<img src={img.url} className="h-full w-full object-cover" alt={`view ${i + 1}`} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Product Info */}
|
{/* Product Info */}
|
||||||
@ -541,7 +569,7 @@ export default function Homepage({ categories = [], bestSellers, topSellingProdu
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-auto pt-8 border-t border-[#1e4a6d]/10">
|
<div className="mt-auto pt-8 border-t border-[#1e4a6d]/10">
|
||||||
<a
|
<a
|
||||||
href={`https://wa.me/628123456789?text=${encodeURIComponent(`Halo VN Grup, saya tertarik dengan produk ${selectedProduct?.name} seharga ${formatCurrency(selectedProduct?.retail_price || 0)}. Bisa tanya-tanya dulu?`)}`}
|
href={`https://wa.me/628123456789?text=${encodeURIComponent(`Halo VN Grup, saya tertarik dengan produk ${selectedProduct?.name} seharga ${formatCurrency(selectedProduct?.retail_price || 0)}. Bisa tanya-tanya dulu?`)}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user