feat(frontend): add blog editor and interactions
Some checks failed
Frontend CI/CD / build (push) Has been cancelled
Frontend CI/CD / deploy (push) Has been cancelled

This commit is contained in:
2026-06-08 21:31:07 +03:30
parent f2b4cfce1a
commit 49dcb1dd1b
10 changed files with 1019 additions and 35 deletions

View File

@@ -1,6 +1,7 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import Markdown from "@/components/Markdown";
import BlogPostInteractions from "@/components/BlogPostInteractions";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
@@ -35,18 +36,22 @@ export async function generateMetadata({
const { slug } = await params;
const post = await loadPost(slug);
const description = cleanText(post.excerpt || post.content).slice(0, 160);
const metaTitle = post.seo_title || post.og_title || post.title;
const metaDescription = post.seo_description || post.og_description || description;
const canonical = post.canonical_url || `/blog/${post.slug}`;
const image = toAbsoluteUrl(
post.absolute_featured_image_url || post.featured_image,
post.og_image_url || post.absolute_featured_image_url || post.featured_image,
apiBaseUrl,
) ?? `${siteUrl}/favicon.ico`;
return {
title: post.title,
description,
alternates: { canonical: `/blog/${post.slug}` },
title: metaTitle,
description: metaDescription,
alternates: { canonical },
robots: post.noindex ? { index: false, follow: true } : undefined,
openGraph: {
title: post.title,
description,
title: post.og_title || metaTitle,
description: post.og_description || metaDescription,
url: `${siteUrl}/blog/${post.slug}`,
siteName: "انجمن علمی کامپیوتر شرق گیلان",
type: "article",
@@ -57,8 +62,8 @@ export async function generateMetadata({
},
twitter: {
card: "summary_large_image",
title: post.title,
description,
title: post.og_title || metaTitle,
description: post.og_description || metaDescription,
images: [image],
},
};
@@ -72,8 +77,9 @@ export default async function BlogDetailPage({
const { slug } = await params;
const post = await loadPost(slug);
const description = cleanText(post.excerpt || post.content).slice(0, 160);
const metaDescription = post.seo_description || post.og_description || description;
const image = toAbsoluteUrl(
post.absolute_featured_image_url || post.featured_image,
post.og_image_url || post.absolute_featured_image_url || post.featured_image,
apiBaseUrl,
) ?? `${siteUrl}/favicon.ico`;
@@ -81,7 +87,7 @@ export default async function BlogDetailPage({
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: post.title,
description,
description: metaDescription,
image: [image],
datePublished: post.published_at || post.created_at,
dateModified: post.updated_at,
@@ -150,6 +156,12 @@ export default async function BlogDetailPage({
<Markdown content={post.content} justify size="base" />
</CardContent>
</Card>
<BlogPostInteractions
slug={post.slug}
initialLikes={post.likes_count ?? 0}
initialSaves={post.saves_count ?? 0}
initialComments={post.comments_count ?? 0}
/>
</div>
</div>
);