SoftTraderFrontend/components/widgets/NewsHeadlines.tsx
2026-03-20 11:10:13 -04:00

103 lines
2.7 KiB
TypeScript

'use client'
import { useEffect, useState } from 'react'
import { getNewsFeed, formatPublishedDate, type NewsItem } from '@/lib/newsApi'
import { SentimentalityBar } from './SentimentalityBar'
interface NewsHeadlinesProps {
keyword?: string // Symbol or topic to fetch news for
onAverageSentiment?: (avg: number | null) => void // Callback with average sentiment score
}
export function NewsHeadlines({ keyword, onAverageSentiment }: NewsHeadlinesProps) {
const [items, setItems] = useState<NewsItem[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
if (!keyword) {
setLoading(false)
setError(null)
setItems([])
onAverageSentiment?.(null)
return
}
setLoading(true)
setError(null)
setItems([])
getNewsFeed(keyword)
.then((newsItems) => {
setItems(newsItems)
if (newsItems.length > 0) {
const avg = newsItems.reduce((sum, item) => sum + item.score, 0) / newsItems.length
onAverageSentiment?.(Math.round(avg))
} else {
onAverageSentiment?.(null)
}
})
.catch((err) => {
setError(err.message)
onAverageSentiment?.(null)
})
.finally(() => setLoading(false))
}, [keyword, onAverageSentiment])
if (!keyword) {
return (
<div className="text-xs text-muted-foreground text-center py-4">
No keyword selected
</div>
)
}
if (loading) {
return (
<div className="text-xs text-muted-foreground text-center py-4">
Loading news...
</div>
)
}
if (error) {
return (
<div className="text-xs text-negative text-center py-4">
{error}
</div>
)
}
if (!items || items.length === 0) {
return (
<div className="text-xs text-muted-foreground text-center py-4">
No news found for "{keyword}"
</div>
)
}
return (
<div className="divide-y divide-border">
{items.map((item, idx) => (
<a
key={idx}
href={item.url}
target="_blank"
rel="noopener noreferrer"
className="block py-3 first:pt-0 last:pb-0 hover:bg-secondary/50 -mx-1 px-1 transition-colors cursor-pointer"
>
<div className="text-sm font-medium text-foreground line-clamp-2">
{item.title}
</div>
<div className="text-xs text-muted-foreground mt-1">
{item.publisher} • {formatPublishedDate(item.publishedAt)}
</div>
<div className="text-xs mt-1.5">
<SentimentalityBar sentiment={Math.round(item.score)} />
</div>
</a>
))}
</div>
)
}