/* ═══════════════════════════════════════════════════════════════
SignalCard — the heart of FoxBot Pro
States: active, wait, invalid, executing
═══════════════════════════════════════════════════════════════ */
const { useState: useStateSC } = React;
function SignalCard({ signal, onAction }) {
const isLong = signal.direction === "LONG";
const dirColor = isLong ? "var(--bull)" : "var(--bear)";
const state = signal.state;
return (
{state === "wait" && (
{signal.waitReason || "WAIT · attente confirmation"}
WAIT
)}
{state === "invalid" && (
SIGNAL INVALIDÉ
{signal.note}
)}
{/* HEADER */}
{signal.pair}/USDT
= 0 ? "bull" : "bear"} style={{ fontFamily: "var(--font-mono)" }}>
{fmtPct(signal.chg24h)}
24h
·
x{signal.leverage}
{/* BODY — 2-column data */}
SIGNAL · TECH
70 ? "bear" : signal.rsi < 30 ? "bull" : "accent"} />
{signal.macd.dir === "up" ? "▲" : "▼"}
{fmtNum(signal.macd.v, { sign: true, decimals: 4 })}
} />
100 ? "warn" : "accent"} hint="vs MA20" />
{fmtNum(signal.bb[0])}
·
{fmtNum(signal.bb[1])}
·
{fmtNum(signal.bb[2])}
} />
PLAN DE TRADE
R/R
= 2 ? "rr-premium" : ""}`}>
{signal.rr.toFixed(1)}
{/* TRADE TRACK — horizontal visualization */}
{/* QUICK PILLS — valeurs uniques par crypto */}
Perte max
−€{(signal.riskEur || 0).toFixed(2)}
Gain TP1
+€{(signal.gainEur || 0).toFixed(2)}
Gain TP2
+€{(signal.gainTp2Eur || 0).toFixed(2)}
Position
€{signal.notional ? signal.notional.toFixed(0) : (signal.capital * signal.leverage).toFixed(0)}
Risque
{/* ACTIONS — 2x2 grid */}
onAction?.("paper-long", signal)}>
PAPER LONG
onAction?.("paper-short", signal)}>
PAPER SHORT
onAction?.("real-long", signal)}>
RÉEL LONG
onAction?.("real-short", signal)}>
RÉEL SHORT
);
}
function DataRow({ label, value, bar, barColor = "accent", hint }) {
return (