Files
New-folder/my-app/src/components/TestimonialSlider.tsx
Şahan Hasret 448cc1cc17 master
2025-12-14 00:10:33 +03:00

146 lines
5.6 KiB
TypeScript

"use client";
import React, { useState, useEffect, useCallback } from 'react';
interface Testimonial {
id: number;
name: string;
role: string;
company: string;
content: string;
avatar?: string;
rating: number;
}
interface TestimonialSliderProps {
testimonials: Testimonial[];
autoPlayInterval?: number;
}
export const TestimonialSlider: React.FC<TestimonialSliderProps> = ({
testimonials,
autoPlayInterval = 5000
}) => {
const [currentIndex, setCurrentIndex] = useState(0);
const [isAnimating, setIsAnimating] = useState(false);
const goToNext = useCallback(() => {
if (isAnimating) return;
setIsAnimating(true);
setCurrentIndex((prev) => (prev + 1) % testimonials.length);
setTimeout(() => setIsAnimating(false), 500);
}, [isAnimating, testimonials.length]);
const goToPrev = useCallback(() => {
if (isAnimating) return;
setIsAnimating(true);
setCurrentIndex((prev) => (prev - 1 + testimonials.length) % testimonials.length);
setTimeout(() => setIsAnimating(false), 500);
}, [isAnimating, testimonials.length]);
useEffect(() => {
const interval = setInterval(goToNext, autoPlayInterval);
return () => clearInterval(interval);
}, [goToNext, autoPlayInterval]);
const currentTestimonial = testimonials[currentIndex];
return (
<div className="relative max-w-4xl mx-auto">
{/* Background decoration */}
<div className="absolute -top-4 -left-4 text-8xl text-zsl-primary/10 font-serif select-none">"</div>
<div className="absolute -bottom-4 -right-4 text-8xl text-zsl-primary/10 font-serif rotate-180 select-none">"</div>
{/* Main card */}
<div className="relative bg-zsl-card/80 backdrop-blur-md border border-zsl-primary/20 rounded-2xl p-8 md:p-12">
{/* Content */}
<div className={`transition-all duration-500 ${isAnimating ? 'opacity-0 translate-y-4' : 'opacity-100 translate-y-0'}`}>
{/* Stars */}
<div className="flex gap-1 mb-6">
{[...Array(5)].map((_, i) => (
<svg
key={i}
className={`w-5 h-5 ${i < currentTestimonial.rating ? 'text-zsl-accent' : 'text-zsl-muted/30'}`}
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
))}
</div>
{/* Quote */}
<p className="text-lg md:text-xl text-white leading-relaxed mb-8">
"{currentTestimonial.content}"
</p>
{/* Author */}
<div className="flex items-center gap-4">
{/* Avatar */}
<div className="w-14 h-14 rounded-full bg-linear-to-br from-zsl-primary to-zsl-accent p-0.5">
<div className="w-full h-full rounded-full bg-zsl-card flex items-center justify-center text-xl font-bold text-zsl-primary">
{currentTestimonial.name.charAt(0)}
</div>
</div>
{/* Info */}
<div>
<div className="font-semibold text-white">{currentTestimonial.name}</div>
<div className="text-sm text-zsl-muted">
{currentTestimonial.role} {currentTestimonial.company}
</div>
</div>
</div>
</div>
{/* Navigation */}
<div className="flex items-center justify-between mt-8 pt-6 border-t border-zsl-primary/10">
{/* Arrows */}
<div className="flex gap-2">
<button
onClick={goToPrev}
className="w-10 h-10 rounded-full border border-zsl-primary/30 flex items-center justify-center text-zsl-primary hover:bg-zsl-primary hover:text-zsl-bg transition-all duration-300"
aria-label="Previous testimonial"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
</button>
<button
onClick={goToNext}
className="w-10 h-10 rounded-full border border-zsl-primary/30 flex items-center justify-center text-zsl-primary hover:bg-zsl-primary hover:text-zsl-bg transition-all duration-300"
aria-label="Next testimonial"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</button>
</div>
{/* Dots */}
<div className="flex gap-2">
{testimonials.map((_, index) => (
<button
key={index}
onClick={() => {
if (!isAnimating) {
setIsAnimating(true);
setCurrentIndex(index);
setTimeout(() => setIsAnimating(false), 500);
}
}}
className={`w-2 h-2 rounded-full transition-all duration-300 ${
index === currentIndex
? 'bg-zsl-primary w-6'
: 'bg-zsl-muted/30 hover:bg-zsl-muted/50'
}`}
aria-label={`Go to testimonial ${index + 1}`}
/>
))}
</div>
</div>
</div>
</div>
);
};