using IBKRTrader.Core.Logging; using IBKRTrader.Core.Workers; using IBKRTrader.Modules.CongressTrading.Database; using IBKRTrader.UI; namespace IBKRTrader.Modules.CongressTrading.UI; /// /// Eigenständiges Fenster des CongressTrading-Moduls. /// Phase 2: Grundgerüst mit DB-Kennzahlen und manuellem Scrape-Trigger. /// Die vollständige Trade-/Positions-Ansicht folgt in Phase 4. /// public sealed class CongressTradingForm : ModuleFormBase { private const string ScrapeWorkerName = "CT-ScrapeWorker"; private readonly CongressRepository _repo; private readonly WorkerEngine _engine; private readonly LoggingService _logger; private readonly Label _lblTrades = new() { AutoSize = true, Location = new Point(20, 70) }; private readonly Label _lblMembers = new() { AutoSize = true, Location = new Point(20, 100) }; private readonly Button _btnRefresh = new() { Text = "Aktualisieren", Location = new Point(20, 140), Width = 140 }; private readonly Button _btnScrape = new() { Text = "Scrape jetzt", Location = new Point(170, 140), Width = 140 }; private readonly Label _lblStatus = new() { AutoSize = true, Location = new Point(20, 185), ForeColor = SystemColors.GrayText }; public CongressTradingForm(CongressRepository repo, WorkerEngine engine, LoggingService logger) : base(CongressTradingModule.ModuleKey) { _repo = repo; _engine = engine; _logger = logger; Text = "Congress Trading [CT]"; BuildLayout(); } private void BuildLayout() { var title = new Label { Text = "Congress Trading", Font = new Font(Font.FontFamily, 14f, FontStyle.Bold), Location = new Point(18, 20), AutoSize = true }; var hint = new Label { Text = "Vollständige Trade- und Positions-Ansicht folgt in Phase 4.", Location = new Point(20, 230), AutoSize = true, ForeColor = SystemColors.GrayText }; _btnRefresh.Click += async (_, _) => await RefreshStatsAsync(); _btnScrape.Click += async (_, _) => await TriggerScrapeAsync(); Controls.Add(title); Controls.Add(_lblTrades); Controls.Add(_lblMembers); Controls.Add(_btnRefresh); Controls.Add(_btnScrape); Controls.Add(_lblStatus); Controls.Add(hint); } protected override async void OnShown(EventArgs e) { base.OnShown(e); await RefreshStatsAsync(); } private async Task RefreshStatsAsync() { try { var trades = await _repo.GetTradeCountAsync(); var members = await _repo.GetMemberCountAsync(); _lblTrades.Text = $"Trades in DB: {trades:N0}"; _lblMembers.Text = $"Mitglieder in DB: {members:N0}"; _lblStatus.Text = $"Aktualisiert: {DateTime.Now:HH:mm:ss}"; } catch (Exception ex) { _lblTrades.Text = "Trades in DB: n/v"; _lblMembers.Text = "Mitglieder in DB: n/v"; _lblStatus.Text = $"DB nicht erreichbar: {ex.Message}"; _logger.Warn(CongressTradingModule.ModuleKey, $"Kennzahlen konnten nicht geladen werden: {ex.Message}"); } } private async Task TriggerScrapeAsync() { try { _lblStatus.Text = "Scrape angestoßen..."; await _engine.TriggerWorkerAsync(ScrapeWorkerName); _logger.Info(CongressTradingModule.ModuleKey, "Scrape-Worker manuell ausgelöst (aus Modul-Fenster)."); } catch (Exception ex) { _lblStatus.Text = $"Scrape fehlgeschlagen: {ex.Message}"; _logger.Error(CongressTradingModule.ModuleKey, "Manueller Scrape-Trigger fehlgeschlagen.", ex); } } }