- Aggiunto TradeHistoryService per persistenza trade/posizioni attive su disco (JSON, auto-save/restore) - Logging centralizzato (LoggingService) con livelli, categorie, simbolo e buffer circolare (500 log) - Nuova pagina Logs: monitoraggio real-time, filtri avanzati, cancellazione log, colorazione livelli - Sezione "Dati Persistenti" in Settings: conteggio trade, dimensione dati, reset con conferma modale - Background service per salvataggio sicuro su shutdown/stop container - Aggiornata sidebar, stili modali/bottoni danger, .gitignore e documentazione (README, CHANGELOG, UNRAID_INSTALL, checklist) - Versione 1.3.0
219 lines
7.6 KiB
Plaintext
219 lines
7.6 KiB
Plaintext
@inherits LayoutComponentBase
|
|
@using TradingBot.Services
|
|
@using TradingBot.Models
|
|
@inject TradingBotService BotService
|
|
@inject SettingsService SettingsService
|
|
@inject NavigationManager Navigation
|
|
@implements IDisposable
|
|
|
|
<div class="trading-bot-layout @(sidebarCollapsed ? "collapsed" : "expanded")">
|
|
<!-- Modern Vertical Sidebar -->
|
|
<aside class="modern-sidebar">
|
|
<!-- Brand Section -->
|
|
<div class="sidebar-brand">
|
|
<div class="brand-container @(sidebarCollapsed ? "minimized" : "")">
|
|
<div class="brand-logo">
|
|
<span class="logo-icon bi bi-graph-up-arrow"></span>
|
|
</div>
|
|
@if (!sidebarCollapsed)
|
|
{
|
|
<div class="brand-info">
|
|
<h1 class="brand-title">Trading<span class="accent">Bot</span></h1>
|
|
<div class="status-badge @(isRunning ? "online" : "offline")">
|
|
<span class="status-indicator"></span>
|
|
<span class="status-text">@(isRunning ? "ATTIVO" : "OFFLINE")</span>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
<button class="collapse-btn" @onclick="ToggleSidebar" title="@(sidebarCollapsed ? "Espandi" : "Minimizza")">
|
|
<span class="bi bi-chevron-@(sidebarCollapsed ? "right" : "left")"></span>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Navigation Menu -->
|
|
<nav class="sidebar-menu">
|
|
<NavLink class="menu-item" href="/" Match="NavLinkMatch.All" title="Dashboard">
|
|
<span class="item-icon bi bi-speedometer2"></span>
|
|
@if (!sidebarCollapsed)
|
|
{
|
|
<span class="item-text">Dashboard</span>
|
|
}
|
|
</NavLink>
|
|
|
|
<NavLink class="menu-item" href="/strategies" title="Strategie">
|
|
<span class="item-icon bi bi-diagram-3"></span>
|
|
@if (!sidebarCollapsed)
|
|
{
|
|
<span class="item-text">Strategie</span>
|
|
}
|
|
</NavLink>
|
|
|
|
<NavLink class="menu-item" href="/assets" title="Asset">
|
|
<span class="item-icon bi bi-coin"></span>
|
|
@if (!sidebarCollapsed)
|
|
{
|
|
<span class="item-text">Asset</span>
|
|
}
|
|
</NavLink>
|
|
|
|
<NavLink class="menu-item" href="/trading" title="Trading">
|
|
<span class="item-icon bi bi-graph-up-arrow"></span>
|
|
@if (!sidebarCollapsed)
|
|
{
|
|
<span class="item-text">Trading</span>
|
|
}
|
|
</NavLink>
|
|
|
|
<NavLink class="menu-item" href="/market" title="Analisi Mercato">
|
|
<span class="item-icon bi bi-bar-chart-line"></span>
|
|
@if (!sidebarCollapsed)
|
|
{
|
|
<span class="item-text">Analisi Mercato</span>
|
|
}
|
|
</NavLink>
|
|
|
|
<NavLink class="menu-item" href="/statistics" title="Statistiche">
|
|
<span class="item-icon bi bi-graph-up"></span>
|
|
@if (!sidebarCollapsed)
|
|
{
|
|
<span class="item-text">Statistiche</span>
|
|
}
|
|
</NavLink>
|
|
|
|
<NavLink class="menu-item" href="/logs" title="Logs">
|
|
<span class="item-icon bi bi-terminal"></span>
|
|
@if (!sidebarCollapsed)
|
|
{
|
|
<span class="item-text">Logs</span>
|
|
}
|
|
</NavLink>
|
|
|
|
<NavLink class="menu-item" href="/settings" title="Impostazioni">
|
|
<span class="item-icon bi bi-gear"></span>
|
|
@if (!sidebarCollapsed)
|
|
{
|
|
<span class="item-text">Impostazioni</span>
|
|
}
|
|
</NavLink>
|
|
</nav>
|
|
|
|
<!-- Portfolio Summary (quando espanso) -->
|
|
@if (!sidebarCollapsed)
|
|
{
|
|
<div class="sidebar-summary">
|
|
<div class="summary-card">
|
|
<div class="summary-row">
|
|
<span class="summary-title">Portfolio</span>
|
|
<span class="summary-amount">$@portfolioValue.ToString("N0")</span>
|
|
</div>
|
|
<div class="summary-row">
|
|
<span class="summary-title">Profitto</span>
|
|
<span class="summary-amount @(totalProfit >= 0 ? "profit" : "loss")">
|
|
$@totalProfit.ToString("N2")
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
</aside>
|
|
|
|
<!-- Main Content Area -->
|
|
<div class="main-area">
|
|
<!-- Top Header Bar -->
|
|
<header class="content-header">
|
|
<div class="header-left">
|
|
<!-- Placeholder for page title -->
|
|
</div>
|
|
<div class="header-right">
|
|
<button class="header-btn notifications" title="Notifiche">
|
|
<span class="bi bi-bell"></span>
|
|
</button>
|
|
<button class="header-btn bot-control @(isRunning ? "running" : "stopped")" @onclick="ToggleBot">
|
|
<span class="bi bi-@(isRunning ? "pause" : "play")-circle-fill"></span>
|
|
<span class="btn-label">@(isRunning ? "Stop" : "Avvia")</span>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Page Content -->
|
|
<main class="page-content">
|
|
@Body
|
|
</main>
|
|
</div>
|
|
</div>
|
|
|
|
@code {
|
|
private bool sidebarCollapsed = false;
|
|
private bool isRunning => BotService.Status.IsRunning;
|
|
private decimal portfolioValue = 0;
|
|
private decimal totalProfit = 0;
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
var settings = SettingsService.GetSettings();
|
|
sidebarCollapsed = settings.SidebarCollapsed;
|
|
|
|
BotService.OnStatusChanged += HandleUpdate;
|
|
BotService.OnPriceUpdated += HandlePriceUpdate;
|
|
SettingsService.OnSettingsChanged += HandleSettingsChanged;
|
|
|
|
UpdateStats();
|
|
|
|
if (settings.AutoStartBot && !BotService.Status.IsRunning)
|
|
{
|
|
BotService.Start();
|
|
}
|
|
}
|
|
|
|
private void ToggleSidebar()
|
|
{
|
|
sidebarCollapsed = !sidebarCollapsed;
|
|
SettingsService.UpdateSetting(nameof(AppSettings.SidebarCollapsed), sidebarCollapsed);
|
|
StateHasChanged(); // Force immediate UI update
|
|
Console.WriteLine($"Sidebar toggled: collapsed={sidebarCollapsed}"); // Debug log
|
|
}
|
|
|
|
private void ToggleBot()
|
|
{
|
|
if (isRunning)
|
|
BotService.Stop();
|
|
else
|
|
BotService.Start();
|
|
}
|
|
|
|
private void UpdateStats()
|
|
{
|
|
portfolioValue = BotService.AssetConfigurations.Values.Sum(c =>
|
|
c.CurrentBalance + (c.CurrentHoldings * (BotService.GetLatestPrice(c.Symbol)?.Price ?? 0)));
|
|
|
|
totalProfit = BotService.AssetConfigurations.Values.Sum(c => c.TotalProfit);
|
|
}
|
|
|
|
private void HandleUpdate()
|
|
{
|
|
UpdateStats();
|
|
InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
private void HandlePriceUpdate(string symbol, MarketPrice price)
|
|
{
|
|
UpdateStats();
|
|
InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
private void HandleSettingsChanged()
|
|
{
|
|
var settings = SettingsService.GetSettings();
|
|
sidebarCollapsed = settings.SidebarCollapsed;
|
|
InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
BotService.OnStatusChanged -= HandleUpdate;
|
|
BotService.OnPriceUpdated -= HandlePriceUpdate;
|
|
SettingsService.OnSettingsChanged -= HandleSettingsChanged;
|
|
}
|
|
}
|