diff --git a/src/components/ColonPost.astro b/src/components/ColonPost.astro
index ee922db..4ea302e 100644
--- a/src/components/ColonPost.astro
+++ b/src/components/ColonPost.astro
@@ -1,8 +1,11 @@
---
-import { getLatestColonPost } from '@lib/api/colon-posts';
+
import Author from '@components/AuthorDisplay.astro';
-const colonPost = await getLatestColonPost();
+const {
+ colonPost=[]
+} = Astro.props;
+
---
{colonPost && (
@@ -42,150 +45,4 @@ const colonPost = await getLatestColonPost();
-)}
-
-
\ No newline at end of file
+)}
\ No newline at end of file
diff --git a/src/components/Header/Header.astro b/src/components/Header/Header.astro
index dfa9640..99fc8be 100644
--- a/src/components/Header/Header.astro
+++ b/src/components/Header/Header.astro
@@ -9,6 +9,7 @@ let menuItems = [];
---
diff --git a/src/components/MainLine.astro b/src/components/MainLine.astro
index d4bea1a..4a4512f 100644
--- a/src/components/MainLine.astro
+++ b/src/components/MainLine.astro
@@ -1,128 +1,26 @@
---
+
// src/components/MainLine.astro
import EndnewsList from '@components/EndnewsList.astro';
import MainPostWidget from '@components/MainPostWidget.astro';
import ColonPost from '@components/ColonPost.astro';
+
+const {
+ mainPost=[],
+ colonPost=[]
+} = Astro.props;
+
---
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/components/MainPostWidget.astro b/src/components/MainPostWidget.astro
index 644fe7f..d875cef 100644
--- a/src/components/MainPostWidget.astro
+++ b/src/components/MainPostWidget.astro
@@ -1,11 +1,11 @@
---
-// src/components/MainPostWidget.astro
-import { getLatestMainPost } from '@lib/api/main-posts';
import Author from '@components/AuthorDisplay.astro';
import CategoryBadge from '@components/CategoryBadge.astro'; // цветная плитка рубрик
-const mainPost = await getLatestMainPost();
+const {
+ mainPost=[]
+} = Astro.props;
if (!mainPost) return null;
@@ -88,115 +88,3 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
{categoryName && }
-
diff --git a/src/lib/api/posts.ts b/src/lib/api/posts.ts
index dc444db..9e42f6d 100644
--- a/src/lib/api/posts.ts
+++ b/src/lib/api/posts.ts
@@ -11,101 +11,156 @@ export interface AnewsPost {
date: string;
}
-export async function getLatestPosts(first = 14, after = null) {
- // Создаем уникальный ключ для кэша
- const cacheKey = `latest-posts:${first}:${after || 'first-page'}`;
+
+
+export async function getLatestPosts(first = 14, after = null, excludeIds = []) {
+ // Нормализуем excludeIds - работаем только с databaseId (числа или строки)
+ const excludeArray = Array.isArray(excludeIds)
+ ? excludeIds.filter(id => id != null).map(id => id.toString())
+ : (excludeIds ? [excludeIds.toString()] : []);
+
+ // Создаем уникальный ключ для кэша с учетом исключений
+ const excludeKey = excludeArray.length ? `exclude:${excludeArray.sort().join(',')}` : '';
+ const cacheKey = `latest-posts:${first}:${after || 'first-page'}${excludeKey ? `:${excludeKey}` : ''}`;
return await cache.wrap(
cacheKey,
async () => {
- const query = `
- query GetLatestProfileArticles($first: Int!, $after: String) {
- profileArticles(
- first: $first
- after: $after
- where: { orderby: { field: DATE, order: DESC } }
- ) {
- 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
+ // Функция для выполнения запроса
+ const fetchPosts = async (limit, cursor) => {
+ const query = `
+ query GetLatestProfileArticles($first: Int!, $after: String) {
+ profileArticles(
+ first: $first
+ after: $after
+ where: { orderby: { field: DATE, order: DESC } }
+ ) {
+ pageInfo {
+ hasNextPage
+ endCursor
+ }
+ edges {
+ cursor
+ node {
+ databaseId
+ title
+ uri
+ date
+ 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
+ }
+ }
+ }
+ }
}
- uri
}
- }
- # Соавторы как массив
- coauthors {
- id
- name
- firstName
- lastName
- url
- description
- }
- categories {
- nodes {
- id
- name
- color
- slug
- uri
- databaseId
- }
- }
- tags {
- nodes {
- id
- name
- slug
- uri
- }
- }
- }
- }
- }
-}
- `;
+ `;
+
+ return await fetchGraphQL(query, { first: limit, after: cursor });
+ };
- const data = await fetchGraphQL(query, { first, after });
+ // Если нет исключений, просто возвращаем результат
+ if (excludeArray.length === 0) {
+ const data = await fetchPosts(first, after);
+ const posts = data.profileArticles?.edges?.map(edge => edge.node) || [];
+
+ return {
+ posts,
+ pageInfo: data.profileArticles?.pageInfo || { hasNextPage: false, endCursor: null }
+ };
+ }
+
+ // Логика с исключениями - дозагружаем недостающие
+ let allPosts = [];
+ let currentAfter = after;
+ let hasMore = true;
+ let totalNeeded = first;
- // Преобразуем edges в nodes
- const posts = data.profileArticles?.edges?.map(edge => edge.node) || [];
+ // Продолжаем загрузку пока не наберем нужное количество или не кончатся посты
+ while (allPosts.length < totalNeeded && hasMore) {
+ // Запрашиваем с запасом, чтобы компенсировать исключения
+ const fetchLimit = Math.max(totalNeeded - allPosts.length + excludeArray.length, 1);
+
+ const data = await fetchPosts(fetchLimit, currentAfter);
+ const edges = data.profileArticles?.edges || [];
+ const pageInfo = data.profileArticles?.pageInfo;
+
+ // Фильтруем исключенные ID - сравниваем ТОЛЬКО databaseId
+ const newPosts = edges
+ .map(edge => edge.node)
+ .filter(node => !excludeArray.includes(node.databaseId?.toString()));
+
+ allPosts = [...allPosts, ...newPosts];
+
+ // Обновляем курсор для следующей страницы
+ currentAfter = pageInfo?.endCursor;
+ hasMore = pageInfo?.hasNextPage && edges.length > 0;
+
+ // Защита от бесконечного цикла (если что-то пошло не так)
+ if (edges.length === 0) break;
+ }
+
+ // Обрезаем до нужного количества
+ const finalPosts = allPosts.slice(0, first);
+
+ // Определяем, есть ли еще страницы
+ const hasNextPage = allPosts.length > first || hasMore;
return {
- posts,
- pageInfo: data.profileArticles?.pageInfo || { hasNextPage: false, endCursor: null }
+ posts: finalPosts,
+ pageInfo: {
+ hasNextPage,
+ endCursor: currentAfter // Последний использованный курсор
+ }
};
},
- { ttl: CACHE_TTL.POSTS } // из конфигурации
+ { ttl: CACHE_TTL.POSTS }
);
}
-
export async function getPostsByCategory(slug, first = 14, after = null) {
// Создаем уникальный ключ для кэша
const cacheKey = `category-posts:${slug}:${first}:${after || 'first-page'}`;
diff --git a/src/pages/articles/index.astro b/src/pages/articles/index.astro
index 06af93a..7410292 100644
--- a/src/pages/articles/index.astro
+++ b/src/pages/articles/index.astro
@@ -3,9 +3,9 @@
import ContentLayout from '@layouts/ContentLayout.astro';
import ContentGrid from '@components/ContentGrid.astro';
-import { getLatestPosts } from '@api/posts.js';
+import { getLatestPosts } from '@api/posts';
-const { posts, pageInfo } = await getLatestPosts(41); // Сразу деструктурируем
+const { posts, pageInfo } = await getLatestPosts(41);
//ISR
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 8d38a8d..d6deacb 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -1,15 +1,10 @@
---
-import { getSiteInfo } from "../lib/wp-api.js";
-import { getLatestPosts } from '@api/posts.js';
-
-import { fetchWPRestGet } from "../lib/api/wp-rest-get-client";
-
-
-import '../styles/home.css';
-
-const site = await getSiteInfo();
-const { posts, pageInfo } = await getLatestPosts(41); // Сразу деструктурируем
+import { getSiteInfo } from "@lib/wp-api.js";
+import { getLatestPosts } from '@api/posts';
+import { getLatestMainPost } from '@lib/api/main-posts';
+import { getLatestColonPost } from '@lib/api/colon-posts';
+import { fetchWPRestGet } from "@lib/api/wp-rest-get-client";
// визуальные компоненты
import MainLayout from '@layouts/MainLayout.astro';
@@ -19,6 +14,39 @@ import MainLine from '@components/MainLine.astro';
import HomeNews from "@components/HomeNews.astro";
+//import '../styles/home.css';
+import '../styles/main.css';
+
+
+//получаем главный пост
+const mainPost = await getLatestMainPost();
+const mainPostId = mainPost?.databaseId;
+
+//колонка
+const colonPost = await getLatestColonPost();
+const colonPostId = mainPost?.databaseId;
+
+
+// Создаем массив исключений
+const excludeIds = [];
+
+// Добавляем ID если они существуют
+if (mainPostId) {
+ excludeIds.push(mainPostId);
+}
+
+if (colonPostId) {
+ excludeIds.push(colonPostId);
+}
+
+
+
+const site = await getSiteInfo();
+const { posts, pageInfo } = await getLatestPosts(41, null, excludeIds ); // Сразу деструктурируем
+
+
+
+
//ISR
export const prerender = false;
---
@@ -30,7 +58,10 @@ export const prerender = false;
-
+excludeIds
Все новости
+
+
+
+
\ No newline at end of file
diff --git a/src/styles/components/colon-post.css b/src/styles/components/colon-post.css
new file mode 100644
index 0000000..cb80949
--- /dev/null
+++ b/src/styles/components/colon-post.css
@@ -0,0 +1,141 @@
+/* Основная карточка */
+ .colon-post-card {
+ background: #ececec;
+ border-radius: 8px;
+ overflow: hidden;
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
+ width: 100%;
+ max-width: 800px; /* опционально, для демо */
+ height: 200px; /* фиксированная высота карточки */
+ }
+
+ .colon-post-card:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
+ }
+
+ /* Flex-контейнер: две части */
+ .split-flex {
+ display: flex;
+ height: 100%;
+ width: 100%;
+ }
+
+ /* ЛЕВЫЙ БЛОК: ровно 30% */
+ .left-photo {
+ flex: 0 0 34%; /* ширина 30%, не растягивается */
+ height: 100%;
+ background: #d4d4d4; /* фон, если нет фото */
+ display: flex;
+ }
+
+ .photo-link {
+ display: flex;
+ width: 100%;
+ height: 100%;
+ text-decoration: none;
+ }
+
+ .photo-img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover; /* заполняет контейнер, сохраняя пропорции и обрезаясь */
+ display: block;
+ transition: transform 0.3s ease;
+ }
+
+ .photo-img:hover {
+ transform: scale(1.05); /* легкий эффект при наведении */
+ }
+
+ .photo-placeholder {
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
+ }
+
+ /* ПРАВЫЙ БЛОК: 70% */
+ .right-content {
+ flex: 1; /* занимает оставшееся место (70%) */
+ height: 100%;
+ padding: 16px 20px; /* внутренние отступы */
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ }
+
+ /* Обёртка для контента, чтобы занять всю высоту и распределить пространство */
+ .content-wrapper {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ width: 100%;
+ }
+
+ /* Заголовок жирным */
+ .bold-title {
+ font-size: 1.125rem;
+ font-weight: 700;
+ line-height: 1.4;
+ color: #2c3e50;
+ margin: 0 0 8px 0;
+ transition: color 0.3s ease;
+ }
+
+ .title-link {
+ text-decoration: none;
+ color: inherit;
+ }
+
+ .title-link:hover .bold-title {
+ color: #3498db;
+ }
+
+ /* Мета-строка: прижимаем к низу */
+ .meta-line {
+ font-size: 0.9rem;
+ color: #666;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.5rem;
+ margin-top: auto; /* это прижимает мету к низу */
+ }
+
+ .separator {
+ color: #aaa;
+ font-weight: 300;
+ }
+
+ .author :global(a) {
+ color: #2c3e50;
+ text-decoration: none;
+ font-weight: 500;
+ }
+
+ .author :global(a:hover) {
+ color: #3498db;
+ text-decoration: underline;
+ }
+
+ /* Адаптивность */
+ @media (max-width: 600px) {
+ .colon-post-card {
+ height: auto;
+ min-height: 180px;
+ }
+
+ .left-photo {
+ flex: 0 0 30%;
+ aspect-ratio: 1 / 1; /* сохраняем квадрат на мобильных */
+ height: auto;
+ }
+
+ .right-content {
+ padding: 12px 16px;
+ }
+
+ .bold-title {
+ font-size: 1.1rem;
+ }
+ }
diff --git a/src/styles/components/main-post-widget.css b/src/styles/components/main-post-widget.css
new file mode 100644
index 0000000..cb3d43c
--- /dev/null
+++ b/src/styles/components/main-post-widget.css
@@ -0,0 +1,110 @@
+/* ОСНОВНОЕ: ограничиваем ширину виджета */
+.main-post-widget {
+ width: 100%;
+ border-radius: 8px;
+ overflow: hidden;
+ background: white;
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
+}
+
+.image-container {
+ position: relative;
+ width: 100%;
+ aspect-ratio: 16/9;
+ overflow: hidden;
+}
+
+.image-link {
+ display: block;
+ width: 100%;
+ height: 100%;
+ text-decoration: none;
+ color: inherit;
+}
+
+.post-image {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ transition: transform 0.3s ease;
+}
+
+.image-link:hover .post-image {
+ transform: scale(1.03);
+}
+
+.image-placeholder {
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(135deg, #f5f5f5, #e0e0e0);
+}
+
+.category-badge {
+ position: absolute;
+ top: 16px;
+ left: 16px;
+ padding: 6px 12px;
+ border-radius: 4px;
+ font-size: 0.625rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ color: white;
+ z-index: 2;
+ line-height: 1;
+}
+
+.content-overlay {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding: 24px;
+ background: linear-gradient(transparent, rgba(0,0,0,0.7) 70%);
+ color: white;
+ z-index: 1;
+}
+
+.date-overlay {
+ font-size: 0.875rem;
+ opacity: 0.9;
+ display: block;
+}
+
+.title-overlay {
+ margin: 0 0 12px 0;
+ font-size: 1.4rem;
+ font-weight: 700;
+ line-height: 1.3;
+}
+
+.title-link {
+ color: white;
+ text-decoration: none;
+ transition: opacity 0.2s ease;
+}
+
+.title-link:hover {
+ opacity: 0.9;
+}
+
+.author-overlay {
+ font-size: 0.875rem;
+ opacity: 0.9;
+}
+
+/* Адаптивность */
+@media (max-width: 1023px) {
+ .main-post-widget {
+ border-radius: 0;
+ max-width: 100%;
+ }
+
+ .content-overlay {
+ padding: 16px;
+ }
+
+ .title-overlay {
+ font-size: 1.25rem;
+ }
+}
\ No newline at end of file
diff --git a/src/styles/components/mainline.css b/src/styles/components/mainline.css
new file mode 100644
index 0000000..6716392
--- /dev/null
+++ b/src/styles/components/mainline.css
@@ -0,0 +1,110 @@
+/* Основной контейнер */
+.three-col-block {
+ display: flex;
+ margin: 30px 0;
+ width: 100%;
+ max-width: 1200px;
+ margin-left: auto;
+ margin-right: auto;
+ flex-wrap: nowrap;
+ align-items: stretch; /* ВАЖНО: растягиваем все колонки на всю высоту */
+}
+
+/* ЛЕВАЯ КОЛОНКА - фиксированная */
+.left-col {
+ flex: 0 0 260px;
+ min-width: 260px;
+ max-width: 260px;
+ box-sizing: border-box;
+ display: flex; /* Добавляем flex для растягивания содержимого */
+}
+
+.left-col > :deep(*) {
+ width: 100%; /* Растягиваем компонент на всю ширину */
+ height: 100%; /* Растягиваем компонент на всю высоту */
+}
+
+/* ЦЕНТРАЛЬНАЯ КОЛОНКА - гибкая */
+.center-col {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ margin: 0 20px;
+ box-sizing: border-box;
+}
+
+.center-top, .center-bottom {
+ #flex: 1 1 0; /* ВАЖНО: оба блока занимают равную высоту */
+ background: #fff;
+ box-sizing: border-box;
+ overflow: hidden;
+ word-wrap: break-word;
+ display: flex; /* Для растягивания внутреннего содержимого */
+ flex-direction: column;
+}
+
+.center-top > :deep(*), .center-bottom > :deep(*) {
+ width: 100%;
+ flex: 1; /* Растягиваем компонент на всю доступную высоту */
+}
+
+/* ПРАВАЯ КОЛОНКА - фиксированная */
+.right-col {
+ flex: 0 0 260px;
+ min-width: 260px;
+ max-width: 260px;
+ background: #f0f0f0;
+ padding: 20px;
+ box-sizing: border-box;
+ display: flex; /* Добавляем flex для растягивания содержимого */
+ flex-direction: column;
+}
+
+.right-col > :deep(*) {
+ width: 100%;
+ flex: 1; /* Растягиваем контент на всю высоту */
+}
+
+/* Ограничиваем контент внутри центральной колонки */
+.center-col > * {
+ max-width: 100%;
+ overflow-wrap: break-word;
+}
+
+/* МОБИЛЬНАЯ ВЕРСИЯ */
+@media (max-width: 768px) {
+ .three-col-block {
+ flex-direction: column;
+ flex-wrap: wrap;
+ max-width: 100%;
+ overflow: visible;
+ align-items: stretch;
+ }
+
+ .left-col, .center-col, .right-col {
+ flex: 1 1 100%;
+ min-width: 100%;
+ max-width: 100%;
+ width: 100%;
+ margin: 0 0 20px 0;
+ }
+
+ .center-col {
+ margin: 0;
+ }
+
+ .right-col{
+ display: none;
+ }
+
+ /* Сбрасываем flex свойства для мобильной версии */
+ .left-col, .right-col {
+ display: block;
+ }
+
+ .center-top, .center-bottom {
+ display: block;
+ }
+}
\ No newline at end of file
diff --git a/src/styles/main.css b/src/styles/main.css
index ee01261..d878467 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -3,7 +3,10 @@
@import './ContentLayout.css';
@import './header.css';
@import './mainmenu.css';
-@import './article.css';
+@import './mainmenu.css';
+@import './components/mainline.css';
+@import './components/colon-post.css';
+@import './components/main-post-widget.css';
@import './footer.css';
@import './embedded-content.css';
@import './components/ContentGrid.css';