Files

293 lines
8.5 KiB
TypeScript

"use client";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { useEffect, useState } from "react";
import { Rating } from "@/components/shadcnblocks/rating";
import { AspectRatio } from "@/components/ui/aspect-ratio";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardTitle,
} from "@/components/ui/card";
import type { CarouselApi } from "@/components/ui/carousel";
import {
Carousel,
CarouselContent,
CarouselItem,
} from "@/components/ui/carousel";
import { cn } from "@/lib/utils";
type ReviewType = {
rate: number;
title: string;
comment?: string;
verifiedBuyer?: boolean;
author: string;
image?: string;
type?: "text" | "image";
};
interface Reviews8Props {
reviews?: ReviewType[];
title?: string;
className?: string;
}
type ReviewCardProps = ReviewType;
const customerRoleLabel = (verified?: boolean) =>
verified ? "خریدار تأییدشده" : "مشتری";
const REVIEWS: ReviewType[] = [
{
rate: 4,
title: "طراحی آگاهانه که حس می‌شود",
comment:
"هر قطعه ظرافت آرامی دارد. معلوم است برای ساخت و جزئیات وقت گذاشته‌اند. ظریف، کاربردی و با کیفیت ساخت عالی.",
author: "لوکا مرادی",
verifiedBuyer: false,
type: "text",
},
{
rate: 5,
title: "مینیمال، ولی پر از شخصیت",
author: "ژوان احمدی",
image:
"/block-media/shadcnblocks/block/ecommerce/clothes/Man-in-White-Hoodie-1.png",
type: "image",
},
{
rate: 4,
title: "تعادل کامل میان سبک و کارایی",
comment:
"دوست دارم همه‌چیز عمدی به نظر برسد؛ نه اضافه، نه کم. این آیتم‌ها بدون شلوغ کردن فضا، به خانه‌ام شخصیت می‌دهند.",
author: "لوکا مرادی",
verifiedBuyer: false,
type: "text",
},
{
rate: 5,
title: "جاودانه و عکاسی زیبا",
author: "ژوان احمدی",
image:
"/block-media/shadcnblocks/block/ecommerce/accessories/Elegant-Jewelry-Portrait-1.png",
type: "image",
},
{
rate: 4,
title: "روزمره را ارتقا می‌دهد",
comment:
"کم پیش می‌آید دکوری پیدا کنید که هم هنری باشد هم کاربردی. این محصولات هم در زندگی روزمره جا می‌گیرند و هم توجه را جلب می‌کنند.",
author: "لوکا مرادی",
verifiedBuyer: false,
type: "text",
},
{
rate: 5,
title: "ظریف، مدرن و خیره‌کننده",
author: "ژوان احمدی",
image:
"/block-media/shadcnblocks/block/ecommerce/accessories/Elegant-Gold-Earrings-1.png",
type: "image",
},
{
rate: 4,
title: "با نیت واقعی ساخته شده",
comment:
"هیچ‌چیز حس تولید انبوه نمی‌دهد. بافت، وزن و پرداخت کار همه گرم و منحصربه‌فردند؛ چیزی که کمتر جایی می‌بینید.",
author: "لوکا مرادی",
verifiedBuyer: false,
type: "text",
},
{
rate: 5,
title: "زیبایی بدون تلاش",
author: "ژوان احمدی",
image:
"/block-media/shadcnblocks/block/ecommerce/accessories/Elegant-Watch-on-Womans-Wrist-1.png",
type: "image",
},
];
const Reviews8 = ({
reviews = REVIEWS,
title = "صداهای واقعی، نظرات صادقانه",
className,
}: Reviews8Props) => {
const [api, setApi] = useState<CarouselApi>();
const [scrollSnaps, setScrollSnaps] = useState<number[]>([]);
const [current, setCurrent] = useState(0);
const onButtonClick = (index: number) => {
if (!api) return;
api.scrollTo(index);
};
useEffect(() => {
if (!api) return;
const updateCurrent = (carouselApi: CarouselApi) => {
setCurrent(carouselApi?.selectedScrollSnap() ?? 0);
};
const updateScrollSnaps = (carouselApi: CarouselApi) => {
setScrollSnaps(carouselApi?.scrollSnapList() ?? []);
};
updateCurrent(api);
updateScrollSnaps(api);
api
.on("reInit", updateCurrent)
.on("select", updateCurrent)
.on("resize", updateScrollSnaps);
return () => {
api
.off("reInit", updateCurrent)
.off("select", updateCurrent)
.off("resize", updateScrollSnaps);
};
}, [api]);
return (
<section
dir="rtl"
lang="fa"
className={cn("overflow-hidden py-32", className)}
>
<div className="container space-y-7.5">
<div className="flex items-start justify-between">
<div className="flex-1">
<h2 className="text-3xl leading-tight tracking-tight md:text-xl lg:text-2xll">
{title}
</h2>
</div>
<div className="flex gap-3 max-md:hidden">
<Button
onClick={() => api?.scrollPrev()}
variant="outline"
className="rounded-full"
size="icon-lg"
aria-label="اسلاید قبلی"
>
<ChevronRight />
</Button>
<Button
onClick={() => api?.scrollNext()}
variant="outline"
className="rounded-full"
size="icon-lg"
aria-label="اسلاید بعدی"
>
<ChevronLeft />
</Button>
</div>
</div>
<div className="space-y-8">
<Carousel
opts={{
align: "start",
direction: "rtl",
}}
setApi={setApi}
className="[&>div]:overflow-visible"
>
<CarouselContent>
{reviews.map(({ type, ...review }, index) => (
<CarouselItem
className="sm:basis-1/2 lg:basis-1/4 xl:basis-1/6"
key={index}
>
{type === "image" ? (
<ImageReviewCard {...review} />
) : (
<TextReviewCard {...review} />
)}
</CarouselItem>
))}
</CarouselContent>
</Carousel>
<div className="flex items-center justify-center gap-4 md:hidden">
{scrollSnaps.map((_, index) => (
<button
type="button"
data-state={current === index ? "active" : "inactive"}
className="h-2.5 w-2.5 rounded-full border transition-all data-[state=active]:w-5 data-[state=active]:border-primary"
key={index}
aria-label={`رفتن به اسلاید ${(index + 1).toLocaleString("fa-IR")}`}
aria-current={current === index ? "true" : undefined}
onClick={() => onButtonClick(index)}
/>
))}
</div>
</div>
</div>
</section>
);
};
const TextReviewCard = ({
rate,
title,
comment,
author,
verifiedBuyer,
}: ReviewCardProps) => {
return (
<Card className="h-full rounded-lg border-primary p-5">
<CardContent className="space-y-5 p-0">
<Rating className="[&_svg]:size-3 [&>div]:size-3" rate={rate} />
<CardTitle className="leading-tight">{title}</CardTitle>
{comment && (
<CardDescription className="leading-relaxed text-foreground">
{comment}
</CardDescription>
)}
<div>
<p className="text-xs">{author}</p>
<p className="text-xs text-muted-foreground">
{customerRoleLabel(verifiedBuyer)}
</p>
</div>
</CardContent>
</Card>
);
};
const ImageReviewCard = ({
image,
title,
rate,
author,
verifiedBuyer,
}: ReviewCardProps) => {
return (
<Card className="h-full border-0 bg-background p-0 shadow-none">
<CardContent className="space-y-5 p-0">
<AspectRatio ratio={1} className="overflow-hidden rounded-lg">
<img
src={image}
alt={`تصویر نظر: ${title}`}
className="block size-full object-cover object-center"
/>
</AspectRatio>
<Rating className="[&_svg]:size-3 [&>div]:size-3" rate={rate} />
<CardTitle className="leading-tight">{title}</CardTitle>
<div>
<p className="text-xs">{author}</p>
<p className="text-xs text-muted-foreground">
{customerRoleLabel(verifiedBuyer)}
</p>
</div>
</CardContent>
</Card>
);
};
export { Reviews8 };