"use client" import { Card, CardContent, CardHeader, CardFooter } from "@/components/ui/card" import { Avatar, AvatarFallback } from "@/components/ui/avatar" import { Star, Quote, ChevronLeft, ChevronRight } from "lucide-react" import { useRef, useState, useEffect, useCallback } from "react" type Testimonial = { id: string clientName: string company: string | null content: string rating: number imageUrl: string | null } /* Static fallback used when the API is unreachable (build-time, etc.) */ const fallbackTestimonials: Testimonial[] = [ { id: "fallback-1", clientName: "Sarah Johnson", company: "TechFlow", content: "NexaFlow transformed our online presence. Their attention to detail and technical expertise is unmatched. They delivered exactly what we needed.", rating: 5, imageUrl: null, }, { id: "fallback-2", clientName: "Michael Chen", company: "StartupX", content: "The team delivered our MVP ahead of schedule and it looks fantastic. They truly understood our vision and brought it to life.", rating: 5, imageUrl: null, }, { id: "fallback-3", clientName: "Emily Davis", company: "Creative Studio", content: "Professional, responsive, and incredibly talented. They truly understand modern web design and the latest technologies.", rating: 5, imageUrl: null, }, { id: "fallback-4", clientName: "David Wilson", company: "InnovateCo", content: "Working with NexaFlow was a game-changer for our business. The website they built exceeded our expectations in every way.", rating: 5, imageUrl: null, }, { id: "fallback-5", clientName: "Lisa Anderson", company: "GrowthHub", content: "From concept to launch, the process was smooth and professional. Our new app has received amazing feedback from users.", rating: 5, imageUrl: null, }, ] function getInitials(name: string): string { return name .split(" ") .map((w) => w[0]) .join("") .toUpperCase() .slice(0, 2) } function TestimonialCard({ testimonial, isActive }: { testimonial: Testimonial; isActive: boolean }) { return ( {/* Animated gradient background */}
{/* Quote decoration */}
{[...Array(testimonial.rating)].map((_, j) => ( ))}
“{testimonial.content}”
{getInitials(testimonial.clientName)}

{testimonial.clientName}

{testimonial.company && (

{testimonial.company}

)}
{/* Bottom accent line */}
{/* Shine effect */}
) } export function TestimonialsCarousel() { const [testimonials, setTestimonials] = useState(fallbackTestimonials) const [currentIndex, setCurrentIndex] = useState(0) const [isVisible, setIsVisible] = useState(false) const [isPaused, setIsPaused] = useState(false) const sectionRef = useRef(null) // Fetch testimonials useEffect(() => { const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:5000/api" fetch(`${apiUrl}/testimonials`) .then((res) => res.json()) .then((data: Testimonial[]) => { if (data.length > 0) setTestimonials(data) }) .catch(() => { // Use fallback testimonials }) }, []) // Intersection observer for reveal animation useEffect(() => { const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { setIsVisible(true) } }, { threshold: 0.1 } ) if (sectionRef.current) { observer.observe(sectionRef.current) } return () => observer.disconnect() }, []) // Auto-scroll carousel useEffect(() => { if (isPaused || testimonials.length <= 3) return const interval = setInterval(() => { setCurrentIndex((prev) => (prev + 1) % testimonials.length) }, 5000) return () => clearInterval(interval) }, [isPaused, testimonials.length]) const goToPrevious = useCallback(() => { setCurrentIndex((prev) => (prev - 1 + testimonials.length) % testimonials.length) }, [testimonials.length]) const goToNext = useCallback(() => { setCurrentIndex((prev) => (prev + 1) % testimonials.length) }, [testimonials.length]) const goToSlide = useCallback((index: number) => { setCurrentIndex(index) }, []) // Get visible testimonials for carousel const getVisibleTestimonials = () => { const visible = [] for (let i = 0; i < 3; i++) { const index = (currentIndex + i) % testimonials.length visible.push({ testimonial: testimonials[index], position: i }) } return visible } return (
setIsPaused(true)} onMouseLeave={() => setIsPaused(false)} > {/* Background decorations */}
{/* Floating quote icons */}
{[...Array(6)].map((_, i) => ( ))}
{/* Section header */}
Testimonials

What Our Clients Say

Don't just take our word for it — hear from the businesses we've helped succeed.

{/* Carousel */}
{/* Navigation buttons */} {/* Cards container */}
{getVisibleTestimonials().map(({ testimonial, position }) => (
))}
{/* Dots navigation */}
{testimonials.slice(0, 6).map((_, index) => (
{/* Trust indicator */}

Trusted by 50+ satisfied clients

) }