From 7739647549fec04fa4d5c9c4bcff175c2890d1d7 Mon Sep 17 00:00:00 2001 From: Profile Profile Date: Mon, 2 Mar 2026 00:39:07 +0300 Subject: [PATCH] add tags logic --- src/components/Header/Header.astro | 23 +- src/components/NewsSingle.astro | 89 ++++++-- src/lib/api/posts.ts | 315 ++++++++++++++++++++++----- src/pages/[...slug].astro | 10 +- src/pages/author/[slug]/[page].astro | 74 +++++++ src/pages/author/[slug]/index.astro | 56 +++++ src/pages/load-more-posts.astro | 4 +- src/pages/tag/[slug]/[page].astro | 0 src/pages/tag/[slug]/___index.astro | 68 ++++++ src/pages/tag/[slug]/index.astro | 82 +++---- src/styles/global.css | 2 +- 11 files changed, 585 insertions(+), 138 deletions(-) create mode 100644 src/pages/author/[slug]/[page].astro create mode 100644 src/pages/author/[slug]/index.astro create mode 100644 src/pages/tag/[slug]/[page].astro create mode 100644 src/pages/tag/[slug]/___index.astro diff --git a/src/components/Header/Header.astro b/src/components/Header/Header.astro index 0d8e9fa..5b9ddc3 100644 --- a/src/components/Header/Header.astro +++ b/src/components/Header/Header.astro @@ -15,8 +15,8 @@
Профиль {category && ( -
- {category.name} + )} @@ -32,6 +32,25 @@ .top-bar{ display: flex; justify-content: space-between; + align-items: center; } + .header__subtitle{ + font-size: 22px; + font-weight: bold; + margin-left: 42px; + position: relative; + } + + .header__subtitle::before{ + content: ''; + position: absolute; + top: 50%; + left: -15px; + width: 3px; + height: 80%; + border-left: 3px solid; + transform: translate(0, -40%); + } + \ No newline at end of file diff --git a/src/components/NewsSingle.astro b/src/components/NewsSingle.astro index d5185cb..84e1808 100644 --- a/src/components/NewsSingle.astro +++ b/src/components/NewsSingle.astro @@ -18,19 +18,18 @@ const { post, pageInfo } = Astro.props;
- +
+ {post.date && ( + + )} +
+ +
+ +
+

{post.title}

