Compare commits
5 Commits
8a418be7c3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e9ad28dcf | ||
|
|
70d6cfc9a7 | ||
|
|
f3d60d3c5b | ||
|
|
7ecd9c2e22 | ||
|
|
e9b95c44b9 |
@@ -125,6 +125,16 @@ function BeforeAfterSlider({ result }: { result: typeof results[0] }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function BeforeAfterGallery() {
|
export default function BeforeAfterGallery() {
|
||||||
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||||
|
|
||||||
|
const goToPrev = () => {
|
||||||
|
setSelectedIndex(prev => prev === 0 ? results.length - 1 : prev - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToNext = () => {
|
||||||
|
setSelectedIndex(prev => prev === results.length - 1 ? 0 : prev + 1);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="py-24 bg-[#faf9f7]">
|
<section className="py-24 bg-[#faf9f7]">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
@@ -143,8 +153,8 @@ export default function BeforeAfterGallery() {
|
|||||||
</h2>
|
</h2>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Two transformations side by side */}
|
{/* Desktop: Two transformations side by side */}
|
||||||
<div className="flex gap-6 max-w-6xl mx-auto">
|
<div className="hidden md:flex gap-6 max-w-6xl mx-auto">
|
||||||
{results.map((result, index) => (
|
{results.map((result, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={result.id}
|
key={result.id}
|
||||||
@@ -159,6 +169,55 @@ export default function BeforeAfterGallery() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile: Carousel with one transformation at a time */}
|
||||||
|
<div className="md:hidden relative max-w-md mx-auto">
|
||||||
|
<div className="overflow-hidden">
|
||||||
|
<motion.div
|
||||||
|
key={selectedIndex}
|
||||||
|
initial={{ opacity: 0, x: 20 }}
|
||||||
|
animate={{ opacity: 1, x: 0 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
>
|
||||||
|
<BeforeAfterSlider result={results[selectedIndex]} />
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Carousel Navigation */}
|
||||||
|
<button
|
||||||
|
onClick={goToPrev}
|
||||||
|
className="absolute left-0 top-1/2 -translate-y-1/2 -translate-x-2 w-10 h-10 bg-white rounded-full shadow-lg flex items-center justify-center"
|
||||||
|
aria-label="Previous transformation"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5 text-gray-700" 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={goToNext}
|
||||||
|
className="absolute right-0 top-1/2 -translate-y-1/2 translate-x-2 w-10 h-10 bg-white rounded-full shadow-lg flex items-center justify-center"
|
||||||
|
aria-label="Next transformation"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5 text-gray-700" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Dot Indicators */}
|
||||||
|
<div className="flex justify-center gap-2 mt-6">
|
||||||
|
{results.map((_, index) => (
|
||||||
|
<button
|
||||||
|
key={index}
|
||||||
|
onClick={() => setSelectedIndex(index)}
|
||||||
|
className={`w-2 h-2 rounded-full transition-all ${
|
||||||
|
selectedIndex === index ? "bg-black w-4" : "bg-gray-300"
|
||||||
|
}`}
|
||||||
|
aria-label={`Go to transformation ${index + 1}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* CTA */}
|
{/* CTA */}
|
||||||
<motion.div
|
<motion.div
|
||||||
className="text-center mt-12"
|
className="text-center mt-12"
|
||||||
|
|||||||
@@ -13,26 +13,8 @@ export default function HeroVideo() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative h-screen w-full overflow-hidden">
|
<section className="relative min-h-screen w-full overflow-hidden">
|
||||||
{/* Video Background */}
|
{/* Background Image with Overlay */}
|
||||||
<div className="absolute inset-0">
|
|
||||||
<video
|
|
||||||
autoPlay
|
|
||||||
muted
|
|
||||||
loop
|
|
||||||
playsInline
|
|
||||||
poster="/images/hero-poster.jpg"
|
|
||||||
className="w-full h-full object-cover"
|
|
||||||
>
|
|
||||||
{/* Placeholder - Add actual video files when available */}
|
|
||||||
{/* <source src="/videos/hero.webm" type="video/webm" /> */}
|
|
||||||
{/* <source src="/videos/hero.mp4" type="video/mp4" /> */}
|
|
||||||
</video>
|
|
||||||
{/* Gradient Overlay */}
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-b from-black/40 via-black/30 to-black/60" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Fallback Background (shown when video isn't loaded) */}
|
|
||||||
<div
|
<div
|
||||||
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
|
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
|
||||||
style={{
|
style={{
|
||||||
@@ -43,7 +25,7 @@ export default function HeroVideo() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="relative z-10 h-full flex flex-col items-center justify-center text-center text-white px-4">
|
<div className="relative z-10 min-h-screen flex flex-col items-center justify-center text-center text-white px-4 py-20">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
|||||||
@@ -27,14 +27,13 @@ export default function ProductCard({ product, index = 0, locale = "SR" }: Produ
|
|||||||
>
|
>
|
||||||
<Link href={`/products/${localized.slug}`} className="group block">
|
<Link href={`/products/${localized.slug}`} className="group block">
|
||||||
{/* Image Container */}
|
{/* Image Container */}
|
||||||
<div className="relative aspect-square bg-[#f8f9fa] overflow-hidden mb-4">
|
<div className="relative w-full aspect-square bg-[#f8f9fa] overflow-hidden mb-4">
|
||||||
{image ? (
|
{image ? (
|
||||||
<Image
|
<img
|
||||||
src={image}
|
src={image}
|
||||||
alt={localized.name}
|
alt={localized.name}
|
||||||
fill
|
className="w-full h-full object-cover object-center transition-transform duration-700 ease-out group-hover:scale-105"
|
||||||
className="object-cover transition-transform duration-700 ease-out group-hover:scale-105"
|
loading="lazy"
|
||||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw"
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="absolute inset-0 flex items-center justify-center text-[#999999]">
|
<div className="absolute inset-0 flex items-center justify-center text-[#999999]">
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ export default function ProductDetail({ product, relatedProducts, locale = "SR"
|
|||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
transition={{ duration: 0.6 }}
|
transition={{ duration: 0.6 }}
|
||||||
className="flex gap-4"
|
className="flex flex-col md:flex-row gap-4"
|
||||||
>
|
>
|
||||||
{/* Thumbnails - Vertical on Desktop, Hidden on Mobile */}
|
{/* Thumbnails - Vertical on Desktop, Hidden on Mobile */}
|
||||||
{images.length > 1 && (
|
{images.length > 1 && (
|
||||||
@@ -167,29 +167,63 @@ export default function ProductDetail({ product, relatedProducts, locale = "SR"
|
|||||||
: "border-transparent hover:border-[#999999]"
|
: "border-transparent hover:border-[#999999]"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Image
|
<img
|
||||||
src={image.url}
|
src={image.url}
|
||||||
alt={image.alt || localized.name}
|
alt={image.alt || localized.name}
|
||||||
fill
|
className="w-full h-full object-cover"
|
||||||
className="object-cover"
|
|
||||||
sizes="80px"
|
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Main Image */}
|
{/* Main Image */}
|
||||||
<div className="relative aspect-square bg-[#f8f9fa] overflow-hidden">
|
<div className="relative w-full aspect-square bg-[#f8f9fa] overflow-hidden flex-1">
|
||||||
{images[selectedImage] && (
|
<img
|
||||||
<Image
|
src={images[selectedImage].url}
|
||||||
src={images[selectedImage].url}
|
alt={images[selectedImage].alt || localized.name}
|
||||||
alt={images[selectedImage].alt || localized.name}
|
className="w-full h-full object-cover"
|
||||||
fill
|
/>
|
||||||
className="object-cover"
|
|
||||||
priority
|
{/* Carousel Navigation - Mobile Only */}
|
||||||
sizes="(max-width: 1024px) 100vw, 50vw"
|
{images.length > 1 && (
|
||||||
/>
|
<>
|
||||||
|
{/* Left Arrow */}
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedImage(prev => prev === 0 ? images.length - 1 : prev - 1)}
|
||||||
|
className="absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 bg-white/80 hover:bg-white rounded-full flex items-center justify-center shadow-md transition-all hover:scale-110 md:hidden"
|
||||||
|
aria-label="Previous image"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5 text-gray-700" 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>
|
||||||
|
|
||||||
|
{/* Right Arrow */}
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedImage(prev => prev === images.length - 1 ? 0 : prev + 1)}
|
||||||
|
className="absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 bg-white/80 hover:bg-white rounded-full flex items-center justify-center shadow-md transition-all hover:scale-110 md:hidden"
|
||||||
|
aria-label="Next image"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5 text-gray-700" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Dot Indicators - Mobile Only */}
|
||||||
|
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2 md:hidden">
|
||||||
|
{images.map((_, index) => (
|
||||||
|
<button
|
||||||
|
key={index}
|
||||||
|
onClick={() => setSelectedImage(index)}
|
||||||
|
className={`w-2 h-2 rounded-full transition-all ${
|
||||||
|
selectedImage === index ? "bg-white w-4" : "bg-white/50"
|
||||||
|
}`}
|
||||||
|
aria-label={`Go to image ${index + 1}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
Reference in New Issue
Block a user