Blookie
Get Started

  1. Blocks
  2. testimonials
  3. 4
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import type { CarouselApi } from '@/components/ui/carousel';
import { Carousel, CarouselContent, CarouselItem } from '@/components/ui/carousel';
import { ArrowLeft, ArrowRight, Star } from 'lucide-react';
import { useCallback, useState } from 'react';
 
interface Testimonial {
    id: number;
    name: string;
    role: string;
    avatar: string;
    rating: number;
    feedback: string;
}
 
const testimonials: Testimonial[] = [
    {
        id: 1,
        name: 'Sarah Chen',
        role: 'Marketing Director',
        avatar: 'https://blookie.io/stock/person1.webp',
        rating: 5,
        feedback: 'This platform has completely transformed how we manage our campaigns. The analytics are great and actionable.',
    },
    {
        id: 2,
        name: 'Michael Rodriguez',
        role: 'Software Engineer',
        avatar: 'https://blookie.io/stock/person2.webp',
        rating: 4,
        feedback: 'Clean, efficient, and well-documented. Integration was seamless and the API is really developer-friendly.',
    },
    {
        id: 3,
        name: 'David Park',
        role: 'Operations Manager',
        avatar: 'https://blookie.io/stock/person3.webp',
        rating: 5,
        feedback: 'Outstanding customer support and the automation features have saved us countless hours every week.',
    },
    {
        id: 4,
        name: 'Emily Thompson',
        role: 'Creative Director',
        avatar: 'https://blookie.io/stock/person4.webp',
        rating: 4,
        feedback: 'The design tools are intuitive and powerful. Our team productivity has increased significantly.',
    },
];
 
export default function TestimonialCarousel() {
    const [api, setApi] = useState<CarouselApi>();
 
    const scrollPrev = useCallback(() => {
        api?.scrollPrev();
    }, [api]);
 
    const scrollNext = useCallback(() => {
        api?.scrollNext();
    }, [api]);
 
    return (
        <section className="py-16 lg:py-32">
            <div className="mx-auto w-full max-w-2xl px-6 lg:max-w-7xl">
                <div className="grid grid-cols-1 items-center gap-12 lg:grid-cols-3">
                    <div className="text-center lg:text-left">
                        <h2 className="text-3xl/tight font-semibold tracking-tight sm:text-4xl/tight">Our Customers Gave Their Feedback</h2>
                        <div className="mt-6 space-x-4">
                            <Button onClick={scrollPrev} variant="outline" size="icon">
                                <ArrowLeft />
                            </Button>
                            <Button onClick={scrollNext} variant="outline" size="icon">
                                <ArrowRight />
                            </Button>
                        </div>
                    </div>
 
                    <div className="lg:col-span-2 lg:ml-auto lg:max-w-2xl">
                        <Carousel
                            setApi={setApi}
                            opts={{
                                align: 'start',
                                loop: true,
                                dragFree: true,
                            }}
                            className="w-full"
                        >
                            <CarouselContent className="-ml-2 sm:-ml-4">
                                {testimonials.map((testimonial: Testimonial) => (
                                    <CarouselItem key={testimonial.id} className="basis-full pl-2 sm:pl-4 md:basis-1/2">
                                        <Card className="hover:border-primary h-full transition-all duration-300 hover:shadow-md">
                                            <CardContent>
                                                <div className="flex items-center gap-4">
                                                    <Avatar className="size-10">
                                                        <AvatarImage src={testimonial.avatar || '/placeholder.svg'} alt={testimonial.name} />
                                                        <AvatarFallback className="text-sm sm:text-base">{testimonial.name.charAt(0)}</AvatarFallback>
                                                    </Avatar>
                                                    <div className="flex flex-col gap-0.5">
                                                        <span className="text-sm font-semibold">{testimonial.name}</span>
                                                        <span className="text-muted-foreground text-xs">{testimonial.role}</span>
                                                    </div>
                                                </div>
                                                <div className="mt-4 flex items-center gap-2">
                                                    {[...Array(5)].map((_, i) => (
                                                        <Star
                                                            key={i}
                                                            className={`size-4 ${
                                                                i < testimonial.rating ? 'fill-amber-400 text-amber-400' : 'text-gray-300'
                                                            }`}
                                                        />
                                                    ))}
                                                </div>
                                                <p className="text-muted-foreground mt-4 text-sm/6">{testimonial.feedback}</p>
                                            </CardContent>
                                        </Card>
                                    </CarouselItem>
                                ))}
                            </CarouselContent>
                        </Carousel>
                    </div>
                </div>
            </div>
        </section>
    );
}