add style author page

This commit is contained in:
Profile Profile
2026-03-05 00:23:51 +03:00
parent e5af3fcfd6
commit 440e3da8c7
2 changed files with 179 additions and 191 deletions

View File

@@ -4,186 +4,96 @@ import { fetchGraphQL } from './graphql-client.js';
import { cache } from '@lib/cache/manager.js'; import { cache } from '@lib/cache/manager.js';
import { CACHE_TTL } from '@lib/cache/cache-ttl'; import { CACHE_TTL } from '@lib/cache/cache-ttl';
export async function getPostsByCoauthorName(coauthorName, first = 14, after = null) { // lib/api/authors.js
// Создаем уникальный ключ для кэша export async function getPostsByCoauthorLogin(coauthorLogin, perLoad = 14, after = null) {
const cacheKey = `coauthor-posts-known:${coauthorName}:${first}:${after || 'first-page'}`; const cacheKey = `coauthor-posts:${coauthorLogin}:${perLoad}:${after || 'first-page'}`;
return await cache.wrap( return await cache.wrap(
cacheKey, cacheKey,
async () => { async () => {
const query = ` try {
query GetPostsByCoauthorName($first: Int!, $after: String, $coauthorName: String!) { const baseUrl = import.meta.env.WP_REST_BASE_URL?.replace(/\/$/, '');
# Информация об авторе
users(where: {search: $coauthorName}) { // Формируем URL
nodes { const url = `${baseUrl}/author/${coauthorLogin}/posts?per_page=${perLoad}${
databaseId after ? `&cursor=${encodeURIComponent(after)}` : ''
name }`;
nicename
email const response = await fetch(url);
avatar {
url if (!response.ok) {
} if (response.status === 404) {
description return { posts: [], pageInfo: { hasNextPage: false, endCursor: null } };
posts {
pageInfo {
total
}
}
} }
throw new Error(`HTTP error: ${response.status}`);
} }
# Посты автора const data = await response.json();
contentNodes(
first: $first
after: $after
where: {
contentTypes: [PROFILE_ARTICLE, ANEW]
coauthorName: $coauthorName
orderby: { field: DATE, order: DESC }
}
) {
pageInfo {
hasNextPage
endCursor
}
nodes {
__typename
id
databaseId
uri
date
... on NodeWithTitle { // Преобразуем данные для компонента
title const posts = (data.data || []).map(post => ({
} // ID и ссылки
databaseId: post.id,
uri: post.link, // компонент использует uri
... on NodeWithFeaturedImage { // Основные поля
featuredImage { title: post.title,
node { date: post.date,
sourceUrl(size: LARGE)
altText
}
}
}
# Для кастомных типов // Картинка - приводим к формату GraphQL
... on ProfileArticle { featuredImage: post.featured_image?.medium ? {
categories { node: {
nodes { sourceUrl: post.featured_image.medium,
name altText: post.title
slug
color
}
}
} }
} : null,
... on ANew { // Категории - приводим к формату nodes
categories { categories: {
nodes { nodes: (post.categories || []).map(cat => ({
name name: cat.name,
slug slug: cat.slug,
color color: cat.color || null
} }))
} },
}
... on NodeWithAuthor { // Соавторы - компонент ожидает массив с name и nickname
author { coauthors: (post.coauthors || []).map(coauthor => ({
node { name: coauthor.name,
name nickname: coauthor.id, // id используем как nickname для ссылки
avatar { node: {
url name: coauthor.name,
nickname: coauthor.id
} }
} }))
}
}
# Coauthors для ProfileArticle
... on ProfileArticle {
coauthors {
id
name
firstName
lastName
url
nicename
}
}
# Coauthors для ANew
... on ANew {
coauthors {
id
name
firstName
lastName
url
nicename
}
}
}
}
}
`;
const data = await fetchGraphQL(query, {
first,
after,
coauthorName
});
// Находим автора (первый результат поиска)
const author = data.users?.nodes?.[0];
if (!author) {
return {
author: null,
posts: [],
pageInfo: { hasNextPage: false, endCursor: null },
totalPosts: 0
};
}
// Обрабатываем посты
const posts = data.contentNodes?.nodes?.map(node => {
// Приводим coauthors к единому формату
if (node.coauthors) {
node.coauthors = node.coauthors.map(coauthor => ({
id: coauthor.id,
nickname: coauthor.nickname,
name: coauthor.name || '',
firstName: coauthor.firstName || '',
lastName: coauthor.lastName || '',
url: coauthor.url || ''
})); }));
}
return node;
}) || [];
return { return {
author: {
id: author.databaseId,
name: author.name,
login: author.nicename,
email: author.email,
avatar: author.avatar?.url,
description: author.description,
totalPosts: author.posts?.pageInfo?.total || posts.length
},
posts, posts,
pageInfo: data.contentNodes?.pageInfo || { pageInfo: {
hasNextPage: false, hasNextPage: data.pagination?.has_next || false,
endCursor: null endCursor: data.pagination?.next_cursor || null
}, }
authorName: coauthorName
}; };
} catch (error) {
console.error(`❌ Error fetching author posts:`, error);
return { posts: [], pageInfo: { hasNextPage: false, endCursor: null } };
}
}, },
{ ttl: CACHE_TTL.POSTS } { ttl: 3600 }
); );
} }
// все посты автора // все посты автора
export async function getPostsByCoauthorLogin(login, first = 14, after = null) { export async function getPostsByCoauthorLoginQL(login, first = 14, after = null) {
// Создаем уникальный ключ для кэша // Создаем уникальный ключ для кэша
const cacheKey = `coauthor-posts-by-login:${login}:${first}:${after || 'first-page'}`; const cacheKey = `coauthor-posts-by-login:${login}:${first}:${after || 'first-page'}`;

View File

@@ -10,6 +10,7 @@ const { slug } = Astro.params;
const author = await getAuthorData(slug); const author = await getAuthorData(slug);
const data = await getPostsByCoauthorLogin(slug); const data = await getPostsByCoauthorLogin(slug);
console.log(data);
const posts = data.posts; const posts = data.posts;
// Если автор не найден - 404 // Если автор не найден - 404
@@ -27,6 +28,7 @@ const posts = data.posts;
{author && ( {author && (
<div class="author-card"> <div class="author-card">
{author.avatar && ( {author.avatar && (
<div class="avatar">
<img <img
src={author.avatar} src={author.avatar}
alt={author.name} alt={author.name}
@@ -34,14 +36,14 @@ const posts = data.posts;
height="192" height="192"
class="avatar" class="avatar"
/> />
</div>
)} )}
<h1>{author.name}</h1> <div class="author-info">
{(author.firstName || author.lastName) && ( {(author.firstName || author.lastName) && (
<p class="full-name"> <h1 class="full-name">
{author.firstName} {author.lastName} {author.firstName} {author.lastName}
</p> </h1>
)} )}
{author.bio && ( {author.bio && (
@@ -62,6 +64,7 @@ const posts = data.posts;
</div> </div>
)} )}
</div> </div>
</div>
)} )}
<ContentGrid <ContentGrid
@@ -70,8 +73,83 @@ const posts = data.posts;
slug={slug} slug={slug}
showCount={false} showCount={false}
type='author' type='author'
perLoad={11} perLoad={5}
/> />
</MainLayout> </MainLayout>
<style>
.author-card {
display: flex;
align-items: stretch; /* Все блоки одной высоты */
min-height: 350px;
width: 100%;
}
.author-card .avatar {
width: 100%;
aspect-ratio: 1/1;
object-fit: cover;
display: block;
height:auto;
background: linear-gradient(to right, transparent 40%, #ececec 40%);
}
.author-card .avatar IMG{
max-width: 300px;
margin: 20px auto;
}
.author-card .author-info {
display:flex;
flex-direction: column;
justify-content: center;
gap: 12px;
background-color: #ececec;
padding: 0 42px;
}
.author-info h1{
font-size: 1.5rem;
}
@media (max-width: 769px) {
.author-card {
flex-direction: column;
min-height: auto; /* Убираем минимальную высоту */
background-color: transparent; /* Убираем фон, перенесем на блоки */
}
.author-card .avatar {
width: 100%;
aspect-ratio: auto; /* Убираем фиксированное соотношение */
height: auto;
background: #ececec; /* Простой фон вместо градиента */
padding: 30px 0; /* Отступы сверху/снизу */
display: flex;
align-items: center;
justify-content: center;
}
.author-card .avatar IMG {
width: min(300px, 80%); /* Не больше 192px, но может быть меньше */
aspect-ratio: 1/1; /* Соотношение сторон 1:1 (квадрат) */
object-fit: cover;
margin: 0;
}
.author-card .author-info {
flex: none; /* Убираем flex: 1 */
width: 100%;
padding: 30px 20px; /* Уменьшаем боковые отступы */
background-color: #ececec;
gap: 12px;
}
}
</style>