"use client"; import { useState, useMemo } from "react"; import { ExternalLink, RefreshCw, TrendingUp, TrendingDown, Minus, Loader2, Zap } from "lucide-react"; import type { NewsItem } from "@/app/api/news/route"; import type { Currency } from "@/lib/types"; // ── Helpers ─────────────────────────────────────────────────────────────────── const CCY_FLAGS: Record = { USD: "🇺🇸", EUR: "🇪🇺", GBP: "🇬🇧", JPY: "🇯🇵", CHF: "🇨🇭", CAD: "🇨🇦", AUD: "🇦🇺", NZD: "🇳🇿", }; const CCY_LIST: Currency[] = ["USD", "EUR", "GBP", "JPY", "CHF", "CAD", "AUD", "NZD"]; const SOURCE_COLORS: Record = { "InvestingLive": "bg-amber-500/20 text-amber-400 border-amber-500/30", "Reuters": "bg-orange-500/20 text-orange-400 border-orange-500/30", "Bloomberg Economics": "bg-sky-500/20 text-sky-400 border-sky-500/30", "Bloomberg CB": "bg-sky-500/20 text-sky-400 border-sky-500/30", "Bloomberg FX": "bg-sky-500/20 text-sky-400 border-sky-500/30", }; const CAT_COLORS: Record = { "Risk-Off": "bg-red-500/15 text-red-400", "Risk-On": "bg-emerald-500/15 text-emerald-400", "Géopolitique": "bg-purple-500/15 text-purple-400", "Énergie": "bg-yellow-500/15 text-yellow-400", "Banque Centrale": "bg-blue-500/15 text-blue-400", "Commodités": "bg-orange-500/15 text-orange-400", "Chine": "bg-red-600/15 text-red-400", "Données Macro": "bg-slate-500/15 text-slate-400", "Commerce": "bg-indigo-500/15 text-indigo-400", "Agriculture": "bg-green-500/15 text-green-400", "Or": "bg-yellow-600/15 text-yellow-400", "Métaux": "bg-gray-400/15 text-gray-300", }; function formatRelativeTime(isoDate: string): string { const diff = Date.now() - new Date(isoDate).getTime(); const mins = Math.floor(diff / 60000); if (mins < 1) return "à l'instant"; if (mins < 60) return `il y a ${mins}min`; const hrs = Math.floor(mins / 60); if (hrs < 24) return `il y a ${hrs}h`; const days = Math.floor(hrs / 24); return `il y a ${days}j`; } // ── Composant principal ─────────────────────────────────────────────────────── interface Props { items: NewsItem[]; loading: boolean; onRefresh: () => void; } export default function NewsTab({ items, loading, onRefresh }: Props) { const [filterCcy, setFilterCcy] = useState("ALL"); const [filterDir, setFilterDir] = useState<"all" | "bullish" | "bearish">("all"); const filtered = useMemo(() => { return items.filter(item => { if (filterCcy !== "ALL" && !item.impacts.some(i => i.ccy === filterCcy)) return false; if (filterDir !== "all") { const hasDir = filterCcy === "ALL" ? item.impacts.some(i => i.direction === filterDir) : item.impacts.some(i => i.ccy === filterCcy && i.direction === filterDir); if (!hasDir) return false; } return true; }); }, [items, filterCcy, filterDir]); // Compter les impacts par devise pour les badges de filtre const ccyCount = useMemo(() => { const counts: Partial> = {}; for (const item of items) { for (const imp of item.impacts) { if (!counts[imp.ccy]) counts[imp.ccy] = { bull: 0, bear: 0 }; if (imp.direction === "bullish") counts[imp.ccy]!.bull++; if (imp.direction === "bearish") counts[imp.ccy]!.bear++; } } return counts; }, [items]); return (
{/* Barre de filtres */}
Filtrer par devise
{(["all", "bullish", "bearish"] as const).map(d => ( ))}
{CCY_LIST.map(ccy => { const cnt = ccyCount[ccy]; if (!cnt) return null; const isActive = filterCcy === ccy; return ( ); })}
{/* Liste de news */} {loading && items.length === 0 ? (
Chargement des actualités…
) : filtered.length === 0 ? (
Aucune actualité pour ce filtre.
) : (
{filtered.map(item => ( ))}
)}
); } // ── NewsCard ────────────────────────────────────────────────────────────────── function NewsCard({ item, activeCcy }: { item: NewsItem; activeCcy: Currency | null }) { const [expanded, setExpanded] = useState(false); const visibleImpacts = activeCcy ? item.impacts.filter(i => i.ccy === activeCcy) : item.impacts; const overallDir = visibleImpacts.some(i => i.direction === "bullish") && visibleImpacts.some(i => i.direction === "bearish") ? "mixed" : visibleImpacts.some(i => i.direction === "bullish") ? "bullish" : visibleImpacts.some(i => i.direction === "bearish") ? "bearish" : "neutral"; const borderColor = overallDir === "bullish" ? "border-emerald-500/20" : overallDir === "bearish" ? "border-red-500/20" : overallDir === "mixed" ? "border-amber-500/20" : "border-slate-700/30"; const bgColor = overallDir === "bullish" ? "bg-emerald-500/5" : overallDir === "bearish" ? "bg-red-500/5" : "bg-slate-800/30"; return (
{/* Header : source + date */}
{item.source} {item.categories.slice(0, 2).map(cat => ( {cat} ))}
{formatRelativeTime(item.publishedAt)}
{/* Titre */} {item.title} {/* Summary si dispo */} {item.summary && (

{item.summary}

)} {/* Impact badges */} {visibleImpacts.length > 0 && (
{visibleImpacts.map(imp => (
{CCY_FLAGS[imp.ccy]} {imp.ccy} {imp.direction === "bullish" ? : imp.direction === "bearish" ? : }
))} {visibleImpacts.length > 0 && ( )}
)} {/* Détail des raisons */} {expanded && visibleImpacts.length > 0 && (
{visibleImpacts.map(imp => (
{CCY_FLAGS[imp.ccy]} {imp.ccy} {imp.direction === "bullish" ? "↑" : imp.direction === "bearish" ? "↓" : "→"} {imp.reason}
))}
)}
); }