{post.secondaryTitle &&

{post.secondaryTitle}

} @@ -57,6 +56,7 @@ const { post, pageInfo } = Astro.props; )} )} + {post.content &&
} @@ -64,15 +64,30 @@ 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 1017e68..a14a46f 100644 --- a/src/lib/api/posts.ts +++ b/src/lib/api/posts.ts @@ -114,62 +114,95 @@ export async function getPostsByCategory(slug, first = 14, after = null) { cacheKey, async () => { const query = ` - query GetPostsByCategory($first: Int!, $after: String, $slug: String!) { - profileArticles( - first: $first - after: $after - where: { - orderby: { field: DATE, order: DESC } - categoryName: $slug - } - ) { - pageInfo { - hasNextPage - endCursor - } - edges { - cursor - node { - id - databaseId - title - uri - date - featuredImage { - node { - sourceUrl(size: LARGE) - altText - } - } - author { - node { - id - name - firstName - lastName - avatar { - url - } - uri - } - } - # Соавторы как массив - coauthors { + query GetPostsByCategory($first: Int!, $after: String, $slug: ID!) { + category(id: $slug, idType: SLUG) { + name + contentNodes( + first: $first + after: $after + where: { + contentTypes: [PROFILE_ARTICLE, ANEW] + orderby: { field: DATE, order: DESC } + } + ) { + pageInfo { + hasNextPage + endCursor + } + edges { + cursor + node { + __typename id - name - firstName - lastName - url - description + databaseId + uri + date + + ... on NodeWithTitle { + title + } + + ... on NodeWithFeaturedImage { + featuredImage { + node { + sourceUrl(size: LARGE) + altText + } + } + } + + # Для ваших кастомных типов + ... on ProfileArticle { + categories { + nodes { + name + slug + color + } + } } - categories { - nodes { - id - name - color - slug - uri - databaseId + + ... on ANew { + categories { + nodes { + name + slug + color + } + } + } + + ... on NodeWithAuthor { + author { + node { + name + avatar { + url + } + } + } + } + + # Coauthors для ProfileArticle (без description) + ... on ProfileArticle { + coauthors { + id + name + firstName + lastName + url + } + } + + # Coauthors для ANew (без description) + ... on ANew { + coauthors { + id + name + firstName + lastName + url + } } } } @@ -180,11 +213,31 @@ export async function getPostsByCategory(slug, first = 14, after = null) { const data = await fetchGraphQL(query, { first, after, slug }); - const posts = data.profileArticles?.edges?.map(edge => edge.node) || []; + // Обрабатываем посты + const posts = data.category?.contentNodes?.edges?.map(edge => { + const node = edge.node; + + // Приводим coauthors к единому формату (если нужно) + if (node.coauthors) { + node.coauthors = node.coauthors.map(author => ({ + id: author.id, + name: author.name || '', + firstName: author.firstName || '', + lastName: author.lastName || '', + url: author.url || '' + })); + } + + return node; + }) || []; return { posts, - pageInfo: data.profileArticles?.pageInfo || { hasNextPage: false, endCursor: null } + pageInfo: data.category?.contentNodes?.pageInfo || { + hasNextPage: false, + endCursor: null + }, + categoryName: data.category?.name || '' }; }, { ttl: CACHE_TTL.POSTS } @@ -192,6 +245,156 @@ export async function getPostsByCategory(slug, first = 14, after = null) { } +export async function getPostsByTag(slug, first = 14, after = null) { + // Создаем уникальный ключ для кэша + const cacheKey = `tag-posts:${slug}:${first}:${after || 'first-page'}`; + + return await cache.wrap( + cacheKey, + async () => { + const query = ` + query GetPostsByTag($first: Int!, $after: String, $slug: ID!) { + tag(id: $slug, idType: NAME) { + id + databaseId + name + slug + count + description + + contentNodes( + first: $first + after: $after + where: { + contentTypes: [PROFILE_ARTICLE, ANEW] + orderby: { field: DATE, order: DESC } + } + ) { + pageInfo { + hasNextPage + endCursor + } + edges { + cursor + node { + __typename + id + databaseId + uri + date + + ... on NodeWithTitle { + title + } + + ... on NodeWithFeaturedImage { + featuredImage { + node { + sourceUrl(size: LARGE) + altText + } + } + } + + ... on NodeWithAuthor { + author { + node { + name + avatar { + url + } + } + } + } + + ... on ProfileArticle { + categories { + nodes { + name + slug + } + } + } + + ... on ANew { + categories { + nodes { + name + slug + } + } + } + + ... on ProfileArticle { + coauthors { + id + name + } + } + + ... on ANew { + coauthors { + id + name + } + } + } + } + } + } + } + `; + + console.log('Fetching with:', { first, after, slug }); // Добавим лог параметров + + const data = await fetchGraphQL(query, { first, after, slug }); + + + // Проверяем структуру data + if (!data?.tag) { + console.log('Tag not found'); + return { + posts: [], + pageInfo: { hasNextPage: false, endCursor: null }, + tagName: slug, + tagSlug: slug, + tagCount: 0, + tagDescription: '' + }; + } + + // Обрабатываем посты + const posts = data.tag?.contentNodes?.edges?.map(edge => { + const node = edge.node; + + // Приводим данные к единому формату + return { + ...node, + // Убеждаемся, что coauthors всегда массив + coauthors: node.coauthors || [], + // Убеждаемся, что categories всегда есть + categories: node.categories || { nodes: [] } + }; + }) || []; + + return { + posts, + pageInfo: data.tag?.contentNodes?.pageInfo || { + hasNextPage: false, + endCursor: null + }, + tagName: data.tag?.name || slug, + tagSlug: data.tag?.slug || slug, + tagCount: data.tag?.count || 0, + tagDescription: data.tag?.description || '' + }; + }, + { ttl: CACHE_TTL.POSTS } + ); +} + + + diff --git a/src/pages/[...slug].astro b/src/pages/[...slug].astro index 81a45b9..840a1df 100644 --- a/src/pages/[...slug].astro +++ b/src/pages/[...slug].astro @@ -13,7 +13,7 @@ import { detectPageType } from '@lib/detect-page-type'; export const prerender = false; -const pathname = Astro.url.pathname; // "/news/society/chto-sluchilos-nochju-27-oktyabrya-2025-goda-1772178/" +const pathname = Astro.url.pathname; const pageInfo = detectPageType(pathname); //определяем тип страницы @@ -37,7 +37,7 @@ if (pageInfo.type === 'single') { //одиночная статья try { article = await getProfileArticleById(pageInfo.postId); //получвем данные поста - title=article.titleж + title=article.title } catch (error) { console.error('Error fetching node:', error); } @@ -64,6 +64,12 @@ if (pageInfo.type === 'single') { //одиночная статья category={category} > +{/* Page (страница) */} +{pageInfo.type === 'unknown' && ( +
✅ Это страница: {pageInfo.pageSlug}
+)} + + {/* Single post */} {pageInfo.type === 'single' && article && ( diff --git a/src/pages/author/[slug]/[page].astro b/src/pages/author/[slug]/[page].astro new file mode 100644 index 0000000..2c84e2e --- /dev/null +++ b/src/pages/author/[slug]/[page].astro @@ -0,0 +1,74 @@ +--- +// pages/author/[slug]/[page].astro +import { wpClient } from '@lib/wp-client'; +import MainLayout from '@layouts/MainLayout.astro'; + +export const prerender = false; + +const { slug, page } = Astro.params; + +// Преобразуем номер страницы в cursor +// Для страницы 1: cursor = 0 +// Для страницы 2: нужно знать ID последнего поста с первой страницы +// Это сложнее для cursor-based пагинации, поэтому либо: +// Вариант 1: Используем номер страницы как offset (нужно изменить эндпоинт) +// Вариант 2: Делаем отдельный запрос для получения cursor по номеру страницы + +const currentPage = parseInt(page) || 1; +let cursor = 0; + +// Если страница > 1, нам нужно получить cursor для этой страницы +// Для простоты пока используем номер страницы как offset в URL +// или модифицируем эндпоинт для поддержки page + +// Временное решение: используем cursor = (page - 1) * limit +// Но это не точно, лучше модифицировать эндпоинт +const limit = 10; +const estimatedCursor = (currentPage - 1) * limit; + +const data = await wpClient.get(`my/v1/author-posts/${slug}`, { + cursor: currentPage === 1 ? 0 : estimatedCursor, + limit +}); + +//if (!data) { +// return Astro.redirect('/404'); +//} + +const { author, posts, pagination } = data; +--- + + +
+

{author.name}

+

Статьи автора

+
+ +
+ {posts.map(post => ( +
+

+ {post.title} +

+ + +
+ ))} +
+ + {pagination.has_more && ( +
+ +
+ )} +
\ No newline at end of file diff --git a/src/pages/author/[slug]/index.astro b/src/pages/author/[slug]/index.astro new file mode 100644 index 0000000..5e3e566 --- /dev/null +++ b/src/pages/author/[slug]/index.astro @@ -0,0 +1,56 @@ +--- +// pages/author/[slug]/index.astro +import { wpClient } from '@lib/wp-client'; + +export const prerender = false; + +const { slug } = Astro.params; + +// Функция для получения данных автора +async function getAuthorData(authorSlug) { + try { + // Используем ваш кастомный эндпоинт или стандартный WP endpoint + const data = await wpClient.get(`my/v1/author-posts/${authorSlug}/1`); + + if (!data) { + // Если кастомный эндпоинт не работает, пробуем стандартный + const users = await wpClient.get('wp/v2/users', { slug: authorSlug }); + if (users && users.length > 0) { + return { author: users[0], posts: [] }; + } + return null; + } + + return data; + } catch (error) { + console.error('Error fetching author:', error); + return null; + } +} + +const authorData = await getAuthorData(slug); + +// Если автор не найден - 404 +//if (!authorData) { +// return Astro.redirect('/404'); +//} + +const { author, posts } = authorData; +--- + +

Author: {author.name || slug}

+ +{posts && posts.length > 0 ? ( +
+

Статьи автора:

+ +
+) : ( +

У автора пока нет статей

+)} \ No newline at end of file diff --git a/src/pages/load-more-posts.astro b/src/pages/load-more-posts.astro index 590af5d..819a81d 100644 --- a/src/pages/load-more-posts.astro +++ b/src/pages/load-more-posts.astro @@ -26,7 +26,7 @@ console.log('📥 Load more request:', { perLoad, after, type, slug, startIndex // Импортируем функции для получения данных -const { getLatestPosts, getPostsByCategory, getAuthorPosts, getTagPosts } = +const { getLatestPosts, getPostsByCategory, getAuthorPosts, getPostsByTag } = await import('../lib/api/posts'); let result; @@ -43,7 +43,7 @@ switch (type) { break; case 'tag': if (!slug) throw new Error('Slug required for tag'); - result = await getTagPosts(slug, perLoad, after); // Используем perLoad + result = await getPostsByTag(slug, perLoad, after); // Используем perLoad break; case 'latest': default: diff --git a/src/pages/tag/[slug]/[page].astro b/src/pages/tag/[slug]/[page].astro new file mode 100644 index 0000000..e69de29 diff --git a/src/pages/tag/[slug]/___index.astro b/src/pages/tag/[slug]/___index.astro new file mode 100644 index 0000000..12349c3 --- /dev/null +++ b/src/pages/tag/[slug]/___index.astro @@ -0,0 +1,68 @@ +--- + +import { slugParse } from '@utils/slugParser'; + + +//api +import { getTag, getTagWithPostsById } from '@api/tags.js'; +import { getArchivePostsById } from '@/lib/api/archiveById'; + +import MainLayout from '@layouts/MainLayout.astro'; +import TagArchive from '@/templates/TagArchive.astro'; + +//ISR +export const prerender = false; + +const { slug } = Astro.params; + +// Используем функцию (правильное название) +const { slug: tagSlug, page: currentPage } = slugParse(slug); + +// Если slug пустой - 404 +if (!tagSlug) { + return Astro.redirect('/404'); +} + + +// Получаем данные тега +const tag = await getTag(tagSlug); + +if (!tag) { + return Astro.redirect('/404'); +} + + +// ---------------------------- +// fetch archive +// ---------------------------- + +const perPage = 35; + +const { posts, pageInfo } = await getArchivePostsById({ + type: 'tag', + id: tag.databaseId, + page: currentPage, + perPage, +}); + +// 404 если страница невалидна +if (!posts.length && currentPage > 1) { + return Astro.redirect('/404'); +} + +--- + + + + + + \ No newline at end of file diff --git a/src/pages/tag/[slug]/index.astro b/src/pages/tag/[slug]/index.astro index 12349c3..e6ac76d 100644 --- a/src/pages/tag/[slug]/index.astro +++ b/src/pages/tag/[slug]/index.astro @@ -1,68 +1,38 @@ --- - -import { slugParse } from '@utils/slugParser'; - - -//api -import { getTag, getTagWithPostsById } from '@api/tags.js'; -import { getArchivePostsById } from '@/lib/api/archiveById'; - import MainLayout from '@layouts/MainLayout.astro'; -import TagArchive from '@/templates/TagArchive.astro'; +import ContentGrid from '@components/ContentGrid.astro'; -//ISR -export const prerender = false; +import { getPostsByTag } from '@lib/api/posts'; + +export const prerender = false; const { slug } = Astro.params; -// Используем функцию (правильное название) -const { slug: tagSlug, page: currentPage } = slugParse(slug); +// Декодируем URL и преобразуем в формат для WP +const decodedSlug = slug ? decodeURIComponent(slug.replace(/\+/g, ' ')) : ''; -// Если slug пустой - 404 -if (!tagSlug) { - return Astro.redirect('/404'); -} - - -// Получаем данные тега -const tag = await getTag(tagSlug); - -if (!tag) { - return Astro.redirect('/404'); -} - - -// ---------------------------- -// fetch archive -// ---------------------------- - -const perPage = 35; - -const { posts, pageInfo } = await getArchivePostsById({ - type: 'tag', - id: tag.databaseId, - page: currentPage, - perPage, -}); - -// 404 если страница невалидна -if (!posts.length && currentPage > 1) { - return Astro.redirect('/404'); -} +// Получаем данные по тегу +const result = await getPostsByTag(decodedSlug); +const posts = result?.posts || []; +const pageInfo = result?.pageInfo || {}; +const tagName = result?.tagName || slug; +const hasNextPage = pageInfo?.hasNextPage || false; +const endCursor = pageInfo?.endCursor || null; --- - - - - \ No newline at end of file +

#{decodedSlug}

+ + + \ No newline at end of file diff --git a/src/styles/global.css b/src/styles/global.css index da6418c..0b76be3 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -10,7 +10,7 @@ html{ } body { - + font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; font-size: 18px; }