From cb52cfad1a29f1e7d09a09f8e037a97eaf6e37b8 Mon Sep 17 00:00:00 2001
From: caty21
Date: Sat, 27 Jun 2026 23:09:02 +0200
Subject: [PATCH] fix: TvChart embed-widget-advanced-chart (candlestick, no
symbol restriction), unique instance IDs, CHF/SNB trial, NZD candlestick in
ReportTab
---
.github/scripts/fetch-rate-data.mjs | 1 +
components/ReportTab.tsx | 4 +-
components/TvChart.tsx | 248 ++++++++++++++--------------
3 files changed, 126 insertions(+), 127 deletions(-)
diff --git a/.github/scripts/fetch-rate-data.mjs b/.github/scripts/fetch-rate-data.mjs
index 4935514..b75c04f 100644
--- a/.github/scripts/fetch-rate-data.mjs
+++ b/.github/scripts/fetch-rate-data.mjs
@@ -10,6 +10,7 @@ const CB_KEYS = [
["CAD", "boc"],
["AUD", "rba"],
["NZD", "rbnz"],
+ ["CHF", "snb"], // BNS β essai, null si l'endpoint n'existe pas sur rateprobability.com
];
const HEADERS = {
diff --git a/components/ReportTab.tsx b/components/ReportTab.tsx
index 1137952..5459e46 100644
--- a/components/ReportTab.tsx
+++ b/components/ReportTab.tsx
@@ -523,7 +523,7 @@ export default function ReportTab({ calEvents, drivers, cotHistory }: Props) {
- {/* 8 currency mini charts */}
+ {/* 8 currency charts β bougie hebdomadaire */}
{[
{ sym: "TVC:DXY", label: "πΊπΈ USD Β· DXY" },
@@ -535,7 +535,7 @@ export default function ReportTab({ calEvents, drivers, cotHistory }: Props) {
{ sym: "FX:AUDUSD", label: "π¦πΊ AUD/USD" },
{ sym: "FX:NZDUSD", label: "π³πΏ NZD/USD" },
].map(({ sym, label }) => (
-
+
))}
diff --git a/components/TvChart.tsx b/components/TvChart.tsx
index 63fe312..0503957 100644
--- a/components/TvChart.tsx
+++ b/components/TvChart.tsx
@@ -2,71 +2,96 @@
import { useEffect, useRef } from "react";
-// ββ TvMiniChart βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
-// Utilise le script embed TradingView dΓ©diΓ© (embed-widget-mini-symbol-overview.js)
-// et NON pas tv.js (qui n'expose pas MiniSymbolOverview).
-// La structure DOM attendue par TradingView :
-//
+// ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+// Chaque instance obtient un identifiant unique injectΓ© dans l'URL du script
+// pour forcer le navigateur Γ rΓ©-exΓ©cuter le script mΓͺme si l'URL est en cache.
+// Sans Γ§a, les 4 charts simultanΓ©s du tab MarchΓ©s ne s'initialisent pas tous.
-interface TvMiniChartProps {
- symbol: string; // ex: "SP:SPX", "TVC:DXY", "FX:EURUSD"
- 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
+function mkInstanceId() {
+ return Math.random().toString(36).slice(2);
}
-export function TvMiniChart({
+// ββ Structure DOM commune attendue par les widgets embed TradingView ββββββββββ
+//
+
+function mountEmbedWidget(
+ wrapper: HTMLDivElement,
+ scriptSrc: string,
+ config: Record,
+ height: number
+) {
+ wrapper.innerHTML = "";
+
+ const widgetDiv = document.createElement("div");
+ widgetDiv.className = "tradingview-widget-container__widget";
+ widgetDiv.style.width = "100%";
+ widgetDiv.style.height = `${height}px`;
+ wrapper.appendChild(widgetDiv);
+
+ const script = document.createElement("script");
+ script.type = "text/javascript";
+ script.async = true;
+ script.src = scriptSrc;
+ script.text = JSON.stringify(config);
+ wrapper.appendChild(script);
+}
+
+// ββ TvAdvancedChart βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+// Graphique avancΓ© (bougies) via embed-widget-advanced-chart.js.
+// Utilise une iframe TradingView β accΓ¨s aux mΓͺmes symboles que le site web
+// (SP:SPX, TVC:VIX, TVC:DXY, TVC:GOLD, etc.) sans restriction "abonnement".
+
+interface TvAdvancedChartProps {
+ symbol: string; // ex: "SP:SPX", "TVC:DXY", "FX:EURUSD"
+ label?: string;
+ interval?: string; // "D", "W", "M", "60", etc.
+ height?: number;
+}
+
+export function TvAdvancedChart({
symbol,
label,
- dateRange = "1M",
- height = 180,
- showInfo = true,
-}: TvMiniChartProps) {
+ interval = "D",
+ height = 250,
+}: TvAdvancedChartProps) {
const wrapperRef = useRef(null);
const initialized = useRef(false);
+ const instanceId = useRef(mkInstanceId());
useEffect(() => {
if (initialized.current || !wrapperRef.current) return;
initialized.current = true;
- const wrapper = wrapperRef.current;
- wrapper.innerHTML = "";
-
- // Div cible du widget
- const widgetDiv = document.createElement("div");
- widgetDiv.className = "tradingview-widget-container__widget";
- wrapper.appendChild(widgetDiv);
-
- // Script embed avec config inline (textContent lu par le script au chargement)
- const script = document.createElement("script");
- script.type = "text/javascript";
- script.async = true;
- script.src = "https://s3.tradingview.com/external-embedding/embed-widget-mini-symbol-overview.js";
- script.text = JSON.stringify({
- symbol,
- width: "100%",
- height,
- locale: "fr",
- dateRange,
- colorTheme: "dark",
- trendLineColor: "#38bdf8",
- underLineColor: "rgba(56,189,248,0.08)",
- underLineBottomColor: "rgba(56,189,248,0)",
- isTransparent: true,
- autosize: false,
- largeChartUrl: "",
- noTimeScale: false,
- chartOnly: !showInfo,
- });
- wrapper.appendChild(script);
+ mountEmbedWidget(
+ wrapperRef.current,
+ `https://s3.tradingview.com/external-embedding/embed-widget-advanced-chart.js?t=${instanceId.current}`,
+ {
+ autosize: false,
+ width: "100%",
+ height,
+ symbol,
+ interval,
+ timezone: "Europe/Paris",
+ theme: "dark",
+ style: "1", // bougies japonaises
+ locale: "fr",
+ backgroundColor: "rgba(8,12,20,0)",
+ gridColor: "rgba(30,45,61,0.5)",
+ hide_top_toolbar: true,
+ hide_legend: false,
+ allow_symbol_change: false,
+ calendar: false,
+ hide_volume: true,
+ isTransparent: true,
+ },
+ height
+ );
return () => {
- wrapper.innerHTML = "";
+ if (wrapperRef.current) wrapperRef.current.innerHTML = "";
initialized.current = false;
};
}, []); // eslint-disable-line
@@ -87,85 +112,59 @@ export function TvMiniChart({
);
}
-// ββ TvAdvancedChart βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
-// Graphique avancΓ© plein format via tv.js (TradingView.widget).
-// Note : certains symboles affichent une popup "disponible uniquement sur TradingView".
+// ββ TvMiniChart βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+// Graphique lΓ©ger (ligne/area) via embed-widget-mini-symbol-overview.js.
+// IdΓ©al pour les aperΓ§us compacts (CurrencyCard, sidebar, etc.).
-declare global {
- interface Window {
- TradingView?: {
- widget: new (config: Record) => void;
- };
- }
+interface TvMiniChartProps {
+ symbol: string;
+ label?: string;
+ dateRange?: string; // "1D","5D","1M","3M","6M","12M","60M","ALL","YTD"
+ height?: number;
+ showInfo?: boolean;
}
-let tvScriptLoaded = false;
-let tvScriptLoading = false;
-const tvCallbacks: (() => void)[] = [];
-
-function loadTvScript(cb: () => void) {
- if (tvScriptLoaded) { cb(); return; }
- tvCallbacks.push(cb);
- if (tvScriptLoading) return;
- tvScriptLoading = true;
- const s = document.createElement("script");
- s.src = "https://s3.tradingview.com/tv.js";
- s.async = true;
- s.onload = () => {
- tvScriptLoaded = true;
- tvCallbacks.forEach(f => f());
- tvCallbacks.length = 0;
- };
- document.head.appendChild(s);
-}
-
-interface TvAdvancedChartProps {
- symbol: string;
- label?: string;
- interval?: string;
- height?: number;
-}
-
-export function TvAdvancedChart({
+export function TvMiniChart({
symbol,
label,
- interval = "W",
- height = 250,
-}: TvAdvancedChartProps) {
- const uid = useRef(`tv_adv_${Math.random().toString(36).slice(2)}`);
- const ref = useRef(null);
- const init = useRef(false);
+ dateRange = "1M",
+ height = 180,
+ showInfo = true,
+}: TvMiniChartProps) {
+ const wrapperRef = useRef(null);
+ const initialized = useRef(false);
+ const instanceId = useRef(mkInstanceId());
useEffect(() => {
- if (init.current) return;
- init.current = true;
+ if (initialized.current || !wrapperRef.current) return;
+ initialized.current = true;
- loadTvScript(() => {
- if (!window.TradingView || !ref.current) return;
- try {
- new window.TradingView.widget({
- autosize: false,
- width: "100%",
- height,
- symbol,
- interval,
- timezone: "Europe/Paris",
- theme: "dark",
- style: "1",
- locale: "fr",
- toolbar_bg: "#0f1623",
- enable_publishing: false,
- hide_top_toolbar: true,
- hide_legend: false,
- save_image: false,
- container_id: uid.current,
- backgroundColor: "rgba(8,12,20,0)",
- gridColor: "rgba(30,45,61,0.5)",
- hide_volume: false,
- studies: [],
- });
- } catch { /* TradingView indisponible */ }
- });
+ mountEmbedWidget(
+ wrapperRef.current,
+ `https://s3.tradingview.com/external-embedding/embed-widget-mini-symbol-overview.js?t=${instanceId.current}`,
+ {
+ symbol,
+ width: "100%",
+ height,
+ locale: "fr",
+ dateRange,
+ colorTheme: "dark",
+ trendLineColor: "#38bdf8",
+ underLineColor: "rgba(56,189,248,0.08)",
+ underLineBottomColor: "rgba(56,189,248,0)",
+ isTransparent: true,
+ autosize: false,
+ largeChartUrl: "",
+ noTimeScale: false,
+ chartOnly: !showInfo,
+ },
+ height
+ );
+
+ return () => {
+ if (wrapperRef.current) wrapperRef.current.innerHTML = "";
+ initialized.current = false;
+ };
}, []); // eslint-disable-line
return (
@@ -176,9 +175,8 @@ export function TvAdvancedChart({
)}