add main and colomn items

This commit is contained in:
Profile Profile
2026-01-29 01:30:06 +03:00
parent 3ca3811a5f
commit 49a4638c75
4 changed files with 406 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
---
// src/components/MainPostWidget.astro
import { getLatestMainPost } from '@lib/api/main-posts';
const mainPost = await getLatestMainPost();
const postDate = mainPost ? new Date(mainPost.date) : null;
const imageUrl = mainPost?.featuredImage?.node?.sourceUrl;
const imageAlt = mainPost?.featuredImage?.node?.altText || mainPost?.title || '';
const category = mainPost?.categories?.nodes?.[0];
const categoryName = category?.name || '';
const categoryColor = category?.color || '#2271b1'; // цвет по умолчанию
let authorName = '';
if (mainPost?.coauthors && mainPost.coauthors.length > 0) {
authorName = mainPost.coauthors.map(author => {
if (author.firstName && author.lastName) {
return `${author.firstName} ${author.lastName}`;
}
return author.name;
}).join(', ');
} else if (mainPost?.author) {
const author = mainPost.author.node;
authorName = author.firstName && author.lastName
? `${author.firstName} ${author.lastName}`
: author.name;
}
---
{mainPost && (
<article class="main-post-card" itemscope itemtype="https://schema.org/Article">
<a href={mainPost.uri} class="post-card-link">
<div class="post-image-container">
{imageUrl ? (
<img
src={imageUrl}
alt={imageAlt}
width="400"
height="400"
loading="lazy"
class="post-image"
itemprop="image"
/>
) : (
<div class="post-image-placeholder"></div>
)}
{categoryName && (
<div
class="post-category-badge"
style={`background-color: ${categoryColor}; color: white;`}
>
{categoryName}
</div>
)}
<div class="post-content-overlay">
<div class="post-meta-overlay">
<time
datetime={mainPost.date}
class="post-date-overlay"
itemprop="datePublished"
>
{postDate && postDate.toLocaleDateString('ru-RU', {
day: 'numeric',
month: 'short',
year: 'numeric'
}).replace(' г.', '')}
</time>
</div>
<h3 class="post-title-overlay" itemprop="headline">
{mainPost.title}
</h3>
{authorName && (
<div class="author-name" itemprop="author">
{authorName}
</div>
)}
</div>
</div>
</a>
<div class="sr-only">
<h3 itemprop="headline">
<a href={mainPost.uri} itemprop="url">{mainPost.title}</a>
</h3>
<time datetime={mainPost.date} itemprop="datePublished">
{postDate && postDate.toLocaleDateString('ru-RU')}
</time>
</div>
</article>
)}

149
src/lib/api/colon-posts.ts Normal file
View File

@@ -0,0 +1,149 @@
// src/lib/api/colon-posts.ts
import { fetchGraphQL } from './graphql-client.js';
import { cache } from '@lib/cache/manager.js';
import { CACHE_TTL } from '@lib/cache/cache-ttl';
/**
* Интерфейс для поста колонки (ProfileArticle с colonItem = true)
*/
export interface ColonPost {
id: string;
databaseId: number;
title: string;
uri: string;
date: string;
colonItem: boolean;
excerpt?: string;
featuredImage?: {
node: {
sourceUrl: string;
altText: string;
};
};
author: {
node: {
id: string;
name: string;
firstName: string;
lastName: string;
avatar: {
url: string;
};
uri: string;
};
};
coauthors?: Array<{
id: string;
name: string;
firstName: string;
lastName: string;
url: string;
description: string;
}>;
categories: {
nodes: Array<{
id: string;
name: string;
color?: string;
slug: string;
uri: string;
databaseId: number;
}>;
};
tags: {
nodes: Array<{
id: string;
name: string;
slug: string;
uri: string;
}>;
};
}
/**
* GraphQL запрос для получения последнего поста колонки
*/
const LATEST_COLON_POST_QUERY = `
query GetLatestColonPost {
profileArticles(
where: {
status: PUBLISH
colonItemEquals: true
orderby: { field: DATE, order: DESC }
}
first: 1
) {
nodes {
id
databaseId
title
uri
date
colonItem
excerpt
featuredImage {
node {
sourceUrl(size: LARGE)
altText
}
}
author {
node {
id
name
firstName
lastName
avatar {
url
}
uri
}
}
coauthors {
id
name
firstName
lastName
url
description
}
categories {
nodes {
id
name
color
slug
uri
databaseId
}
}
tags {
nodes {
id
name
slug
uri
}
}
}
}
}
`;
/**
* Получает последний пост колонки
*
* @returns {Promise<ColonPost | null>} Последний пост колонки или null
*/
export async function getLatestColonPost(): Promise<ColonPost | null> {
const cacheKey = 'latest-colon-post';
return await cache.wrap(
cacheKey,
async () => {
const data = await fetchGraphQL(LATEST_COLON_POST_QUERY);
return data?.profileArticles?.nodes?.[0] || null;
},
CACHE_TTL.SHORT
);
}

