Files
Encelado/TradingBot/Components/Layout/MainLayout.razor
Alberto Balbo 92c8e57a8c Persistenza dati e logging avanzato con UI e Unraid
- 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
2025-12-22 11:24:17 +01:00

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;
}
}