From 7e97a079fc0e3ceca903658dae72d407252d5436 Mon Sep 17 00:00:00 2001 From: caty21 Date: Sat, 27 Jun 2026 17:17:44 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20news=20=E2=80=94=20compact=20filters=20?= =?UTF-8?q?bar,=20remove=20currency=20bias=20cards,=20live=201min?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Supprime le bloc "BIAIS ACTUALITÉS PAR DEVISE" (cartes devise) - Supprime le bouton Prioritaires standalone - Intègre Live/Actualiser dans la barre de filtres - Live passe à 1min (au lieu de 5min) - Labels Direction/Catégorie en amber/or - Filtre Prioritaire intégré dans la ligne direction - Filtres sur 2 lignes compactes Co-Authored-By: Claude Sonnet 4.6 --- components/NewsTab.tsx | 330 +++++++++++++++++------------------------ 1 file changed, 133 insertions(+), 197 deletions(-) diff --git a/components/NewsTab.tsx b/components/NewsTab.tsx index 324020c..c825992 100644 --- a/components/NewsTab.tsx +++ b/components/NewsTab.tsx @@ -14,29 +14,26 @@ const CCY_FLAGS: Record = { USD: "🇺🇸", EUR: "🇪🇺", GBP: "🇬🇧", JPY: "🇯🇵", CHF: "🇨🇭", CAD: "🇨🇦", AUD: "🇦🇺", NZD: "🇳🇿", }; -const CCY_LIST: Currency[] = ["USD", "EUR", "GBP", "JPY", "CHF", "CAD", "AUD", "NZD"]; -// Catégories prioritaires avec icône et couleur 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" }, + "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" }, }; -// Ordre d'affichage des filtres catégorie (par priorité signal) const PRIORITY_CATS = [ "Discours BC", "Décision Taux", "Probabilités Taux", "Chef d'État", "Données Clés", "Emploi", "Inflation", @@ -71,18 +68,17 @@ interface Props { } export default function NewsTab({ items, loading, onRefresh }: Props) { - const [filterCcy, setFilterCcy] = useState("ALL"); - 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 [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 5 minutes + // 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()); }, 5 * 60_000); + intervalRef.current = setInterval(() => { onRefresh(); setLastRefreshAt(new Date()); }, 60_000); return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; }, [autoRefresh, onRefresh]); @@ -91,175 +87,125 @@ export default function NewsTab({ items, loading, onRefresh }: Props) { const filtered = useMemo(() => items.filter(item => { if (priorityOnly && !isPriorityItem(item)) return false; - if (filterCcy !== "ALL" && !item.impacts.some(i => i.ccy === filterCcy)) return false; if (filterCat !== "ALL" && !item.categories.includes(filterCat)) return false; - if (filterDir !== "all") { - if (filterCcy !== "ALL") { - if (!item.impacts.some(i => i.ccy === filterCcy && i.direction === filterDir)) return false; - } else { - if (!item.impacts.some(i => i.direction === filterDir)) 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, filterCcy, filterCat, filterDir, priorityOnly]); + }), [items, filterCat, filterDir, priorityOnly]); - // Catégories présentes dans le feed actuel 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]); - // Comptage par devise - 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, total: 0 }; - counts[imp.ccy]!.total++; - if (imp.direction === "bullish") counts[imp.ccy]!.bull++; - if (imp.direction === "bearish") counts[imp.ccy]!.bear++; - } - } - return counts; - }, [items]); - - // Résumé par devise (pour headline) - const ccySummary = useMemo(() => { - const summary: Partial> = {}; - for (const [ccy, cnt] of Object.entries(ccyCount) as [Currency, { bull: number; bear: number }][]) { - if (cnt.bull > cnt.bear * 1.5) summary[ccy] = "bullish"; - else if (cnt.bear > cnt.bull * 1.5) summary[ccy] = "bearish"; - else if (cnt.bull > 0 && cnt.bear > 0) summary[ccy] = "mixed"; - else summary[ccy] = "neutral"; - } - return summary; - }, [ccyCount]); + const anyFilter = filterCat !== "ALL" || filterDir !== "all" || priorityOnly; + const clearFilters = () => { setFilterCat("ALL"); setFilterDir("all"); setPriorityOnly(false); }; return (
- {/* ── Headline résumé par devise ──────────────────────────────────────── */} - {!loading && items.length > 0 && ( -
-
- - Biais actualités par devise + {/* ── 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" : ""}`} -
- {/* Bouton Prioritaires */} - - {/* Auto-refresh */} - - -
+ )}
-
- {CCY_LIST.map(ccy => { - const cnt = ccyCount[ccy]; - const summary = ccySummary[ccy] ?? "neutral"; - const bgCls = summary === "bullish" ? "bg-emerald-500/10 border-emerald-500/20" - : summary === "bearish" ? "bg-red-500/10 border-red-500/20" - : summary === "mixed" ? "bg-amber-500/10 border-amber-500/20" - : "bg-slate-800/40 border-slate-700/30"; - const arrow = summary === "bullish" ? "↑" : summary === "bearish" ? "↓" - : summary === "mixed" ? "↕" : "→"; - const arrowCls = summary === "bullish" ? "text-emerald-400" : summary === "bearish" ? "text-red-400" - : summary === "mixed" ? "text-amber-400" : "text-slate-600"; +
+ + {/* Ligne 2 : catégories */} + {activeCats.length > 0 && ( +
+ + Catégorie + + + {activeCats.map(cat => { + const meta = CATEGORY_META[cat]; + const isActive = filterCat === cat; return ( - ); })}
-
- )} - - {/* ── Filtres ─────────────────────────────────────────────────────────── */} -
- {/* Direction */} -
- Direction -
- {(["all", "bullish", "bearish"] as const).map(d => ( - - ))} -
-
- - {/* Catégories */} - {activeCats.length > 0 && ( -
- Catégorie -
- - {activeCats.map(cat => { - const meta = CATEGORY_META[cat]; - const isActive = filterCat === cat; - return ( - - ); - })} -
-
- )} -
- - {/* ── Compteur résultats ──────────────────────────────────────────────── */} -
- - {loading ? "Chargement…" : `${filtered.length} article${filtered.length > 1 ? "s" : ""}`} - {filterCcy !== "ALL" && ` · ${CCY_FLAGS[filterCcy]} ${filterCcy}`} - {filterCat !== "ALL" && ` · ${filterCat}`} - - {(filterCcy !== "ALL" || filterCat !== "ALL" || filterDir !== "all" || priorityOnly) && ( - )}
@@ -277,11 +223,7 @@ export default function NewsTab({ items, loading, onRefresh }: Props) { ) : (
{filtered.map(item => ( - + ))}
)} @@ -291,20 +233,15 @@ export default function NewsTab({ items, loading, onRefresh }: Props) { // ── NewsCard ────────────────────────────────────────────────────────────────── -function NewsCard({ item, activeCcy }: { item: NewsItem; activeCcy: Currency | null }) { +function NewsCard({ item }: { item: NewsItem }) { 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" + 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"; - // Détecter les catégories prioritaires const isPriority = item.categories.some(c => ["Discours BC", "Décision Taux", "Crise", "Guerre", "Chef d'État"].includes(c) ); @@ -320,8 +257,7 @@ function NewsCard({ item, activeCcy }: { item: NewsItem; activeCcy: Currency | n overallDir === "bearish" ? "bg-red-500/5" : "bg-slate-800/30"; - // Catégorie la plus prioritaire - const topCat = PRIORITY_CATS.find(p => item.categories.includes(p)); + const topCat = PRIORITY_CATS.find(p => item.categories.includes(p)); const topMeta = topCat ? CATEGORY_META[topCat] : null; return ( @@ -359,10 +295,10 @@ function NewsCard({ item, activeCcy }: { item: NewsItem; activeCcy: Currency | n

)} - {/* Impact badges + bouton détail */} - {visibleImpacts.length > 0 && ( + {/* Impact badges + heure + bouton analyse */} + {item.impacts.length > 0 && (
- {visibleImpacts.map(imp => ( + {item.impacts.map(imp => (
{formatRelativeTime(item.publishedAt)}
@@ -386,9 +322,9 @@ function NewsCard({ item, activeCcy }: { item: NewsItem; activeCcy: Currency | n )} {/* Détail des raisons */} - {expanded && visibleImpacts.length > 0 && ( + {expanded && item.impacts.length > 0 && (
- {visibleImpacts.map(imp => ( + {item.impacts.map(imp => (