201 lines
6.2 KiB
TypeScript
201 lines
6.2 KiB
TypeScript
import type { LucideIcon } from "lucide-react";
|
|
import { AudioLines, AudioWaveform } from "lucide-react";
|
|
|
|
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
type ImageItem = {
|
|
src: string;
|
|
alt: string;
|
|
};
|
|
|
|
type ImageVariant = "1" | "2" | "3";
|
|
|
|
interface Item {
|
|
id: string;
|
|
title: string;
|
|
summary: string;
|
|
number?: string;
|
|
images: ImageItem[];
|
|
Icon?: LucideIcon;
|
|
variant: ImageVariant;
|
|
}
|
|
|
|
interface CardImageProps {
|
|
variant: ImageVariant;
|
|
images: ImageItem[];
|
|
Icon?: LucideIcon;
|
|
}
|
|
|
|
const LIST: Item[] = [
|
|
{
|
|
id: "file-upload",
|
|
title: "پیشنهاد عنوان",
|
|
summary:
|
|
"تحلیل فنی، محتوا و رقبا در یک ابزار جمع میشود. تیم محتوا میداند روی کدام صفحه و کلمه باید تمرکز کند.",
|
|
images: [
|
|
{
|
|
src: "/block-media/shadcnblocks/block/photos/ivan-bandura-Kj2tYAl4HZg-unsplash.jpg",
|
|
alt: "داشبورد SEO",
|
|
},
|
|
],
|
|
Icon: AudioWaveform,
|
|
variant: "1",
|
|
},
|
|
{
|
|
id: "secure-fast",
|
|
title: "تحلیل کلمه کلیدی",
|
|
summary:
|
|
"ساختار عنوان و توضیحات را برای کلیک بیشتر بهینه کنید.",
|
|
images: [
|
|
{
|
|
src: "/block-media/shadcnblocks/block/photos/ivan-bandura-3QqzCTIfUJI-unsplash.jpg",
|
|
alt: "تحلیل کلمات کلیدی",
|
|
},
|
|
],
|
|
variant: "2",
|
|
},
|
|
{
|
|
id: "manage-files",
|
|
title: "ممیزی فنی",
|
|
summary:
|
|
"کلمات با پتانسیل بالا را پیدا کنید و استراتژی محتوا را بر همان اساس بچینید.",
|
|
images: [
|
|
{
|
|
src: "/block-media/shadcnblocks/block/photos/vasilis-karkalas-qOaeVSKyhhE-unsplash.jpg",
|
|
alt: "گزارش رتبه جستجو",
|
|
},
|
|
],
|
|
Icon: AudioLines,
|
|
variant: "3",
|
|
},
|
|
];
|
|
|
|
const CardImage = ({ variant, images, Icon }: CardImageProps) => {
|
|
switch (variant) {
|
|
case "1":
|
|
return (
|
|
<div className="absolute end-[30%] bottom-[3.75rem]">
|
|
<div className="aspect-[1.645714286] w-[25rem]">
|
|
<div className="flex size-full items-center -space-x-[10%] rtl:flex-row-reverse rtl:space-x-reverse">
|
|
<div className="w-full shrink-0">
|
|
<AspectRatio
|
|
className="overflow-hidden rounded-2xl"
|
|
ratio={1.558095238}
|
|
>
|
|
<img
|
|
src={images[0].src}
|
|
alt={images[0].alt}
|
|
className="block size-full object-cover object-center"
|
|
/>
|
|
</AspectRatio>
|
|
</div>
|
|
<div className="w-[16%] shrink-0">
|
|
<AspectRatio
|
|
className="w-full overflow-hidden rounded-lg shadow"
|
|
ratio={1}
|
|
>
|
|
<div className="flex size-full bg-background">
|
|
{Icon && (
|
|
<Icon className="m-auto size-[65%] stroke-muted-foreground/80 stroke-1" />
|
|
)}
|
|
</div>
|
|
</AspectRatio>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
case "2":
|
|
return (
|
|
<div className="absolute end-1/6 -bottom-[5%]">
|
|
<div className="aspect-video w-[31.25rem] overflow-hidden rounded-2xl shadow-2xl sm:w-[42.5rem]">
|
|
<img
|
|
src={images[0].src}
|
|
alt={images[0].alt}
|
|
className="block size-full object-cover object-center"
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
default:
|
|
return (
|
|
<div className="absolute end-1/6 -bottom-[5%]">
|
|
<div className="relative aspect-[1.195592287] w-[21.25rem] sm:w-[25rem]">
|
|
<div className="absolute bottom-0 aspect-[1.287833828] w-full overflow-hidden rounded-2xl">
|
|
<img
|
|
src={images[0].src}
|
|
alt={images[0].alt}
|
|
className="block size-full object-cover object-center"
|
|
/>
|
|
</div>
|
|
<div className="absolute end-0 top-0 flex aspect-square w-[20%] translate-x-1/2 overflow-hidden rounded-lg bg-background shadow rtl:-translate-x-1/2">
|
|
{Icon && (
|
|
<Icon className="m-auto size-[65%] stroke-muted-foreground/80 stroke-1" />
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
};
|
|
|
|
const Card = ({ title, summary, images, Icon, number, variant }: Item) => {
|
|
const numberBorderColor = "var(--muted-foreground)";
|
|
|
|
return (
|
|
<div
|
|
className="relative flex h-full min-h-[39.375rem] w-full max-w-[34rem] flex-col justify-between gap-4 overflow-hidden rounded-[0.75rem] bg-muted"
|
|
style={{
|
|
backgroundImage:
|
|
"url('/block-media/shadcnblocks/block/patterns/p6.png')",
|
|
}}
|
|
>
|
|
<div className="flex w-full flex-col gap-4 p-10 pe-[3.75rem] pb-0 text-start md:p-[3.75rem]">
|
|
<h3 className="text-xl leading-none">{title}</h3>
|
|
<p className="text-xl leading-normal text-pretty">{summary}</p>
|
|
</div>
|
|
<div className="relative size-full min-h-[21.875rem]">
|
|
<CardImage images={images} Icon={Icon} variant={variant} />
|
|
</div>
|
|
<div
|
|
className="absolute end-5 top-5 size-fit text-2xll leading-none font-light text-muted opacity-25"
|
|
style={{
|
|
textShadow: `
|
|
2px 0 ${numberBorderColor}, -2px 0 ${numberBorderColor},
|
|
0 2px ${numberBorderColor}, 0 -2px ${numberBorderColor},
|
|
1px 1px ${numberBorderColor}, -1px -1px ${numberBorderColor},
|
|
1px -1px ${numberBorderColor}, -1px 1px ${numberBorderColor}
|
|
`,
|
|
}}
|
|
>
|
|
{number}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
interface Feature219Props {
|
|
className?: string;
|
|
}
|
|
|
|
const Feature219 = ({ className }: Feature219Props) => {
|
|
return (
|
|
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
|
|
<div className="container">
|
|
<div className="grid items-center justify-center gap-6 xl:grid-cols-3">
|
|
{LIST.map((item, index) => (
|
|
<Card
|
|
key={item.id}
|
|
number={(index + 1).toLocaleString("fa-IR")}
|
|
{...item}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { Feature219 };
|