684 lines
23 KiB
TypeScript
684 lines
23 KiB
TypeScript
"use client";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { ArrowRight, Slash } from "lucide-react";
|
|
import { Fragment, useCallback, useMemo, useState } from "react";
|
|
import { ControllerRenderProps, useForm } from "react-hook-form";
|
|
import { Controller } from "react-hook-form";
|
|
import { z } from "zod";
|
|
|
|
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import {
|
|
Breadcrumb,
|
|
BreadcrumbItem,
|
|
BreadcrumbLink,
|
|
BreadcrumbList,
|
|
BreadcrumbSeparator,
|
|
} from "@/components/ui/breadcrumb";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Card, CardContent } from "@/components/ui/card";
|
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
import {
|
|
Field,
|
|
FieldError,
|
|
FieldGroup,
|
|
FieldLabel,
|
|
} from "@/components/ui/field";
|
|
import { Input } from "@/components/ui/input";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface BreadcrumbItem {
|
|
label: string;
|
|
link: string;
|
|
}
|
|
|
|
interface Post {
|
|
categoryKey: string;
|
|
category: string;
|
|
title: string;
|
|
summary: string;
|
|
link: string;
|
|
cta: string;
|
|
thumbnail: string;
|
|
}
|
|
|
|
interface Category {
|
|
label: string;
|
|
value: string;
|
|
}
|
|
|
|
interface FilterFormProps {
|
|
categories: Array<Category>;
|
|
onCategoryChange: (selectedCategories: string[]) => void;
|
|
}
|
|
|
|
interface BlogsResultProps {
|
|
posts: Array<Post>;
|
|
categories: Array<Category>;
|
|
}
|
|
|
|
interface BreadcrumbBlogProps {
|
|
breadcrumb: Array<BreadcrumbItem>;
|
|
}
|
|
|
|
const POSTS_PER_PAGE = 6;
|
|
|
|
const BREADCRUMB: Array<BreadcrumbItem> = [
|
|
{
|
|
label: "منابع",
|
|
link: "#",
|
|
},
|
|
{
|
|
label: "گزارشها",
|
|
link: "#",
|
|
},
|
|
];
|
|
|
|
const CATEGORIES: Array<Category> = [
|
|
{
|
|
label: "همه",
|
|
value: "all",
|
|
},
|
|
{
|
|
label: "بهرهوری",
|
|
value: "productivity",
|
|
},
|
|
{
|
|
label: "دسترسپذیری",
|
|
value: "accessibility",
|
|
},
|
|
{
|
|
label: "کارایی",
|
|
value: "performance",
|
|
},
|
|
];
|
|
|
|
const PRIMARY_POST: Post = {
|
|
categoryKey: "all",
|
|
category: "ویژه: نوآوری",
|
|
title: "چگونه هوش مصنوعی توسعه فرانتاند را متحول میکند",
|
|
summary:
|
|
"ببینید ابزارهایی مثل GitHub Copilot، مولدهای طراحی هوش مصنوعی و دستیارهای کدنویسی چگونه ساخت UI و تحویل قابلیتها را برای توسعهدهندگان تغییر میدهند.",
|
|
link: "#",
|
|
cta: "کشف آینده",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-1.svg",
|
|
};
|
|
|
|
const POSTS: Array<Post> = [
|
|
{
|
|
categoryKey: "productivity",
|
|
category: "بهرهوری",
|
|
title: "۵ افزونه ویرایشگر کد که ساعتها وقت شما را ذخیره میکند",
|
|
summary:
|
|
"افزونههای ضروری برای افزایش بهرهوری کدنویسی و سادهتر کردن گردش کار.",
|
|
link: "#",
|
|
cta: "ویرایشگر را تقویت کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-2.svg",
|
|
},
|
|
{
|
|
categoryKey: "productivity",
|
|
category: "بهرهوری",
|
|
title: "مدیریت زمان برای توسعهدهندگان: چه چیزی واقعاً جواب میدهد",
|
|
summary:
|
|
"استراتژیهای اثباتشده برای جلوگیری از فرسودگی و مدیریت وظایف بدون استرس.",
|
|
link: "#",
|
|
cta: "زمان را مدیریت کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-3.svg",
|
|
},
|
|
{
|
|
categoryKey: "productivity",
|
|
category: "بهرهوری",
|
|
title: "خودکارسازی گردش کار با اجرای خودکار وظایفها",
|
|
summary:
|
|
"از Gulp، اسکریپتهای npm و GitHub Actions برای خودکار کردن کارهای تکراری توسعه استفاده کنید.",
|
|
link: "#",
|
|
cta: "همین حالا خودکار کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-4.svg",
|
|
},
|
|
{
|
|
categoryKey: "productivity",
|
|
category: "بهرهوری",
|
|
title: "روتینهای روزانه مؤثر برای توسعهدهندگان",
|
|
summary:
|
|
"روتینهایی که توسعهدهندگان برتر برای ماندن پرانرژی، خلاق و متمرکز دنبال میکنند.",
|
|
link: "#",
|
|
cta: "جریان کاری خود را بیابید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-5.svg",
|
|
},
|
|
{
|
|
categoryKey: "productivity",
|
|
category: "بهرهوری",
|
|
title: "تسلط بر کنترل نسخه با این میانبرها",
|
|
summary:
|
|
"گردش کار version control را با aliasها و نکات قدرتمند Git تسریع کنید.",
|
|
link: "#",
|
|
cta: "Git را سریعتر کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-6.svg",
|
|
},
|
|
{
|
|
categoryKey: "productivity",
|
|
category: "بهرهوری",
|
|
title: "کاهش تعویض زمینه در کار توسعه",
|
|
summary:
|
|
"حواسپرتی را کم کنید و با تمرکز عمیق روی کد پیش بروید.",
|
|
link: "#",
|
|
cta: "متمرکز بمانید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-1.svg",
|
|
},
|
|
{
|
|
categoryKey: "productivity",
|
|
category: "بهرهوری",
|
|
title: "راهاندازی دورکاری: ابزارهای محیط بدون حواسپرتی",
|
|
summary:
|
|
"فضا و استک نرمافزاری را برای حداکثر بهرهوری در کار از خانه تنظیم کنید.",
|
|
link: "#",
|
|
cta: "محیط کار را ارتقا دهید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-2.svg",
|
|
},
|
|
{
|
|
categoryKey: "productivity",
|
|
category: "بهرهوری",
|
|
title: "پومودورو برای برنامهنویسان: واقعاً مؤثر است؟",
|
|
summary:
|
|
"بررسی عملی تکنیک پومودورو و اثربخشی آن در توسعه نرمافزار.",
|
|
link: "#",
|
|
cta: "روش را امتحان کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-3.svg",
|
|
},
|
|
{
|
|
categoryKey: "accessibility",
|
|
category: "دسترسپذیری",
|
|
title: "چرا دسترسپذیری باید بخشی از نسخه اولیه محصول شما باشد",
|
|
summary:
|
|
"فراگیر کردن محصول از روز اول، قابلیت استفاده و دسترسی را بهبود میدهد.",
|
|
link: "#",
|
|
cta: "دلیلش را بدانید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-4.svg",
|
|
},
|
|
{
|
|
categoryKey: "accessibility",
|
|
category: "دسترسپذیری",
|
|
title: "استفاده درست از نقشهای برچسب دسترسپذیری در وباپ",
|
|
summary:
|
|
"چگونه با نقشها و landmarkهای ARIA پشتیبانی screen reader را تقویت کنید.",
|
|
link: "#",
|
|
cta: "معناشناسی را بهبود دهید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-5.svg",
|
|
},
|
|
{
|
|
categoryKey: "accessibility",
|
|
category: "دسترسپذیری",
|
|
title: "نکات کنتراست رنگ برای خوانایی بهتر",
|
|
summary:
|
|
"ترکیبهای رنگی دسترسپذیر مطابق استاندارد WCAG را بیاموزید.",
|
|
link: "#",
|
|
cta: "رنگها را اصلاح کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-6.svg",
|
|
},
|
|
{
|
|
categoryKey: "accessibility",
|
|
category: "دسترسپذیری",
|
|
title: "ناوبری با کیبورد: تجربهای که نادیده گرفته میشود",
|
|
summary:
|
|
"وبسایت را کاملاً با کیبورد قابل استفاده کنید برای دسترسپذیری و سرعت.",
|
|
link: "#",
|
|
cta: "ناوبری را تست کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-1.svg",
|
|
},
|
|
{
|
|
categoryKey: "accessibility",
|
|
category: "دسترسپذیری",
|
|
title: "فرمهای دسترسپذیر: برچسب، خطا و بازخورد",
|
|
summary:
|
|
"با دستورالعملهای واضح برای screen reader و کاربران، فرمها را قابل فهمتر کنید.",
|
|
link: "#",
|
|
cta: "فرمها را اصلاح کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-2.svg",
|
|
},
|
|
{
|
|
categoryKey: "accessibility",
|
|
category: "دسترسپذیری",
|
|
title: "تست با صفحهخوان: راهنمای مبتدی",
|
|
summary:
|
|
"چگونه با screen readerهای رایج سایت را تست کنید و به چه چیزهایی گوش دهید.",
|
|
link: "#",
|
|
cta: "تست را شروع کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-3.svg",
|
|
},
|
|
{
|
|
categoryKey: "accessibility",
|
|
category: "دسترسپذیری",
|
|
title: "تفکر طراحی فراگیر در توسعه رابط کاربری",
|
|
summary:
|
|
"رابطهایی طراحی کنید که از ابتدا همه تواناییها را در نظر بگیرند.",
|
|
link: "#",
|
|
cta: "برای همه طراحی کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-4.svg",
|
|
},
|
|
{
|
|
categoryKey: "accessibility",
|
|
category: "دسترسپذیری",
|
|
title: "ممیزی دسترسپذیری: ابزارها و چکلیستها",
|
|
summary:
|
|
"ممیزی کامل با Axe، Lighthouse و چکلیستهای دستی انجام دهید.",
|
|
link: "#",
|
|
cta: "ممیزی کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-5.svg",
|
|
},
|
|
{
|
|
categoryKey: "performance",
|
|
category: "کارایی",
|
|
title: "بارگذاری هوشمند تصاویر با استاندارد وب مدرن",
|
|
summary:
|
|
"زمان بارگذاری را با lazy-loading بومی و استراتژیهای جایگزین بهبود دهید.",
|
|
link: "#",
|
|
cta: "تصاویر را بهینه کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-6.svg",
|
|
},
|
|
{
|
|
categoryKey: "performance",
|
|
category: "کارایی",
|
|
title: "فشردهسازی برنامهنویسی وب بدون خراب کردن اپ",
|
|
summary:
|
|
"بهترین روشهای minify و tree-shaking برای bundleهای سریعتر.",
|
|
link: "#",
|
|
cta: "کد را کوچک کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-1.svg",
|
|
},
|
|
{
|
|
categoryKey: "performance",
|
|
category: "کارایی",
|
|
title: "شاخصهای عملکرد وب توضیح داده شد: جابجایی چیدمان، زمان بارگذاری اصلی، تأخیر اولین تعامل",
|
|
summary:
|
|
"اندازهگیری و بهبود Core Web Vitals برای تجربه کاربری بهتر.",
|
|
link: "#",
|
|
cta: "شاخصها را بهبود دهید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-2.svg",
|
|
},
|
|
{
|
|
categoryKey: "performance",
|
|
category: "کارایی",
|
|
title: "رندر سمت سرور در برابر کلاینت: کدام سریعتر است؟",
|
|
summary:
|
|
"SSR و CSR را مقایسه کنید و بدانید هر کدام چه زمانی برای کارایی بهتر مناسباند.",
|
|
link: "#",
|
|
cta: "گزینهها را بررسی کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-3.svg",
|
|
},
|
|
{
|
|
categoryKey: "performance",
|
|
category: "کارایی",
|
|
title: "بهینهسازی فونت برای بارگذاری سریعتر",
|
|
summary:
|
|
"فونت را بدون مسدود کردن رندر یا جابجایی layout بارگذاری کنید.",
|
|
link: "#",
|
|
cta: "فونتها را سریع کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-4.svg",
|
|
},
|
|
{
|
|
categoryKey: "performance",
|
|
category: "کارایی",
|
|
title: "کاهش حجم بسته با تقسیم هوشمند بار",
|
|
summary:
|
|
"با import پویا و chunking مبتنی بر مسیر، زمان بار اولیه را کم کنید.",
|
|
link: "#",
|
|
cta: "تقسیمبندی کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-5.svg",
|
|
},
|
|
{
|
|
categoryKey: "performance",
|
|
category: "کارایی",
|
|
title: "استراتژیهای کش برای وباپهای مدرن",
|
|
summary:
|
|
"از کش HTTP، service worker و CDN برای سرعت و پشتیبانی آفلاین بهره ببرید.",
|
|
link: "#",
|
|
cta: "هوشمندانه کش کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-6.svg",
|
|
},
|
|
{
|
|
categoryKey: "performance",
|
|
category: "کارایی",
|
|
title: "تحلیل گلوگاههای کارایی با ابزار تحلیل مرورگر",
|
|
summary:
|
|
"با تب Performance در DevTools مشکلات زمان اجرا را پیدا و رفع کنید.",
|
|
link: "#",
|
|
cta: "همین حالا تحلیل کنید",
|
|
thumbnail:
|
|
"/block-media/shadcnblocks/block/placeholder-1.svg",
|
|
},
|
|
];
|
|
|
|
const FilterFormSchema = z.object({
|
|
items: z.array(z.string()).refine((value) => value.length > 0, {
|
|
message: "حداقل یک دسته باید انتخاب شود.",
|
|
}),
|
|
});
|
|
|
|
const FilterForm = ({ categories, onCategoryChange }: FilterFormProps) => {
|
|
const form = useForm<z.infer<typeof FilterFormSchema>>({
|
|
resolver: zodResolver(FilterFormSchema),
|
|
defaultValues: {
|
|
items: [CATEGORIES[0].value],
|
|
},
|
|
});
|
|
|
|
const handleCheckboxChange = useCallback(
|
|
(
|
|
checked: boolean | string,
|
|
categoryValue: string,
|
|
field: ControllerRenderProps<z.infer<typeof FilterFormSchema>, "items">,
|
|
) => {
|
|
let updatedValues = checked
|
|
? [...field.value, categoryValue]
|
|
: field.value.filter((value: string) => value !== categoryValue);
|
|
|
|
// If no categories are checked, add "all"
|
|
if (updatedValues.length === 0) {
|
|
form.setValue("items", ["all"]);
|
|
onCategoryChange(["all"]);
|
|
return;
|
|
}
|
|
|
|
// Remove "all" if specific category is checked
|
|
if (updatedValues.includes("all")) {
|
|
updatedValues = updatedValues.filter((v: string) => v !== "all");
|
|
}
|
|
|
|
// Avoid unnecessary updates
|
|
if (JSON.stringify(field.value) !== JSON.stringify(updatedValues)) {
|
|
form.setValue("items", updatedValues);
|
|
onCategoryChange(updatedValues);
|
|
}
|
|
},
|
|
[form, onCategoryChange],
|
|
);
|
|
|
|
return (
|
|
<form>
|
|
<Controller
|
|
control={form.control}
|
|
name="items"
|
|
render={({ field }) => (
|
|
<FieldGroup className="flex w-full flex-row flex-wrap gap-2.5">
|
|
{categories.map((category) => {
|
|
const isChecked = field.value?.includes(category.value);
|
|
const inputId = `category-${category.value}`;
|
|
return (
|
|
<FieldLabel
|
|
key={category.value}
|
|
htmlFor={inputId}
|
|
className="flex cursor-pointer items-center gap-2.5 rounded-full bg-muted px-2.5 py-1.5"
|
|
>
|
|
{category.label}
|
|
<Checkbox
|
|
id={inputId}
|
|
checked={isChecked}
|
|
onCheckedChange={(checked) =>
|
|
handleCheckboxChange(checked, category.value, field)
|
|
}
|
|
/>
|
|
</FieldLabel>
|
|
);
|
|
})}
|
|
<FieldError
|
|
errors={
|
|
form.formState.errors.items
|
|
? [form.formState.errors.items]
|
|
: undefined
|
|
}
|
|
/>
|
|
</FieldGroup>
|
|
)}
|
|
/>
|
|
</form>
|
|
);
|
|
};
|
|
|
|
const ResourcesResult = ({ posts, categories }: BlogsResultProps) => {
|
|
const [visibleCount, setVisibleCount] = useState(POSTS_PER_PAGE);
|
|
const [selectedCategories, setSelectedCategories] = useState<string[]>([
|
|
CATEGORIES[0].value,
|
|
]);
|
|
const handleCategoryChange = useCallback((selected: string[]) => {
|
|
setSelectedCategories(selected);
|
|
setVisibleCount(POSTS_PER_PAGE);
|
|
}, []);
|
|
|
|
const handleLoadMore = useCallback(() => {
|
|
setVisibleCount((prev) => prev + POSTS_PER_PAGE);
|
|
}, []);
|
|
const filteredPosts = useMemo(() => {
|
|
return posts.filter(
|
|
(post) =>
|
|
selectedCategories.includes(post.categoryKey) ||
|
|
selectedCategories.includes("all"),
|
|
);
|
|
}, [posts, selectedCategories]);
|
|
|
|
const postsToDisplay = filteredPosts.length > 0 ? filteredPosts : posts;
|
|
|
|
const hasMore = visibleCount < postsToDisplay.length;
|
|
|
|
return (
|
|
<div>
|
|
<FilterForm
|
|
categories={categories}
|
|
onCategoryChange={handleCategoryChange}
|
|
/>
|
|
<div className="flex w-full flex-col gap-4 py-8 lg:gap-8">
|
|
<div className="grid gap-10 md:grid-cols-2 lg:grid-cols-3">
|
|
{postsToDisplay.slice(0, visibleCount).map((post) => (
|
|
<ResourcesCard key={post.title} {...post} />
|
|
))}
|
|
</div>
|
|
<div className="flex justify-center">
|
|
{hasMore && (
|
|
<Button variant="secondary" onClick={handleLoadMore}>
|
|
بارگذاری بیشتر
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const BreadcrumbBlog = ({ breadcrumb }: BreadcrumbBlogProps) => {
|
|
return (
|
|
<Breadcrumb>
|
|
<BreadcrumbList>
|
|
{breadcrumb.map((item, i) => {
|
|
return (
|
|
<Fragment key={`${item.label}`}>
|
|
<BreadcrumbItem>
|
|
<BreadcrumbLink href={item.link}>{item.label}</BreadcrumbLink>
|
|
</BreadcrumbItem>
|
|
{i < breadcrumb.length - 1 ? (
|
|
<BreadcrumbSeparator>
|
|
<Slash />
|
|
</BreadcrumbSeparator>
|
|
) : null}
|
|
</Fragment>
|
|
);
|
|
})}
|
|
</BreadcrumbList>
|
|
</Breadcrumb>
|
|
);
|
|
};
|
|
|
|
const EmailFormSchema = z.object({
|
|
email: z
|
|
.string()
|
|
.min(1, "ایمیل الزامی است")
|
|
.email("آدرس ایمیل نامعتبر است"),
|
|
});
|
|
|
|
const EmailForm = () => {
|
|
const form = useForm<z.infer<typeof EmailFormSchema>>({
|
|
resolver: zodResolver(EmailFormSchema),
|
|
defaultValues: {
|
|
email: "",
|
|
},
|
|
});
|
|
|
|
function onSubmit(values: z.infer<typeof EmailFormSchema>) {
|
|
console.log(values);
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={form.handleSubmit(onSubmit)} className="w-full">
|
|
<Controller
|
|
control={form.control}
|
|
name="email"
|
|
render={({ field, fieldState }) => (
|
|
<Field data-invalid={fieldState.invalid}>
|
|
<FieldLabel htmlFor="emailInput" className="sr-only">
|
|
ایمیل
|
|
</FieldLabel>
|
|
<div className="w-full">
|
|
<div className="relative flex w-full flex-col gap-2 lg:block">
|
|
<Input
|
|
{...field}
|
|
type="email"
|
|
id="emailInput"
|
|
dir="rtl"
|
|
placeholder="ایمیل خود را وارد کنید"
|
|
className="h-fit bg-background py-4 ps-5 pe-5 text-start lg:pr-[13.75rem]"
|
|
aria-invalid={fieldState.invalid}
|
|
/>
|
|
<div className="top-1/2 right-2.5 lg:absolute lg:-translate-y-1/2">
|
|
<Button
|
|
type="submit"
|
|
className="w-full rounded-full lg:w-fit"
|
|
>
|
|
مشاهده محصول در عمل
|
|
<ArrowRight className="rtl:rotate-180" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
{fieldState.invalid && (
|
|
<FieldError errors={[fieldState.error]} className="py-1" />
|
|
)}
|
|
</div>
|
|
</Field>
|
|
)}
|
|
/>
|
|
</form>
|
|
);
|
|
};
|
|
|
|
const ResourcesCard = ({
|
|
category,
|
|
title,
|
|
thumbnail,
|
|
summary,
|
|
link,
|
|
cta,
|
|
}: Post) => {
|
|
return (
|
|
<a href={link} className="block h-full w-full">
|
|
<Card className="size-full border py-0">
|
|
<CardContent className="p-0">
|
|
<div className="border-b p-2.5 text-start text-sm leading-[1.2] font-medium text-muted-foreground">
|
|
{category}
|
|
</div>
|
|
<AspectRatio ratio={1.520833333} className="overflow-hidden">
|
|
<img
|
|
src={thumbnail}
|
|
alt={title}
|
|
className="block size-full object-cover object-center"
|
|
/>
|
|
</AspectRatio>
|
|
<div className="flex w-full flex-col gap-5 p-5 text-start">
|
|
<h2 className="text-lg leading-none font-bold md:text-xl">
|
|
{title}
|
|
</h2>
|
|
<div className="w-full max-w-[20rem]">
|
|
<p className="text-sm leading-[1.4] font-medium text-foreground">
|
|
{summary}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<Badge className="rounded-full">
|
|
{cta}
|
|
<ArrowRight className="rtl:rotate-180" />
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</a>
|
|
);
|
|
};
|
|
|
|
interface Resources1Props {
|
|
className?: string;
|
|
}
|
|
|
|
const Resources1 = ({ className }: Resources1Props) => {
|
|
return (
|
|
<section dir="rtl" lang="fa" className={cn("pb-32", className)}>
|
|
<div className="bg-muted bg-[url('/block-media/shadcnblocks/block/patterns/dot-pattern-2.svg')] bg-[length:3.125rem_3.125rem] bg-repeat">
|
|
<div className="container flex flex-col items-start justify-start gap-16 py-20 lg:flex-row lg:items-center lg:justify-between">
|
|
<div className="flex w-full flex-col justify-between gap-12">
|
|
<div className="flex w-full max-w-[36rem] flex-col gap-8 text-start">
|
|
<BreadcrumbBlog breadcrumb={BREADCRUMB} />
|
|
<div className="flex w-full flex-col gap-5">
|
|
<h1 className="text-[2.5rem] leading-[1.2] font-semibold md:text-2xll lg:text-2xll">
|
|
مرور گزارشها
|
|
</h1>
|
|
<p className="text-xl leading-[1.4] font-semibold text-foreground">
|
|
بهترین گزارش، گزارشی است که خواننده را با محتوای جذاب و
|
|
پژوهشمحور، در قالبی روشن و قابلدرک، درگیر نگه میدارد.
|
|
</p>
|
|
</div>
|
|
<div className="max-w-[30rem]">
|
|
<EmailForm />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="w-full max-w-[27.5rem]">
|
|
<ResourcesCard {...PRIMARY_POST} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="py-20">
|
|
<div className="container flex flex-col gap-8 text-start">
|
|
<h2 className="text-[1.75rem] leading-none font-medium md:text-[2.25rem] lg:text-[2rem]">
|
|
همه گزارشها
|
|
</h2>
|
|
<div>
|
|
<ResourcesResult posts={POSTS} categories={CATEGORIES} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { Resources1 };
|