auto: sync 2026-06-03 00:59

This commit is contained in:
Capucine Gest
2026-06-03 00:59:04 +02:00
parent d075ed484a
commit 34cd885497
4 changed files with 924 additions and 3 deletions
+22
View File
@@ -0,0 +1,22 @@
import { NextResponse } from "next/server";
import { fetchAllNews } from "@/lib/newsfeed";
import type { NewsItem } from "@/lib/newsfeed";
export type { NewsItem } from "@/lib/newsfeed";
let _cache: { data: NewsItem[]; ts: number } | null = null;
const TTL = 30 * 60_000; // 30 min
export async function GET() {
if (_cache && Date.now() - _cache.ts < TTL) {
return NextResponse.json({ items: _cache.data, fetchedAt: new Date(_cache.ts).toISOString() });
}
const items = await fetchAllNews();
_cache = { data: items, ts: Date.now() };
return NextResponse.json(
{ items, fetchedAt: new Date().toISOString() },
{ headers: { "Cache-Control": "s-maxage=1800, stale-while-revalidate=3600" } }
);
}
+29 -3
View File
@@ -11,7 +11,9 @@ import DriversBar from "@/components/DriversBar";
import CalendarTab from "@/components/CalendarTab";
import SentimentPairsTab from "@/components/SentimentPairsTab";
import YieldsTab from "@/components/YieldsTab";
import NewsTab from "@/components/NewsTab";
import type { CalendarEvent } from "@/app/api/calendar/route";
import type { NewsItem } from "@/app/api/news/route";
const REFRESH_MS = parseInt(process.env.NEXT_PUBLIC_REFRESH_INTERVAL_MS ?? "3600000");
@@ -23,7 +25,9 @@ export default function Dashboard() {
const [cot, setCot] = useState<Record<string, CotEntry> | null>(null);
const [calEvents, setCalEvents] = useState<CalendarEvent[]>([]);
const [nextWeekAvail, setNextWeekAvail] = useState(false);
const [activeTab, setActiveTab] = useState<"dashboard" | "calendar" | "pairs" | "yields">("dashboard");
const [activeTab, setActiveTab] = useState<"dashboard" | "calendar" | "pairs" | "yields" | "news">("dashboard");
const [newsItems, setNewsItems] = useState<NewsItem[]>([]);
const [newsLoading, setNewsLoading] = useState(false);
const [rawSymbols, setRawSymbols] = useState<Array<{ name: string; longPercentage: number; shortPercentage: number; totalPositions: number }> | null>(null);
const [rateProbabilities, setRateProbabilities] = useState<RateProbData | null>(null);
const [lastRefresh, setLastRefresh] = useState<Date>(new Date());
@@ -212,6 +216,23 @@ export default function Dashboard() {
return () => clearInterval(id);
}, [refresh]);
const refreshNews = useCallback(async () => {
setNewsLoading(true);
try {
const res = await fetch("/api/news");
if (res.ok) {
const json = await res.json();
setNewsItems(json.items ?? []);
}
} finally {
setNewsLoading(false);
}
}, []);
useEffect(() => {
if (activeTab === "news" && newsItems.length === 0) refreshNews();
}, [activeTab, newsItems.length, refreshNews]);
const handleDivergenceUpdate = useCallback((currency: Currency, score: number) => {
setActiveDivergences((prev) => {
const filtered = prev.filter((d) => d.currency !== currency);
@@ -271,7 +292,7 @@ export default function Dashboard() {
{/* Tab navigation */}
<div className="flex gap-0 border-b border-slate-800 mb-4">
{(["dashboard", "calendar", "pairs", "yields"] as const).map((tab) => (
{(["dashboard", "calendar", "pairs", "yields", "news"] as const).map((tab) => (
<button
key={tab}
onClick={() => setActiveTab(tab)}
@@ -284,7 +305,8 @@ export default function Dashboard() {
{tab === "dashboard" ? "Dashboard"
: tab === "calendar" ? "📅 Calendrier"
: tab === "pairs" ? "↕ Paires"
: "📈 Yields 10Y"}
: tab === "yields" ? "📈 Yields 10Y"
: "📰 Actualités"}
</button>
))}
</div>
@@ -345,6 +367,10 @@ export default function Dashboard() {
<YieldsTab yieldsData={yields} />
)}
{activeTab === "news" && (
<NewsTab items={newsItems} loading={newsLoading} onRefresh={refreshNews} />
)}
{/* Legend */}
<div className="mt-4 pt-3 border-t border-slate-800 flex items-center gap-5 flex-wrap text-[10px] text-slate-600">
<div className="flex items-center gap-1.5"><span className="w-2 h-2 rounded-full bg-emerald-500 shrink-0" /> Haussier / Sous-évalué</div>