"use client"; import { useEffect, useState, useCallback } from "react"; import { RefreshCw, TrendingUp, AlertTriangle, Zap } from "lucide-react"; import { CURRENCIES, CURRENCY_META } from "@/lib/constants"; import type { Currency, DriverData } from "@/lib/types"; import CurrencyCard from "@/components/CurrencyCard"; import DriversBar from "@/components/DriversBar"; const REFRESH_MS = parseInt(process.env.NEXT_PUBLIC_REFRESH_INTERVAL_MS ?? "3600000"); export default function Dashboard() { const [drivers, setDrivers] = useState(null); const [expectations, setExpectations] = useState | null>(null); const [yields, setYields] = useState<{ yields: Record; spreads: Record } | null>(null); const [lastRefresh, setLastRefresh] = useState(new Date()); const [loading, setLoading] = useState(true); const [activeDivergences, setActiveDivergences] = useState<{ currency: Currency; score: number }[]>([]); const refresh = useCallback(async () => { setLoading(true); try { const [driversRes, expectRes, yieldsRes, fxRes] = await Promise.allSettled([ fetch("/api/drivers").then((r) => r.json()), fetch("/api/expectations").then((r) => r.json()), fetch("/api/yields").then((r) => r.json()), fetch("/api/fx").then((r) => r.json()), ]); if (driversRes.status === "fulfilled") { const driversData = driversRes.value; // Merge DXY from /api/fx into drivers if (fxRes.status === "fulfilled" && fxRes.value?.dxy != null) { driversData.dxy = fxRes.value.dxy; } setDrivers(driversData); } if (expectRes.status === "fulfilled") setExpectations(expectRes.value); if (yieldsRes.status === "fulfilled") setYields(yieldsRes.value); setLastRefresh(new Date()); } finally { setLoading(false); } }, []); useEffect(() => { refresh(); const id = setInterval(refresh, REFRESH_MS); return () => clearInterval(id); }, [refresh]); const handleDivergenceUpdate = useCallback((currency: Currency, score: number) => { setActiveDivergences((prev) => { const filtered = prev.filter((d) => d.currency !== currency); if (Math.abs(score) >= 2) return [...filtered, { currency, score }]; return filtered; }); }, []); const divergenceCount = activeDivergences.filter((d) => Math.abs(d.score) >= 2).length; return (
{/* Header */}

Forex Macro Dashboard

USD · EUR · GBP · JPY · CHF · CAD · AUD · NZD — v8.0

{divergenceCount > 0 && (
{divergenceCount} divergence{divergenceCount > 1 ? "s" : ""} active{divergenceCount > 1 ? "s" : ""}
)}
{lastRefresh.toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit" })}
{/* Global drivers bar */} {drivers && } {/* Active divergences summary */} {activeDivergences.length > 0 && (
{activeDivergences .sort((a, b) => Math.abs(b.score) - Math.abs(a.score)) .map(({ currency, score }) => (
{CURRENCY_META[currency].flag} {currency} SD:{score > 0 ? "+" : ""}{score}
))}
)} {/* Currency cards grid */}
{CURRENCIES.map((currency) => ( ))}
{/* Footer */}
); }