mirror of
https://github.com/caty21/forex-dashboard.git
synced 2026-07-27 20:37:45 +00:00
fix: filters use string[] (not Set), charts use TvAdvancedChart for reliable multi-instance rendering
This commit is contained in:
+5
-5
@@ -14,7 +14,7 @@ import YieldsTab from "@/components/YieldsTab";
|
||||
import NewsTab from "@/components/NewsTab";
|
||||
import CotTab from "@/components/CotTab";
|
||||
import ReportTab from "@/components/ReportTab";
|
||||
import { TvMiniChart } from "@/components/TvChart";
|
||||
import { TvAdvancedChart } from "@/components/TvChart";
|
||||
import type { CalendarEvent } from "@/app/api/calendar/route";
|
||||
import type { NewsItem } from "@/app/api/news/route";
|
||||
import type { CotHistory } from "@/app/api/cot-history/route";
|
||||
@@ -607,10 +607,10 @@ export default function Dashboard() {
|
||||
<div className="h-px flex-1 bg-sky-500/20" />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<TvMiniChart symbol="SP:SPX" label="S&P 500 · Daily" dateRange="12M" height={220} />
|
||||
<TvMiniChart symbol="TVC:VIX" label="VIX · Daily" dateRange="12M" height={220} />
|
||||
<TvMiniChart symbol="TVC:DXY" label="DXY Dollar Index · Weekly" dateRange="60M" height={220} />
|
||||
<TvMiniChart symbol="TVC:GOLD" label="Or (XAU/USD) · Weekly" dateRange="60M" height={220} />
|
||||
<TvAdvancedChart symbol="SP:SPX" label="S&P 500 · Daily" interval="D" height={220} />
|
||||
<TvAdvancedChart symbol="TVC:VIX" label="VIX · Daily" interval="D" height={220} />
|
||||
<TvAdvancedChart symbol="TVC:DXY" label="DXY Dollar Index · Weekly" interval="W" height={220} />
|
||||
<TvAdvancedChart symbol="TVC:GOLD" label="Or (XAU/USD) · Weekly" interval="W" height={220} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
+15
-18
@@ -91,7 +91,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function NewsTab({ items, loading, onRefresh }: Props) {
|
||||
const [selectedCats, setSelectedCats] = useState<Set<string>>(new Set());
|
||||
const [selectedCats, setSelectedCats] = useState<string[]>([]);
|
||||
const [filterDir, setFilterDir] = useState<"all" | "bullish" | "bearish">("all");
|
||||
const [priorityOnly, setPriorityOnly] = useState(false);
|
||||
const [autoRefresh, setAutoRefresh] = useState(true);
|
||||
@@ -106,23 +106,20 @@ export default function NewsTab({ items, loading, onRefresh }: Props) {
|
||||
}, [autoRefresh, onRefresh]);
|
||||
|
||||
const toggleCat = (cat: string) => {
|
||||
setSelectedCats(prev => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(cat)) next.delete(cat);
|
||||
else next.add(cat);
|
||||
return next;
|
||||
});
|
||||
setSelectedCats(prev =>
|
||||
prev.includes(cat) ? prev.filter(c => c !== cat) : [...prev, cat]
|
||||
);
|
||||
};
|
||||
|
||||
// Filtre + tri — articles avec catégories macro et articles sans
|
||||
const { topFiltered, otherItems } = useMemo(() => {
|
||||
const hasActiveFilter = selectedCats.size > 0 || filterDir !== "all" || priorityOnly;
|
||||
const hasActiveFilter = selectedCats.length > 0 || filterDir !== "all" || priorityOnly;
|
||||
|
||||
// Articles filtrés (tous)
|
||||
const allFiltered = items
|
||||
.filter(item => {
|
||||
if (priorityOnly && !item.categories.some(c => HIGH_PRIO.has(c))) return false;
|
||||
if (selectedCats.size > 0 && !item.categories.some(c => selectedCats.has(c))) return false;
|
||||
if (selectedCats.length > 0 && !item.categories.some(c => selectedCats.includes(c))) return false;
|
||||
if (filterDir !== "all" && !item.impacts.some(i => i.direction === filterDir)) return false;
|
||||
return true;
|
||||
})
|
||||
@@ -143,14 +140,14 @@ export default function NewsTab({ items, loading, onRefresh }: Props) {
|
||||
|
||||
// Boutons de catégories : dynamiques + catégories "toujours visibles"
|
||||
const activeCats = useMemo(() => {
|
||||
const set = new Set<string>();
|
||||
for (const item of items) item.categories.forEach(c => set.add(c));
|
||||
ALWAYS_VISIBLE.forEach(c => set.add(c)); // toujours afficher ces catégories
|
||||
return PRIORITY_CATS.filter(c => set.has(c));
|
||||
const seen = new Set<string>();
|
||||
for (const item of items) item.categories.forEach(c => seen.add(c));
|
||||
ALWAYS_VISIBLE.forEach(c => seen.add(c));
|
||||
return PRIORITY_CATS.filter(c => seen.has(c));
|
||||
}, [items]);
|
||||
|
||||
const anyFilter = selectedCats.size > 0 || filterDir !== "all" || priorityOnly;
|
||||
const clearFilters = () => { setSelectedCats(new Set()); setFilterDir("all"); setPriorityOnly(false); };
|
||||
const anyFilter = selectedCats.length > 0 || filterDir !== "all" || priorityOnly;
|
||||
const clearFilters = () => { setSelectedCats([]); setFilterDir("all"); setPriorityOnly(false); };
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
@@ -226,9 +223,9 @@ export default function NewsTab({ items, loading, onRefresh }: Props) {
|
||||
<span className="text-[9px] text-amber-400 uppercase tracking-wider font-semibold shrink-0">
|
||||
Catégorie
|
||||
</span>
|
||||
<button onClick={() => setSelectedCats(new Set())}
|
||||
<button onClick={() => setSelectedCats([])}
|
||||
className={`text-[10px] px-2 py-0.5 rounded-full font-semibold border transition-colors shrink-0 ${
|
||||
selectedCats.size === 0
|
||||
selectedCats.length === 0
|
||||
? "bg-slate-700 text-slate-300 border-slate-600"
|
||||
: "text-slate-500 border-slate-700/40 hover:text-slate-300"
|
||||
}`}>
|
||||
@@ -236,7 +233,7 @@ export default function NewsTab({ items, loading, onRefresh }: Props) {
|
||||
</button>
|
||||
{activeCats.map(cat => {
|
||||
const meta = CATEGORY_META[cat];
|
||||
const isActive = selectedCats.has(cat);
|
||||
const isActive = selectedCats.includes(cat);
|
||||
return (
|
||||
<button key={cat} onClick={() => toggleCat(cat)}
|
||||
className={`flex items-center gap-1 text-[10px] px-2 py-0.5 rounded-full font-semibold border transition-colors shrink-0 ${
|
||||
|
||||
Reference in New Issue
Block a user