feat: Add second transformation with side-by-side sliders

- First transformation: use_case_2 (4-6 weeks)
- Second transformation: use_case_3 (6-8 weeks)
- Both sliders displayed side by side on same line
This commit is contained in:
Unchained
2026-03-22 08:32:22 +02:00
parent 9e901d7dfe
commit bf6362d3ad

View File

@@ -13,9 +13,18 @@ const results = [
rating: 5, rating: 5,
reviewCount: 2847, reviewCount: 2847,
}, },
{
id: 2,
name: "Skin Radiance Transformation",
beforeImg: "https://minio-api.nodecrew.me/saleor/marketing/use_case_3.webp",
afterImg: "https://minio-api.nodecrew.me/saleor/marketing/use_case_3_1.webp",
timeline: "6-8 Weeks",
rating: 5,
reviewCount: 1856,
},
]; ];
export default function BeforeAfterGallery() { function BeforeAfterSlider({ result }: { result: typeof results[0] }) {
const [sliderPosition, setSliderPosition] = useState(50); const [sliderPosition, setSliderPosition] = useState(50);
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
@@ -33,6 +42,89 @@ export default function BeforeAfterGallery() {
setSliderPosition(Math.max(0, Math.min(100, x))); setSliderPosition(Math.max(0, Math.min(100, x)));
}; };
return (
<div className="flex-1 min-w-0">
{/* Before/After Slider */}
<div
ref={containerRef}
className="relative aspect-[4/3] rounded-2xl overflow-hidden shadow-2xl cursor-ew-resize select-none"
onMouseMove={handleMouseMove}
onTouchMove={handleTouchMove}
>
{/* After Image */}
<img
src={result.afterImg}
alt="After - Smooth skin"
className="absolute inset-0 w-full h-full object-cover"
/>
{/* Before Image (clipped) */}
<div
className="absolute inset-0 overflow-hidden"
style={{ width: `${sliderPosition}%` }}
>
<img
src={result.beforeImg}
alt="Before - Wrinkled skin"
className="absolute inset-0 h-full object-cover"
style={{ width: `${100 / (sliderPosition / 100)}%`, maxWidth: 'none' }}
/>
</div>
{/* Slider Handle */}
<div
className="absolute top-0 bottom-0 w-1 bg-white shadow-lg cursor-ew-resize"
style={{ left: `${sliderPosition}%`, transform: 'translateX(-50%)' }}
>
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-10 h-10 bg-white rounded-full shadow-lg flex items-center justify-center">
<svg className="w-5 h-5 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
</svg>
</div>
</div>
{/* Labels */}
<div className="absolute top-3 left-3 bg-black/70 text-white px-3 py-1.5 rounded-full text-xs font-medium backdrop-blur-sm">
BEFORE
</div>
<div className="absolute top-3 right-3 bg-black/70 text-white px-3 py-1.5 rounded-full text-xs font-medium backdrop-blur-sm">
AFTER
</div>
</div>
{/* Timeline and Rating */}
<div className="flex items-center justify-center gap-4 mt-4">
<div className="flex items-center gap-1.5">
<svg className="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="text-xs font-medium">{result.timeline}</span>
</div>
<div className="flex items-center gap-1.5">
<div className="flex">
{[1, 2, 3, 4, 5].map((star) => (
<svg key={star} className="w-4 h-4 fill-gold text-gold" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
</svg>
))}
</div>
<span className="text-xs text-[#666666]">({result.reviewCount.toLocaleString()})</span>
</div>
</div>
{/* Verified Badge */}
<div className="flex items-center justify-center gap-1.5 mt-2">
<svg className="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
<span className="text-xs text-green-700 font-medium">Verified</span>
</div>
</div>
);
}
export default function BeforeAfterGallery() {
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">
@@ -51,106 +143,25 @@ export default function BeforeAfterGallery() {
</h2> </h2>
</motion.div> </motion.div>
<div className="max-w-4xl mx-auto"> {/* Two transformations side by side */}
{/* Before/After Slider */} <div className="flex gap-6 max-w-6xl mx-auto">
{results.map((result, index) => (
<motion.div <motion.div
ref={containerRef} key={result.id}
className="relative aspect-[4/3] rounded-2xl overflow-hidden shadow-2xl cursor-ew-resize select-none" initial={{ opacity: 0, y: 30 }}
onMouseMove={handleMouseMove}
onTouchMove={handleTouchMove}
initial={{ opacity: 0, scale: 0.98 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
>
{/* After Image (full width, underneath) */}
<img
src={results[0].afterImg}
alt="After - Smooth skin"
className="absolute inset-0 w-full h-full object-cover"
/>
{/* Before Image (clipped) */}
<div
className="absolute inset-0 overflow-hidden"
style={{ width: `${sliderPosition}%` }}
>
<img
src={results[0].beforeImg}
alt="Before - Wrinkled skin"
className="absolute inset-0 h-full object-cover"
style={{ width: `${100 / (sliderPosition / 100)}%`, maxWidth: 'none' }}
/>
</div>
{/* Slider Handle */}
<div
className="absolute top-0 bottom-0 w-1 bg-white shadow-lg cursor-ew-resize"
style={{ left: `${sliderPosition}%`, transform: 'translateX(-50%)' }}
>
{/* Handle Circle */}
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-12 h-12 bg-white rounded-full shadow-lg flex items-center justify-center">
<svg className="w-6 h-6 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
</svg>
</div>
</div>
{/* Before Label */}
<div className="absolute top-4 left-4 bg-black/70 text-white px-4 py-2 rounded-full text-sm font-medium backdrop-blur-sm">
BEFORE
</div>
{/* After Label */}
<div className="absolute top-4 right-4 bg-black/70 text-white px-4 py-2 rounded-full text-sm font-medium backdrop-blur-sm">
AFTER
</div>
</motion.div>
{/* Timeline and Rating */}
<motion.div
className="flex items-center justify-center gap-8 mt-8"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.2 }} transition={{ duration: 0.6, delay: index * 0.1 }}
className="flex-1 min-w-0"
> >
<div className="flex items-center gap-2"> <BeforeAfterSlider result={result} />
<svg className="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> </motion.div>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="text-sm font-medium">{results[0].timeline}</span>
</div>
<div className="flex items-center gap-2">
<div className="flex">
{[1, 2, 3, 4, 5].map((star) => (
<svg key={star} className="w-5 h-5 fill-gold text-gold" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
</svg>
))} ))}
</div> </div>
<span className="text-sm text-[#666666]">({results[0].reviewCount.toLocaleString()} reviews)</span>
</div>
</motion.div>
{/* Verified Results Badge */}
<motion.div
className="flex items-center justify-center gap-2 mt-4"
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.3 }}
>
<svg className="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
<span className="text-sm text-green-700 font-medium">Verified Results</span>
</motion.div>
{/* CTA */} {/* CTA */}
<motion.div <motion.div
className="text-center mt-10" className="text-center mt-12"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
@@ -164,7 +175,6 @@ export default function BeforeAfterGallery() {
</a> </a>
</motion.div> </motion.div>
</div> </div>
</div>
</section> </section>
); );
} }