55 lines
2.2 KiB
JavaScript
55 lines
2.2 KiB
JavaScript
import React, { useEffect, useState } from 'react';
|
||
|
||
function formatDate(s) {
|
||
const d = new Date(s);
|
||
if (Number.isNaN(d.getTime())) return s;
|
||
return d.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' });
|
||
}
|
||
|
||
export default function News() {
|
||
const [state, setState] = useState({ loading: true, items: [], error: null });
|
||
|
||
useEffect(() => {
|
||
fetch('/api/news.json', { cache: 'no-store' })
|
||
.then((r) => (r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`))))
|
||
.then((data) => setState({ loading: false, items: data.items || [], error: null }))
|
||
.catch((e) => setState({ loading: false, items: [], error: e.message }));
|
||
}, []);
|
||
|
||
return (
|
||
<div>
|
||
<h1 className="font-serif text-3xl font-bold mb-2">Новости</h1>
|
||
<p className="text-xs text-muted mb-6 pb-4 border-b border-rule">
|
||
Агрегатор новостей о Пушкино из внешних источников. Обновляется автоматически.
|
||
</p>
|
||
|
||
{state.loading && <p className="text-muted">Загружаем новости…</p>}
|
||
{state.error && (
|
||
<p className="text-muted">
|
||
Не удалось загрузить новости. Загляните позже.
|
||
</p>
|
||
)}
|
||
{!state.loading && !state.error && state.items.length === 0 && (
|
||
<p className="text-muted">Пока нет новостей.</p>
|
||
)}
|
||
|
||
<ul className="space-y-6">
|
||
{state.items.map((item, i) => (
|
||
<li key={item.guid || item.link || i} className="border-b border-rule pb-4 last:border-0">
|
||
<a href={item.link} target="_blank" rel="noopener noreferrer" className="font-serif text-lg font-bold text-ink hover:text-accent no-underline">
|
||
{item.title}
|
||
</a>
|
||
<div className="text-xs text-muted mt-1">
|
||
{item.source && <span>{item.source}</span>}
|
||
{item.pubDate && <span> · {formatDate(item.pubDate)}</span>}
|
||
</div>
|
||
{item.description && (
|
||
<p className="text-sm text-ink/80 mt-2">{item.description}</p>
|
||
)}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
);
|
||
}
|