Build & Push Docker Image / build (push) Has been cancelled
Refresh the homepage around new hero/services/roadmap sections, shared design tokens, and updated brand assets. Co-authored-by: Cursor <cursoragent@cursor.com>
183 lines
6.3 KiB
TypeScript
183 lines
6.3 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import Image from "next/image";
|
|
import { ChevronLeft, ImageIcon } from "lucide-react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Container } from "@/components/container";
|
|
import { getFeaturedImageUrl, type WpPost } from "@/lib/wp-api";
|
|
import { formatDateShamsi } from "@/lib/format-date";
|
|
import { layout } from "@/lib/layout";
|
|
import { cn } from "@/lib/utils";
|
|
import { type } from "@/lib/typography";
|
|
|
|
function stripHtml(html: string): string {
|
|
return html.replace(/<[^>]*>/g, "").trim();
|
|
}
|
|
|
|
/** کارت با تصویر بهصورت پسزمینه و عنوان + توضیح در پایین با overlay */
|
|
function PostCard({
|
|
post,
|
|
size = "default",
|
|
}: {
|
|
post: WpPost;
|
|
size?: "large" | "default";
|
|
}) {
|
|
const excerpt =
|
|
stripHtml(post.excerpt.rendered) ||
|
|
stripHtml(post.content.rendered).slice(0, size === "large" ? 280 : 120);
|
|
const date = post.date ? formatDateShamsi(post.date) : null;
|
|
const authorName = post._embedded?.author?.[0]?.name ?? null;
|
|
const thumbnailUrl = getFeaturedImageUrl(post, size === "large" ? "medium_large" : "medium");
|
|
const href = "#";
|
|
|
|
return (
|
|
<Link
|
|
href={href}
|
|
className={cn(
|
|
"group relative flex overflow-hidden rounded-xl border border-border shadow-sm transition-all duration-300 hover:shadow-lg hover:border-border/80",
|
|
size === "large" ? "min-h-[420px]" : "min-h-[200px]"
|
|
)}
|
|
>
|
|
{/* پسزمینه: تصویر تمام کارت */}
|
|
{thumbnailUrl ? (
|
|
<Image
|
|
src={thumbnailUrl}
|
|
alt=""
|
|
fill
|
|
className="object-cover transition-transform duration-500 ease-out group-hover:scale-105"
|
|
sizes={size === "large" ? "50vw, 600px" : "25vw, 300px"}
|
|
/>
|
|
) : (
|
|
<div className="absolute inset-0 flex items-center justify-center bg-muted" aria-hidden>
|
|
<ImageIcon className="size-12 text-muted-foreground/40" />
|
|
</div>
|
|
)}
|
|
{/* گرادیان شفاف و متن در پایین */}
|
|
<div className="absolute inset-0 bg-gradient-to-t from-black/85 via-black/30 to-transparent" />
|
|
<div
|
|
className="absolute bottom-0 left-0 right-0 p-4 text-white transition-all duration-300 md:-bottom-16 md:p-5 md:[mask-image:linear-gradient(to_bottom,black_0%,transparent)] md:[mask-size:100%_100%] group-hover:bottom-0 group-hover:[mask-image:none]"
|
|
>
|
|
<h3
|
|
className={cn(
|
|
"font-bold leading-tight line-clamp-2 drop-shadow-sm",
|
|
size === "large" ? cn(type.h3, "text-white") : cn(type["card-title"], "text-white")
|
|
)}
|
|
dangerouslySetInnerHTML={{ __html: post.title.rendered }}
|
|
/>
|
|
<p
|
|
className={cn(
|
|
"mt-2 text-white/90 leading-relaxed line-clamp-2",
|
|
size === "large" ? cn(type.body, "line-clamp-3 text-white/90") : cn(type["body-sm"], "text-white/90")
|
|
)}
|
|
>
|
|
{excerpt}
|
|
</p>
|
|
</div>
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
/** کارت ردیفی: تصویر کنار عنوان، بدون توضیح */
|
|
function PostCardRow({ post }: { post: WpPost }) {
|
|
const date = post.date ? formatDateShamsi(post.date) : null;
|
|
const authorName = post._embedded?.author?.[0]?.name ?? null;
|
|
const thumbnailUrl = getFeaturedImageUrl(post, "medium");
|
|
const href = "#";
|
|
|
|
return (
|
|
<Link
|
|
href={href}
|
|
className="group flex min-h-[70px] overflow-hidden rounded-xl transition-all duration-300"
|
|
>
|
|
{/* نیمه عنوان + نویسنده */}
|
|
<div className="flex flex-1 flex-col justify-center p-4">
|
|
<h3
|
|
className={cn(type["card-title"], "line-clamp-2 text-foreground")}
|
|
dangerouslySetInnerHTML={{ __html: post.title.rendered }}
|
|
/>
|
|
</div>
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
const DEFAULT_TITLE = "از دانشنامه";
|
|
const DEFAULT_CAPTION =
|
|
"از داده تا بینش؛ یادداشتها و تجربههایی که مسیر شما را روشنتر میکنند.";
|
|
const DEFAULT_CTA_LABEL = "مشاهده همه مقالات";
|
|
const DEFAULT_CTA_HREF = "#";
|
|
|
|
interface FeaturedPostsGridProps {
|
|
posts: WpPost[];
|
|
/** عنوان بخش */
|
|
title?: string;
|
|
/** کپشن زیر عنوان */
|
|
caption?: string;
|
|
/** متن دکمه CTA */
|
|
ctaLabel?: string;
|
|
/** لینک CTA (پیشفرض: # — آزمایشی) */
|
|
ctaHref?: string;
|
|
}
|
|
|
|
export function FeaturedPostsGrid({
|
|
posts,
|
|
title = DEFAULT_TITLE,
|
|
caption = DEFAULT_CAPTION,
|
|
ctaLabel = DEFAULT_CTA_LABEL,
|
|
ctaHref = DEFAULT_CTA_HREF,
|
|
}: FeaturedPostsGridProps) {
|
|
if (!posts.length) return null;
|
|
|
|
const [first, second, third, fourth] = posts;
|
|
|
|
return (
|
|
<section
|
|
className={cn("section-shell bg-section-canvas", layout.sectionY)}
|
|
aria-label={title}
|
|
>
|
|
<Container>
|
|
<header className="mb-10 md:mb-12">
|
|
<h2 className={cn(type.h2, "text-center")}>
|
|
{title}
|
|
</h2>
|
|
<p className={cn(type["section-intro"], "mx-auto mt-3 max-w-2xl")}>
|
|
{caption}
|
|
</p>
|
|
</header>
|
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3 lg:gap-5">
|
|
{/* کارت بزرگ: در RTL سمت چپ، در LTR سمت راست */}
|
|
{first && (
|
|
<div className="lg:col-span-2 lg:min-h-0">
|
|
<PostCard post={first} size="large" />
|
|
</div>
|
|
)}
|
|
{/* سه کارت کوچک: یک بالا، دو پایین */}
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:order-1 lg:grid-cols-1 lg:gap-5">
|
|
{second && (
|
|
<div className="min-h-[200px]">
|
|
<PostCard post={second} size="default" />
|
|
</div>
|
|
)}
|
|
{third && (
|
|
<div className="min-h-[120px]">
|
|
<PostCard post={third} size="default" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="mt-12 flex flex-col items-center justify-center gap-3 text-center md:mt-16">
|
|
<p className={type.caption}>
|
|
مطالب بیشتر در دانشنامه
|
|
</p>
|
|
<Button asChild size="lg">
|
|
<Link href={ctaHref}>
|
|
{ctaLabel}
|
|
<ChevronLeft aria-hidden />
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
</Container>
|
|
</section>
|
|
);
|
|
}
|