This commit is contained in:
Your Name committed 2026-07-12 03:08:40 -04:00
1 parent 07f04e2065
commit 5d111b2866
2 files changed
+32 -49

No files matched your search

+31 -48
View File
@@ -12,71 +12,40 @@ export default function ProductImageGallery({ images, fallback, alt = 'Product I
const [currentIndex, setCurrentIndex] = useState(0) const [currentIndex, setCurrentIndex] = useState(0)
// Sort images by order if they exist, else just use the array // Sort images by order if they exist, else just use the array
const sortedImages = images?.length ? [...images].sort((a, b) => a.order - b.order) : [] const sortedImages = images?.length ? [...images].sort((a, b) => (a.order || 0) - (b.order || 0)) : []
const hasImages = sortedImages.length > 0
const currentImageUrl = hasImages ? sortedImages[currentIndex].url : fallback if (sortedImages.length === 0) return null
const handleNext = () => {
setCurrentIndex((prev) => (prev === sortedImages.length - 1 ? 0 : prev + 1))
}
const handlePrev = () => {
setCurrentIndex((prev) => (prev === 0 ? sortedImages.length - 1 : prev - 1))
}
// If there's only one image, don't show thumbnails
if (sortedImages.length === 1) {
return ( return (
<div className="space-y-4"> <div className="relative aspect-square w-full flex-1 overflow-hidden rounded-3xl bg-pop-bg shadow-sm border border-pop-black/10 flex items-center justify-center">
{/* Main Image */}
<div className="relative rounded-2xl overflow-hidden h-[480px] group bg-pop-bg shadow-sm border border-pop-black/10 flex items-center justify-center">
<img <img
src={currentImageUrl} src={sortedImages[0].url}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700" className="w-full h-full object-cover"
alt={alt} alt={alt}
onError={(e) => { (e.target as HTMLImageElement).src = fallback }} onError={(e) => { (e.target as HTMLImageElement).src = fallback }}
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/10 to-transparent pointer-events-none" />
{badgeText && ( {badgeText && (
<div className="absolute top-6 left-6 bg-pop-bg backdrop-blur-sm px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-widest text-pop-pink shadow-sm pointer-events-none"> <div className="absolute top-6 left-6 bg-pop-bg backdrop-blur-sm px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-widest text-pop-pink shadow-sm pointer-events-none z-10">
{badgeText} {badgeText}
</div> </div>
)} )}
{/* Navigation Arrows */}
{sortedImages.length > 1 && (
<>
<button
onClick={handlePrev}
className="absolute left-4 top-1/2 -translate-y-1/2 w-10 h-10 bg-pop-bg backdrop-blur-md rounded-full flex items-center justify-center text-pop-black opacity-0 group-hover:opacity-100 transition-opacity hover:bg-pop-bg hover:text-pop-pink hover:scale-110 shadow-sm"
aria-label="Previous image"
>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
</button>
<button
onClick={handleNext}
className="absolute right-4 top-1/2 -translate-y-1/2 w-10 h-10 bg-pop-bg backdrop-blur-md rounded-full flex items-center justify-center text-pop-black opacity-0 group-hover:opacity-100 transition-opacity hover:bg-pop-bg hover:text-pop-pink hover:scale-110 shadow-sm"
aria-label="Next image"
>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</button>
</>
)}
</div> </div>
)
}
return (
<div className="flex flex-col-reverse md:flex-row items-start gap-4 w-full">
{/* Thumbnails */} {/* Thumbnails */}
{sortedImages.length > 1 && ( <div className="flex flex-row md:flex-col gap-3 overflow-x-auto md:overflow-y-auto w-full md:w-20 lg:w-24 flex-none hide-scrollbar snap-x md:snap-y snap-mandatory pb-2 md:pb-0 md:pr-2">
<div className="flex gap-4 overflow-x-auto pb-2 scrollbar-hide">
{sortedImages.map((image, idx) => ( {sortedImages.map((image, idx) => (
<button <button
key={image.id || idx} key={image.id || idx}
type="button"
onClick={() => setCurrentIndex(idx)} onClick={() => setCurrentIndex(idx)}
className={`relative flex-shrink-0 w-16 h-16 rounded-xl overflow-hidden border-2 transition-all ${ className={`relative aspect-square w-20 md:w-full flex-none overflow-hidden rounded-2xl border-2 transition-all snap-center ${
currentIndex === idx ? 'border-pop-black shadow-md' : 'border-transparent hover:border-pop-black/30' currentIndex === idx ? 'border-pop-pink shadow-md' : 'border-transparent opacity-60 hover:opacity-100'
}`} }`}
> >
<img <img
@@ -85,11 +54,25 @@ export default function ProductImageGallery({ images, fallback, alt = 'Product I
className="w-full h-full object-cover" className="w-full h-full object-cover"
onError={(e) => { (e.target as HTMLImageElement).src = fallback }} onError={(e) => { (e.target as HTMLImageElement).src = fallback }}
/> />
<div className={`absolute inset-0 bg-black/10 transition-opacity ${currentIndex === idx ? 'opacity-0' : 'opacity-100 group-hover:opacity-0'}`} />
</button> </button>
))} ))}
</div> </div>
{/* Main Image */}
<div className="relative aspect-square w-full flex-1 overflow-hidden rounded-3xl bg-pop-bg shadow-sm border border-pop-black/10 flex items-center justify-center">
<img
key={currentIndex}
src={sortedImages[currentIndex].url}
className="w-full h-full object-cover animate-fade-in"
alt={alt}
onError={(e) => { (e.target as HTMLImageElement).src = fallback }}
/>
{badgeText && (
<div className="absolute top-6 left-6 bg-pop-bg backdrop-blur-sm px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-widest text-pop-pink shadow-sm pointer-events-none z-10">
{badgeText}
</div>
)} )}
</div> </div>
</div>
) )
} }
+1 -1
View File
@@ -185,7 +185,7 @@ export default function ProductDetailPage() {
<Navbar categories={categories} /> <Navbar categories={categories} />
<div className="max-w-7xl mx-auto px-6 pt-24 md:pt-32 pb-24 space-y-12 md:space-y-24"> <div className="max-w-7xl mx-auto px-6 pt-24 md:pt-28 pb-24 space-y-8">
{/* Breadcrumb */} {/* Breadcrumb */}
<div className="flex items-center flex-wrap gap-2 text-[10px] md:text-xs text-spa-stone/60 uppercase tracking-widest font-semibold mb-6 md:mb-0"> <div className="flex items-center flex-wrap gap-2 text-[10px] md:text-xs text-spa-stone/60 uppercase tracking-widest font-semibold mb-6 md:mb-0">
<Link to="/" className="hover:text-spa-forest transition-colors">{t('common.home')}</Link> <Link to="/" className="hover:text-spa-forest transition-colors">{t('common.home')}</Link>