149
src/lib/api/main-posts.ts Normal file
View File

@@ -0,0 +1,149 @@
// src/lib/api/main-posts.ts
import { fetchGraphQL } from './graphql-client.js';
import { cache } from '@lib/cache/manager.js';
import { CACHE_TTL } from '@lib/cache/cache-ttl';
/**
* Интерфейс для главного поста (ProfileArticle с mainItem = true)
*/
export interface MainPost {
id: string;
databaseId: number;
title: string;
uri: string;
date: string;
mainItem: boolean;
excerpt?: string;
featuredImage?: {
node: {
sourceUrl: string;
altText: string;
};
};
author: {
node: {
id: string;
name: string;
firstName: string;
lastName: string;
avatar: {
url: string;
};
uri: string;
};
};
coauthors?: Array<{
id: string;
name: string;
firstName: string;
lastName: string;
url: string;
description: string;
}>;
categories: {
nodes: Array<{
id: string;
name: string;
color?: string;
slug: string;
uri: string;
databaseId: number;
}>;
};
tags: {
nodes: Array<{
id: string;
name: string;
slug: string;
uri: string;
}>;
};
}
/**
* GraphQL запрос для получения последнего главного поста
*/
const LATEST_MAIN_POST_QUERY = `
query GetLatestMainPost {
profileArticles(
where: {
status: PUBLISH
mainItemEquals: true
orderby: { field: DATE, order: DESC }
}
first: 1
) {
nodes {
id
databaseId
title
uri
date
mainItem
excerpt
featuredImage {
node {
sourceUrl(size: LARGE)
altText
}
}
author {
node {
id
name
firstName
lastName
avatar {
url
}
uri
}
}
coauthors {
id
name
firstName
lastName
url
description
}
categories {
nodes {
id
name
color
slug
uri
databaseId
}
}
tags {
nodes {
id
name
slug
uri
}
}
}
}
}
`;
/**
* Получает последний главный пост
*
* @returns {Promise<MainPost | null>} Последний главный пост или null
*/
export async function getLatestMainPost(): Promise<MainPost | null> {
const cacheKey = 'latest-main-post';
return await cache.wrap(
cacheKey,
async () => {
const data = await fetchGraphQL(LATEST_MAIN_POST_QUERY);
return data?.profileArticles?.nodes?.[0] || null;
},
CACHE_TTL.SHORT
);
}

View File

@@ -1,14 +1,18 @@
---
import { getSiteInfo } from "../lib/wp-api.js";
import { getLatestPosts } from '@api/posts.js';
import { getLatestColonPost } from '@lib/api/colon-posts';
const site = await getSiteInfo();
const { posts, pageInfo } = await getLatestPosts(41); // Сразу деструктурируем
const colonPost = await getLatestColonPost(); //последний пост колонки
// визуальные компоненты
import MainLayout from '@layouts/MainLayout.astro';
import ContentGrid from '@components/ContentGrid.astro';
import EndnewsList from '@components/EndnewsList.astro';
import MainPostWidget from '@/components/MainPostWidget.astro';
//ISR
export const prerender = false;
@@ -21,6 +25,15 @@ export const prerender = false;
<h1>{site.title}</h1>
{site.description && <p>{site.description}</p>}
<MainPostWidget />
{colonPost && (
<div>
<h3>{colonPost.title}</h3>
<a href={colonPost.uri}>Читать колонку</a>
</div>
)}
<div class="maimnewsline">
<EndnewsList />
</div>