226 lines
9.4 KiB
TypeScript
226 lines
9.4 KiB
TypeScript
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Card, CardContent, CardFooter } from "@/components/ui/card";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const testimonials = [
|
|
{
|
|
name: "سارا احمدی",
|
|
role: "مدیرعامل و بنیانگذار",
|
|
avatar:
|
|
"/block-media/shadcnblocks/block/avatar/avatar9.jpg",
|
|
content:
|
|
"این کتابخانه کامپوننت، روش ساخت محصولات ما را متحول کرد. کل داشبورد را در نصف زمانی که معمولاً لازم بود، تحویل دادیم.",
|
|
},
|
|
{
|
|
name: "مازیار رضایی",
|
|
role: "مدیر فناوری",
|
|
avatar:
|
|
"/block-media/shadcnblocks/block/avatar/avatar7.jpg",
|
|
content:
|
|
"توجه به دسترسپذیری و عملکرد قابل تحسین است. امتیاز ابزار تحلیل عملکرد ما پس از استفاده از این کامپوننتها بهطور محسوسی بهبود یافت.",
|
|
},
|
|
{
|
|
name: "نیلوفر حسینی",
|
|
role: "مدیر محصول",
|
|
avatar:
|
|
"/block-media/shadcnblocks/block/avatar/avatar10.jpg",
|
|
content:
|
|
"بالاخره یک سیستم طراحی که توسعهدهندگان واقعاً دوست دارند از آن استفاده کنند. مستندات روشن است، کامپوننتها انعطافپذیرند و مقادیر پیشفرض منطقیاند. کم پیش میآید هر سه را با هم ببینید.",
|
|
},
|
|
{
|
|
name: "امیر محمدی",
|
|
role: "سرپرست فنی",
|
|
avatar:
|
|
"/block-media/shadcnblocks/block/avatar/avatar7.webp",
|
|
content:
|
|
"سالها کتابخانههای مختلف رابط کاربری را امتحان کردیم. این یکی تعادل عالی بین پیشفرضهای مشخص و انعطاف سفارشیسازی برقرار میکند.",
|
|
},
|
|
{
|
|
name: "رؤیا کاظمی",
|
|
role: "طراح ارشد",
|
|
avatar:
|
|
"/block-media/shadcnblocks/block/avatar/avatar2.webp",
|
|
content:
|
|
"بهعنوان طراح، قدردانم که کامپوننتها بهطور دقیق با طرحهای طراحی رابط ما همخواناند. تحویل از طراحی به توسعه هرگز اینقدر روان نبوده. هر پیکسل هدفمند است و سیستم فاصلهگذاری حفظ یکنواختی در صفحات را آسان میکند. با کتابخانههای زیادی کار کردهام، اما این اولین بار است که مجبور نیستم مدام استایلها را بازنویسی کنم.",
|
|
},
|
|
{
|
|
name: "پارسا موسوی",
|
|
role: "توسعهدهنده فولاستک",
|
|
avatar:
|
|
"/block-media/shadcnblocks/block/avatar/avatar11.jpg",
|
|
content:
|
|
"پشتیبانی زبان نوعایمن عالی است. تکمیل خودکار بدون دردسر کار میکند و خطاها را پیش از رسیدن به محیط تولید میگیرم. تجربه توسعه در سطح بالایی است.",
|
|
},
|
|
{
|
|
name: "مهسا نوری",
|
|
role: "مهندس تجربه کاربری",
|
|
avatar:
|
|
"/block-media/shadcnblocks/block/avatar/avatar2.jpg",
|
|
content:
|
|
"این کامپوننتها موارد خاصی را پوشش میدهند که حتی از وجودشان بیخبر بودم. حالت تاریک، پشتیبانی راستبهچپ و ناوبری با صفحهکلید همه از پیش آمادهاند.",
|
|
},
|
|
{
|
|
name: "علی رحمانی",
|
|
role: "مدیر مهندسی",
|
|
avatar:
|
|
"/block-media/shadcnblocks/block/avatar/avatar12.jpg",
|
|
content:
|
|
"سرعت تیم ما پس از استفاده از این کتابخانه بهطور محسوسی افزایش یافت. زمان کمتر برای کدهای تکراری رابط کاربری یعنی زمان بیشتر برای منطق کسبوکار.",
|
|
},
|
|
];
|
|
|
|
interface Testimonial1Props {
|
|
className?: string;
|
|
}
|
|
|
|
const Testimonial1 = ({ className }: Testimonial1Props) => {
|
|
const getColumns = (count: number) => {
|
|
return Array.from({ length: count }, (_, colIdx) =>
|
|
testimonials.filter((_, i) => i % count === colIdx),
|
|
);
|
|
};
|
|
|
|
const mobileColumns = getColumns(2);
|
|
const tabletColumns = getColumns(3);
|
|
const desktopColumns = getColumns(4);
|
|
|
|
return (
|
|
<section
|
|
dir="rtl"
|
|
lang="fa"
|
|
className={cn("overflow-hidden border-b pt-32", className)}
|
|
>
|
|
<div className="container">
|
|
<div className="flex flex-col items-center gap-6">
|
|
<Badge variant="outline">نظرات مشتریان</Badge>
|
|
<h2 className="text-center text-3xl tracking-tight lg:text-2xll">
|
|
با مشتریان راضی ما آشنا شوید
|
|
</h2>
|
|
<p className="text-center text-muted-foreground lg:text-lg">
|
|
تجربه تیمهایی را بخوانید که با کامپوننتهای ما گردش کار خود را
|
|
متحول کردهاند.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="container mt-10 h-[450px] md:h-[500px] lg:h-[522px]">
|
|
{/* Mobile: 2 columns */}
|
|
<div className="flex gap-4 md:hidden">
|
|
{mobileColumns.map((column, colIdx) => (
|
|
<div
|
|
key={colIdx}
|
|
className={cn(
|
|
"flex w-1/2 flex-col gap-4",
|
|
colIdx === 1 && "mt-8",
|
|
)}
|
|
>
|
|
{column.map((testimonial, idx) => (
|
|
<Card key={idx}>
|
|
<CardContent className="px-4 text-sm leading-6 text-muted-foreground">
|
|
<q>{testimonial.content}</q>
|
|
</CardContent>
|
|
<CardFooter className="px-4">
|
|
<div className="flex gap-3 leading-5">
|
|
<Avatar className="size-8 rounded-full ring-1 ring-input">
|
|
<AvatarImage
|
|
src={testimonial.avatar}
|
|
alt={testimonial.name}
|
|
/>
|
|
</Avatar>
|
|
<div className="text-xs">
|
|
<p className="font-medium">{testimonial.name}</p>
|
|
<p className="text-muted-foreground">
|
|
{testimonial.role}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</CardFooter>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Tablet: 3 columns */}
|
|
<div className="hidden gap-4 md:flex lg:hidden">
|
|
{tabletColumns.map((column, colIdx) => (
|
|
<div
|
|
key={colIdx}
|
|
className={cn(
|
|
"flex w-1/3 flex-col gap-4",
|
|
colIdx === 1 && "mt-10",
|
|
colIdx === 2 && "mt-20",
|
|
)}
|
|
>
|
|
{column.map((testimonial, idx) => (
|
|
<Card key={idx}>
|
|
<CardContent className="px-6 leading-7 text-muted-foreground">
|
|
<q>{testimonial.content}</q>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<div className="flex gap-4 leading-5">
|
|
<Avatar className="size-9 rounded-full ring-1 ring-input">
|
|
<AvatarImage
|
|
src={testimonial.avatar}
|
|
alt={testimonial.name}
|
|
/>
|
|
</Avatar>
|
|
<div className="text-sm">
|
|
<p className="font-medium">{testimonial.name}</p>
|
|
<p className="text-muted-foreground">
|
|
{testimonial.role}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</CardFooter>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Desktop: 4 columns */}
|
|
<div className="hidden gap-4 lg:flex">
|
|
{desktopColumns.map((column, colIdx) => (
|
|
<div
|
|
key={colIdx}
|
|
className={cn(
|
|
"flex w-1/4 flex-col gap-4",
|
|
colIdx === 1 && "mt-10",
|
|
colIdx === 2 && "mt-20",
|
|
)}
|
|
>
|
|
{column.map((testimonial, idx) => (
|
|
<Card key={idx}>
|
|
<CardContent className="px-6 leading-7 text-muted-foreground">
|
|
<q>{testimonial.content}</q>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<div className="flex gap-4 leading-5">
|
|
<Avatar className="size-9 rounded-full ring-1 ring-input">
|
|
<AvatarImage
|
|
src={testimonial.avatar}
|
|
alt={testimonial.name}
|
|
/>
|
|
</Avatar>
|
|
<div className="text-sm">
|
|
<p className="font-medium">{testimonial.name}</p>
|
|
<p className="text-muted-foreground">
|
|
{testimonial.role}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</CardFooter>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { Testimonial1 };
|