Adds Google Trends, NewsAPI, and Google News RSS scoring for members,
mirroring the existing bill interest pipeline. Member profiles now show
a Public Interest chart (with signal breakdown) and a Related News panel.
Key changes:
- New member_trend_scores + member_news_articles tables (migration 0008)
- fetch_gnews_articles() added to news_service for unlimited RSS article storage
- Bill news fetcher now combines NewsAPI + Google News RSS (more coverage)
- New member_interest Celery worker with scheduled news + trend tasks
- GET /members/{id}/trend and /news API endpoints
- TrendChart redesigned with signal breakdown badges and bar+line combo chart
- NewsPanel accepts generic article shape (bills and members)
Co-Authored-By: Jack Levy
54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import { ExternalLink, Newspaper } from "lucide-react";
|
|
import { formatDate } from "@/lib/utils";
|
|
|
|
interface ArticleLike {
|
|
id: number;
|
|
source?: string;
|
|
headline?: string;
|
|
url?: string;
|
|
published_at?: string;
|
|
}
|
|
|
|
interface NewsPanelProps {
|
|
articles?: ArticleLike[];
|
|
}
|
|
|
|
export function NewsPanel({ articles }: NewsPanelProps) {
|
|
return (
|
|
<div className="bg-card border border-border rounded-lg p-4">
|
|
<h3 className="font-semibold text-sm flex items-center gap-2 mb-3">
|
|
<Newspaper className="w-4 h-4" />
|
|
Related News
|
|
{articles && articles.length > 0 && (
|
|
<span className="text-xs text-muted-foreground font-normal">({articles.length})</span>
|
|
)}
|
|
</h3>
|
|
|
|
{!articles || articles.length === 0 ? (
|
|
<p className="text-xs text-muted-foreground italic">No news articles found yet.</p>
|
|
) : (
|
|
<ul className="space-y-3">
|
|
{articles.slice(0, 8).map((article) => (
|
|
<li key={article.id} className="group">
|
|
<a
|
|
href={article.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="block hover:text-primary transition-colors"
|
|
>
|
|
<p className="text-xs font-medium line-clamp-2 leading-snug group-hover:underline">
|
|
{article.headline}
|
|
<ExternalLink className="w-3 h-3 inline ml-1 opacity-50" />
|
|
</p>
|
|
<p className="text-xs text-muted-foreground mt-0.5">
|
|
{article.source} · {formatDate(article.published_at)}
|
|
</p>
|
|
</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|