Files

208 lines
6.4 KiB
TypeScript

import { type ReactNode, type SVGProps, useId } from "react";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { cn } from "@/lib/utils";
type FeatureItem = {
id: string;
title: ReactNode;
description: string[];
image: {
src: string;
alt: string;
className?: string;
};
imagePosition: "content" | "header";
className?: string;
headerClassName?: string;
contentClassName?: string;
};
const ITEMS: FeatureItem[] = [
{
id: "var-flexibility-hero",
title: (
<>
مدیریت پروژه
<br />
هم‌راستایی
</>
),
description: [
"برنامه تیم را هماهنگ کنید و تداخل وظایف را کاهش دهید.",
"نسخه درست فایل همیشه در دسترس همه اعضا باشد.",
],
image: {
src: "/block-media/shadcnblocks/block/placeholder-1.svg",
alt: "تقویم تیم",
},
imagePosition: "content",
className:
"flex flex-col gap-6 overflow-hidden py-6 ps-6 md:col-span-3 md:flex-row md:gap-12",
headerClassName: "flex-2 p-0 text-start md:flex-1",
contentClassName:
"relative w-full self-start overflow-hidden rounded-s-xl border p-0 md:flex-1",
},
{
id: "var-flexibility-flow",
title: "تقویم تیمی",
description: [
"کار گروهی",
"بحث را کنار هر تسک نگه دارید تا تصمیم‌ها گم نشود.",
],
image: {
src: "/block-media/shadcnblocks/block/placeholder-1.svg",
alt: "فضای همکاری تیمی",
className: "aspect-[2/1.1] rounded-xl",
},
className: "flex flex-col justify-center md:col-span-2",
contentClassName: "flex items-center justify-center",
imagePosition: "content",
},
{
id: "var-flexibility-diagram",
title: "اشتراک فایل",
description: [
"وضعیت هر کار را در یک نما ببینید و گلوگاه‌ها را زودتر پیدا کنید.",
],
image: {
src: "/block-media/shadcnblocks/block/placeholder-1.svg",
alt: "برد مدیریت پروژه",
className: "mb-6 aspect-2/1 flex-1 self-center rounded-xl",
},
className: "md:col-span-2",
headerClassName: "h-full text-start",
imagePosition: "header",
},
{
id: "var-flexibility-transfer",
title: "منشن و بحث",
description: [
"همکاری تیمی",
],
image: {
src: "/block-media/shadcnblocks/block/placeholder-1.svg",
alt: "تقویم تیم",
},
imagePosition: "content",
className: "overflow-hidden md:col-span-3",
headerClassName: "text-start",
contentClassName:
"relative mt-4 ms-8 aspect-[2/1.25] w-full overflow-hidden rounded-t-xl p-0 md:mx-auto md:max-w-[400px] lg:max-w-[500px]",
},
];
export const Feature193 = () => {
return (
<section dir="rtl" lang="fa" className="relative overflow-hidden py-32">
<h3 className="container font-mono text-sm font-semibold text-accent-foreground">
ردیابی زمان
</h3>
<div className="relative z-10 container mt-8 grid grid-cols-1 gap-6 md:grid-cols-5">
{ITEMS.map((item) => (
<Card
key={item.id}
className={cn("col-span-1 shadow-xl", item.className)}
>
<CardHeader className={item.headerClassName}>
{item.imagePosition === "header" && (
<img
src={item.image.src}
alt={item.image.alt}
className={cn("object-cover", item.image.className)}
/>
)}
<CardTitle className="mt-0! text-3xl text-balance">
{item.title}
</CardTitle>
{item.description.map((desc) => (
<CardDescription
key={desc}
className="mt-3 text-base leading-snug font-medium text-pretty"
>
{desc}
</CardDescription>
))}
</CardHeader>
<CardContent className={item.contentClassName}>
{item.imagePosition === "content" && (
<img
src={item.image.src}
alt={item.image.alt}
className={cn(
"self-center object-cover",
item.image.className,
)}
/>
)}
</CardContent>
</Card>
))}
</div>
<>
<div className="absolute inset-0">
<div className="absolute top-1/2 size-[700px] -translate-y-1/2 rounded-full bg-gradient-1/30 blur-[300px] will-change-transform" />
<div className="absolute top-1/2 end-0 size-[700px] -translate-y-1/2 -rotate-12 rounded-full bg-gradient-2/15 blur-[300px] will-change-transform rtl:rotate-12" />
<div className="absolute end-20 bottom-10 h-[500px] w-[800px] -rotate-12 rounded-full bg-gradient-3/[0.06] blur-[100px] will-change-transform rtl:rotate-12" />
</div>
<div className="absolute -inset-x-20 top-0 [mask-image:radial-gradient(circle_at_center,black_0%,black_20%,transparent_85%)]">
<PlusSigns className="h-full w-full text-foreground/[0.05]" />
</div>
</>
</section>
);
};
interface PlusSignsProps extends SVGProps<SVGSVGElement> {
className?: string;
}
const PlusSigns = ({ className, ...props }: PlusSignsProps) => {
const GAP = 16;
const STROKE_WIDTH = 1;
const PLUS_SIZE = 6;
const id = useId();
const patternId = `plus-pattern-${id}`;
return (
<svg width={GAP * 2} height={GAP * 2} className={className} {...props}>
<defs>
<pattern
id={patternId}
x="0"
y="0"
width={GAP}
height={GAP}
patternUnits="userSpaceOnUse"
>
<line
x1={GAP / 2}
y1={(GAP - PLUS_SIZE) / 2}
x2={GAP / 2}
y2={(GAP + PLUS_SIZE) / 2}
stroke="currentColor"
strokeWidth={STROKE_WIDTH}
/>
<line
x1={(GAP - PLUS_SIZE) / 2}
y1={GAP / 2}
x2={(GAP + PLUS_SIZE) / 2}
y2={GAP / 2}
stroke="currentColor"
strokeWidth={STROKE_WIDTH}
/>
</pattern>
</defs>
<rect width="100%" height="100%" fill={`url(#${patternId})`} />
</svg>
);
};