import { useEffect, useState } from 'react'; import { Newspaper, TrendingUp, TrendingDown, Minus, ExternalLink, RefreshCw } from 'lucide-react'; import type { NewsFeed as NewsFeedType, NewsArticle, Sentiment } from '@/types'; import { newsApi } from '@/services/api'; export default function NewsFeed() { const [newsFeed, setNewsFeed] = useState(null); const [isLoading, setIsLoading] = useState(true); const [filter, setFilter] = useState<'ALL' | Sentiment>('ALL'); const [autoRefresh, setAutoRefresh] = useState(false); const loadNews = async () => { try { setIsLoading(true); const data = await newsApi.getNewsFeed(50); setNewsFeed(data); } catch (error) { console.error('Error loading news:', error); } finally { setIsLoading(false); } }; useEffect(() => { loadNews(); }, []); useEffect(() => { if (autoRefresh) { const interval = setInterval(loadNews, 300000); // Refresh every 5 minutes return () => clearInterval(interval); } }, [autoRefresh]); const getSentimentIcon = (sentiment: Sentiment) => { switch (sentiment) { case 'POSITIVE': return ; case 'NEGATIVE': return ; case 'NEUTRAL': return ; } }; const getSentimentColor = (sentiment: Sentiment) => { switch (sentiment) { case 'POSITIVE': return 'border-l-green-500 bg-green-500/5'; case 'NEGATIVE': return 'border-l-red-500 bg-red-500/5'; case 'NEUTRAL': return 'border-l-gray-500 bg-gray-500/5'; } }; const getImpactBadge = (impact: string) => { const colors = { HIGH: 'bg-red-500/20 text-red-500', MEDIUM: 'bg-yellow-500/20 text-yellow-500', LOW: 'bg-blue-500/20 text-blue-500', }; return colors[impact as keyof typeof colors] || colors.LOW; }; const filteredArticles = newsFeed?.articles.filter( (article) => filter === 'ALL' || article.sentiment === filter ) || []; return (

Market News

{newsFeed && (

{newsFeed.total_count} articles • Overall: {newsFeed.overall_sentiment}

)}
{newsFeed && (
)}
{isLoading && !newsFeed ? (
) : filteredArticles.length > 0 ? ( filteredArticles.map((article) => ( )) ) : (

No news articles found

)}
); } function NewsArticleCard({ article }: { article: NewsArticle }) { const getSentimentColor = (sentiment: Sentiment) => { switch (sentiment) { case 'POSITIVE': return 'border-l-green-500 bg-green-500/5'; case 'NEGATIVE': return 'border-l-red-500 bg-red-500/5'; case 'NEUTRAL': return 'border-l-gray-500 bg-gray-500/5'; } }; const getSentimentIcon = (sentiment: Sentiment) => { switch (sentiment) { case 'POSITIVE': return ; case 'NEGATIVE': return ; case 'NEUTRAL': return ; } }; const getImpactBadge = (impact: string) => { const colors = { HIGH: 'bg-red-500/20 text-red-500', MEDIUM: 'bg-yellow-500/20 text-yellow-500', LOW: 'bg-blue-500/20 text-blue-500', }; return colors[impact as keyof typeof colors] || colors.LOW; }; const formatDate = (dateStr: string) => { const date = new Date(dateStr); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); if (diffMins < 60) { return `${diffMins}m ago`; } else if (diffHours < 24) { return `${diffHours}h ago`; } else { return date.toLocaleDateString(); } }; return (
window.open(article.url, '_blank')} >
{getSentimentIcon(article.sentiment)} {article.source} {article.impact_on_gold} {formatDate(article.published_at)}

{article.title}

{article.description && (

{article.description}

)}
{article.category} Relevance: {(article.relevance_score * 100).toFixed(0)}%
); }