diff --git a/src/components/BlogThumbnail.tsx b/src/components/BlogThumbnail.tsx new file mode 100644 index 0000000..7afefc5 --- /dev/null +++ b/src/components/BlogThumbnail.tsx @@ -0,0 +1,58 @@ +import type * as Types from "@/lib/types"; +import { cn } from "@/lib/utils"; + +type BlogThumbnailProps = { + post: Pick; + imageUrl?: string | null; + className?: string; + imageClassName?: string; + priority?: boolean; +}; + +function initials(title: string) { + return title + .trim() + .split(/\s+/) + .filter(Boolean) + .slice(0, 2) + .map((part) => part[0]) + .join(""); +} + +export default function BlogThumbnail({ + post, + imageUrl, + className, + imageClassName, + priority = false, +}: BlogThumbnailProps) { + if (imageUrl) { + return ( +
+ {post.title} +
+ ); + } + + return ( +
+
+
+ + {post.category?.name || "بلاگ"} + + {initials(post.title) || "گـ"} +
+
+ ); +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index cc3a59f..7f21335 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -35,6 +35,24 @@ export function formatJalali(iso?: string, withTime: boolean = true): string { } } +export function formatJalaliDate(iso?: string): string { + if (!iso) return '—'; + try { + const date = new Date(iso); + const locale = Intl.DateTimeFormat.supportedLocalesOf(['fa-IR-u-ca-persian']).length > 0 + ? 'fa-IR-u-ca-persian' + : 'fa-IR'; + + return new Intl.DateTimeFormat(locale, { + day: 'numeric', + month: 'long', + year: 'numeric', + }).format(date); + } catch { + return '—'; + } +} + const DEFAULT_THUMB = "/placeholder.svg"; const pickFirstUrl = (...values: Array) => @@ -66,6 +84,22 @@ export const getEventSeoImageUrl = (event: Types.EventListItemSchema) => export const getThumbUrl = getEventCardImageUrl; +export const getBlogCardImageUrl = (post: Types.PostListSchema) => + [ + post.absolute_featured_image_thumbnail_url, + post.absolute_featured_image_preview_url, + post.absolute_featured_image_url, + post.featured_image, + ].find((value) => Boolean(value)); + +export const getBlogHeroImageUrl = (post: Types.PostListSchema) => + [ + post.absolute_featured_image_preview_url, + post.absolute_featured_image_url, + post.absolute_featured_image_thumbnail_url, + post.featured_image, + ].find((value) => Boolean(value)); + export const getGalleryImagePreviewUrl = ( image: | Types.EventGalleryItem diff --git a/src/views/Blog.tsx b/src/views/Blog.tsx index e164336..c853446 100644 --- a/src/views/Blog.tsx +++ b/src/views/Blog.tsx @@ -1,12 +1,14 @@ "use client"; import { useCallback, useEffect, useState } from "react"; +import BlogThumbnail from "@/components/BlogThumbnail"; +import { Input } from "@/components/ui/input"; import { Link, useLocation, useNavigate } from "@/lib/router"; import { api } from "@/lib/api"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Input } from "@/components/ui/input"; -import type * as Types from "@/lib/types"; import { blogPostPath } from "@/lib/blog-routes"; +import { apiBaseUrl, toAbsoluteUrl } from "@/lib/site"; +import type * as Types from "@/lib/types"; +import { formatJalaliDate, getBlogCardImageUrl } from "@/lib/utils"; type BlogProps = { initialPosts?: Types.PostListSchema[]; @@ -60,59 +62,55 @@ export default function Blog({ }, [location.pathname, navigate, search]); return ( -
-
-

وبلاگ

- -
+
+
+
+
+

خواندنی‌های انجمن

+

بلاگ

+

+ نوشته‌های آموزشی، تجربه‌های دانشجویی و یادداشت‌های تخصصی اعضای انجمن علمی. +

+
setSearch(e.target.value)} - className="max-w-md" + onChange={(event) => setSearch(event.target.value)} + className="max-w-md rounded-full bg-background/80 text-right shadow-sm backdrop-blur" />
{loading ? (

در حال بارگذاری...

) : posts.length === 0 ? ( -

مقاله‌ای یافت نشد

+

+ نوشته‌ای پیدا نشد. +

) : ( -
+
{posts.map((post) => ( - - - {(post.absolute_featured_image_thumbnail_url || post.absolute_featured_image_preview_url || post.absolute_featured_image_url || post.featured_image) ? ( -
- {post.title} -
- ) : null} - - {post.title} - - {post.category?.name && ( - {post.category.name} - )} - {new Date(post.created_at).toLocaleDateString("fa-IR")} - - - - {post.excerpt && ( -

- {post.excerpt} -

- )} -

- نویسنده: {post.author.first_name} {post.author.last_name} -

-
-
+ + +
+

+ {post.title} +

+

+ {post.excerpt || post.seo_description || "خلاصه‌ای برای این نوشته ثبت نشده است."} +

+ +
))}