Aggiunta Bootstrap 5.3.3 (CSS, JS, RTL, mappe) al progetto
Sono stati aggiunti tutti i file principali di Bootstrap 5.3.3, inclusi CSS, JavaScript (bundle, ESM, UMD, minificati), versioni RTL, utility, reboot, griglia e relative mappe delle sorgenti. Questi file abilitano un sistema di design moderno, responsive e accessibile, con supporto per layout LTR e RTL, debugging avanzato tramite source map e tutte le funzionalità di Bootstrap per lo sviluppo dell’interfaccia utente. Nessuna modifica ai file esistenti.
This commit is contained in:
128
TradingBot/Components/Shared/AdvancedChart.razor
Normal file
128
TradingBot/Components/Shared/AdvancedChart.razor
Normal file
@@ -0,0 +1,128 @@
|
||||
@using TradingBot.Models
|
||||
|
||||
<div class="advanced-chart">
|
||||
@if (PriceData == null || PriceData.Count < 2)
|
||||
{
|
||||
<div class="chart-loading">
|
||||
<div class="loading-spinner"></div>
|
||||
<span>In attesa di dati sufficienti...</span>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<svg viewBox="0 0 @Width @Height" class="chart-svg" preserveAspectRatio="none">
|
||||
<defs>
|
||||
<linearGradient id="gradient-@ColorId" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="@Color" stop-opacity="0.4" />
|
||||
<stop offset="100%" stop-color="@Color" stop-opacity="0.05" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Grid Lines -->
|
||||
@for (int i = 0; i <= 4; i++)
|
||||
{
|
||||
var y = (Height * i / 4.0);
|
||||
<line x1="0" y1="@y" x2="@Width" y2="@y"
|
||||
stroke="rgba(51, 65, 85, 0.3)" stroke-width="1" stroke-dasharray="4 4" />
|
||||
}
|
||||
|
||||
<!-- Area Fill -->
|
||||
@if (!string.IsNullOrEmpty(GetAreaPath()))
|
||||
{
|
||||
<path d="@GetAreaPath()" fill="url(#gradient-@ColorId)" />
|
||||
}
|
||||
|
||||
<!-- Line Chart -->
|
||||
@if (!string.IsNullOrEmpty(GetPolylinePoints()))
|
||||
{
|
||||
<polyline fill="none" stroke="@Color" stroke-width="3"
|
||||
points="@GetPolylinePoints()"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
}
|
||||
</svg>
|
||||
|
||||
@if (Indicators != null)
|
||||
{
|
||||
<div class="indicators-overlay">
|
||||
<div class="indicator-badge">
|
||||
<span class="indicator-label">RSI:</span>
|
||||
<span class="indicator-value @GetRSIClass()">@Indicators.RSI.ToString("F1")</span>
|
||||
</div>
|
||||
<div class="indicator-badge">
|
||||
<span class="indicator-label">MACD:</span>
|
||||
<span class="indicator-value">@Indicators.MACD.ToString("F2")</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public List<decimal>? PriceData { get; set; }
|
||||
[Parameter] public string Color { get; set; } = "#6366f1";
|
||||
[Parameter] public TechnicalIndicators? Indicators { get; set; }
|
||||
|
||||
private int Width = 800;
|
||||
private int Height = 300;
|
||||
private string ColorId => Guid.NewGuid().ToString("N").Substring(0, 8);
|
||||
|
||||
private string GetPolylinePoints()
|
||||
{
|
||||
if (PriceData == null || PriceData.Count < 2) return string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
var max = PriceData.Max();
|
||||
var min = PriceData.Min();
|
||||
var range = max - min;
|
||||
if (range == 0) range = max * 0.01m; // 1% range if all values are same
|
||||
|
||||
var points = new List<string>();
|
||||
var padding = Height * 0.1; // 10% padding
|
||||
var chartHeight = Height - (padding * 2);
|
||||
|
||||
for (int i = 0; i < PriceData.Count; i++)
|
||||
{
|
||||
var x = (i / (double)(PriceData.Count - 1)) * Width;
|
||||
var normalizedValue = (double)((PriceData[i] - min) / range);
|
||||
var y = padding + (chartHeight * (1 - normalizedValue));
|
||||
points.Add($"{x:F2},{y:F2}");
|
||||
}
|
||||
|
||||
return string.Join(" ", points);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetAreaPath()
|
||||
{
|
||||
var polyline = GetPolylinePoints();
|
||||
if (string.IsNullOrEmpty(polyline)) return string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
var points = polyline.Split(' ');
|
||||
if (points.Length < 2) return string.Empty;
|
||||
|
||||
var firstPoint = points[0].Split(',');
|
||||
var lastPoint = points[points.Length - 1].Split(',');
|
||||
|
||||
return $"M {firstPoint[0]},{Height} L {polyline} L {lastPoint[0]},{Height} Z";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetRSIClass()
|
||||
{
|
||||
if (Indicators == null) return "rsi-neutral";
|
||||
if (Indicators.RSI > 70) return "rsi-overbought";
|
||||
if (Indicators.RSI < 30) return "rsi-oversold";
|
||||
return "rsi-neutral";
|
||||
}
|
||||
}
|
||||
82
TradingBot/Components/Shared/AdvancedChart.razor.css
Normal file
82
TradingBot/Components/Shared/AdvancedChart.razor.css
Normal file
@@ -0,0 +1,82 @@
|
||||
.advanced-chart {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 300px;
|
||||
background: #0a0e27;
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chart-svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.chart-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 300px;
|
||||
gap: 1rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border: 3px solid #1e293b;
|
||||
border-top-color: #6366f1;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.indicators-overlay {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.indicator-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(99, 102, 241, 0.2);
|
||||
border-radius: 0.375rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.indicator-label {
|
||||
color: #94a3b8;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.indicator-value {
|
||||
color: white;
|
||||
font-weight: 700;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.indicator-value.rsi-overbought {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.indicator-value.rsi-oversold {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.indicator-value.rsi-neutral {
|
||||
color: #f59e0b;
|
||||
}
|
||||
344
TradingBot/Components/Shared/AssetSettings.razor
Normal file
344
TradingBot/Components/Shared/AssetSettings.razor
Normal file
@@ -0,0 +1,344 @@
|
||||
@using TradingBot.Models
|
||||
@using TradingBot.Services
|
||||
@inject TradingBotService BotService
|
||||
|
||||
<div class="asset-settings-modal @(IsOpen ? "open" : "")">
|
||||
<div class="modal-overlay" @onclick="Close"></div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>
|
||||
<span class="bi bi-gear-fill"></span>
|
||||
Impostazioni {{Config?.Symbol}}
|
||||
</h3>
|
||||
<button class="btn-close" @onclick="Close">
|
||||
<span class="bi bi-x-lg"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (Config != null)
|
||||
{
|
||||
<div class="modal-body">
|
||||
|
||||
<!-- Basic Settings -->
|
||||
<div class="settings-section">
|
||||
<h4 class="section-title">Impostazioni Base</h4>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Stato</label>
|
||||
<div class="toggle-wrapper">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox"
|
||||
@bind="Config.IsEnabled" />
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label">
|
||||
{{Config.IsEnabled ? "Attivo" : "Inattivo"}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Bilancio Iniziale ($)</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
@bind="Config.InitialBalance"
|
||||
step="100"
|
||||
min="0" />
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>Bilancio Corrente ($)</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
value="@Config.CurrentBalance"
|
||||
readonly />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Holdings</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
value="@Config.CurrentHoldings"
|
||||
readonly />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Risk Management -->
|
||||
<div class="settings-section">
|
||||
<h4 class="section-title">Gestione del Rischio</h4>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>Stop Loss (%)</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
@bind="Config.StopLossPercentage"
|
||||
step="0.5"
|
||||
min="0"
|
||||
max="100" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Take Profit (%)</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
@bind="Config.TakeProfitPercentage"
|
||||
step="0.5"
|
||||
min="0"
|
||||
max="100" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Dimensione Massima Posizione ($)</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
@bind="Config.MaxPositionSize"
|
||||
step="10"
|
||||
min="0" />
|
||||
<small class="form-hint">
|
||||
Massimo valore totale della posizione in dollari
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trading Constraints -->
|
||||
<div class="settings-section">
|
||||
<h4 class="section-title">Limiti di Trading</h4>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>Min Trade ($)</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
@bind="Config.MinTradeAmount"
|
||||
step="1"
|
||||
min="1" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Max Trade ($)</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
@bind="Config.MaxTradeAmount"
|
||||
step="10"
|
||||
min="1" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Max Operazioni Giornaliere</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
@bind="Config.MaxDailyTrades"
|
||||
step="1"
|
||||
min="1"
|
||||
max="100" />
|
||||
<small class="form-hint">
|
||||
Operazioni oggi: {{Config.DailyTradeCount}} / {{Config.MaxDailyTrades}}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Strategy Parameters -->
|
||||
<div class="settings-section">
|
||||
<h4 class="section-title">Parametri Strategia</h4>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Strategia</label>
|
||||
<input type="text"
|
||||
class="form-input"
|
||||
value="@Config.StrategyName"
|
||||
readonly />
|
||||
</div>
|
||||
|
||||
@if (Config.StrategyParameters.ContainsKey("ShortPeriod"))
|
||||
{
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>Periodo Corto</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
value="@Config.StrategyParameters["ShortPeriod"]"
|
||||
@onchange="@((e) => UpdateStrategyParameter("ShortPeriod", e.Value))"
|
||||
step="1"
|
||||
min="5"
|
||||
max="50" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Periodo Lungo</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
value="@Config.StrategyParameters["LongPeriod"]"
|
||||
@onchange="@((e) => UpdateStrategyParameter("LongPeriod", e.Value))"
|
||||
step="1"
|
||||
min="10"
|
||||
max="100" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Config.StrategyParameters.ContainsKey("SignalThreshold"))
|
||||
{
|
||||
<div class="form-group">
|
||||
<label>Soglia Segnale</label>
|
||||
<input type="number"
|
||||
class="form-input"
|
||||
value="@Config.StrategyParameters["SignalThreshold"]"
|
||||
@onchange="@((e) => UpdateStrategyParameter("SignalThreshold", e.Value))"
|
||||
step="0.1"
|
||||
min="0"
|
||||
max="5" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Current Stats -->
|
||||
<div class="settings-section stats-section">
|
||||
<h4 class="section-title">Statistiche Correnti</h4>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Profitto Totale</span>
|
||||
<span class="stat-value @(Config.TotalProfit >= 0 ? "profit" : "loss")">
|
||||
${{Config.TotalProfit:N2}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">% Profitto</span>
|
||||
<span class="stat-value @(Config.ProfitPercentage >= 0 ? "profit" : "loss")">
|
||||
{{Config.ProfitPercentage:F2}}%
|
||||
</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Prezzo Medio Entrata</span>
|
||||
<span class="stat-value">
|
||||
${{Config.AverageEntryPrice:N2}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Ultimo Trade</span>
|
||||
<span class="stat-value">
|
||||
{{(Config.LastTradeTime?.ToLocalTime().ToString("HH:mm:ss") ?? "Mai")}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn-secondary" @onclick="ResetToDefaults">
|
||||
<span class="bi bi-arrow-counterclockwise"></span>
|
||||
Reset
|
||||
</button>
|
||||
<button class="btn-primary" @onclick="SaveSettings">
|
||||
<span class="bi bi-check-lg"></span>
|
||||
Salva Modifiche
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public bool IsOpen { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Symbol { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback OnClose { get; set; }
|
||||
|
||||
private AssetConfiguration? Config { get; set; }
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (IsOpen && !string.IsNullOrEmpty(Symbol))
|
||||
{
|
||||
LoadConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadConfiguration()
|
||||
{
|
||||
if (BotService.AssetConfigurations.TryGetValue(Symbol!, out var config))
|
||||
{
|
||||
// Create a copy to avoid modifying the original until saved
|
||||
Config = new AssetConfiguration
|
||||
{
|
||||
Symbol = config.Symbol,
|
||||
Name = config.Name,
|
||||
IsEnabled = config.IsEnabled,
|
||||
InitialBalance = config.InitialBalance,
|
||||
CurrentBalance = config.CurrentBalance,
|
||||
CurrentHoldings = config.CurrentHoldings,
|
||||
AverageEntryPrice = config.AverageEntryPrice,
|
||||
StrategyName = config.StrategyName,
|
||||
StrategyParameters = new Dictionary<string, object>(config.StrategyParameters),
|
||||
MaxPositionSize = config.MaxPositionSize,
|
||||
StopLossPercentage = config.StopLossPercentage,
|
||||
TakeProfitPercentage = config.TakeProfitPercentage,
|
||||
MinTradeAmount = config.MinTradeAmount,
|
||||
MaxTradeAmount = config.MaxTradeAmount,
|
||||
MaxDailyTrades = config.MaxDailyTrades,
|
||||
LastTradeTime = config.LastTradeTime,
|
||||
DailyTradeCount = config.DailyTradeCount,
|
||||
DailyTradeCountReset = config.DailyTradeCountReset
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateStrategyParameter(string key, object? value)
|
||||
{
|
||||
if (Config != null && value != null)
|
||||
{
|
||||
if (value is string strValue)
|
||||
{
|
||||
if (decimal.TryParse(strValue, out var decValue))
|
||||
{
|
||||
Config.StrategyParameters[key] = decValue;
|
||||
}
|
||||
else if (int.TryParse(strValue, out var intValue))
|
||||
{
|
||||
Config.StrategyParameters[key] = intValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetToDefaults()
|
||||
{
|
||||
if (Config != null)
|
||||
{
|
||||
Config.InitialBalance = 1000m;
|
||||
Config.StopLossPercentage = 5m;
|
||||
Config.TakeProfitPercentage = 10m;
|
||||
Config.MaxPositionSize = 100m;
|
||||
Config.MinTradeAmount = 10m;
|
||||
Config.MaxTradeAmount = 500m;
|
||||
Config.MaxDailyTrades = 10;
|
||||
Config.StrategyParameters = new Dictionary<string, object>
|
||||
{
|
||||
{ "ShortPeriod", 10 },
|
||||
{ "LongPeriod", 30 },
|
||||
{ "SignalThreshold", 0.5m }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveSettings()
|
||||
{
|
||||
if (Config != null && !string.IsNullOrEmpty(Symbol))
|
||||
{
|
||||
BotService.UpdateAssetConfiguration(Symbol, Config);
|
||||
await Close();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Close()
|
||||
{
|
||||
IsOpen = false;
|
||||
await OnClose.InvokeAsync();
|
||||
}
|
||||
}
|
||||
362
TradingBot/Components/Shared/AssetSettings.razor.css
Normal file
362
TradingBot/Components/Shared/AssetSettings.razor.css
Normal file
@@ -0,0 +1,362 @@
|
||||
/* Asset Settings Modal */
|
||||
.asset-settings-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.asset-settings-modal.open {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
width: 90%;
|
||||
max-width: 700px;
|
||||
max-height: 90vh;
|
||||
background: #0f172a;
|
||||
border: 1px solid #1e293b;
|
||||
border-radius: 0.75rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
||||
animation: modal-in 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes modal-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Modal Header */
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1.5rem;
|
||||
background: #1e293b;
|
||||
border-bottom: 1px solid #334155;
|
||||
border-radius: 0.75rem 0.75rem 0 0;
|
||||
}
|
||||
|
||||
.modal-header h3 {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.btn-close {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 0.375rem;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #94a3b8;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-close:hover {
|
||||
background: #334155;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Modal Body */
|
||||
.modal-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* Settings Section */
|
||||
.settings-section {
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
border-bottom: 1px solid #1e293b;
|
||||
}
|
||||
|
||||
.settings-section:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0 0 1.5rem 0;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-size: 0.875rem;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
/* Form Elements */
|
||||
.form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.form-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
background: #020617;
|
||||
border: 1px solid #334155;
|
||||
border-radius: 0.5rem;
|
||||
color: white;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
outline: none;
|
||||
border-color: #6366f1;
|
||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
||||
}
|
||||
|
||||
.form-input:disabled,
|
||||
.form-input:read-only {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
background: #1e293b;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
display: block;
|
||||
margin-top: 0.375rem;
|
||||
font-size: 0.75rem;
|
||||
color: #64748b;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
/* Toggle Switch */
|
||||
.toggle-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 3rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #334155;
|
||||
transition: 0.3s;
|
||||
border-radius: 1.5rem;
|
||||
}
|
||||
|
||||
.toggle-slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 1.125rem;
|
||||
width: 1.125rem;
|
||||
left: 0.1875rem;
|
||||
bottom: 0.1875rem;
|
||||
background-color: white;
|
||||
transition: 0.3s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.toggle-switch input:checked + .toggle-slider {
|
||||
background-color: #6366f1;
|
||||
}
|
||||
|
||||
.toggle-switch input:checked + .toggle-slider:before {
|
||||
transform: translateX(1.5rem);
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
/* Stats Section */
|
||||
.stats-section {
|
||||
background: #020617;
|
||||
border: 1px solid #1e293b;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.75rem;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.stat-value.profit {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.stat-value.loss {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
/* Modal Footer */
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.75rem;
|
||||
padding: 1.5rem;
|
||||
background: #020617;
|
||||
border-top: 1px solid #1e293b;
|
||||
border-radius: 0 0 0.75rem 0.75rem;
|
||||
}
|
||||
|
||||
.btn-primary, .btn-secondary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 12px -2px rgba(99, 102, 241, 0.3);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: transparent;
|
||||
color: #94a3b8;
|
||||
border: 1px solid #334155;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #1e293b;
|
||||
border-color: #475569;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Scrollbar Styling */
|
||||
.modal-body::-webkit-scrollbar {
|
||||
width: 0.5rem;
|
||||
}
|
||||
|
||||
.modal-body::-webkit-scrollbar-track {
|
||||
background: #0f172a;
|
||||
}
|
||||
|
||||
.modal-body::-webkit-scrollbar-thumb {
|
||||
background: #334155;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.modal-body::-webkit-scrollbar-thumb:hover {
|
||||
background: #475569;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.modal-content {
|
||||
width: 95%;
|
||||
max-height: 95vh;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.modal-header, .modal-body, .modal-footer {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
0
TradingBot/Components/Shared/TabNavigation.razor
Normal file
0
TradingBot/Components/Shared/TabNavigation.razor
Normal file
Reference in New Issue
Block a user