67 lines
2.6 KiB
JavaScript
67 lines
2.6 KiB
JavaScript
import { useState, useEffect } from 'react';
|
|
|
|
const API_BASE = import.meta.env.VITE_API_URL || '';
|
|
|
|
function SentimentDot({ sentiment }) {
|
|
if (sentiment === 'BULLISH') return <span className="inline-block w-2 h-2 rounded-full bg-emerald-400 mr-2 flex-shrink-0" />;
|
|
if (sentiment === 'BEARISH') return <span className="inline-block w-2 h-2 rounded-full bg-red-400 mr-2 flex-shrink-0" />;
|
|
return <span className="inline-block w-2 h-2 rounded-full bg-slate-500 mr-2 flex-shrink-0" />;
|
|
}
|
|
|
|
export default function NewsFeedPanel() {
|
|
const [news, setNews] = useState([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const fetchNews = () => {
|
|
fetch(`${API_BASE}/api/market/news`)
|
|
.then(r => r.json())
|
|
.then(d => { if (d.success) setNews(d.data || []); })
|
|
.catch(console.error)
|
|
.finally(() => setLoading(false));
|
|
};
|
|
fetchNews();
|
|
const interval = setInterval(fetchNews, 300000); // 5min
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
return (
|
|
<div className="bg-slate-900 border border-slate-700/50 rounded-xl overflow-hidden">
|
|
<div className="flex items-center justify-between px-3 py-2.5 border-b border-slate-700/50">
|
|
<h3 className="text-sm font-semibold text-white">📰 Market News</h3>
|
|
{loading && <span className="text-xs text-blue-400 animate-pulse">Loading...</span>}
|
|
</div>
|
|
<div className="divide-y divide-slate-800/30 max-h-[400px] overflow-y-auto">
|
|
{loading ? (
|
|
Array.from({ length: 6 }).map((_, i) => (
|
|
<div key={i} className="p-3 animate-pulse">
|
|
<div className="h-3 bg-slate-800 rounded mb-1.5" />
|
|
<div className="h-3 bg-slate-800 rounded w-2/3" />
|
|
</div>
|
|
))
|
|
) : news.length === 0 ? (
|
|
<div className="p-4 text-slate-500 text-xs text-center">No news available</div>
|
|
) : (
|
|
news.map((item, i) => (
|
|
<a
|
|
key={i}
|
|
href={item.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-start gap-2 p-3 hover:bg-slate-800/40 transition-colors group"
|
|
>
|
|
<SentimentDot sentiment={item.sentiment} />
|
|
<div className="flex-1 min-w-0">
|
|
<div className="text-xs text-slate-300 group-hover:text-white transition-colors line-clamp-2 leading-snug">
|
|
{item.title}
|
|
</div>
|
|
<div className="text-xs text-slate-600 mt-1">{item.source}</div>
|
|
</div>
|
|
</a>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|