mirror of
https://github.com/caty21/forex-dashboard.git
synced 2026-07-27 20:37:45 +00:00
feat: onglet Marchés + TvMiniChart sans popup + cache RP 48h + OIS default
- page.tsx: onglet '🌍 Marchés' — S&P 500, VIX, DXY, Or (TvMiniChart 2x2) - TvChart: prop dateRange configurable sur TvMiniChart (défaut 1M) - ReportTab: TvAdvancedChart → TvMiniChart (plus de popup TV), Or remplace US10Y S&P 500/VIX dateRange=12M (daily), DXY/Or dateRange=60M (weekly) - rateprobability: cache TTL 4h → 48h (OIS revient même sans GitHub Actions hourly) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+20
-2
@@ -14,6 +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 type { CalendarEvent } from "@/app/api/calendar/route";
|
||||
import type { NewsItem } from "@/app/api/news/route";
|
||||
import type { CotHistory } from "@/app/api/cot-history/route";
|
||||
@@ -28,7 +29,7 @@ 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" | "news" | "cot" | "report">("dashboard");
|
||||
const [activeTab, setActiveTab] = useState<"dashboard" | "calendar" | "pairs" | "yields" | "news" | "cot" | "report" | "markets">("dashboard");
|
||||
const [newsItems, setNewsItems] = useState<NewsItem[]>([]);
|
||||
const [newsLoading, setNewsLoading] = useState(false);
|
||||
const [cotHistory, setCotHistory] = useState<CotHistory | null>(null);
|
||||
@@ -350,7 +351,7 @@ export default function Dashboard() {
|
||||
|
||||
{/* Tab navigation */}
|
||||
<div className="flex gap-0 border-b border-slate-800 mb-4">
|
||||
{(["dashboard", "calendar", "pairs", "yields", "news", "cot", "report"] as const).map((tab) => (
|
||||
{(["dashboard", "markets", "calendar", "pairs", "yields", "news", "cot", "report"] as const).map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
@@ -361,6 +362,7 @@ export default function Dashboard() {
|
||||
}`}
|
||||
>
|
||||
{tab === "dashboard" ? "Dashboard"
|
||||
: tab === "markets" ? "🌍 Marchés"
|
||||
: tab === "calendar" ? "📅 Calendrier"
|
||||
: tab === "pairs" ? "↕ Paires"
|
||||
: tab === "yields" ? "📈 Yields 10Y"
|
||||
@@ -597,6 +599,22 @@ export default function Dashboard() {
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === "markets" && (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="h-px flex-1 bg-sky-500/20" />
|
||||
<span className="text-sky-400 text-xs font-bold uppercase tracking-[0.3em]">Vue d'ensemble · Marchés Globaux</span>
|
||||
<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} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "calendar" && (
|
||||
<CalendarTab events={calEvents} loading={loading} nextWeekAvail={nextWeekAvail} />
|
||||
)}
|
||||
|
||||
@@ -509,12 +509,12 @@ export default function ReportTab({ calEvents, drivers, cotHistory }: Props) {
|
||||
<div className="h-px flex-1 bg-sky-500/30" />
|
||||
</div>
|
||||
|
||||
{/* Macro overview : S&P, VIX, DXY, US10Y */}
|
||||
{/* Macro overview : S&P, VIX, DXY, Or */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<TvAdvancedChart symbol="SP:SPX" label="S&P 500 · Weekly" interval="W" 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:US10Y" label="US 10Y Yield · Weekly" interval="W" height={220} />
|
||||
<TvMiniChart symbol="SP:SPX" label="S&P 500 · Daily" dateRange="12M" height={200} />
|
||||
<TvMiniChart symbol="TVC:VIX" label="VIX · Daily" dateRange="12M" height={200} />
|
||||
<TvMiniChart symbol="TVC:DXY" label="DXY Dollar Index · Weekly" dateRange="60M" height={200} />
|
||||
<TvMiniChart symbol="TVC:GOLD" label="Or (XAU/USD) · Weekly" dateRange="60M" height={200} />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 pt-2">
|
||||
|
||||
@@ -13,11 +13,12 @@ declare global {
|
||||
}
|
||||
|
||||
interface TvMiniChartProps {
|
||||
symbol: string; // ex: "FX:EURUSD", "TVC:DXY", "SP:SPX"
|
||||
label?: string; // titre affiché au-dessus
|
||||
interval?: "W" | "D" | "M";
|
||||
height?: number;
|
||||
showInfo?: boolean; // afficher nom + prix sous le graphique
|
||||
symbol: string; // ex: "FX:EURUSD", "TVC:DXY", "SP:SPX"
|
||||
label?: string; // titre affiché au-dessus
|
||||
interval?: "W" | "D" | "M";
|
||||
dateRange?: string; // "1D","5D","1M","3M","6M","12M","60M","ALL","YTD"
|
||||
height?: number;
|
||||
showInfo?: boolean; // afficher nom + prix sous le graphique
|
||||
}
|
||||
|
||||
// Script TradingView chargé une seule fois
|
||||
@@ -41,7 +42,7 @@ function loadTvScript(cb: () => void) {
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
|
||||
export function TvMiniChart({ symbol, label, height = 180, showInfo = true }: TvMiniChartProps) {
|
||||
export function TvMiniChart({ symbol, label, dateRange = "1M", height = 180, showInfo = true }: TvMiniChartProps) {
|
||||
const uid = useId().replace(/:/g, "_");
|
||||
const id = `tv_mini_${uid}`;
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
@@ -60,7 +61,7 @@ export function TvMiniChart({ symbol, label, height = 180, showInfo = true }: Tv
|
||||
width: "100%",
|
||||
height,
|
||||
locale: "fr",
|
||||
dateRange: "1M",
|
||||
dateRange,
|
||||
colorTheme: "dark",
|
||||
trendLineColor: "#38bdf8",
|
||||
underLineColor: "rgba(56,189,248,0.08)",
|
||||
|
||||
@@ -237,7 +237,7 @@ function loadCachedRPBody(ccy: string, slug: string): Record<string, unknown> |
|
||||
const entry = parsed.data?.[ccy] as Record<string, unknown> | undefined;
|
||||
if (!entry) return null;
|
||||
const ageMs = Date.now() - new Date(parsed.fetchedAt).getTime();
|
||||
if (ageMs > 4 * 60 * 60 * 1000) { // ignore si > 4h
|
||||
if (ageMs > 48 * 60 * 60 * 1000) { // ignore si > 48h (GitHub Actions peut ne pas tourner quotidiennement)
|
||||
console.warn(`[rate-prob] cache stale (${Math.round(ageMs / 3600000)}h), skipping`);
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user