Files
aragrp/components/blog-post-card.tsx
ar.badieiandCursor c2376b3b9e
Build & Push Docker Image / build (push) Has been cancelled
feat: redesign landing page with brand system and section blocks
Refresh the homepage around new hero/services/roadmap sections, shared design tokens, and updated brand assets.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-23 17:00:55 +03:30

175 lines
6.6 KiB
TypeScript

import Link from "next/link";
import Image from "next/image";
import { ImageIcon, User } from "lucide-react";
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { getFeaturedImageUrl, type WpPost } from "@/lib/wp-api";
import { formatDateShamsi } from "@/lib/format-date";
import { formatAuthorName } from "@/lib/blog-utils";
import { type } from "@/lib/typography";
import { cn } from "@/lib/utils";
function stripHtml(html: string): string {
return html.replace(/<[^>]*>/g, "").trim();
}
type BlogPostCardVariant = "default" | "tiny" | "related";
export function BlogPostCard({
post,
variant = "default",
}: {
post: WpPost;
variant?: BlogPostCardVariant;
}) {
const excerpt = stripHtml(post.excerpt.rendered) || stripHtml(post.content.rendered).slice(0, 160);
const modifiedDate = post.modified ? formatDateShamsi(post.modified) : null;
const authorName = formatAuthorName(post._embedded?.author?.[0]?.name);
const thumbnailUrl = getFeaturedImageUrl(post, "full");
if (variant === "related") {
return (
<Link href="#" className="block h-full min-w-0">
<Card className="h-full overflow-hidden transition-all rounded-md duration-300 hover:shadow-md flex flex-col p-0 gap-0">
<CardContent className="p-3 sm:p-4 grid grid-cols-3 gap-3 sm:gap-4 min-w-0">
{thumbnailUrl ? (
<div className="relative rounded-md aspect-[4/3] w-full overflow-hidden bg-muted shrink-0 col-span-1 min-w-0">
<Image
src={thumbnailUrl}
alt=""
fill
className="object-cover"
quality={100}
sizes="(max-width: 640px) 33vw, (max-width: 1024px) 20vw, 200px"
/>
</div>
) : (
<div
className="relative rounded-md flex aspect-[4/3] w-full items-center justify-center overflow-hidden bg-muted shrink-0 col-span-1"
aria-hidden
>
<ImageIcon className="size-10 text-muted-foreground/50" />
</div>
)}
<div className="col-span-2 min-w-0 flex flex-col justify-center">
<h4 className={cn(type["card-title"], "line-clamp-3")}>
{post.title.rendered}
</h4>
<div className={cn("mt-3 inline-flex w-full flex-wrap items-center gap-x-4 gap-y-1 rounded-sm bg-muted p-1.5", type["body-sm"], "text-muted-foreground")}>
{authorName && (
<span className="inline-flex flex-1 items-center gap-1.5 min-w-0">
<User className="size-4 shrink-0" aria-hidden />
<span className="truncate">{authorName}</span>
</span>
)}
{modifiedDate && (
<time dateTime={post.modified} className="shrink-0">
{modifiedDate}
</time>
)}
</div>
</div>
</CardContent>
</Card>
</Link>
);
}
if (variant === "tiny") {
return (
<Link href="#" className="block h-full">
<Card className="h-full overflow-hidden transition-all rounded-md duration-300 hover:shadow-md flex flex-col p-2">
<CardContent className="p-2 grid grid-cols-3 gap-4">
{thumbnailUrl ? (
<div className="relative rounded-md aspect-[4/3] w-full overflow-hidden bg-muted shrink-0 col-span-1">
<Image
src={thumbnailUrl}
alt=""
fill
className="object-cover"
quality={100}
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 400px"
/>
</div>
) : (
<div
className="relative rounded-md flex aspect-[4/3] w-full items-center justify-center overflow-hidden bg-muted shrink-0"
aria-hidden
>
<ImageIcon className="size-8 text-muted-foreground/50" />
</div>
)}
<div className="col-span-2">
<h4 className={cn(type["body-sm"], "line-clamp-2 font-semibold leading-snug")}>
{post.title.rendered}
</h4>
<div className={cn("mt-2 inline-flex w-full flex-wrap items-center gap-x-4 gap-y-1 rounded-sm bg-muted p-1", type.caption)}>
{authorName && (
<span className="inline-flex flex-1 items-center gap-1.5">
<User className="size-3.5 shrink-0" aria-hidden />
{authorName}
</span>
)}
{modifiedDate && (
<time dateTime={post.modified}>
{modifiedDate}
</time>
)}
</div>
</div>
</CardContent>
</Card>
</Link>
);
}
return (
<Link href="#" className="block h-full">
<Card className="h-full overflow-hidden transition-all duration-300 hover:-translate-y-1 hover:shadow-lg pt-0 gap-1">
{thumbnailUrl ? (
<div className="relative aspect-video w-full overflow-hidden bg-muted">
<Image
src={thumbnailUrl}
alt=""
fill
className="object-cover"
quality={100}
priority
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 400px"
/>
</div>
) : (
<div
className="relative flex aspect-video w-full items-center justify-center overflow-hidden bg-muted"
aria-hidden
>
<ImageIcon className="size-12 text-muted-foreground/50" />
</div>
)}
<CardHeader className="pb-2">
<CardTitle className="my-2 line-clamp-2">
{post.title.rendered}
</CardTitle>
<div className={cn("inline-flex flex-wrap items-center gap-x-4 gap-y-1 rounded-sm bg-muted p-1", type.caption)}>
{authorName && (
<span className="inline-flex flex-1 items-center gap-1.5">
<User className="size-3.5 shrink-0" aria-hidden />
{authorName}
</span>
)}
{modifiedDate && (
<time dateTime={post.modified}>
{modifiedDate}
</time>
)}
</div>
</CardHeader>
<CardContent className="pt-0">
<p className={cn(type["card-desc"], "line-clamp-3")}>
{excerpt}
</p>
</CardContent>
</Card>
</Link>
);
}