mirror of
https://github.com/caty21/forex-dashboard.git
synced 2026-08-25 10:28:05 +00:00
feat: sync button now syncs all tabs — macro slide, main tab, signaux slide
When sync is active, clicking any tab/indicator on any card propagates to all other cards (overview/mispricing/focus + ois/cot/sent + mon/infl/cro/empl). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
b82c680f83
commit
bcc30ed5ba
+8
-2
@@ -46,8 +46,10 @@ export default function Dashboard() {
|
|||||||
const [comparisonSection, setComparisonSection] = useState<Exclude<MacroSection,"all">>("inflation");
|
const [comparisonSection, setComparisonSection] = useState<Exclude<MacroSection,"all">>("inflation");
|
||||||
const [comparisonCurrencies, setComparisonCurrencies] = useState<Currency[] | "all">("all");
|
const [comparisonCurrencies, setComparisonCurrencies] = useState<Currency[] | "all">("all");
|
||||||
const [focusCurrency, setFocusCurrency] = useState<Currency | "all">("all");
|
const [focusCurrency, setFocusCurrency] = useState<Currency | "all">("all");
|
||||||
const [globalMacroSlide, setGlobalMacroSlide] = useState<"mon"|"infl"|"cro"|"empl">("mon");
|
const [globalMacroSlide, setGlobalMacroSlide] = useState<"mon"|"infl"|"cro"|"empl">("mon");
|
||||||
const [macroSyncEnabled, setMacroSyncEnabled] = useState(false);
|
const [globalCardTab, setGlobalCardTab] = useState<"overview"|"mispricing"|"focus">("overview");
|
||||||
|
const [globalSignauxSlide, setGlobalSignauxSlide] = useState<"ois"|"cot"|"sent">("ois");
|
||||||
|
const [macroSyncEnabled, setMacroSyncEnabled] = useState(false);
|
||||||
|
|
||||||
// ── Sentiment multi-paires Myfxbook → {CCY: {longPct, shortPct, pair}} ──────
|
// ── Sentiment multi-paires Myfxbook → {CCY: {longPct, shortPct, pair}} ──────
|
||||||
// Pour chaque devise, on calcule le % "long CCY" en moyenne pondérée (par volume)
|
// Pour chaque devise, on calcule le % "long CCY" en moyenne pondérée (par volume)
|
||||||
@@ -585,6 +587,10 @@ export default function Dashboard() {
|
|||||||
macroSection={macroSection}
|
macroSection={macroSection}
|
||||||
syncMacroSlide={macroSyncEnabled ? globalMacroSlide : undefined}
|
syncMacroSlide={macroSyncEnabled ? globalMacroSlide : undefined}
|
||||||
onMacroSlideChange={macroSyncEnabled ? setGlobalMacroSlide : undefined}
|
onMacroSlideChange={macroSyncEnabled ? setGlobalMacroSlide : undefined}
|
||||||
|
syncCardTab={macroSyncEnabled ? globalCardTab : undefined}
|
||||||
|
onCardTabChange={macroSyncEnabled ? (setGlobalCardTab as (id: "overview"|"mispricing"|"focus") => void) : undefined}
|
||||||
|
syncSignauxSlide={macroSyncEnabled ? globalSignauxSlide : undefined}
|
||||||
|
onSignauxSlideChange={macroSyncEnabled ? setGlobalSignauxSlide : undefined}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ interface Props {
|
|||||||
macroSection?: MacroSection;
|
macroSection?: MacroSection;
|
||||||
syncMacroSlide?: "mon" | "infl" | "cro" | "empl";
|
syncMacroSlide?: "mon" | "infl" | "cro" | "empl";
|
||||||
onMacroSlideChange?: (id: "mon" | "infl" | "cro" | "empl") => void;
|
onMacroSlideChange?: (id: "mon" | "infl" | "cro" | "empl") => void;
|
||||||
|
syncCardTab?: "overview" | "mispricing" | "focus";
|
||||||
|
onCardTabChange?: (id: "overview" | "mispricing" | "focus") => void;
|
||||||
|
syncSignauxSlide?: "ois" | "cot" | "sent";
|
||||||
|
onSignauxSlideChange?: (id: "ois" | "cot" | "sent") => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tab = "overview" | "mispricing" | "focus";
|
type Tab = "overview" | "mispricing" | "focus";
|
||||||
@@ -600,6 +604,7 @@ function OISEnhancedBlock({ ratePath }: { ratePath: CBRatePath }) {
|
|||||||
export default function CurrencyCard({
|
export default function CurrencyCard({
|
||||||
currency, expectations, yields, sentiment, cot, ratePath, onDivergenceUpdate,
|
currency, expectations, yields, sentiment, cot, ratePath, onDivergenceUpdate,
|
||||||
calEvents, macroSection, syncMacroSlide, onMacroSlideChange,
|
calEvents, macroSection, syncMacroSlide, onMacroSlideChange,
|
||||||
|
syncCardTab, onCardTabChange, syncSignauxSlide, onSignauxSlideChange,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const meta = CURRENCY_META[currency];
|
const meta = CURRENCY_META[currency];
|
||||||
|
|
||||||
@@ -636,6 +641,8 @@ export default function CurrencyCard({
|
|||||||
const prevSyncMacroRef = useRef<MacroSlide>(syncMacroSlide ?? "mon");
|
const prevSyncMacroRef = useRef<MacroSlide>(syncMacroSlide ?? "mon");
|
||||||
const [signauxSlide, setSignauxSlide] = useState<SignauxSlide>("ois");
|
const [signauxSlide, setSignauxSlide] = useState<SignauxSlide>("ois");
|
||||||
const [signauxSlideDir, setSignauxSlideDir] = useState<1|-1>(1);
|
const [signauxSlideDir, setSignauxSlideDir] = useState<1|-1>(1);
|
||||||
|
const prevSyncCardTabRef = useRef<Tab>(syncCardTab ?? "overview");
|
||||||
|
const prevSyncSignauxSlideRef = useRef<SignauxSlide>(syncSignauxSlide ?? "ois");
|
||||||
|
|
||||||
// ── Data fetch ───────────────────────────────────────────────────────────────
|
// ── Data fetch ───────────────────────────────────────────────────────────────
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
@@ -924,13 +931,36 @@ export default function CurrencyCard({
|
|||||||
}
|
}
|
||||||
}, [syncMacroSlide]);
|
}, [syncMacroSlide]);
|
||||||
|
|
||||||
|
// Sync tab principal depuis une autre carte
|
||||||
|
useEffect(() => {
|
||||||
|
if (syncCardTab === undefined) return;
|
||||||
|
const prev = prevSyncCardTabRef.current;
|
||||||
|
if (syncCardTab !== prev) {
|
||||||
|
setActiveTab(syncCardTab);
|
||||||
|
prevSyncCardTabRef.current = syncCardTab;
|
||||||
|
}
|
||||||
|
}, [syncCardTab]);
|
||||||
|
|
||||||
|
// Sync sous-slide Signaux depuis une autre carte
|
||||||
|
useEffect(() => {
|
||||||
|
if (syncSignauxSlide === undefined) return;
|
||||||
|
const order: SignauxSlide[] = ["ois", "cot", "sent"];
|
||||||
|
const prev = prevSyncSignauxSlideRef.current;
|
||||||
|
if (syncSignauxSlide !== prev) {
|
||||||
|
setSignauxSlideDir(order.indexOf(syncSignauxSlide) >= order.indexOf(prev) ? 1 : -1);
|
||||||
|
setSignauxSlide(syncSignauxSlide);
|
||||||
|
prevSyncSignauxSlideRef.current = syncSignauxSlide;
|
||||||
|
}
|
||||||
|
}, [syncSignauxSlide]);
|
||||||
|
|
||||||
const goToSignauxSlide = useCallback((id: SignauxSlide) => {
|
const goToSignauxSlide = useCallback((id: SignauxSlide) => {
|
||||||
const order: SignauxSlide[] = ["ois", "cot", "sent"];
|
const order: SignauxSlide[] = ["ois", "cot", "sent"];
|
||||||
setSignauxSlide(prev => {
|
setSignauxSlide(prev => {
|
||||||
setSignauxSlideDir(order.indexOf(id) >= order.indexOf(prev) ? 1 : -1);
|
setSignauxSlideDir(order.indexOf(id) >= order.indexOf(prev) ? 1 : -1);
|
||||||
return id;
|
return id;
|
||||||
});
|
});
|
||||||
}, []);
|
onSignauxSlideChange?.(id);
|
||||||
|
}, [onSignauxSlideChange]);
|
||||||
|
|
||||||
// ── Recent published events for this currency (last 72h, non-low impact) ─────
|
// ── Recent published events for this currency (last 72h, non-low impact) ─────
|
||||||
const recentEvents = useMemo(() => {
|
const recentEvents = useMemo(() => {
|
||||||
@@ -1234,7 +1264,7 @@ export default function CurrencyCard({
|
|||||||
{TABS.map((t) => (
|
{TABS.map((t) => (
|
||||||
<button
|
<button
|
||||||
key={t.id}
|
key={t.id}
|
||||||
onClick={() => setActiveTab(t.id)}
|
onClick={() => { setActiveTab(t.id); onCardTabChange?.(t.id); }}
|
||||||
className={`flex items-center gap-1.5 px-3 py-2 text-[11px] font-medium rounded-t-lg transition-all ${
|
className={`flex items-center gap-1.5 px-3 py-2 text-[11px] font-medium rounded-t-lg transition-all ${
|
||||||
activeTab === t.id
|
activeTab === t.id
|
||||||
? "text-white bg-slate-800"
|
? "text-white bg-slate-800"
|
||||||
|
|||||||
Reference in New Issue
Block a user