add RelatedPosts and MoreArticles

This commit is contained in:
Profile Profile
2026-03-15 22:50:42 +03:00
parent ed96b01985
commit 944069f48d
6 changed files with 262 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
---
import { getLatestPosts } from '@api/posts.js';
import ContentGrid from '@components/ContentGrid.astro';
const { posts, pageInfo } = await getLatestPosts(11);
---
<ContentGrid
items={posts}
pageInfo={pageInfo}
type="latest"
perLoad={11}
showCount={false}
/>

View File

@@ -0,0 +1,41 @@
---
// RelatedPosts.astro
import { getRelatedPosts } from '@api/posts.js';
const relatedPosts = await getRelatedPosts();
const hasPosts = relatedPosts && relatedPosts.length > 0;
---
{
hasPosts && (
<section class="related-posts">
<div class="related-container">
<div class="related-posts__header">
<h2 class="related-posts__title">САМОЕ ЧИТАЕМОЕ</h2>
</div>
<div class="related-posts__flex">
{relatedPosts.map((post) => (
<a href={post.url} class="related-post">
{post.image ? (
<div class="related-post__image-wrapper">
<img
src={post.image}
alt={post.imageAlt}
class="related-post__image"
loading="lazy"
/>
</div>
) : (
<div class="related-post__image-wrapper related-post__image-wrapper--placeholder">
<span>Нет изображения</span>
</div>
)}
<h3 class="related-post__title">{post.title}</h3>
</a>
))}
</div>
</div>
</section>
)
}

View File

@@ -4,6 +4,8 @@ import Author from '@components/AuthorDisplay.astro';
import Subscribe from '@components/SubscribePost.astro'; import Subscribe from '@components/SubscribePost.astro';
import ShareButtons from '@components/ShareButtons.astro'; import ShareButtons from '@components/ShareButtons.astro';
import EmbeddedPost from '@components/EmbeddedPost.astro'; // шаблоны ссылок на статьи import EmbeddedPost from '@components/EmbeddedPost.astro'; // шаблоны ссылок на статьи
import RelatedPosts from '@components/Content/RelatedPosts.astro';
import MoreArticles from '@components/Content/MoreArticles.astro';
@@ -73,6 +75,8 @@ const { post, pageInfo } = Astro.props;
<div>Новость не найдена</div> <div>Новость не найдена</div>
)} )}
{/* Блок с тегами */} {/* Блок с тегами */}
{post.tags?.nodes && post.tags.nodes.length > 0 && ( {post.tags?.nodes && post.tags.nodes.length > 0 && (
<div class="tags-block"> <div class="tags-block">
@@ -91,3 +95,5 @@ const { post, pageInfo } = Astro.props;
</div> </div>
)} )}
<RelatedPosts />
<MoreArticles />

View File

@@ -244,6 +244,50 @@ export async function getPostsByCategory(slug, first = 14, after = null) {
); );
} }
export async function getRelatedPosts() {
const cacheKey = 'related-posts-widget';
return await cache.wrap(
cacheKey,
async () => {
const query = `
query GetRelatedPosts {
profileArticles(
first: 3
where: { orderby: { field: DATE, order: DESC } }
) {
nodes {
title
uri
featuredImage {
node {
sourceUrl(size: THUMBNAIL) # THUMBNAIL для компактности
altText
}
}
}
}
}
`;
const data = await fetchGraphQL(query);
// Форматируем данные для удобного использования
const posts = data.profileArticles?.nodes?.map(post => ({
title: post.title,
// Убираем домен из URL, оставляем только путь
url: post.uri.replace(/^https?:\/\/[^\/]+/, ''),
image: post.featuredImage?.node?.sourceUrl || null,
imageAlt: post.featuredImage?.node?.altText || post.title
})) || [];
return posts;
},
{ ttl: CACHE_TTL.POSTS } // Кэш на 1 час, так как это виджет
);
}
export async function getPostsByTag(slug, first = 14, after = null) { export async function getPostsByTag(slug, first = 14, after = null) {
// Создаем уникальный ключ для кэша // Создаем уникальный ключ для кэша

View File

@@ -0,0 +1,152 @@
/* Сброс отступов для контейнера */
.related-posts {
padding: 40px 0;
margin: 0;
width: 100%;
}
.related-container {
width: 100%;
}
/* Хедер с серым фоном и черной рамкой */
.related-posts__header {
background-color: #f5f5f5;
border-top: 4px solid #000;
padding: 15px 0 15px 15px;
margin-bottom: 20px;
}
/* Заголовок */
.related-posts__title {
font-size: 1rem;
font-weight: bold;
margin: 0;
color: #333;
text-transform: uppercase;
letter-spacing: 1px;
}
/* Flex контейнер - строго 3 колонки на десктопе */
.related-posts__flex {
display: flex;
flex-wrap: wrap;
gap: 30px;
}
/* Карточка поста - строго 3 в ряд на всех десктопах */
.related-post {
text-decoration: none;
color: inherit;
display: flex;
flex-direction: column;
transition: transform 0.2s ease;
background-color: transparent;
flex: 0 1 calc(33.333% - 20px); /* Изменил с 1 1 на 0 1 */
min-width: 0; /* Сбрасываем min-width для десктопа */
}
.related-post:hover {
transform: translateY(-4px);
}
/* Контейнер для изображения */
.related-post__image-wrapper {
position: relative;
width: 100%;
padding-bottom: 66.67%; /* Соотношение сторон 3:2 */
background-color: #e0e0e0;
border-radius: 8px;
overflow: hidden;
margin-bottom: 15px;
}
.related-post__image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
/* Плейсхолдер если нет изображения */
.related-post__image-wrapper--placeholder {
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 14px;
}
/* Заголовок поста */
.related-post__title {
font-size: 16px;
line-height: 1.4;
margin: 0;
color: #333;
font-weight: 500;
}
.related-post:hover .related-post__title {
color: #0066cc;
}
/* ТОЛЬКО для мобильных - один в ряд */
@media (max-width: 768px) {
.related-posts {
padding: 30px 0;
}
.related-posts__header {
padding: 12px 0;
margin-bottom: 20px;
}
.related-posts__title {
font-size: 20px;
}
.related-posts__flex {
gap: 20px;
}
.related-post {
flex: 1 1 100%; /* На мобильных полная ширина */
flex-direction: row;
align-items: center;
gap: 15px;
}
.related-post__image-wrapper {
width: 100px;
padding-bottom: 75px;
margin-bottom: 0;
flex-shrink: 0;
}
.related-post__title {
font-size: 15px;
flex: 1;
}
}
/* Очень маленькие экраны */
@media (max-width: 480px) {
.related-posts__header {
padding: 10px 0;
}
.related-posts__title {
font-size: 18px;
}
.related-post__image-wrapper {
width: 80px;
padding-bottom: 60px;
}
.related-post__title {
font-size: 14px;
}
}

View File

@@ -5,6 +5,7 @@
@import './embedded-content.css'; @import './embedded-content.css';
@import './components/ContentGrid.css'; @import './components/ContentGrid.css';
@import './components/theme-colors.css'; @import './components/theme-colors.css';
@import './components/RelatedPosts.css';
html{ html{