registry/default/sections/testimonial-2.tsx
"use client";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
import Image from "next/image";
import React from "react";
import { Button } from "@/components/ui/button";
import {
Carousel,
type CarouselApi,
CarouselContent,
CarouselItem,
} from "@/components/ui/carousel";
import { Separator } from "@/components/ui/separator";
const stats = [
{
description: "in build and deploy",
value: "3x Faster",
},
{
description: "in page speeds",
value: "56% increase",
},
{
description: "of average time saved",
value: "+4 year",
},
];
const testimonials = [
{
avatar:
"https://res.cloudinary.com/dvwtcsh5v/image/upload/v1770279333/a1.jpg",
id: 1,
name: "Marc Spector",
quote:
"How do you simplify complex operations for a growing team? Cnippet provided the tools to streamline everything effortlessly.",
role: "Head of Customer Success",
},
{
avatar:
"https://res.cloudinary.com/dvwtcsh5v/image/upload/v1770279333/a2.jpg",
id: 2,
name: "Angela Moss",
quote:
"How do we scale efficiently without losing quality? Beam delivered the infrastructure to grow confidently and sustainably.",
role: "VP of Engineering",
},
{
avatar:
"https://res.cloudinary.com/dvwtcsh5v/image/upload/v1770279333/a3.jpg",
id: 3,
name: "Alex Rivera",
quote:
"Can we achieve faster time-to-market while maintaining security? Cnippet provided the framework to innovate without compromise.",
role: "Product Manager",
},
{
avatar:
"https://res.cloudinary.com/dvwtcsh5v/image/upload/v1770279333/a4.jpg",
id: 4,
name: "Riya Patel",
quote:
"How can we create seamless experiences across all touchpoints? Cnippet provided the design system to unify our entire product.",
role: "Design Director",
},
{
avatar:
"https://res.cloudinary.com/dvwtcsh5v/image/upload/v1770279333/a5.jpg",
id: 5,
name: "David Kim",
quote:
"How do we maintain uptime while shipping continuously? Cnippet provided the reliability tools to deploy with confidence.",
role: "Engineering Lead",
},
];
export default function Component() {
const [api, setApi] = React.useState<CarouselApi | null>(null);
const [_current, setCurrent] = React.useState(0);
React.useEffect(() => {
if (!api) return;
setCurrent(api.selectedScrollSnap());
const onSelect = () => {
setCurrent(api.selectedScrollSnap());
};
api.on("select", onSelect);
return () => {
api.off("select", onSelect);
};
}, [api]);
const scrollPrev = React.useCallback(() => {
api?.scrollPrev();
}, [api]);
const scrollNext = React.useCallback(() => {
api?.scrollNext();
}, [api]);
return (
<section className="w-full bg-white py-12 md:py-24 dark:bg-background">
<div className="mx-auto max-w-4xl px-6 lg:px-0">
<div className="grid grid-cols-1 sm:grid-cols-12">
<div className="relative sm:col-span-10">
<div className="absolute right-0 z-20 h-full w-10 bg-linear-to-l from-white dark:from-background" />
<Carousel
className="w-full"
opts={{ align: "start", loop: true }}
setApi={setApi}
>
<CarouselContent>
{testimonials.map((testimonial) => (
<CarouselItem key={testimonial.id}>
<div className="space-y-12">
<h2 className="relative max-w-4xl font-normal text-4xl text-neutral-900 tracking-tight sm:text-4xl dark:text-neutral-100">
<svg
className="absolute top-1 left-0 -z-10 w-24 max-w-full text-neutral-200 dark:text-neutral-700"
fill="currentColor"
height="86"
viewBox="0 0 108 86"
width="108"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M38.8484 16.236L15 43.5793L78.2688 15L18.1218 71L93 34.1172L70.2047 65.2739"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="28"
/>
</svg>
{testimonial.quote.split("Cnippet provided").length >
1 ? (
<>
{testimonial.quote.split("Cnippet provided")[0]}
<span className="text-gray-400">
Cnippet provided
</span>
{testimonial.quote
.split("Cnippet provided")[1]
.split("streamline").length > 1 ? (
<>
{
testimonial.quote
.split("Cnippet provided")[1]
.split("streamline")[0]
}
<span className="text-gray-400">
streamline
</span>
{
testimonial.quote
.split("Cnippet provided")[1]
.split("streamline")[1]
}
</>
) : (
testimonial.quote.split("Cnippet provided")[1]
)}
</>
) : (
testimonial.quote
)}
</h2>
<div className="flex items-center gap-4">
<Image
alt={testimonial.name}
className="size-14 rounded-full object-cover"
height={400}
src={testimonial.avatar}
width={400}
/>
<div>
<p className="font-semibold text-gray-900 text-lg dark:text-neutral-100">
{testimonial.name}
</p>
<p className="text-gray-600 text-sm dark:text-neutral-400">
{testimonial.role}
</p>
</div>
</div>
</div>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
</div>
<div className="flex items-end justify-end sm:col-span-2">
<div className="flex items-center gap-2">
<Button
className="size-10 rounded-lg border border-blue-600 bg-blue-700 p-0 text-gray-900 hover:bg-blue-600"
onClick={scrollPrev}
size="icon"
variant="ghost"
>
<ChevronLeftIcon className="size-5 text-white" />
</Button>
<Button
className="size-10 rounded-lg border border-blue-600 bg-blue-700 p-0 text-gray-900 hover:bg-blue-600"
onClick={scrollNext}
size="icon"
variant="ghost"
>
<ChevronRightIcon className="size-5 text-white" />
</Button>
</div>
</div>
</div>
<Separator className="my-12" />
<div className="grid grid-cols-1 gap-6 sm:grid-cols-3">
{stats.map((stat) => (
<div
className="rounded-2xl bg-gray-100 p-8 text-center dark:bg-neutral-800"
key={stat.value}
>
<h3 className="mb-3 font-semibold text-4xl text-gray-900 sm:text-4xl! dark:text-neutral-100">
{stat.value}
</h3>
<p className="text-gray-600 text-sm leading-relaxed dark:text-neutral-400">
{stat.description}
</p>
</div>
))}
</div>
</div>
</section>
);
}