add RelatedPosts and MoreArticles
This commit is contained in:
18
src/components/Content/MoreArticles.astro
Normal file
18
src/components/Content/MoreArticles.astro
Normal 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}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
41
src/components/Content/RelatedPosts.astro
Normal file
41
src/components/Content/RelatedPosts.astro
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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 />
|
||||||
@@ -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) {
|
||||||
// Создаем уникальный ключ для кэша
|
// Создаем уникальный ключ для кэша
|
||||||
|
|||||||
152
src/styles/components/RelatedPosts.css
Normal file
152
src/styles/components/RelatedPosts.css
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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{
|
||||||
|
|||||||
Reference in New Issue
Block a user