diff --git a/src/components/Content/MoreArticles.astro b/src/components/Content/MoreArticles.astro new file mode 100644 index 0000000..df57864 --- /dev/null +++ b/src/components/Content/MoreArticles.astro @@ -0,0 +1,18 @@ +--- +import { getLatestPosts } from '@api/posts.js'; +import ContentGrid from '@components/ContentGrid.astro'; + +const { posts, pageInfo } = await getLatestPosts(11); + +--- + + + + + diff --git a/src/components/Content/RelatedPosts.astro b/src/components/Content/RelatedPosts.astro new file mode 100644 index 0000000..31d2767 --- /dev/null +++ b/src/components/Content/RelatedPosts.astro @@ -0,0 +1,41 @@ +--- +// RelatedPosts.astro +import { getRelatedPosts } from '@api/posts.js'; + +const relatedPosts = await getRelatedPosts(); +const hasPosts = relatedPosts && relatedPosts.length > 0; +--- + +{ + hasPosts && ( + + ) +} \ No newline at end of file diff --git a/src/components/NewsSingle.astro b/src/components/NewsSingle.astro index 122097c..d50f8f6 100644 --- a/src/components/NewsSingle.astro +++ b/src/components/NewsSingle.astro @@ -4,6 +4,8 @@ import Author from '@components/AuthorDisplay.astro'; import Subscribe from '@components/SubscribePost.astro'; import ShareButtons from '@components/ShareButtons.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;
Новость не найдена
)} + + {/* Блок с тегами */} {post.tags?.nodes && post.tags.nodes.length > 0 && (
@@ -91,3 +95,5 @@ const { post, pageInfo } = Astro.props;
)} + + \ No newline at end of file diff --git a/src/lib/api/posts.ts b/src/lib/api/posts.ts index 55548a5..dc444db 100644 --- a/src/lib/api/posts.ts +++ b/src/lib/api/posts.ts @@ -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) { // Создаем уникальный ключ для кэша diff --git a/src/styles/components/RelatedPosts.css b/src/styles/components/RelatedPosts.css new file mode 100644 index 0000000..616db76 --- /dev/null +++ b/src/styles/components/RelatedPosts.css @@ -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; + } + } \ No newline at end of file diff --git a/src/styles/global.css b/src/styles/global.css index 6ac2258..52de374 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -5,6 +5,7 @@ @import './embedded-content.css'; @import './components/ContentGrid.css'; @import './components/theme-colors.css'; +@import './components/RelatedPosts.css'; html{