mirror of
https://github.com/caty21/forex-dashboard.git
synced 2026-08-12 20:18:05 +00:00
auto: sync 2026-06-03 00:59
This commit is contained in:
@@ -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
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user