refactor(ui): tokenize overview status surfaces
This commit is contained in:
parent
29a6e2fbdb
commit
a230679f41
@ -43,6 +43,30 @@ const WIN_RATE_WINDOW_OPTIONS: Array<{ key: WinRateWindow; label: string; ms?: n
|
||||
{ key: 'all', label: 'All' }
|
||||
];
|
||||
const REFRESH_INTERVAL_MS = 30_000;
|
||||
const overviewWarningBorder = '1px solid color-mix(in oklab, var(--bl-warning) 28%, transparent)';
|
||||
const overviewWarningSurface = 'var(--bl-warning-muted)';
|
||||
const overviewWarningText = 'var(--bl-warning)';
|
||||
const overviewDangerBorder = '1px solid color-mix(in oklab, var(--bl-danger) 28%, transparent)';
|
||||
const overviewDangerSurface = 'var(--bl-danger-muted)';
|
||||
const overviewDangerText = 'var(--bl-danger)';
|
||||
const overviewMutedText = 'var(--bl-text-faint)';
|
||||
const overviewQuietText = 'var(--bl-text-quiet)';
|
||||
const overviewInfoText = 'var(--bl-info-strong)';
|
||||
const overviewAttentionText = 'var(--bl-warning)';
|
||||
const overviewSuccessText = 'var(--bl-success)';
|
||||
const overviewNeutralText = 'var(--foreground)';
|
||||
const overviewSoftBorder = '1px solid var(--bl-border-subtle)';
|
||||
const overviewActiveBorder = '1px solid color-mix(in oklab, var(--bl-success) 45%, transparent)';
|
||||
const overviewActiveSurface = 'color-mix(in oklab, var(--bl-success) 8%, transparent)';
|
||||
const overviewMutedSurface = 'color-mix(in oklab, var(--foreground) 3%, transparent)';
|
||||
const overviewSectionBorder = '1px solid color-mix(in oklab, var(--bl-border) 70%, transparent)';
|
||||
const overviewCardGradient = 'linear-gradient(145deg, color-mix(in oklab, var(--bl-surface-overlay) 92%, var(--card-elevated)), color-mix(in oklab, var(--bl-surface-overlay) 84%, var(--background)))';
|
||||
const overviewCardBorder = '1px solid color-mix(in oklab, var(--bl-border) 55%, transparent)';
|
||||
const overviewDivider = '1px solid color-mix(in oklab, var(--bl-border) 45%, transparent)';
|
||||
const overviewBotMuted = 'color-mix(in oklab, var(--muted-foreground) 62%, var(--background))';
|
||||
const overviewBotQuiet = 'color-mix(in oklab, var(--muted-foreground) 52%, var(--background))';
|
||||
const overviewTagBorder = '1px solid color-mix(in oklab, var(--bl-border) 82%, transparent)';
|
||||
|
||||
|
||||
const formatUptime = (ms: number): string => {
|
||||
if (ms < 60000) return `${Math.floor(ms / 1000)}s`;
|
||||
@ -569,9 +593,9 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
marginBottom: '12px',
|
||||
padding: '8px 10px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid rgba(251, 191, 36, 0.25)',
|
||||
background: 'rgba(251, 191, 36, 0.08)',
|
||||
color: '#fbbf24',
|
||||
border: overviewWarningBorder,
|
||||
background: overviewWarningSurface,
|
||||
color: overviewWarningText,
|
||||
fontSize: '0.75rem'
|
||||
}}>
|
||||
Canonical lifecycle is unavailable{canonicalLoading ? ' (loading)' : ''}. Showing fallback values from DB/runtime sources.
|
||||
@ -583,9 +607,9 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
marginBottom: '12px',
|
||||
padding: '8px 10px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid rgba(248, 113, 113, 0.28)',
|
||||
background: 'rgba(248, 113, 113, 0.08)',
|
||||
color: '#fca5a5',
|
||||
border: overviewDangerBorder,
|
||||
background: overviewDangerSurface,
|
||||
color: overviewDangerText,
|
||||
fontSize: '0.75rem'
|
||||
}}>
|
||||
Canonical lifecycle snapshot is truncated ({canonicalSnapshot.diagnostics.orderRows} rows). Narrow scope before using this for operational decisions.
|
||||
@ -593,7 +617,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
)}
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px', flexWrap: 'wrap' }}>
|
||||
<span style={{ fontSize: '0.72rem', color: '#71717a', textTransform: 'uppercase', letterSpacing: '0.04em' }}>
|
||||
<span style={{ fontSize: '0.72rem', color: overviewMutedText, textTransform: 'uppercase', letterSpacing: '0.04em' }}>
|
||||
Win Rate Window:
|
||||
</span>
|
||||
{WIN_RATE_WINDOW_OPTIONS.map((option) => {
|
||||
@ -603,9 +627,9 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
key={option.key}
|
||||
onClick={() => setWinRateWindow(option.key)}
|
||||
style={{
|
||||
border: active ? '1px solid rgba(0,255,136,0.45)' : '1px solid rgba(255,255,255,0.1)',
|
||||
color: active ? '#00ff88' : '#a1a1aa',
|
||||
background: active ? 'rgba(0,255,136,0.08)' : 'rgba(255,255,255,0.03)',
|
||||
border: active ? overviewActiveBorder : overviewSoftBorder,
|
||||
color: active ? overviewSuccessText : overviewQuietText,
|
||||
background: active ? overviewActiveSurface : overviewMutedSurface,
|
||||
borderRadius: '8px',
|
||||
padding: '4px 8px',
|
||||
fontSize: '0.7rem',
|
||||
@ -641,13 +665,13 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
<div className="status-item">
|
||||
<span className="label">Allocated:</span>
|
||||
<span className="value">{formatUsd(allocatedCapital)}</span>
|
||||
{profileCount > 0 && <span style={{ fontSize: '0.65rem', color: '#666', marginLeft: '4px' }}>({profileCount} profiles)</span>}
|
||||
{profileCount > 0 && <span style={{ fontSize: '0.65rem', color: overviewQuietText, marginLeft: '4px' }}>({profileCount} profiles)</span>}
|
||||
</div>
|
||||
<div className="status-item">
|
||||
<span className="label">Capital Used:</span>
|
||||
<span className="value" style={{ color: '#38bdf8' }}>{formatUsd(capitalUsed)}</span>
|
||||
<span className="value" style={{ color: overviewInfoText }}>{formatUsd(capitalUsed)}</span>
|
||||
{overAllocatedCapital > 1e-8 && (
|
||||
<span style={{ fontSize: '0.65rem', color: '#f59e0b' }}>
|
||||
<span style={{ fontSize: '0.65rem', color: overviewAttentionText }}>
|
||||
capped (+{formatUsd(overAllocatedCapital)} raw)
|
||||
</span>
|
||||
)}
|
||||
@ -676,10 +700,10 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
</div>
|
||||
<div className="status-item">
|
||||
<span className="label">Win Rate ({winRateWindowConfig.label}):</span>
|
||||
<span className="value" style={{ color: displayWindowAggregate.winRate >= 50 ? '#4ade80' : '#fbbf24' }}>
|
||||
<span className="value" style={{ color: displayWindowAggregate.winRate >= 50 ? overviewSuccessText : overviewWarningText }}>
|
||||
{displayWindowAggregate.winRate.toFixed(1)}%
|
||||
</span>
|
||||
<span style={{ fontSize: '0.65rem', color: '#71717a' }}>
|
||||
<span style={{ fontSize: '0.65rem', color: overviewMutedText }}>
|
||||
{displayWindowAggregate.tradeCount} trades
|
||||
</span>
|
||||
</div>
|
||||
@ -693,7 +717,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
>
|
||||
{pnlWindow.durationLabel}
|
||||
</span>
|
||||
<span style={{ fontSize: '0.65rem', color: '#71717a' }}>
|
||||
<span style={{ fontSize: '0.65rem', color: overviewMutedText }}>
|
||||
{pnlWindow.fromTs > 0
|
||||
? `From ${new Date(pnlWindow.fromTs).toLocaleDateString()}`
|
||||
: 'Awaiting trade history'}
|
||||
@ -702,7 +726,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
</div>
|
||||
|
||||
{/* --- NEW: Alpaca Account Health --- */}
|
||||
<div className="bot-status-bar" style={{ marginTop: '16px', borderTop: '1px solid rgba(255,255,255,0.08)', paddingTop: '16px' }}>
|
||||
<div className="bot-status-bar" style={{ marginTop: '16px', borderTop: overviewSectionBorder, paddingTop: '16px' }}>
|
||||
<div className="status-item">
|
||||
<span className="label">Broker Balance (Alpaca):</span>
|
||||
<span className={`value ${botState.accountSnapshot ? 'status-online' : 'status-offline'}`}>
|
||||
@ -711,7 +735,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
: 'Waiting for snapshot...'}
|
||||
</span>
|
||||
{botState.accountSnapshot && (
|
||||
<span style={{ fontSize: '0.65rem', color: '#71717a' }}>
|
||||
<span style={{ fontSize: '0.65rem', color: overviewMutedText }}>
|
||||
Cash: {formatUsd(botState.accountSnapshot.cash)} ({botState.accountSnapshot.currency})
|
||||
</span>
|
||||
)}
|
||||
@ -725,28 +749,28 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
</div>
|
||||
)}
|
||||
{/* Parity & Self-Healing Heartbeat */}
|
||||
<div className="status-item" style={{ borderLeft: '1px solid rgba(255,255,255,0.1)', paddingLeft: '16px' }}>
|
||||
<div className="status-item" style={{ borderLeft: overviewSoftBorder, paddingLeft: '16px' }}>
|
||||
<span className="label">Parity Heartbeat:</span>
|
||||
<span className={`value ${botState.health?.reconciliationParityMismatchTrades ? 'status-offline' : 'status-online'}`}>
|
||||
{botState.health?.reconciliationParityMismatchTrades || 0} Mismatches
|
||||
</span>
|
||||
{Number(botState.health?.reconciliationParityAutoClosedTrades || 0) > 0 && (
|
||||
<span style={{ fontSize: '0.65rem', color: '#4ade80', marginLeft: '4px' }}>
|
||||
<span style={{ fontSize: '0.65rem', color: overviewSuccessText, marginLeft: '4px' }}>
|
||||
({botState.health?.reconciliationParityAutoClosedTrades} Self-Healed)
|
||||
</span>
|
||||
)}
|
||||
{Number(botState.health?.reconciliationParityQuarantinedTrades || 0) > 0 && (
|
||||
<span style={{ fontSize: '0.65rem', color: '#facc15', marginLeft: '4px' }}>
|
||||
<span style={{ fontSize: '0.65rem', color: overviewWarningText, marginLeft: '4px' }}>
|
||||
({botState.health?.reconciliationParityQuarantinedTrades} Quarantined)
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{/* Recent Failures Summary */}
|
||||
{(botState.orderFailures || []).length > 0 && (
|
||||
<div className="status-item" style={{ marginLeft: 'auto', borderLeft: '1px solid rgba(255,255,255,0.1)', paddingLeft: '16px' }}>
|
||||
<span className="label" style={{ color: '#ef4444' }}>Recent Rejections:</span>
|
||||
<span className="value" style={{ color: '#ef4444' }}>{(botState.orderFailures || []).length}</span>
|
||||
<span style={{ fontSize: '0.65rem', color: '#71717a' }}>
|
||||
<div className="status-item" style={{ marginLeft: 'auto', borderLeft: overviewSoftBorder, paddingLeft: '16px' }}>
|
||||
<span className="label" style={{ color: overviewDangerText }}>Recent Rejections:</span>
|
||||
<span className="value" style={{ color: overviewDangerText }}>{(botState.orderFailures || []).length}</span>
|
||||
<span style={{ fontSize: '0.65rem', color: overviewMutedText }}>
|
||||
Latest: {(botState.orderFailures || [])[0].symbol} ({(botState.orderFailures || [])[0].reason?.substring(0, 20)}...)
|
||||
</span>
|
||||
</div>
|
||||
@ -772,37 +796,37 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
profileCapitalRows.map((row) => {
|
||||
const overAllocated = row.overAllocatedAmount > 1e-8;
|
||||
const stateColor = row.runtimeTone === 'running'
|
||||
? '#4ade80'
|
||||
? overviewSuccessText
|
||||
: row.runtimeTone === 'cooldown'
|
||||
? '#facc15'
|
||||
? overviewWarningText
|
||||
: row.runtimeTone === 'armed'
|
||||
? '#38bdf8'
|
||||
? overviewInfoText
|
||||
: row.runtimeTone === 'blocked'
|
||||
? '#f87171'
|
||||
: '#a1a1aa';
|
||||
? overviewDangerText
|
||||
: overviewQuietText;
|
||||
return (
|
||||
<tr key={row.id}>
|
||||
<td style={{ fontWeight: 700 }}>{row.name}</td>
|
||||
<td style={{ textAlign: 'right' }}>{formatUsd(row.allocated)}</td>
|
||||
<td style={{ textAlign: 'right', color: '#38bdf8' }}>{formatUsd(row.used)}</td>
|
||||
<td style={{ textAlign: 'right', color: overAllocated ? '#f87171' : '#4ade80' }}>
|
||||
<td style={{ textAlign: 'right', color: overviewInfoText }}>{formatUsd(row.used)}</td>
|
||||
<td style={{ textAlign: 'right', color: overAllocated ? overviewDangerText : overviewSuccessText }}>
|
||||
{formatUsd(row.remaining)}
|
||||
{overAllocated && (
|
||||
<div style={{ color: '#f59e0b', fontSize: '0.65rem' }}>
|
||||
<div style={{ color: overviewAttentionText, fontSize: '0.65rem' }}>
|
||||
raw +{formatUsd(row.overAllocatedAmount)}
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', color: overAllocated ? '#f87171' : '#cbd5e1' }}>
|
||||
<td style={{ textAlign: 'right', color: overAllocated ? overviewDangerText : overviewNeutralText }}>
|
||||
{row.utilizationPct.toFixed(1)}%
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', color: row.winRate >= 50 ? '#4ade80' : '#fbbf24' }}>
|
||||
<td style={{ textAlign: 'right', color: row.winRate >= 50 ? overviewSuccessText : overviewWarningText }}>
|
||||
{row.winRate.toFixed(1)}%
|
||||
<span style={{ color: '#71717a', marginLeft: '4px', fontSize: '0.65rem' }}>
|
||||
<span style={{ color: overviewMutedText, marginLeft: '4px', fontSize: '0.65rem' }}>
|
||||
({row.tradeCount})
|
||||
</span>
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', color: row.netPnl >= 0 ? '#4ade80' : '#f87171' }}>
|
||||
<td style={{ textAlign: 'right', color: row.netPnl >= 0 ? overviewSuccessText : overviewDangerText }}>
|
||||
{formatUsd(row.netPnl)}
|
||||
</td>
|
||||
<td>
|
||||
@ -810,7 +834,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
<div style={{ color: stateColor, fontWeight: 700 }}>
|
||||
{row.runtimeState}
|
||||
</div>
|
||||
<div style={{ color: '#71717a', fontSize: '0.68rem' }}>
|
||||
<div style={{ color: overviewMutedText, fontSize: '0.68rem' }}>
|
||||
{row.runtimeDetail}
|
||||
</div>
|
||||
</div>
|
||||
@ -822,11 +846,11 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
<tr>
|
||||
<td style={{ fontWeight: 700 }}>Account (Fallback)</td>
|
||||
<td style={{ textAlign: 'right' }}>{formatUsd(allocatedCapital)}</td>
|
||||
<td style={{ textAlign: 'right', color: '#38bdf8' }}>{formatUsd(capitalUsed)}</td>
|
||||
<td style={{ textAlign: 'right', color: overAllocatedCapital > 1e-8 ? '#f87171' : '#4ade80' }}>
|
||||
<td style={{ textAlign: 'right', color: overviewInfoText }}>{formatUsd(capitalUsed)}</td>
|
||||
<td style={{ textAlign: 'right', color: overAllocatedCapital > 1e-8 ? overviewDangerText : overviewSuccessText }}>
|
||||
{formatUsd(remainingCapital)}
|
||||
{overAllocatedCapital > 1e-8 && (
|
||||
<div style={{ color: '#f59e0b', fontSize: '0.65rem' }}>
|
||||
<div style={{ color: overviewAttentionText, fontSize: '0.65rem' }}>
|
||||
raw +{formatUsd(overAllocatedCapital)}
|
||||
</div>
|
||||
)}
|
||||
@ -835,9 +859,9 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
{allocatedCapital > 0 ? ((capitalUsed / allocatedCapital) * 100).toFixed(1) : '0.0'}%
|
||||
</td>
|
||||
<td style={{ textAlign: 'right' }}>-</td>
|
||||
<td style={{ textAlign: 'right', color: displayRealizedPnl >= 0 ? '#4ade80' : '#f87171' }}>{formatUsd(displayRealizedPnl)}</td>
|
||||
<td style={{ textAlign: 'right', color: displayRealizedPnl >= 0 ? overviewSuccessText : overviewDangerText }}>{formatUsd(displayRealizedPnl)}</td>
|
||||
<td>
|
||||
<span style={{ color: '#a1a1aa', fontWeight: 700 }}>No signal</span>
|
||||
<span style={{ color: overviewQuietText, fontWeight: 700 }}>No signal</span>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
@ -848,7 +872,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
{/* NEW: Recent Order Rejections List */}
|
||||
{botState.orderFailures && botState.orderFailures.length > 0 && (
|
||||
<div style={{ marginBottom: '32px' }}>
|
||||
<h3 style={{ fontSize: '0.9rem', fontWeight: 700, marginBottom: '12px', color: '#ef4444' }}>Recent Order Rejections</h3>
|
||||
<h3 style={{ fontSize: '0.9rem', fontWeight: 700, marginBottom: '12px', color: overviewDangerText }}>Recent Order Rejections</h3>
|
||||
<div className="table-container">
|
||||
<table className="pro-table w-full">
|
||||
<thead>
|
||||
@ -865,17 +889,17 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
<tbody>
|
||||
{botState.orderFailures.slice(0, 10).map((fail, idx) => (
|
||||
<tr key={idx}>
|
||||
<td style={{ color: '#71717a', fontSize: '0.75rem' }}>{new Date(fail.timestamp).toLocaleString()}</td>
|
||||
<td style={{ color: overviewMutedText, fontSize: '0.75rem' }}>{new Date(fail.timestamp).toLocaleString()}</td>
|
||||
<td style={{ fontWeight: 600 }}>{fail.symbol}</td>
|
||||
<td className={`side-${fail.side.toLowerCase()}`}>{fail.side}</td>
|
||||
<td>{fail.qty}</td>
|
||||
<td style={{ color: '#ef4444', maxWidth: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={fail.reason}>
|
||||
<td style={{ color: overviewDangerText, maxWidth: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={fail.reason}>
|
||||
{fail.reason}
|
||||
</td>
|
||||
<td style={{ color: '#a1a1aa', fontFamily: 'monospace' }} title={fail.subTag || ''}>
|
||||
<td style={{ color: overviewQuietText, fontFamily: 'monospace' }} title={fail.subTag || ''}>
|
||||
{compactTag(fail.subTag)}
|
||||
</td>
|
||||
<td style={{ color: '#71717a' }}>{fail.profileId || 'Unknown'}</td>
|
||||
<td style={{ color: overviewMutedText }}>{fail.profileId || 'Unknown'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
@ -908,7 +932,7 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
{profileSignals.length > 0 && (
|
||||
<div style={{ marginBottom: '0.6rem', display: 'flex', gap: '0.35rem', flexWrap: 'wrap' }}>
|
||||
{profileSignals.map((ps, idx) => (
|
||||
<span key={`${symbol}-ps-${idx}`} style={{ fontSize: '0.55rem', fontWeight: 700, letterSpacing: '0.06em', padding: '0.15rem 0.35rem', borderRadius: '8px', border: '1px solid rgba(255,255,255,0.12)', color: '#c8c8d0' }} title={ps.reason || ps.signal}>
|
||||
<span key={`${symbol}-ps-${idx}`} style={{ fontSize: '0.55rem', fontWeight: 700, letterSpacing: '0.06em', padding: '0.15rem 0.35rem', borderRadius: '8px', border: overviewTagBorder, color: overviewNeutralText }} title={ps.reason || ps.signal}>
|
||||
{(ps.profileName || `P${idx + 1}`)}: {ps.signal}
|
||||
</span>
|
||||
))}
|
||||
@ -943,18 +967,18 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
: 'NEUTRAL';
|
||||
|
||||
const directionLabel = bias4h === 'BULLISH' ? 'Uptrend' : bias4h === 'BEARISH' ? 'Downtrend' : 'Sideways';
|
||||
const directionColor = bias4h === 'BULLISH' ? '#00ff88' : bias4h === 'BEARISH' ? '#ff3366' : '#f59e0b';
|
||||
const directionColor = bias4h === 'BULLISH' ? overviewSuccessText : bias4h === 'BEARISH' ? overviewDangerText : overviewAttentionText;
|
||||
|
||||
const rsi = data.indicators.rsi_1h || 50;
|
||||
const momentumLabel = rsi > 60 ? 'Building strongly' : rsi > 50 ? 'Gaining' : rsi > 40 ? 'Neutral' : 'Fading';
|
||||
const momentumColor = rsi > 55 ? '#00ff88' : rsi > 45 ? '#f59e0b' : '#ff3366';
|
||||
const momentumColor = rsi > 55 ? overviewSuccessText : rsi > 45 ? overviewAttentionText : overviewDangerText;
|
||||
|
||||
const sessionIsOff = data.session === 'OFF' || !data.session;
|
||||
const sessionLabel = sessionIsOff ? 'Outside window' : 'Active now';
|
||||
const sessionColor = sessionIsOff ? '#555' : '#00ff88';
|
||||
const sessionColor = sessionIsOff ? overviewBotMuted : overviewSuccessText;
|
||||
|
||||
const volLabel = data.volatility === 'HIGH' ? 'Very Active' : data.volatility === 'MEDIUM' ? 'Moderate' : data.volatility === 'LOW' ? 'Calm' : 'Normal';
|
||||
const volColor = data.volatility === 'HIGH' ? '#f59e0b' : data.volatility === 'MEDIUM' ? '#60a5fa' : '#a1a1aa';
|
||||
const volColor = data.volatility === 'HIGH' ? overviewAttentionText : data.volatility === 'MEDIUM' ? overviewInfoText : overviewQuietText;
|
||||
|
||||
const botStatusEmoji = normalizedSignal === 'BUY' ? '📈' : normalizedSignal === 'SELL' ? '📉' : normalizedSignal === 'MIXED' ? '⚡' : '👀';
|
||||
const botStatusLabel =
|
||||
@ -963,17 +987,17 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
normalizedSignal === 'MIXED' ? 'Conditions mixed — holding off' :
|
||||
'Watching markets for the right setup';
|
||||
const botStatusColor =
|
||||
normalizedSignal === 'BUY' ? '#00ff88' :
|
||||
normalizedSignal === 'SELL' ? '#ff3366' :
|
||||
normalizedSignal === 'MIXED' ? '#f59e0b' :
|
||||
'#555';
|
||||
normalizedSignal === 'BUY' ? overviewSuccessText :
|
||||
normalizedSignal === 'SELL' ? overviewDangerText :
|
||||
normalizedSignal === 'MIXED' ? overviewAttentionText :
|
||||
overviewBotMuted;
|
||||
|
||||
const change24h = data.change24h || 0;
|
||||
|
||||
return (
|
||||
<div key={symbol} style={{
|
||||
background: 'linear-gradient(145deg, rgba(20,21,26,0.9), rgba(14,15,18,0.95))',
|
||||
border: '1px solid rgba(255,255,255,0.05)',
|
||||
background: overviewCardGradient,
|
||||
border: overviewCardBorder,
|
||||
borderRadius: '20px',
|
||||
padding: '24px',
|
||||
display: 'flex',
|
||||
@ -989,11 +1013,11 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
{/* Header */}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '20px' }}>
|
||||
<div>
|
||||
<div style={{ fontSize: '1.1rem', fontWeight: 900, color: '#fff', letterSpacing: '-0.02em' }}>
|
||||
<div style={{ fontSize: '1.1rem', fontWeight: 900, color: overviewNeutralText, letterSpacing: '-0.02em' }}>
|
||||
{symbol.split('/')[0]}
|
||||
<span style={{ fontSize: '0.7rem', color: '#444', fontWeight: 600, marginLeft: '4px' }}>/{symbol.split('/')[1] || 'USDT'}</span>
|
||||
<span style={{ fontSize: '0.7rem', color: overviewBotQuiet, fontWeight: 600, marginLeft: '4px' }}>/{symbol.split('/')[1] || 'USDT'}</span>
|
||||
</div>
|
||||
<div style={{ fontSize: '0.75rem', fontWeight: 700, color: change24h >= 0 ? '#00ff88' : '#ff3366', marginTop: '2px', fontFamily: 'monospace' }}>
|
||||
<div style={{ fontSize: '0.75rem', fontWeight: 700, color: change24h >= 0 ? overviewSuccessText : overviewDangerText, marginTop: '2px', fontFamily: 'monospace' }}>
|
||||
{change24h >= 0 ? '+' : ''}{change24h.toFixed(2)}% today
|
||||
</div>
|
||||
</div>
|
||||
@ -1009,19 +1033,19 @@ export const OverviewTab = ({ botState, previewAsCustomer = false, connected = t
|
||||
{ label: 'Market Activity', value: volLabel, color: volColor },
|
||||
].map(({ label, value, color }) => (
|
||||
<div key={label} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<span style={{ fontSize: '0.78rem', color: '#555', fontWeight: 600 }}>{label}</span>
|
||||
<span style={{ fontSize: '0.78rem', color: overviewBotMuted, fontWeight: 600 }}>{label}</span>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
||||
<div style={{ width: '6px', height: '6px', borderRadius: '50%', background: color, boxShadow: `0 0 6px ${color}` }} />
|
||||
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: '#ccc' }}>{value}</span>
|
||||
<span style={{ fontSize: '0.78rem', fontWeight: 800, color: overviewNeutralText }}>{value}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Bot Status Footer */}
|
||||
<div style={{ marginTop: '20px', paddingTop: '16px', borderTop: '1px solid rgba(255,255,255,0.04)' }}>
|
||||
<div style={{ fontSize: '0.68rem', color: '#444', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: '4px' }}>Bot Status</div>
|
||||
<div style={{ fontSize: '0.82rem', fontWeight: 800, color: botStatusColor === '#555' ? '#666' : botStatusColor }}>
|
||||
<div style={{ marginTop: '20px', paddingTop: '16px', borderTop: overviewDivider }}>
|
||||
<div style={{ fontSize: '0.68rem', color: overviewBotQuiet, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: '4px' }}>Bot Status</div>
|
||||
<div style={{ fontSize: '0.82rem', fontWeight: 800, color: botStatusColor === overviewBotMuted ? overviewQuietText : botStatusColor }}>
|
||||
{botStatusLabel}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user