"use client"; import { useState, useMemo, useEffect, useRef } from "react"; import { ExternalLink, RefreshCw, TrendingUp, TrendingDown, Minus, Loader2, Radio, AlertTriangle, Landmark, Globe, BarChart2, Zap, } from "lucide-react"; import type { NewsItem } from "@/app/api/news/route"; import type { Currency } from "@/lib/types"; // ── Constantes ──────────────────────────────────────────────────────────────── const CCY_FLAGS: Record = { USD: "🇺🇸", EUR: "🇪🇺", GBP: "🇬🇧", JPY: "🇯🇵", CHF: "🇨🇭", CAD: "🇨🇦", AUD: "🇦🇺", NZD: "🇳🇿", }; const CATEGORY_META: Record = { "Discours BC": { icon: , color: "bg-blue-500/20 text-blue-400 border-blue-500/30", label: "Discours BC" }, "Chef d'État": { icon: , color: "bg-violet-500/20 text-violet-400 border-violet-500/30", label: "Chef d'État" }, "Décision Taux": { icon: , color: "bg-amber-500/20 text-amber-400 border-amber-500/30", label: "Décision Taux" }, "Probabilités Taux": { icon: , color: "bg-sky-500/20 text-sky-400 border-sky-500/30", label: "OIS / Proba Taux" }, "Données Clés": { icon: , color: "bg-slate-400/20 text-slate-300 border-slate-500/30", label: "Données Clés" }, "Emploi": { icon: , color: "bg-slate-400/20 text-slate-300 border-slate-500/30", label: "Emploi" }, "Inflation": { icon: , color: "bg-orange-500/20 text-orange-400 border-orange-500/30",label: "Inflation" }, "Crise": { icon: , color: "bg-red-600/20 text-red-400 border-red-500/30", label: "Crise" }, "Guerre": { icon: , color: "bg-red-700/20 text-red-400 border-red-600/30", label: "Guerre" }, "Géopolitique": { icon: , color: "bg-purple-500/20 text-purple-400 border-purple-500/30",label: "Géopolitique" }, "Risk-Off": { icon: , color: "bg-red-500/20 text-red-400 border-red-500/30", label: "Risk-Off" }, "Risk-On": { icon: , color: "bg-emerald-500/20 text-emerald-400 border-emerald-500/30", label: "Risk-On" }, "Énergie": { icon: , color: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30",label: "Énergie" }, "Banque Centrale": { icon: , color: "bg-blue-500/20 text-blue-400 border-blue-500/30", label: "Banque Centrale" }, "Commodités": { icon: , color: "bg-orange-500/20 text-orange-400 border-orange-500/30",label: "Commodités" }, "Chine": { icon: , color: "bg-red-600/20 text-red-400 border-red-600/30", label: "Chine" }, }; const PRIORITY_CATS = [ "Discours BC", "Décision Taux", "Probabilités Taux", "Chef d'État", "Données Clés", "Emploi", "Inflation", "Crise", "Guerre", "Géopolitique", "Risk-Off", "Risk-On", "Énergie", "Commodités", "Chine", ]; const SOURCE_COLORS: Record = { "InvestingLive": "bg-amber-500/20 text-amber-400", "Reuters": "bg-orange-500/20 text-orange-400", "Bloomberg Economics": "bg-sky-500/20 text-sky-400", "Bloomberg CB": "bg-sky-500/20 text-sky-400", "Bloomberg FX": "bg-sky-500/20 text-sky-400", }; 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`; return `il y a ${Math.floor(hrs / 24)}j`; } // ── Composant principal ─────────────────────────────────────────────────────── interface Props { items: NewsItem[]; loading: boolean; onRefresh: () => void; } export default function NewsTab({ items, loading, onRefresh }: Props) { const [filterCat, setFilterCat] = useState("ALL"); const [filterDir, setFilterDir] = useState<"all" | "bullish" | "bearish">("all"); const [priorityOnly, setPriorityOnly] = useState(false); const [autoRefresh, setAutoRefresh] = useState(true); const [lastRefreshAt, setLastRefreshAt] = useState(null); const intervalRef = useRef | null>(null); // Auto-refresh toutes les 1 minute quand actif useEffect(() => { if (!autoRefresh) { if (intervalRef.current) clearInterval(intervalRef.current); return; } intervalRef.current = setInterval(() => { onRefresh(); setLastRefreshAt(new Date()); }, 60_000); return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; }, [autoRefresh, onRefresh]); const isPriorityItem = (item: NewsItem) => item.categories.some(c => ["Discours BC", "Décision Taux", "Crise", "Guerre", "Chef d'État", "Probabilités Taux"].includes(c)); const filtered = useMemo(() => items.filter(item => { if (priorityOnly && !isPriorityItem(item)) return false; if (filterCat !== "ALL" && !item.categories.includes(filterCat)) return false; if (filterDir !== "all" && !item.impacts.some(i => i.direction === filterDir)) return false; return true; // eslint-disable-next-line react-hooks/exhaustive-deps }), [items, filterCat, filterDir, priorityOnly]); const activeCats = useMemo(() => { const set = new Set(); for (const item of items) item.categories.forEach(c => set.add(c)); return PRIORITY_CATS.filter(c => set.has(c)); }, [items]); const anyFilter = filterCat !== "ALL" || filterDir !== "all" || priorityOnly; const clearFilters = () => { setFilterCat("ALL"); setFilterDir("all"); setPriorityOnly(false); }; return (
{/* ── Barre filtres ──────────────────────────────────────────────────── */}
{/* Ligne 1 : contrôles + direction + prioritaire */}
{/* Live 1min */} {/* Actualiser */}
{/* Label Direction */} Direction {/* Boutons direction */} {(["all", "bullish", "bearish"] as const).map(d => ( ))}
{/* Prioritaire toggle */} {/* Compteur + effacer */}
{loading ? "…" : `${filtered.length} article${filtered.length !== 1 ? "s" : ""}`} {anyFilter && ( )}
{/* Ligne 2 : catégories */} {activeCats.length > 0 && (
Catégorie {activeCats.map(cat => { const meta = CATEGORY_META[cat]; const isActive = filterCat === cat; return ( ); })}
)}
{/* ── Liste ───────────────────────────────────────────────────────────── */} {loading && items.length === 0 ? (
Chargement des actualités… InvestingLive · Reuters · Bloomberg
) : filtered.length === 0 ? (
Aucune actualité pour ce filtre.
) : (
{filtered.map(item => ( ))}
)}
); } // ── NewsCard ────────────────────────────────────────────────────────────────── function NewsCard({ item }: { item: NewsItem }) { const [expanded, setExpanded] = useState(false); const overallDir = item.impacts.some(i => i.direction === "bullish") && item.impacts.some(i => i.direction === "bearish") ? "mixed" : item.impacts.some(i => i.direction === "bullish") ? "bullish" : item.impacts.some(i => i.direction === "bearish") ? "bearish" : "neutral"; const isPriority = item.categories.some(c => ["Discours BC", "Décision Taux", "Crise", "Guerre", "Chef d'État"].includes(c) ); const borderCls = overallDir === "bullish" ? "border-emerald-500/25" : overallDir === "bearish" ? "border-red-500/25" : overallDir === "mixed" ? "border-amber-500/20" : "border-slate-700/30"; const bgCls = overallDir === "bullish" ? "bg-emerald-500/5" : overallDir === "bearish" ? "bg-red-500/5" : "bg-slate-800/30"; const topCat = PRIORITY_CATS.find(p => item.categories.includes(p)); const topMeta = topCat ? CATEGORY_META[topCat] : null; return (
{/* Header */}
{item.source} {topMeta && topCat && ( {topMeta.icon} {topMeta.label} )} {isPriority && ( ⚡ Prioritaire )}
{/* Titre */} {item.title} {/* Résumé */} {item.summary && (

{item.summary}

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