add client rest api
This commit is contained in:
@@ -1,275 +1,257 @@
|
|||||||
---
|
---
|
||||||
import { getLatestAnews } from '../lib/api/posts.js';
|
import { getLatestAnews } from '../lib/api/posts.js';
|
||||||
|
import { fetchWPRestGet } from "@/lib/api/wp-rest-get-client";
|
||||||
|
|
||||||
// Функции для работы с датами
|
|
||||||
|
// Даты/время
|
||||||
function formatDate(dateString: string): string {
|
function formatDate(dateString: string): string {
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const yesterday = new Date(today);
|
const yesterday = new Date(today);
|
||||||
yesterday.setDate(yesterday.getDate() - 1);
|
yesterday.setDate(yesterday.getDate() - 1);
|
||||||
|
|
||||||
if (date.toDateString() === today.toDateString()) {
|
if (date.toDateString() === today.toDateString()) return 'Сегодня';
|
||||||
return 'Сегодня';
|
if (date.toDateString() === yesterday.toDateString()) return 'Вчера';
|
||||||
}
|
|
||||||
|
|
||||||
if (date.toDateString() === yesterday.toDateString()) {
|
return date.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' });
|
||||||
return 'Вчера';
|
|
||||||
}
|
|
||||||
|
|
||||||
return date.toLocaleDateString('ru-RU', {
|
|
||||||
day: 'numeric',
|
|
||||||
month: 'long',
|
|
||||||
year: 'numeric'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTime(dateString: string): string {
|
function formatTime(dateString: string): string {
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
return date.toLocaleTimeString('ru-RU', {
|
return date.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' });
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function groupByDate(posts: Array<{ date: string }>) {
|
function groupByDate(posts: Array<{ date: string }>) {
|
||||||
const grouped: Record<string, Array<any>> = {};
|
const grouped: Record<string, Array<any>> = {};
|
||||||
|
posts.forEach((post) => {
|
||||||
posts.forEach(post => {
|
const d = new Date(post.date);
|
||||||
const date = new Date(post.date);
|
const key = d.toISOString().split('T')[0];
|
||||||
const dateKey = date.toISOString().split('T')[0];
|
(grouped[key] ??= []).push(post);
|
||||||
|
|
||||||
if (!grouped[dateKey]) {
|
|
||||||
grouped[dateKey] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
grouped[dateKey].push(post);
|
|
||||||
});
|
});
|
||||||
|
return Object.fromEntries(Object.entries(grouped).sort((a, b) => b[0].localeCompare(a[0])));
|
||||||
const sortedEntries = Object.entries(grouped).sort((a, b) =>
|
|
||||||
b[0].localeCompare(a[0])
|
|
||||||
);
|
|
||||||
|
|
||||||
return Object.fromEntries(sortedEntries);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получаем данные при сборке (используем правильное имя функции)
|
// Новости
|
||||||
const posts = await getLatestAnews(20);
|
const newsPosts = await getLatestAnews(20);
|
||||||
const groupedPosts = groupByDate(posts);
|
const groupedNews = groupByDate(newsPosts);
|
||||||
const totalPosts = posts.length;
|
|
||||||
|
// Топ-10
|
||||||
|
type TopItem = { uri?: string; link?: string; title: string; date: string };
|
||||||
|
type ApiResp = { top?: { items?: TopItem[] } };
|
||||||
|
|
||||||
|
let topPosts: TopItem[] = [];
|
||||||
|
try {
|
||||||
|
const data = await fetchWPRestGet<ApiResp>("my/v1/news-top", { per_page: 20 });
|
||||||
|
topPosts = (data?.top?.items ?? []).slice(0, 10);
|
||||||
|
} catch {
|
||||||
|
topPosts = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasNews = newsPosts.length > 0;
|
||||||
|
const hasTop = topPosts.length > 0;
|
||||||
|
|
||||||
|
// По умолчанию открываем вкладку, где есть данные
|
||||||
|
const defaultTab: "news" | "top" = hasTop ? "top" : "news";
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- HTML разметка -->
|
{(hasNews || hasTop) && (
|
||||||
<div class="endnews-container">
|
<div class="endnews-container">
|
||||||
<!-- Шапка с заголовком и счетчиком -->
|
|
||||||
<div class="endnews-header">
|
<div class="endnews-header">
|
||||||
<h4 class="endnews-title">Новости</h4>
|
<h4 class="endnews-title">Новости</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Список новостей с группировкой по датам -->
|
{/* Radio-кнопки ВНЕ .endnews-tabs для работы CSS-селекторов */}
|
||||||
<div class="latestnews-list">
|
<input
|
||||||
{Object.entries(groupedPosts).map(([dateKey, datePosts]) => {
|
class="endnews-tab-input"
|
||||||
const dateStr = new Date(dateKey).toLocaleDateString('ru-RU', {
|
type="radio"
|
||||||
day: 'numeric',
|
name="endnews-tab"
|
||||||
month: 'long',
|
id="endnews-tab-news"
|
||||||
year: 'numeric'
|
checked={defaultTab === "news"}
|
||||||
});
|
/>
|
||||||
|
<input
|
||||||
|
class="endnews-tab-input"
|
||||||
|
type="radio"
|
||||||
|
name="endnews-tab"
|
||||||
|
id="endnews-tab-top"
|
||||||
|
checked={defaultTab === "top"}
|
||||||
|
/>
|
||||||
|
|
||||||
return (
|
{/* Только labels в блоке табов */}
|
||||||
<div class="latestnews-date-group" key={dateKey}>
|
<div class="endnews-tabs">
|
||||||
|
<label class="endnews-tab-label" for="endnews-tab-news">Новости</label>
|
||||||
<div class="latestnews-date">{formatDate(dateKey + 'T00:00:00')}</div>
|
<label class="endnews-tab-label" for="endnews-tab-top">Топ‑10</label>
|
||||||
|
|
||||||
|
|
||||||
<!-- Список новостей за эту дату -->
|
|
||||||
{datePosts.map((post, index) => {
|
|
||||||
const postNumber = index + 1;
|
|
||||||
const isLastInGroup = postNumber === datePosts.length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<article class="lastnews-item" key={post.uri}>
|
|
||||||
|
|
||||||
<div class="lastnews-time">
|
|
||||||
{formatTime(post.date)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Заголовок новости -->
|
{/* Контент: две панели, показываем нужную через :checked */}
|
||||||
|
<div class="latestnews-list">
|
||||||
|
<section class="endnews-panel endnews-panel--news" aria-label="Новости">
|
||||||
|
{Object.entries(groupedNews).map(([dateKey, datePosts]) => (
|
||||||
|
<div class="latestnews-date-group" key={dateKey}>
|
||||||
|
<div class="latestnews-date">{formatDate(dateKey + 'T00:00:00')}</div>
|
||||||
|
|
||||||
|
{datePosts.map((post) => (
|
||||||
|
<article class="lastnews-item" key={post.uri}>
|
||||||
|
<div class="lastnews-time">{formatTime(post.date)}</div>
|
||||||
<div class="lastnews-content">
|
<div class="lastnews-content">
|
||||||
<a
|
<a href={post.uri || '#'} class="endnews-link">
|
||||||
href={post.uri || '#'}
|
|
||||||
class="endnews-link"
|
|
||||||
>
|
|
||||||
{post.title}
|
{post.title}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</article>
|
</article>
|
||||||
);
|
))}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
))}
|
||||||
})}
|
</section>
|
||||||
|
|
||||||
|
<section class="endnews-panel endnews-panel--top" aria-label="Топ-10">
|
||||||
|
{topPosts.map((post, i) => (
|
||||||
|
<article class="lastnews-item" key={(post.uri ?? post.link ?? post.title) + i}>
|
||||||
|
<div class="ltopnews-content">
|
||||||
|
<a href={post.uri ?? post.link ?? '#'} class="endnews-link">
|
||||||
|
{post.title}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{!hasTop && <div class="endnews-empty">Топ пока пуст</div>}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.endnews-container { width:100%; }
|
||||||
|
|
||||||
/* Базовые стили */
|
.endnews-header{
|
||||||
.endnews-container {
|
padding:12px 16px;
|
||||||
width: 100%;
|
background:#B61D1D;
|
||||||
|
border-radius:6px 6px 0 0;
|
||||||
|
}
|
||||||
|
.endnews-title{ color:#fff; margin:0; font-size:1.1rem; font-weight:600; }
|
||||||
|
|
||||||
|
/* Скрываем radio-кнопки */
|
||||||
|
.endnews-tab-input{
|
||||||
|
position:absolute;
|
||||||
|
opacity:0;
|
||||||
|
pointer-events:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.endnews-header {
|
/* Tabs */
|
||||||
padding: 12px 16px;
|
.endnews-tabs{
|
||||||
background: #B61D1D;
|
display:flex;
|
||||||
border-radius: 6px 6px 0 0;
|
gap:8px;
|
||||||
|
padding:10px 12px;
|
||||||
|
border:1px solid #ECECEC;
|
||||||
|
border-top:none;
|
||||||
|
background:#fff;
|
||||||
|
}
|
||||||
|
.endnews-tab-label{
|
||||||
|
cursor:pointer;
|
||||||
|
user-select:none;
|
||||||
|
font-size:0.85rem;
|
||||||
|
font-weight:700;
|
||||||
|
padding:8px 10px;
|
||||||
|
border-radius:6px;
|
||||||
|
background:#f5f5f5;
|
||||||
|
color:#505258;
|
||||||
|
transition: background 0.2s, color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.endnews-title {
|
/* Активный таб - используем общий сиблинг-селектор */
|
||||||
color: white;
|
#endnews-tab-news:checked ~ .endnews-tabs label[for="endnews-tab-news"],
|
||||||
margin: 0;
|
#endnews-tab-top:checked ~ .endnews-tabs label[for="endnews-tab-top"]{
|
||||||
font-size: 1.1rem;
|
background:#B61D1D;
|
||||||
font-weight: 600;
|
color:#fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.latestnews-list {
|
/* list container */
|
||||||
display: flex;
|
.latestnews-list{
|
||||||
flex-direction: column;
|
border:1px solid #ECECEC;
|
||||||
border: 1px solid #ECECEC;
|
border-top:none;
|
||||||
border-top: none;
|
border-radius:0 0 6px 6px;
|
||||||
border-radius: 0 0 6px 6px;
|
background:#fff;
|
||||||
background: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.latestnews-date-group {
|
/* Панели скрыты по умолчанию */
|
||||||
padding: 12px 16px;
|
.endnews-panel{ display:none; }
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
|
/* Показываем нужную панель по выбранному radio */
|
||||||
|
#endnews-tab-news:checked ~ .latestnews-list .endnews-panel--news{ display:block; }
|
||||||
|
#endnews-tab-top:checked ~ .latestnews-list .endnews-panel--top { display:block; }
|
||||||
|
|
||||||
|
/* Существующий стиль списка */
|
||||||
|
.latestnews-date-group{ padding:12px 16px; border-bottom:1px solid #f0f0f0; }
|
||||||
|
.latestnews-date-group:last-child{ border-bottom:none; }
|
||||||
|
|
||||||
|
.latestnews-date{
|
||||||
|
font-size:0.625rem;
|
||||||
|
line-height:1.1;
|
||||||
|
color:#505258;
|
||||||
|
margin:0 0 0.4375rem;
|
||||||
|
text-transform:uppercase;
|
||||||
|
letter-spacing:0.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.latestnews-date-group:last-child {
|
.lastnews-item{
|
||||||
border-bottom: none;
|
display:flex;
|
||||||
|
align-items:flex-start;
|
||||||
|
gap:12px;
|
||||||
|
margin-bottom:10px;
|
||||||
|
contain:content;
|
||||||
|
}
|
||||||
|
.lastnews-item:last-child{ margin-bottom:0; }
|
||||||
|
|
||||||
|
.lastnews-time{
|
||||||
|
font-size:0.75rem;
|
||||||
|
font-weight:700;
|
||||||
|
line-height:1.2;
|
||||||
|
color:#B61D1D;
|
||||||
|
min-width:2.5rem;
|
||||||
|
flex-shrink:0;
|
||||||
|
margin-top:1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.latestnews-date {
|
.lastnews-content{
|
||||||
font-size: 0.625rem;
|
line-height:1.3;
|
||||||
line-height: 1.1;
|
font-weight:500;
|
||||||
color: #505258;
|
font-size:0.9em;
|
||||||
margin: 0 0 0.4375rem;
|
color:#000;
|
||||||
text-transform: uppercase;
|
transition:color 0.2s;
|
||||||
letter-spacing: 0.5px;
|
flex:1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lastnews-item {
|
.topnews-content{
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 12px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
contain: content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lastnews-item:last-child {
|
line-height:1.3;
|
||||||
margin-bottom: 0;
|
font-weight:500;
|
||||||
}
|
font-size:0.9em;
|
||||||
|
color:#000;
|
||||||
|
transition:color 0.2s;
|
||||||
|
margin-left: 4px;
|
||||||
|
|
||||||
.lastnews-time {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 1.2;
|
|
||||||
color: #B61D1D;
|
|
||||||
min-width: 2.5rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-top: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lastnews-content {
|
|
||||||
line-height: 1.3;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 0.9em;
|
|
||||||
color: #000;
|
|
||||||
transition: color 0.2s;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.endnews-link {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.endnews-link:hover {
|
|
||||||
color: #B61D1D;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Десктоп: фиксированная высота со скроллом */
|
|
||||||
@media (min-width: 1024px) {
|
|
||||||
.endnews-container {
|
|
||||||
height: 500px; /* Фиксированная высота */
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.latestnews-list {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto; /* Вертикальный скролл */
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Кастомный скроллбар для красоты */
|
|
||||||
.latestnews-list::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.latestnews-list::-webkit-scrollbar-track {
|
|
||||||
background: #f5f5f5;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.latestnews-list::-webkit-scrollbar-thumb {
|
|
||||||
background: #d0d0d0;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.latestnews-list::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: #b0b0b0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Мобильные устройства: автоматическая высота */
|
|
||||||
@media (max-width: 1023px) {
|
|
||||||
.endnews-container {
|
|
||||||
height: auto; /* Автоматическая высота */
|
|
||||||
}
|
|
||||||
|
|
||||||
.latestnews-list {
|
|
||||||
max-height: none;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Планшеты (опционально) */
|
|
||||||
@media (min-width: 768px) and (max-width: 1023px) {
|
|
||||||
.endnews-container {
|
|
||||||
height: auto;
|
|
||||||
/* или можно задать другую фиксированную высоту для планшетов */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Анимация появления скролла (опционально) */
|
|
||||||
.latestnews-list {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Улучшение для touch-устройств */
|
|
||||||
@media (hover: none) and (pointer: coarse) {
|
|
||||||
.endnews-link {
|
|
||||||
padding: 8px 0; /* Увеличение touch-зоны */
|
|
||||||
}
|
|
||||||
|
|
||||||
.lastnews-item {
|
|
||||||
margin: 12px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.endnews-link{ color:inherit; text-decoration:none; display:block; }
|
||||||
|
.endnews-link:hover{ color:#B61D1D; }
|
||||||
|
|
||||||
|
.endnews-sub{
|
||||||
|
margin-top:4px;
|
||||||
|
font-size:0.75rem;
|
||||||
|
color:#6b6d72;
|
||||||
|
font-weight:500;
|
||||||
|
}
|
||||||
|
.endnews-empty{ padding:12px 16px; color:#6b6d72; font-size:0.85rem; }
|
||||||
|
|
||||||
|
/* Десктоп скролл */
|
||||||
|
@media (min-width:1024px){
|
||||||
|
.endnews-container{ height:500px; display:flex; flex-direction:column; }
|
||||||
|
.latestnews-list{ flex:1; overflow-y:auto; overflow-x:hidden; }
|
||||||
|
}
|
||||||
|
@media (max-width:1023px){
|
||||||
|
.endnews-container{ height:auto; }
|
||||||
|
.latestnews-list{ overflow:visible; }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,13 @@ const category = mainPost?.categories?.nodes?.[0];
|
|||||||
const categoryName = category?.name || '';
|
const categoryName = category?.name || '';
|
||||||
const categoryColor = category?.color || '#2271b1';
|
const categoryColor = category?.color || '#2271b1';
|
||||||
|
|
||||||
|
const authorName = mainPost?.author?.node?.name || '';
|
||||||
|
|
||||||
const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
month: 'short',
|
month: 'short',
|
||||||
year: 'numeric'
|
year: 'numeric'
|
||||||
}).replace(' г.', '');
|
}).replace(' г.', '');
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<article
|
<article
|
||||||
@@ -30,12 +31,12 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
|||||||
itemtype="https://schema.org/Article"
|
itemtype="https://schema.org/Article"
|
||||||
itemid={mainPost.uri}
|
itemid={mainPost.uri}
|
||||||
>
|
>
|
||||||
|
<div class="image-container">
|
||||||
<a
|
<a
|
||||||
href={mainPost.uri}
|
href={mainPost.uri}
|
||||||
class="post-card-link"
|
class="image-link"
|
||||||
aria-label={`Читать статью: ${mainPost.title}`}
|
aria-label={`Читать статью: ${mainPost.title}`}
|
||||||
>
|
>
|
||||||
<div class="image-container">
|
|
||||||
{imageUrl ? (
|
{imageUrl ? (
|
||||||
<img
|
<img
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
@@ -49,6 +50,7 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
|||||||
) : (
|
) : (
|
||||||
<div class="image-placeholder" aria-hidden="true"></div>
|
<div class="image-placeholder" aria-hidden="true"></div>
|
||||||
)}
|
)}
|
||||||
|
</a>
|
||||||
|
|
||||||
{categoryName && (
|
{categoryName && (
|
||||||
<div
|
<div
|
||||||
@@ -67,7 +69,9 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="title-overlay">
|
<h2 class="title-overlay">
|
||||||
|
<a href={mainPost.uri} class="title-link">
|
||||||
{mainPost.title}
|
{mainPost.title}
|
||||||
|
</a>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{authorName && (
|
{authorName && (
|
||||||
@@ -77,7 +81,6 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
|
||||||
|
|
||||||
<meta itemprop="url" content={mainPost.uri} />
|
<meta itemprop="url" content={mainPost.uri} />
|
||||||
<meta itemprop="datePublished" content={isoDate} />
|
<meta itemprop="datePublished" content={isoDate} />
|
||||||
@@ -89,20 +92,12 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
|||||||
/* ОСНОВНОЕ: ограничиваем ширину виджета */
|
/* ОСНОВНОЕ: ограничиваем ширину виджета */
|
||||||
.main-post-widget {
|
.main-post-widget {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 800px; /* Вот это ключевое правило! */
|
|
||||||
margin: 0 auto; /* Центрируем */
|
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: white;
|
background: white;
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-card-link {
|
|
||||||
display: block;
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-container {
|
.image-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -110,6 +105,14 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.image-link {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.post-image {
|
.post-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -117,7 +120,7 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
|||||||
transition: transform 0.3s ease;
|
transition: transform 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-card-link:hover .post-image {
|
.image-link:hover .post-image {
|
||||||
transform: scale(1.03);
|
transform: scale(1.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +167,16 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
|||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-link {
|
||||||
color: white;
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-link:hover {
|
||||||
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.author-overlay {
|
.author-overlay {
|
||||||
@@ -177,7 +189,7 @@ const formattedDate = postDate.toLocaleDateString('ru-RU', {
|
|||||||
@media (max-width: 1023px) {
|
@media (max-width: 1023px) {
|
||||||
.main-post-widget {
|
.main-post-widget {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
max-width: 100%; /* На мобильных занимает всю ширину */
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-overlay {
|
.content-overlay {
|
||||||
|
|||||||
45
src/lib/api/wp-rest-get-client.ts
Normal file
45
src/lib/api/wp-rest-get-client.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// src/lib/api/wp-rest-get-client.ts
|
||||||
|
|
||||||
|
function joinUrl(base: string, path: string) {
|
||||||
|
return `${base.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchWPRestGet<T = any>(
|
||||||
|
path: string,
|
||||||
|
query: Record<string, any> = {}
|
||||||
|
): Promise<T> {
|
||||||
|
const baseUrl = import.meta.env.WP_REST_BASE_URL; // например: https://site.ru
|
||||||
|
if (!baseUrl) {
|
||||||
|
throw new Error("WP_REST_BASE_URL is not defined in environment variables");
|
||||||
|
}
|
||||||
|
|
||||||
|
const endpoint = joinUrl(baseUrl, "/wp-json/");
|
||||||
|
const url = new URL(joinUrl(endpoint, path));
|
||||||
|
|
||||||
|
console.log(url);
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
for (const [k, v] of Object.entries(query)) {
|
||||||
|
if (v === undefined || v === null) continue;
|
||||||
|
if (Array.isArray(v)) v.forEach((x) => params.append(k, String(x)));
|
||||||
|
else params.set(k, String(v));
|
||||||
|
}
|
||||||
|
url.search = params.toString(); // URLSearchParams -> querystring [web:87]
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url.toString(), { method: "GET" });
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// WP REST почти всегда JSON
|
||||||
|
return (await response.json()) as T;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.error("WP REST GET request failed:", error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
throw new Error("Unknown error in WP REST GET request");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
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 { fetchWPRestGet } from "../lib/api/wp-rest-get-client";
|
||||||
|
|
||||||
|
|
||||||
import '../styles/home.css';
|
import '../styles/home.css';
|
||||||
|
|
||||||
@@ -14,6 +16,7 @@ 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 MainLine from '@components/MainLine.astro';
|
import MainLine from '@components/MainLine.astro';
|
||||||
|
import HomeNews from "@components/HomeNews.astro";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -23,10 +26,8 @@ export const prerender = false;
|
|||||||
|
|
||||||
<MainLayout
|
<MainLayout
|
||||||
title={site.title}
|
title={site.title}
|
||||||
description="Информационное агентство Деловой журнал Профиль"
|
description= {site.description}
|
||||||
>
|
>
|
||||||
<h1>{site.title}</h1>mainline_1
|
|
||||||
{site.description && <p>{site.description}</p>}
|
|
||||||
|
|
||||||
<!-- index.astro -->
|
<!-- index.astro -->
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user