Testimonials
This commit is contained in:
112
latosa-frontend/components/testimonial.tsx
Normal file
112
latosa-frontend/components/testimonial.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
"use client";
|
||||
|
||||
import { Star } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Carousel,
|
||||
CarouselApi,
|
||||
CarouselContent,
|
||||
CarouselItem,
|
||||
} from "@/components/ui/carousel";
|
||||
|
||||
const testimonials = [
|
||||
{
|
||||
id: "testimonial-1",
|
||||
text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Elig doloremque mollitia fugiat omnis! Porro facilis quo animi consequatur. Explicabo.",
|
||||
name: "Customer Name",
|
||||
role: "Position at Company",
|
||||
avatar: "https://shadcnblocks.com/images/block/avatar-1.webp",
|
||||
},
|
||||
{
|
||||
id: "testimonial-2",
|
||||
text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Elig doloremque mollitia fugiat omnis! Porro facilis quo animi consequatur. Explicabo.",
|
||||
name: "Customer Name",
|
||||
role: "Position at Company",
|
||||
avatar: "https://shadcnblocks.com/images/block/avatar-2.webp",
|
||||
},
|
||||
{
|
||||
id: "testimonial-3",
|
||||
text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Elig doloremque mollitia fugiat omnis! Porro facilis quo animi consequatur. Explicabo.",
|
||||
name: "Customer Name",
|
||||
role: "Position at Company",
|
||||
avatar: "https://shadcnblocks.com/images/block/avatar-3.webp",
|
||||
},
|
||||
];
|
||||
|
||||
const Testimonial14 = () => {
|
||||
const [api, setApi] = useState<CarouselApi>();
|
||||
const [current, setCurrent] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (!api) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updateCurrent = () => {
|
||||
setCurrent(api.selectedScrollSnap());
|
||||
};
|
||||
|
||||
api.on("select", updateCurrent);
|
||||
return () => {
|
||||
api.off("select", updateCurrent);
|
||||
};
|
||||
}, [api]);
|
||||
|
||||
return (
|
||||
<section className="py-32">
|
||||
<Carousel setApi={setApi}>
|
||||
<CarouselContent>
|
||||
{testimonials.map((testimonial) => (
|
||||
<CarouselItem key={testimonial.id}>
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<p className="mb-8 max-w-4xl font-medium md:px-8 lg:text-3xl">
|
||||
“{testimonial.text}”
|
||||
</p>
|
||||
<Avatar className="mb-2 size-12 md:size-24">
|
||||
<AvatarImage src={testimonial.avatar} />
|
||||
<AvatarFallback>
|
||||
{testimonial.name}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<p className="mb-1 text-sm font-medium md:text-lg">
|
||||
{testimonial.name}
|
||||
</p>
|
||||
<p className="mb-2 text-sm text-muted-foreground md:text-lg">
|
||||
{testimonial.role}
|
||||
</p>
|
||||
<div className="mt-2 flex items-center gap-0.5">
|
||||
<Star className="size-5 fill-primary stroke-none" />
|
||||
<Star className="size-5 fill-primary stroke-none" />
|
||||
<Star className="size-5 fill-primary stroke-none" />
|
||||
<Star className="size-5 fill-primary stroke-none" />
|
||||
<Star className="size-5 fill-primary stroke-none" />
|
||||
</div>
|
||||
</div>
|
||||
</CarouselItem>
|
||||
))}
|
||||
</CarouselContent>
|
||||
</Carousel>
|
||||
<div className="flex justify-center py-16">
|
||||
{testimonials.map((testimonial, index) => (
|
||||
<Button
|
||||
key={testimonial.id}
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
api?.scrollTo(index);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`size-2.5 rounded-full ${index === current ? "bg-primary" : "bg-input"}`}
|
||||
/>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Testimonial14;
|
||||
50
latosa-frontend/components/ui/avatar.tsx
Normal file
50
latosa-frontend/components/ui/avatar.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Avatar = React.forwardRef<
|
||||
React.ComponentRef<typeof AvatarPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
||||
|
||||
const AvatarImage = React.forwardRef<
|
||||
React.ComponentRef<typeof AvatarPrimitive.Image>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Image
|
||||
ref={ref}
|
||||
className={cn("aspect-square h-full w-full", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
||||
|
||||
const AvatarFallback = React.forwardRef<
|
||||
React.ComponentRef<typeof AvatarPrimitive.Fallback>,
|
||||
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AvatarPrimitive.Fallback
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
||||
|
||||
export { Avatar, AvatarImage, AvatarFallback };
|
||||
Reference in New Issue
Block a user