"use client"; import { useEffect, useState, useCallback } from "react"; import { RefreshCw, TrendingUp, AlertTriangle, Zap, Database } from "lucide-react"; import { CURRENCIES, CURRENCY_META } from "@/lib/constants"; import type { Currency, DriverData } from "@/lib/types"; import { saveCache, loadCache, formatCacheDate } from "@/lib/localCache"; 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 [driversFromCache, setDriversFromCache] = useState(false); const [driversCacheAge, setDriversCacheAge] = useState(null); 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()), ]); // ── Drivers (marchés globaux) ────────────────────────────────────────── if (driversRes.status === "fulfilled" && !driversRes.value?.error) { const driversData = driversRes.value as DriverData; if (fxRes.status === "fulfilled" && fxRes.value?.dxy != null) { driversData.dxy = fxRes.value.dxy; } setDrivers(driversData); setDriversFromCache(false); setDriversCacheAge(null); saveCache("drivers", driversData); } else { // Fallback localStorage const cached = loadCache("drivers"); if (cached) { setDrivers(cached.data); setDriversFromCache(true); setDriversCacheAge(formatCacheDate(cached.savedAt)); } } // ── Attentes de taux ────────────────────────────────────────────────── if (expectRes.status === "fulfilled" && !expectRes.value?.error) { setExpectations(expectRes.value); saveCache("expectations", expectRes.value); } else { const cached = loadCache>("expectations"); if (cached) setExpectations(cached.data); } // ── Rendements obligataires ─────────────────────────────────────────── if (yieldsRes.status === "fulfilled" && !yieldsRes.value?.error) { setYields(yieldsRes.value); saveCache("yields", yieldsRes.value); } else { const cached = loadCache("yields"); if (cached && cached.data) setYields(cached.data); } 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" : ""}
)}
{driversFromCache && driversCacheAge && ( cache {driversCacheAge} )} {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 */}
); }