add main and colomn items
This commit is contained in:
95
src/components/MainPostWidget.astro
Normal file
95
src/components/MainPostWidget.astro
Normal 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
149
src/lib/api/colon-posts.ts
Normal 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
149
src/lib/api/main-posts.ts
Normal 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
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,14 +1,18 @@
|
|||||||
---
|
---
|
||||||
import { getSiteInfo } from "../lib/wp-api.js";
|
import { getSiteInfo } from "../lib/wp-api.js";
|
||||||
import { getLatestPosts } from '@api/posts.js';
|
import { getLatestPosts } from '@api/posts.js';
|
||||||
|
import { getLatestColonPost } from '@lib/api/colon-posts';
|
||||||
|
|
||||||
const site = await getSiteInfo();
|
const site = await getSiteInfo();
|
||||||
const { posts, pageInfo } = await getLatestPosts(41); // Сразу деструктурируем
|
const { posts, pageInfo } = await getLatestPosts(41); // Сразу деструктурируем
|
||||||
|
const colonPost = await getLatestColonPost(); //последний пост колонки
|
||||||
|
|
||||||
// визуальные компоненты
|
// визуальные компоненты
|
||||||
import MainLayout from '@layouts/MainLayout.astro';
|
import MainLayout from '@layouts/MainLayout.astro';
|
||||||
import ContentGrid from '@components/ContentGrid.astro';
|
import ContentGrid from '@components/ContentGrid.astro';
|
||||||
import EndnewsList from '@components/EndnewsList.astro';
|
import EndnewsList from '@components/EndnewsList.astro';
|
||||||
|
import MainPostWidget from '@/components/MainPostWidget.astro';
|
||||||
|
|
||||||
|
|
||||||
//ISR
|
//ISR
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
@@ -21,6 +25,15 @@ export const prerender = false;
|
|||||||
<h1>{site.title}</h1>
|
<h1>{site.title}</h1>
|
||||||
{site.description && <p>{site.description}</p>}
|
{site.description && <p>{site.description}</p>}
|
||||||
|
|
||||||
|
<MainPostWidget />
|
||||||
|
|
||||||
|
{colonPost && (
|
||||||
|
<div>
|
||||||
|
<h3>{colonPost.title}</h3>
|
||||||
|
<a href={colonPost.uri}>Читать колонку</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div class="maimnewsline">
|
<div class="maimnewsline">
|
||||||
<EndnewsList />
|
<EndnewsList />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user