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:
341
TradingBot/Components/Pages/Assets.razor
Normal file
341
TradingBot/Components/Pages/Assets.razor
Normal file
@@ -0,0 +1,341 @@
|
||||
@page "/assets"
|
||||
@using TradingBot.Models
|
||||
@using TradingBot.Services
|
||||
@inject TradingBotService BotService
|
||||
@implements IDisposable
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<PageTitle>Asset - TradingBot</PageTitle>
|
||||
|
||||
<div class="assets-page">
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1>Gestione Asset</h1>
|
||||
<p class="subtitle">Visualizza, configura e assegna strategie ai tuoi asset di trading</p>
|
||||
</div>
|
||||
<div class="header-controls">
|
||||
<div class="view-toggle">
|
||||
<button class="toggle-btn @(viewMode == "grid" ? "active" : "")" @onclick="@(() => viewMode = "grid")">
|
||||
<span class="bi bi-grid-3x3-gap"></span>
|
||||
</button>
|
||||
<button class="toggle-btn @(viewMode == "list" ? "active" : "")" @onclick="@(() => viewMode = "list")">
|
||||
<span class="bi bi-list-ul"></span>
|
||||
</button>
|
||||
</div>
|
||||
<select class="filter-select" @bind="filterStatus">
|
||||
<option value="all">Tutti gli Asset</option>
|
||||
<option value="active">Solo Attivi</option>
|
||||
<option value="inactive">Solo Inattivi</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary Stats -->
|
||||
<div class="assets-summary">
|
||||
<div class="summary-stat">
|
||||
<div class="stat-icon">
|
||||
<span class="bi bi-coin"></span>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<span class="stat-label">Totale Asset</span>
|
||||
<span class="stat-value">@totalAssets</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-stat success">
|
||||
<div class="stat-icon">
|
||||
<span class="bi bi-check-circle"></span>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<span class="stat-label">Asset Attivi</span>
|
||||
<span class="stat-value">@activeAssets</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-stat warning">
|
||||
<div class="stat-icon">
|
||||
<span class="bi bi-diagram-3"></span>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<span class="stat-label">Strategie Assegnate</span>
|
||||
<span class="stat-value">@assignedStrategies</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-stat info">
|
||||
<div class="stat-icon">
|
||||
<span class="bi bi-currency-dollar"></span>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<span class="stat-label">Valore Totale</span>
|
||||
<span class="stat-value">$@totalValue.ToString("N0")</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Assets Grid/List -->
|
||||
@if (viewMode == "grid")
|
||||
{
|
||||
<div class="assets-grid">
|
||||
@foreach (var config in GetFilteredAssets())
|
||||
{
|
||||
var price = BotService.GetLatestPrice(config.Symbol);
|
||||
var stats = BotService.AssetStatistics.TryGetValue(config.Symbol, out var s) ? s : null;
|
||||
|
||||
<div class="asset-card @(config.IsEnabled ? "enabled" : "disabled")">
|
||||
<div class="asset-card-header">
|
||||
<div class="asset-info">
|
||||
<div class="asset-icon">@config.Symbol.Substring(0, 1)</div>
|
||||
<div class="asset-title">
|
||||
<h3>@config.Name</h3>
|
||||
<span class="asset-symbol">@config.Symbol</span>
|
||||
</div>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox"
|
||||
checked="@config.IsEnabled"
|
||||
@onchange="@((e) => ToggleAsset(config.Symbol, (bool)e.Value!))" />
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="asset-card-body">
|
||||
@if (price != null)
|
||||
{
|
||||
<div class="price-section">
|
||||
<div class="current-price">$@price.Price.ToString("N2")</div>
|
||||
<div class="price-change @(price.Change24h >= 0 ? "positive" : "negative")">
|
||||
<span class="bi @(price.Change24h >= 0 ? "bi-arrow-up" : "bi-arrow-down")"></span>
|
||||
@Math.Abs(price.Change24h).ToString("F2")% (24h)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="asset-metrics">
|
||||
<div class="metric">
|
||||
<span class="metric-label">Holdings</span>
|
||||
<span class="metric-value">@config.CurrentHoldings.ToString("F6")</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Valore</span>
|
||||
<span class="metric-value">$@((config.CurrentBalance + config.CurrentHoldings * (price?.Price ?? 0)).ToString("N2"))</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Profitto</span>
|
||||
<span class="metric-value @(config.TotalProfit >= 0 ? "profit" : "loss")">
|
||||
$@config.TotalProfit.ToString("N2")
|
||||
</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Trades</span>
|
||||
<span class="metric-value">@(stats?.TotalTrades ?? 0)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="strategy-section">
|
||||
<label class="strategy-label">Strategia Assegnata</label>
|
||||
<select class="strategy-select"
|
||||
value="@config.StrategyName"
|
||||
@onchange="@((e) => AssignStrategy(config.Symbol, e.Value?.ToString() ?? ""))">
|
||||
<option value="">Nessuna strategia</option>
|
||||
<option value="RSI + MACD Cross">RSI + MACD Cross</option>
|
||||
<option value="Media Mobile Semplice">Media Mobile Semplice</option>
|
||||
<option value="Scalping Veloce">Scalping Veloce</option>
|
||||
<option value="Trend Following">Trend Following</option>
|
||||
<option value="Mean Reversion">Mean Reversion</option>
|
||||
<option value="Conservative">Conservative</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="asset-card-footer">
|
||||
<button class="btn-secondary btn-sm" @onclick="@(() => OpenAssetDetails(config.Symbol))">
|
||||
<span class="bi bi-gear"></span>
|
||||
Configura
|
||||
</button>
|
||||
<button class="btn-secondary btn-sm" @onclick="@(() => ViewChart(config.Symbol))">
|
||||
<span class="bi bi-graph-up"></span>
|
||||
Grafico
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- List View -->
|
||||
<div class="assets-table">
|
||||
<div class="table-header">
|
||||
<div class="th">Asset</div>
|
||||
<div class="th">Prezzo</div>
|
||||
<div class="th">Var. 24h</div>
|
||||
<div class="th">Holdings</div>
|
||||
<div class="th">Valore</div>
|
||||
<div class="th">Profitto</div>
|
||||
<div class="th">Strategia</div>
|
||||
<div class="th">Stato</div>
|
||||
<div class="th">Azioni</div>
|
||||
</div>
|
||||
|
||||
@foreach (var config in GetFilteredAssets())
|
||||
{
|
||||
var price = BotService.GetLatestPrice(config.Symbol);
|
||||
var stats = BotService.AssetStatistics.TryGetValue(config.Symbol, out var s) ? s : null;
|
||||
|
||||
<div class="table-row @(config.IsEnabled ? "enabled" : "disabled")">
|
||||
<div class="cell-asset">
|
||||
<div class="asset-icon-small">@config.Symbol.Substring(0, 1)</div>
|
||||
<div>
|
||||
<div class="asset-name">@config.Name</div>
|
||||
<div class="asset-symbol-small">@config.Symbol</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
@if (price != null)
|
||||
{
|
||||
<span class="price-value">$@price.Price.ToString("N2")</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-muted">-</span>
|
||||
}
|
||||
</div>
|
||||
<div class="cell">
|
||||
@if (price != null)
|
||||
{
|
||||
<span class="change-badge @(price.Change24h >= 0 ? "positive" : "negative")">
|
||||
@(price.Change24h >= 0 ? "+" : "")@price.Change24h.ToString("F2")%
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-muted">-</span>
|
||||
}
|
||||
</div>
|
||||
<div class="cell">
|
||||
<span class="mono-value">@config.CurrentHoldings.ToString("F6")</span>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<span class="mono-value">$@((config.CurrentBalance + config.CurrentHoldings * (price?.Price ?? 0)).ToString("N2"))</span>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<span class="mono-value @(config.TotalProfit >= 0 ? "profit" : "loss")">
|
||||
$@config.TotalProfit.ToString("N2")
|
||||
</span>
|
||||
</div>
|
||||
<div class="cell-strategy">
|
||||
<select class="strategy-select-small"
|
||||
value="@config.StrategyName"
|
||||
@onchange="@((e) => AssignStrategy(config.Symbol, e.Value?.ToString() ?? ""))">
|
||||
<option value="">Nessuna</option>
|
||||
<option value="RSI + MACD Cross">RSI + MACD</option>
|
||||
<option value="Media Mobile Semplice">SMA</option>
|
||||
<option value="Scalping Veloce">Scalping</option>
|
||||
<option value="Trend Following">Trend</option>
|
||||
<option value="Mean Reversion">Mean Rev.</option>
|
||||
<option value="Conservative">Conserv.</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<label class="toggle-switch-small">
|
||||
<input type="checkbox"
|
||||
checked="@config.IsEnabled"
|
||||
@onchange="@((e) => ToggleAsset(config.Symbol, (bool)e.Value!))" />
|
||||
<span class="toggle-slider-small"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="cell-actions">
|
||||
<button class="btn-icon-small" title="Configura" @onclick="@(() => OpenAssetDetails(config.Symbol))">
|
||||
<span class="bi bi-gear"></span>
|
||||
</button>
|
||||
<button class="btn-icon-small" title="Grafico" @onclick="@(() => ViewChart(config.Symbol))">
|
||||
<span class="bi bi-graph-up"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string viewMode = "grid";
|
||||
private string filterStatus = "all";
|
||||
private int totalAssets = 0;
|
||||
private int activeAssets = 0;
|
||||
private int assignedStrategies = 0;
|
||||
private decimal totalValue = 0;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
BotService.OnStatusChanged += HandleUpdate;
|
||||
BotService.OnTradeExecuted += HandleTradeExecuted;
|
||||
BotService.OnPriceUpdated += HandlePriceUpdate;
|
||||
|
||||
RefreshData();
|
||||
}
|
||||
|
||||
private void RefreshData()
|
||||
{
|
||||
totalAssets = BotService.AssetConfigurations.Count;
|
||||
activeAssets = BotService.AssetConfigurations.Values.Count(c => c.IsEnabled);
|
||||
assignedStrategies = BotService.AssetConfigurations.Values.Count(c => !string.IsNullOrEmpty(c.StrategyName));
|
||||
|
||||
totalValue = BotService.AssetConfigurations.Values.Sum(c =>
|
||||
{
|
||||
var price = BotService.GetLatestPrice(c.Symbol);
|
||||
return c.CurrentBalance + (c.CurrentHoldings * (price?.Price ?? 0));
|
||||
});
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private IEnumerable<AssetConfiguration> GetFilteredAssets()
|
||||
{
|
||||
var assets = BotService.AssetConfigurations.Values.OrderBy(c => c.Symbol);
|
||||
|
||||
return filterStatus switch
|
||||
{
|
||||
"active" => assets.Where(c => c.IsEnabled),
|
||||
"inactive" => assets.Where(c => !c.IsEnabled),
|
||||
_ => assets
|
||||
};
|
||||
}
|
||||
|
||||
private void ToggleAsset(string symbol, bool enabled)
|
||||
{
|
||||
BotService.ToggleAsset(symbol, enabled);
|
||||
RefreshData();
|
||||
}
|
||||
|
||||
private void AssignStrategy(string symbol, string strategyName)
|
||||
{
|
||||
if (BotService.AssetConfigurations.TryGetValue(symbol, out var config))
|
||||
{
|
||||
config.StrategyName = strategyName;
|
||||
RefreshData();
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenAssetDetails(string symbol)
|
||||
{
|
||||
// TODO: Open modal or navigate to asset detail page
|
||||
}
|
||||
|
||||
private void ViewChart(string symbol)
|
||||
{
|
||||
var navManager = Navigation;
|
||||
navManager?.NavigateTo($"/market?symbol={symbol}");
|
||||
}
|
||||
|
||||
private void HandleUpdate() => InvokeAsync(RefreshData);
|
||||
private void HandleTradeExecuted(Trade trade) => InvokeAsync(RefreshData);
|
||||
private void HandlePriceUpdate(string symbol, MarketPrice price) => InvokeAsync(RefreshData);
|
||||
|
||||
[Inject] private NavigationManager? Navigation { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BotService.OnStatusChanged -= HandleUpdate;
|
||||
BotService.OnTradeExecuted -= HandleTradeExecuted;
|
||||
BotService.OnPriceUpdated -= HandlePriceUpdate;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user