Files
reui-blocks/blocks/product-list1.tsx
T

202 lines
4.9 KiB
TypeScript

import { AspectRatio } from "@/components/ui/aspect-ratio";
import { Badge } from "@/components/ui/badge";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { cn } from "@/lib/utils";
interface ProductPrice {
regular: number;
sale?: number;
currency: string;
}
interface Product {
name: string;
image: {
src: string;
alt: string;
};
link: string;
description: string;
price: ProductPrice;
badge?: {
text: string;
color?: string;
};
}
type ProductCardProps = Product;
type ProductList = Array<Product>;
const PRODUCTS_LIST: ProductList = [
{
name: "وکسون کور‌استپ ۰۸ ال‌ایکس",
image: {
src: "/block-media/shadcnblocks/block/ecommerce/clothes/joshua-diaz-ETNoDLl8yFE-unsplash-1.jpg",
alt: "کفش ورزشی با طراحی سه‌رنگ",
},
link: "#",
description:
"راحتی روزمره در کنار استایل جسورانه سه‌رنگ، در طراحی عمل‌گرایانه.",
price: {
regular: 499000.0,
sale: 399000.0,
currency: "ریال",
},
badge: {
text: "دارد سریع فروش می‌رود!",
color: "oklch(57.7% 0.245 27.325)",
},
},
{
name: "جاکت شهری راحت",
image: {
src: "/block-media/shadcnblocks/block/ecommerce/clothes/Man-in-Hoodie-&-Jacket-2.png",
alt: "جاکت پفی جین با طیف آبی",
},
link: "#",
description:
"پفی جین با طیف آبی ملایم، مناسب لایه‌لایه پوشیدن در همه فصل‌ها.",
price: {
regular: 180000.0,
currency: "ریال",
},
},
{
name: "کیف مزون لیورا",
image: {
src: "/block-media/shadcnblocks/block/ecommerce/clothes/Woman-with-Tote-Bag-1.png",
alt: "کیف زنانه توت‌بگ",
},
link: "#",
description:
"کیفی شیک که به‌راحتی از حالت شانه‌ای به کراس‌بادی یا دسته‌دار تبدیل می‌شود.",
price: {
regular: 420000.0,
currency: "ریال",
},
badge: {
text: "جدید",
},
},
];
interface ProductList1Props {
className?: string;
}
const ProductList1 = ({ className }: ProductList1Props) => {
return (
<section className={cn("py-32", className)}>
<div className="container">
<div className="grid place-items-center gap-6 md:grid-cols-2 xl:grid-cols-3">
{PRODUCTS_LIST.map((item, index) => (
<ProductCard key={`product-list-1-card-${index}`} {...item} />
))}
</div>
</div>
</section>
);
};
const ProductCard = ({
name,
description,
link,
image,
badge,
price,
}: ProductCardProps) => {
const { regular, sale, currency } = price;
return (
<a
href={link}
className="block h-full w-full max-w-md transition-opacity hover:opacity-80"
>
<Card className="h-full overflow-hidden p-0">
<CardHeader className="relative block p-0">
<AspectRatio ratio={1.268115942} className="overflow-hidden">
<img
src={image.src}
alt={image.alt}
className="block size-full object-cover object-center"
/>
</AspectRatio>
{badge && (
<Badge
style={{
backgroundColor: badge.color,
}}
className="absolute start-4 top-4"
>
{badge.text}
</Badge>
)}
</CardHeader>
<CardContent className="flex h-full flex-col gap-4 pb-6">
<CardTitle className="text-xl font-semibold">{name}</CardTitle>
<CardDescription className="font-medium text-muted-foreground">
{description}
</CardDescription>
<div className="mt-auto">
<ProductCardPrice
regular={regular}
sale={sale}
currency={currency}
/>
</div>
</CardContent>
</Card>
</a>
);
};
const formatCurrency = (
value: number,
currency: string = "ریال",
locale: string = "fa-IR",
) => {
return `${value.toLocaleString(locale)} ${currency}`;
};
const ProductCardPrice = ({
regular,
sale,
currency,
}: {
regular: number;
sale?: number;
currency: string;
}) => {
const onSale = sale != null;
return (
<div className="flex flex-wrap items-center gap-x-2 text-lg font-semibold">
{onSale ? (
<span className="leading-tight text-foreground">
{formatCurrency(sale, currency)}
</span>
) : null}
<span
className={cn(
"leading-tight",
onSale
? "text-muted-foreground line-through"
: "text-foreground",
)}
>
{formatCurrency(regular, currency)}
</span>
</div>
);
};
export { ProductList1 };