235 lines
5.8 KiB
TypeScript
235 lines
5.8 KiB
TypeScript
import { Rating } from "@/components/shadcnblocks/rating";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Card,
|
|
CardAction,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface Product {
|
|
name: string;
|
|
brand: string;
|
|
image: {
|
|
src: string;
|
|
alt: string;
|
|
};
|
|
link: string;
|
|
price: ProductPrice;
|
|
rate: number;
|
|
}
|
|
|
|
interface PromoCard {
|
|
image: string;
|
|
title: string;
|
|
cta: {
|
|
label: string;
|
|
link: string;
|
|
};
|
|
tagline: string;
|
|
}
|
|
|
|
type PromoCardProps = PromoCard;
|
|
|
|
type ProductCardProps = Product;
|
|
|
|
interface ProductPrice {
|
|
regular: number;
|
|
sale?: number;
|
|
currency: string;
|
|
}
|
|
|
|
type ProductList = Array<Product>;
|
|
|
|
const PROMO_CARD: PromoCard = {
|
|
image:
|
|
"/block-media/shadcnblocks/block/ecommerce/clothes/Summer-Fashion-Duo-1.png",
|
|
title: "کالکشن مد تابستانه",
|
|
tagline: "لایههای سبک، اثر پررنگ.",
|
|
cta: {
|
|
label: "مشاهده تازهها",
|
|
link: "#",
|
|
},
|
|
};
|
|
|
|
const PRODUCTS_LIST: ProductList = [
|
|
{
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/clothes/Sporty-Casual-Portrait-1.png",
|
|
alt: "تاپ کراپ صورتی با طراحی شیک",
|
|
},
|
|
name: "تاپ کراپ رز کات",
|
|
brand: "آتلیه رزه",
|
|
link: "#",
|
|
price: {
|
|
regular: 590000.0,
|
|
currency: "ریال",
|
|
},
|
|
rate: 5,
|
|
},
|
|
{
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/clothes/Woman-in-Embroidered-Blue-Top-1.png",
|
|
alt: "پیراهن آبی آستینکوتاه گلدوزیشده",
|
|
},
|
|
name: "بلوز آستینکوتاه آژور بلوم",
|
|
brand: "آتلیه رزه",
|
|
link: "#",
|
|
price: {
|
|
regular: 780000.0,
|
|
sale: 600000.0,
|
|
currency: "ریال",
|
|
},
|
|
rate: 4,
|
|
},
|
|
{
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/clothes/Person-in-Activewear-1.png",
|
|
alt: "تاپ ورزشی سبز",
|
|
},
|
|
name: "تاپ ورزشی پالسفلکس",
|
|
brand: "رزه اکتیو",
|
|
link: "#",
|
|
price: {
|
|
regular: 790000.0,
|
|
currency: "ریال",
|
|
},
|
|
rate: 4,
|
|
},
|
|
];
|
|
|
|
interface ProductList4Props {
|
|
className?: string;
|
|
}
|
|
|
|
const ProductList4 = ({ className }: ProductList4Props) => {
|
|
return (
|
|
<section className={cn("py-32", className)}>
|
|
<div className="container grid w-full gap-7 md:grid-cols-3 lg:grid-cols-4">
|
|
<div className="md:col-span-3 lg:col-span-1">
|
|
<PromoCard {...PROMO_CARD} />
|
|
</div>
|
|
{PRODUCTS_LIST.map((item, index) => (
|
|
<ProductCard key={`product-list-4-card-${index}`} {...item} />
|
|
))}
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
const PromoCard = ({ image, title, cta, tagline }: PromoCardProps) => {
|
|
return (
|
|
<Card
|
|
style={{
|
|
backgroundImage: `url(${image})`,
|
|
}}
|
|
className="relative flex h-full min-h-105 w-full min-w-47.5 flex-col justify-between gap-5 overflow-hidden rounded-md bg-cover bg-center bg-no-repeat p-7 before:absolute before:inset-0 before:bg-gradient-to-br before:from-white/60 before:to-transparent lg:max-w-80 lg:bg-top"
|
|
>
|
|
<CardHeader className="z-10 p-0">
|
|
<CardTitle className="mb-2.5 max-w-40 text-xl leading-tight font-medium dark:text-black">
|
|
{title}
|
|
</CardTitle>
|
|
<CardDescription className="text-sm text-foreground dark:text-black">
|
|
{tagline}
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardAction className="z-10 w-full">
|
|
<Button asChild className="w-full">
|
|
<a href={cta.link}>{cta.label}</a>
|
|
</Button>
|
|
</CardAction>
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
const ProductCard = ({
|
|
name,
|
|
link,
|
|
image,
|
|
price,
|
|
brand,
|
|
rate,
|
|
}: ProductCardProps) => {
|
|
const { regular, sale, currency } = price;
|
|
|
|
return (
|
|
<a href={link} className="block h-full w-full min-w-47.5 md:max-w-80">
|
|
<Card className="group h-full min-h-105 gap-3.5 rounded-none border-none bg-background p-0 shadow-none">
|
|
<CardHeader className="block p-0">
|
|
<div className="h-80 w-full overflow-hidden rounded-md">
|
|
<img
|
|
src={image.src}
|
|
alt={image.alt}
|
|
className="block size-full object-cover object-center"
|
|
/>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="flex h-full flex-col gap-2 p-0">
|
|
<div className="mb-1.5 flex justify-between gap-2">
|
|
<div className="text-xs font-medium text-muted-foreground uppercase">
|
|
{brand}
|
|
</div>
|
|
<Rating
|
|
rate={rate}
|
|
className="[&_svg]:size-3.5 [&_svg]:fill-yellow-500 [&_svg]:stroke-yellow-500 [&>div]:size-3.5"
|
|
/>
|
|
</div>
|
|
<CardTitle className="text-lg leading-tight font-semibold">
|
|
{name}
|
|
</CardTitle>
|
|
<ProductCardPrice
|
|
regular={regular}
|
|
sale={sale}
|
|
currency={currency}
|
|
/>
|
|
</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">
|
|
<span
|
|
className={cn(
|
|
"leading-tight",
|
|
onSale
|
|
? "text-muted-foreground line-through"
|
|
: "text-foreground",
|
|
)}
|
|
>
|
|
{formatCurrency(regular, currency)}
|
|
</span>
|
|
{onSale ? (
|
|
<span className="leading-tight text-foreground">
|
|
{formatCurrency(sale, currency)}
|
|
</span>
|
|
) : null}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export { ProductList4 };
|