astro_avtourist/frontend/src/pages/blog/[slug].astro

158 lines
3.9 KiB
Text
Raw Normal View History

---
import ArticleLayout from '@layouts/ArticleLayout.astro';
import { SITE_URL } from '@constants';
import PostCommentForm from '@components/blog/PostCommentForm.astro';
import RelatedPosts from '@components/blog/RelatedPosts.astro';
import ArticleTableOfContents from '@components/blog/ArticleTableOfContents.astro';
import { getPostBySlug, getPosts, getPostImageUrl, getPostVotesStats } from '@lib/pb';
import { marked } from 'marked';
export const prerender = false;
const slug = Astro.params.slug;
if (!slug) {
return Astro.redirect('/blog');
}
const post = await getPostBySlug(slug);
if (!post) {
return Astro.redirect('/blog');
}
const { likes = 0, dislikes = 0 } = await getPostVotesStats(post.id).catch(() => ({ likes: 0, dislikes: 0 }));
// Конвертируем markdown в HTML
const contentHtml = marked(post.content || '');
// Извлекаем заголовки для оглавления
const headingRegex = /^(#{2,3})\s+(.+)$/gm;
const tocItems: { id: string; text: string; level: number }[] = [];
const body = post.content || '';
let match;
let headingIndex = 0;
while ((match = headingRegex.exec(body)) !== null) {
const level = match[1].length;
const text = match[2].trim();
tocItems.push({ level, id: `heading-${headingIndex++}`, text });
}
// Форматируем дату
const formatDate = (date: string) => {
return new Date(date).toLocaleDateString('ru-RU', {
day: 'numeric',
month: 'long',
year: 'numeric'
});
};
// Для related posts берем те же категории
const { posts: relatedPosts } = await getPosts({ perPage: 4, category: post.category });
const filteredRelated = relatedPosts.filter(p => p.slug !== slug).slice(0, 3);
const currentUrl = `${SITE_URL}/blog/${slug}`;
const heroImage = getPostImageUrl(post);
---
<ArticleLayout
title={`${post.title} — автоюрист в Сургуте`}
description={post.description}
canonicalLink={`${SITE_URL}/blog/${slug}`}
breadcrumbs={[
{ label: 'Главная', href: '/' },
{ label: 'Блог', href: '/blog' },
{ label: post.title }
]}
heroImage={heroImage}
heroAlt={post.title}
category={post.category}
postTitle={post.title}
date={formatDate(post.date)}
author={post.author}
readTime={post.readTime}
postId={post.id}
postUrl={currentUrl}
initialLikes={likes}
initialDislikes={dislikes}
>
<!-- Содержимое статьи -->
<div class="post-content" set:html={contentHtml} />
<!-- Форма комментариев -->
<PostCommentForm
postId={post.id}
isAuthorized={false}
/>
<!-- Похожие статьи -->
<RelatedPosts
posts={filteredRelated}
currentSlug={slug}
/>
<!-- Оглавление в сайдбаре -->
<ArticleTableOfContents items={tocItems} slot="sidebar" />
</ArticleLayout>
<style>
/* Post Content */
.post-content {
padding: 0;
color: #334155;
line-height: 1.8;
}
.post-content :global(h2) {
color: #1e293b;
font-size: 1.75rem;
font-weight: 700;
margin: 2rem 0 1rem;
letter-spacing: -0.02em;
}
.post-content :global(h3) {
color: #1e293b;
font-size: 1.375rem;
font-weight: 700;
margin: 1.75rem 0 1rem;
}
.post-content :global(p) {
margin: 0 0 1.25rem;
font-size: 1.05rem;
}
.post-content :global(ul), .post-content :global(ol) {
margin: 1rem 0;
padding-left: 1.5rem;
}
.post-content :global(li) {
margin: 0.5rem 0;
font-size: 1.05rem;
}
.post-content :global(blockquote) {
margin: 2rem 0;
padding: 1.5rem 2rem;
background: #f8fafc;
border-left: 4px solid #d4af37;
border-radius: 0 0.75rem 0.75rem 0;
font-style: italic;
}
@media (max-width: 768px) {
.post-content {
padding: 2rem 0;
}
.post-content :global(h2) {
font-size: 1.5rem;
}
.post-content :global(h3) {
font-size: 1.25rem;
}
}
</style>