215 lines
7.0 KiB
TypeScript
215 lines
7.0 KiB
TypeScript
"use client";
|
||
|
||
import { Check, X as XIcon } from "lucide-react";
|
||
|
||
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
||
import { Badge } from "@/components/ui/badge";
|
||
import { Button } from "@/components/ui/button";
|
||
import {
|
||
Table,
|
||
TableBody,
|
||
TableCell,
|
||
TableHead,
|
||
TableHeader,
|
||
TableRow,
|
||
} from "@/components/ui/table";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
interface ProductSpec {
|
||
name: string;
|
||
values: (string | boolean)[];
|
||
}
|
||
|
||
interface Product {
|
||
id: string;
|
||
name: string;
|
||
image: string;
|
||
price: number;
|
||
originalPrice?: number;
|
||
rating: number;
|
||
badge?: string;
|
||
}
|
||
|
||
interface CompareProducts1Props {
|
||
products?: Product[];
|
||
specs?: ProductSpec[];
|
||
className?: string;
|
||
}
|
||
|
||
const DEFAULT_PRODUCTS: Product[] = [
|
||
{
|
||
id: "1",
|
||
name: "هدفون بیسیم پرو",
|
||
image:
|
||
"/block-media/shadcnblocks/block/ecommerce/electronics/Modern-White-Headphones-1.png",
|
||
price: 299,
|
||
rating: 4.8,
|
||
},
|
||
{
|
||
id: "2",
|
||
name: "هدفون استودیویی پلاس",
|
||
image:
|
||
"/block-media/shadcnblocks/block/ecommerce/electronics/Sleek-Black-Headphones-1.jpeg",
|
||
price: 249,
|
||
originalPrice: 279,
|
||
rating: 4.5,
|
||
},
|
||
{
|
||
id: "3",
|
||
name: "هدفون سری پایه",
|
||
image:
|
||
"/block-media/shadcnblocks/block/ecommerce/electronics/Modern-Headphones-1.jpeg",
|
||
price: 149,
|
||
rating: 4.2,
|
||
},
|
||
];
|
||
|
||
const DEFAULT_SPECS: ProductSpec[] = [
|
||
{ name: "اندازه درایور", values: ["۴۰ میلیمتر", "۵۰ میلیمتر", "۴۰ میلیمتر"] },
|
||
{
|
||
name: "پاسخ فرکانسی",
|
||
values: ["۲۰Hz–۲۰kHz", "۱۰Hz–۴۰kHz", "۲۰Hz–۲۰kHz"],
|
||
},
|
||
{ name: "حذف نویز فعال", values: [true, true, false] },
|
||
{ name: "عمر باتری", values: ["۳۰ ساعت", "۲۴ ساعت", "۲۰ ساعت"] },
|
||
{ name: "بیسیم", values: [true, true, true] },
|
||
{ name: "نسخه بلوتوث", values: ["۵٫۳", "۵٫۲", "۵٫۰"] },
|
||
{ name: "طراحی تاشو", values: [true, false, true] },
|
||
{ name: "وزن", values: ["۲۵۰ گرم", "۲۸۰ گرم", "۲۲۰ گرم"] },
|
||
{ name: "کیف همراه", values: [true, true, false] },
|
||
{ name: "گارانتی", values: ["۲ سال", "۲ سال", "۱ سال"] },
|
||
];
|
||
|
||
const formatPrice = (price: number) =>
|
||
new Intl.NumberFormat("fa-IR", {
|
||
style: "currency",
|
||
currency: "IRR",
|
||
maximumFractionDigits: 0,
|
||
}).format(price);
|
||
|
||
const formatRating = (rating: number) =>
|
||
rating.toLocaleString("fa-IR", {
|
||
minimumFractionDigits: 1,
|
||
maximumFractionDigits: 1,
|
||
});
|
||
|
||
const CompareProducts1 = ({
|
||
products = DEFAULT_PRODUCTS,
|
||
specs = DEFAULT_SPECS,
|
||
className,
|
||
}: CompareProducts1Props) => {
|
||
const renderValue = (value: string | boolean) => {
|
||
if (typeof value === "boolean") {
|
||
return value ? (
|
||
<Check className="size-5 text-emerald-600" aria-label="دارد" />
|
||
) : (
|
||
<XIcon className="size-5 text-muted-foreground" aria-label="ندارد" />
|
||
);
|
||
}
|
||
return value;
|
||
};
|
||
|
||
return (
|
||
<section
|
||
dir="rtl"
|
||
lang="fa"
|
||
className={cn("py-16 text-start md:py-24", className)}
|
||
>
|
||
<div className="container">
|
||
<div className="mb-8 text-center">
|
||
<h2 className="text-xl font-bold tracking-tight md:text-3xl">
|
||
مقایسه محصولات
|
||
</h2>
|
||
<p className="mt-2 text-muted-foreground">
|
||
ویژگیها، مشخصات فنی و تفاوت محصولات را مقایسه کنید و بهترین انتخاب را داشته باشید.
|
||
</p>
|
||
</div>
|
||
|
||
<div className="overflow-x-auto">
|
||
<Table>
|
||
<TableHeader>
|
||
<TableRow className="border-b-0 hover:bg-transparent">
|
||
<TableHead className="w-48 border-e" />
|
||
{products.map((product, index) => (
|
||
<TableHead
|
||
key={product.id}
|
||
className={cn(
|
||
"min-w-[200px] text-center",
|
||
index < products.length - 1 && "border-e",
|
||
)}
|
||
>
|
||
<div className="pb-4">
|
||
<div className="mx-auto size-48 overflow-hidden rounded-lg bg-muted">
|
||
<AspectRatio ratio={1}>
|
||
<img
|
||
src={product.image}
|
||
alt={product.name}
|
||
className="size-full object-cover"
|
||
/>
|
||
</AspectRatio>
|
||
</div>
|
||
<div className="-mt-3 flex h-6 items-center justify-center">
|
||
{product.badge && (
|
||
<Badge className="mx-auto">{product.badge}</Badge>
|
||
)}
|
||
</div>
|
||
<h3 className="mt-2 text-lg leading-tight font-semibold">
|
||
{product.name}
|
||
</h3>
|
||
<div className="flex min-h-[1.5rem] items-center justify-center gap-2">
|
||
<span className="text-lg">
|
||
{formatPrice(product.price)}
|
||
</span>
|
||
{product.originalPrice && (
|
||
<span className="text-sm text-muted-foreground line-through">
|
||
{formatPrice(product.originalPrice)}
|
||
</span>
|
||
)}
|
||
</div>
|
||
<div className="flex items-center justify-center gap-1">
|
||
<span className="text-amber-500" aria-hidden="true">
|
||
★
|
||
</span>
|
||
<span className="text-sm">
|
||
{formatRating(product.rating)}
|
||
</span>
|
||
</div>
|
||
<Button className="mt-4 w-full" size="sm">
|
||
افزودن به سبد
|
||
</Button>
|
||
</div>
|
||
</TableHead>
|
||
))}
|
||
</TableRow>
|
||
</TableHeader>
|
||
<TableBody>
|
||
{specs.map((spec, index) => (
|
||
<TableRow key={index}>
|
||
<TableCell className="border-e font-medium">
|
||
{spec.name}
|
||
</TableCell>
|
||
{spec.values.map((value, vIndex) => (
|
||
<TableCell
|
||
key={vIndex}
|
||
className={cn(
|
||
"text-center",
|
||
vIndex < spec.values.length - 1 && "border-e",
|
||
)}
|
||
>
|
||
<div className="flex items-center justify-center">
|
||
{renderValue(value)}
|
||
</div>
|
||
</TableCell>
|
||
))}
|
||
</TableRow>
|
||
))}
|
||
</TableBody>
|
||
</Table>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
};
|
||
|
||
export { CompareProducts1 };
|