diff --git a/Mimante/AutoBidder.csproj b/Mimante/AutoBidder.csproj
index 7cb7e5b..67b8469 100644
--- a/Mimante/AutoBidder.csproj
+++ b/Mimante/AutoBidder.csproj
@@ -12,9 +12,13 @@
+
+
+
+
diff --git a/Mimante/Controls/AuctionMonitorControl.xaml b/Mimante/Controls/AuctionMonitorControl.xaml
new file mode 100644
index 0000000..508a25d
--- /dev/null
+++ b/Mimante/Controls/AuctionMonitorControl.xaml
@@ -0,0 +1,566 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Mimante/Controls/AuctionMonitorControl.xaml.cs b/Mimante/Controls/AuctionMonitorControl.xaml.cs
new file mode 100644
index 0000000..71453c1
--- /dev/null
+++ b/Mimante/Controls/AuctionMonitorControl.xaml.cs
@@ -0,0 +1,244 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace AutoBidder.Controls
+{
+ ///
+ /// Interaction logic for AuctionMonitorControl.xaml
+ ///
+ public partial class AuctionMonitorControl : UserControl
+ {
+ public AuctionMonitorControl()
+ {
+ InitializeComponent();
+ }
+
+ // Event handlers - these will bubble up to MainWindow
+ private void StartButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(StartClickedEvent, this));
+ }
+
+ private void PauseAllButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(PauseAllClickedEvent, this));
+ }
+
+ private void StopButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(StopClickedEvent, this));
+ }
+
+ private void AddUrlButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(AddUrlClickedEvent, this));
+ }
+
+ private void RemoveUrlButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(RemoveUrlClickedEvent, this));
+ }
+
+ private void ExportButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(ExportClickedEvent, this));
+ }
+
+ private void MultiAuctionsGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(AuctionSelectionChangedEvent, this));
+ }
+
+ private void MultiAuctionsGrid_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Delete && MultiAuctionsGrid.SelectedItem != null)
+ {
+ var result = MessageBox.Show(
+ "Sei sicuro di voler eliminare l'asta selezionata?",
+ "Conferma Eliminazione",
+ MessageBoxButton.YesNo,
+ MessageBoxImage.Question);
+
+ if (result == MessageBoxResult.Yes)
+ {
+ RaiseEvent(new RoutedEventArgs(RemoveUrlClickedEvent, this));
+ }
+ }
+ }
+
+ private void CopyAuctionUrlButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(CopyUrlClickedEvent, this));
+ }
+
+ private void ResetSettingsButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(ResetSettingsClickedEvent, this));
+ }
+
+ private void ClearBiddersButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(ClearBiddersClickedEvent, this));
+ }
+
+ private void ClearLogButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(ClearLogClickedEvent, this));
+ }
+
+ private void ClearGlobalLogButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(ClearGlobalLogClickedEvent, this));
+ }
+
+ private void SelectedTimerClick_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(TimerClickChangedEvent, this));
+ }
+
+ private void SelectedDelayMs_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(DelayMsChangedEvent, this));
+ }
+
+ private void SelectedMinPrice_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(MinPriceChangedEvent, this));
+ }
+
+ private void SelectedMaxPrice_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(MaxPriceChangedEvent, this));
+ }
+
+ private void SelectedMaxClicks_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(MaxClicksChangedEvent, this));
+ }
+
+ // Routed Events
+ public static readonly RoutedEvent StartClickedEvent = EventManager.RegisterRoutedEvent(
+ "StartClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent PauseAllClickedEvent = EventManager.RegisterRoutedEvent(
+ "PauseAllClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent StopClickedEvent = EventManager.RegisterRoutedEvent(
+ "StopClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent AddUrlClickedEvent = EventManager.RegisterRoutedEvent(
+ "AddUrlClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent RemoveUrlClickedEvent = EventManager.RegisterRoutedEvent(
+ "RemoveUrlClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent ExportClickedEvent = EventManager.RegisterRoutedEvent(
+ "ExportClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent AuctionSelectionChangedEvent = EventManager.RegisterRoutedEvent(
+ "AuctionSelectionChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent CopyUrlClickedEvent = EventManager.RegisterRoutedEvent(
+ "CopyUrlClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent ResetSettingsClickedEvent = EventManager.RegisterRoutedEvent(
+ "ResetSettingsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent ClearBiddersClickedEvent = EventManager.RegisterRoutedEvent(
+ "ClearBiddersClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent ClearLogClickedEvent = EventManager.RegisterRoutedEvent(
+ "ClearLogClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent ClearGlobalLogClickedEvent = EventManager.RegisterRoutedEvent(
+ "ClearGlobalLogClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent TimerClickChangedEvent = EventManager.RegisterRoutedEvent(
+ "TimerClickChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent DelayMsChangedEvent = EventManager.RegisterRoutedEvent(
+ "DelayMsChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent MinPriceChangedEvent = EventManager.RegisterRoutedEvent(
+ "MinPriceChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent MaxPriceChangedEvent = EventManager.RegisterRoutedEvent(
+ "MaxPriceChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public static readonly RoutedEvent MaxClicksChangedEvent = EventManager.RegisterRoutedEvent(
+ "MaxClicksChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AuctionMonitorControl));
+
+ public event RoutedEventHandler StartClicked
+ {
+ add { AddHandler(StartClickedEvent, value); }
+ remove { RemoveHandler(StartClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler PauseAllClicked
+ {
+ add { AddHandler(PauseAllClickedEvent, value); }
+ remove { RemoveHandler(PauseAllClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler StopClicked
+ {
+ add { AddHandler(StopClickedEvent, value); }
+ remove { RemoveHandler(StopClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler AddUrlClicked
+ {
+ add { AddHandler(AddUrlClickedEvent, value); }
+ remove { RemoveHandler(AddUrlClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler RemoveUrlClicked
+ {
+ add { AddHandler(RemoveUrlClickedEvent, value); }
+ remove { RemoveHandler(RemoveUrlClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler ExportClicked
+ {
+ add { AddHandler(ExportClickedEvent, value); }
+ remove { RemoveHandler(ExportClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler AuctionSelectionChanged
+ {
+ add { AddHandler(AuctionSelectionChangedEvent, value); }
+ remove { RemoveHandler(AuctionSelectionChangedEvent, value); }
+ }
+
+ public event RoutedEventHandler CopyUrlClicked
+ {
+ add { AddHandler(CopyUrlClickedEvent, value); }
+ remove { RemoveHandler(CopyUrlClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler ResetSettingsClicked
+ {
+ add { AddHandler(ResetSettingsClickedEvent, value); }
+ remove { RemoveHandler(ResetSettingsClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler ClearBiddersClicked
+ {
+ add { AddHandler(ClearBiddersClickedEvent, value); }
+ remove { RemoveHandler(ClearBiddersClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler ClearLogClicked
+ {
+ add { AddHandler(ClearLogClickedEvent, value); }
+ remove { RemoveHandler(ClearLogClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler ClearGlobalLogClicked
+ {
+ add { AddHandler(ClearGlobalLogClickedEvent, value); }
+ remove { RemoveHandler(ClearGlobalLogClickedEvent, value); }
+ }
+ }
+}
diff --git a/Mimante/Controls/BrowserControl.xaml b/Mimante/Controls/BrowserControl.xaml
new file mode 100644
index 0000000..0de4282
--- /dev/null
+++ b/Mimante/Controls/BrowserControl.xaml
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Mimante/Controls/BrowserControl.xaml.cs b/Mimante/Controls/BrowserControl.xaml.cs
new file mode 100644
index 0000000..5c6c695
--- /dev/null
+++ b/Mimante/Controls/BrowserControl.xaml.cs
@@ -0,0 +1,137 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using Microsoft.Web.WebView2.Core;
+
+namespace AutoBidder.Controls
+{
+ ///
+ /// Interaction logic for BrowserControl.xaml
+ ///
+ public partial class BrowserControl : UserControl
+ {
+ public BrowserControl()
+ {
+ InitializeComponent();
+ }
+
+ private void BrowserBackButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(BrowserBackClickedEvent, this));
+ }
+
+ private void BrowserForwardButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(BrowserForwardClickedEvent, this));
+ }
+
+ private void BrowserRefreshButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(BrowserRefreshClickedEvent, this));
+ }
+
+ private void BrowserHomeButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(BrowserHomeClickedEvent, this));
+ }
+
+ private void BrowserGoButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(BrowserGoClickedEvent, this));
+ }
+
+ private void BrowserAddAuctionButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(BrowserAddAuctionClickedEvent, this));
+ }
+
+ private void EmbeddedWebView_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
+ {
+ var args = new BrowserNavigationEventArgs(BrowserNavigationStartingEvent, this)
+ {
+ Uri = e.Uri
+ };
+ RaiseEvent(args);
+ }
+
+ private void EmbeddedWebView_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(BrowserNavigationCompletedEvent, this));
+ }
+
+ private void EmbeddedWebView_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ e.Handled = true;
+ }
+
+ // Routed Events
+ public static readonly RoutedEvent BrowserBackClickedEvent = EventManager.RegisterRoutedEvent(
+ "BrowserBackClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl));
+
+ public static readonly RoutedEvent BrowserForwardClickedEvent = EventManager.RegisterRoutedEvent(
+ "BrowserForwardClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl));
+
+ public static readonly RoutedEvent BrowserRefreshClickedEvent = EventManager.RegisterRoutedEvent(
+ "BrowserRefreshClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl));
+
+ public static readonly RoutedEvent BrowserHomeClickedEvent = EventManager.RegisterRoutedEvent(
+ "BrowserHomeClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl));
+
+ public static readonly RoutedEvent BrowserGoClickedEvent = EventManager.RegisterRoutedEvent(
+ "BrowserGoClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl));
+
+ public static readonly RoutedEvent BrowserAddAuctionClickedEvent = EventManager.RegisterRoutedEvent(
+ "BrowserAddAuctionClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl));
+
+ public static readonly RoutedEvent BrowserNavigationStartingEvent = EventManager.RegisterRoutedEvent(
+ "BrowserNavigationStarting", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl));
+
+ public static readonly RoutedEvent BrowserNavigationCompletedEvent = EventManager.RegisterRoutedEvent(
+ "BrowserNavigationCompleted", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl));
+
+ public event RoutedEventHandler BrowserBackClicked
+ {
+ add { AddHandler(BrowserBackClickedEvent, value); }
+ remove { RemoveHandler(BrowserBackClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler BrowserForwardClicked
+ {
+ add { AddHandler(BrowserForwardClickedEvent, value); }
+ remove { RemoveHandler(BrowserForwardClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler BrowserRefreshClicked
+ {
+ add { AddHandler(BrowserRefreshClickedEvent, value); }
+ remove { RemoveHandler(BrowserRefreshClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler BrowserHomeClicked
+ {
+ add { AddHandler(BrowserHomeClickedEvent, value); }
+ remove { RemoveHandler(BrowserHomeClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler BrowserGoClicked
+ {
+ add { AddHandler(BrowserGoClickedEvent, value); }
+ remove { RemoveHandler(BrowserGoClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler BrowserAddAuctionClicked
+ {
+ add { AddHandler(BrowserAddAuctionClickedEvent, value); }
+ remove { RemoveHandler(BrowserAddAuctionClickedEvent, value); }
+ }
+ }
+
+ public class BrowserNavigationEventArgs : RoutedEventArgs
+ {
+ public string? Uri { get; set; }
+
+ public BrowserNavigationEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source)
+ {
+ }
+ }
+}
diff --git a/Mimante/Controls/SettingsControl.xaml b/Mimante/Controls/SettingsControl.xaml
new file mode 100644
index 0000000..b03ff8d
--- /dev/null
+++ b/Mimante/Controls/SettingsControl.xaml
@@ -0,0 +1,293 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1. Apri Chrome e vai su https://it.bidoo.com
+ 2. Effettua il login con le tue credenziali
+ 3. Premi F12 per aprire Developer Tools
+ 4. Vai alla tab "Application" → "Storage" → "Cookies" → "https://it.bidoo.com"
+ 5. Copia TUTTA la stringa di cookie (seleziona tutti i cookie e copia i valori)
+ 6. Formato: "cookie1=value1; cookie2=value2; __stattrb=xxxxx; ..."
+ 7. Incolla la stringa completa qui sopra
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Mimante/Controls/SettingsControl.xaml.cs b/Mimante/Controls/SettingsControl.xaml.cs
new file mode 100644
index 0000000..e775c02
--- /dev/null
+++ b/Mimante/Controls/SettingsControl.xaml.cs
@@ -0,0 +1,154 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace AutoBidder.Controls
+{
+ ///
+ /// Interaction logic for SettingsControl.xaml
+ ///
+ public partial class SettingsControl : UserControl
+ {
+ public SettingsControl()
+ {
+ InitializeComponent();
+ }
+
+ // Propriet pubbliche per accesso da MainWindow
+ public TextBox DefaultTimerClickTextBox => DefaultTimerClick;
+ public TextBox DefaultDelayMsTextBox => DefaultDelayMs;
+ public TextBox DefaultMinPriceTextBox => DefaultMinPrice;
+ public TextBox DefaultMaxPriceTextBox => DefaultMaxPrice;
+ public TextBox DefaultMaxClicksTextBox => DefaultMaxClicks;
+
+ // Event handlers singoli (per backward compatibility)
+ private void SaveCookieButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(SaveCookieClickedEvent, this));
+ }
+
+ private void ImportCookieFromBrowserButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(ImportCookieClickedEvent, this));
+ }
+
+ private void CancelCookieButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(CancelCookieClickedEvent, this));
+ }
+
+ private void ExportBrowseButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(ExportBrowseClickedEvent, this));
+ }
+
+ private void SaveSettingsButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(SaveSettingsClickedEvent, this));
+ }
+
+ private void CancelSettingsButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(CancelSettingsClickedEvent, this));
+ }
+
+ private void SaveDefaultsButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(SaveDefaultsClickedEvent, this));
+ }
+
+ private void CancelDefaultsButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(CancelDefaultsClickedEvent, this));
+ }
+
+ // Nuovi eventi unificati
+ private void SaveAllSettings_Click(object sender, RoutedEventArgs e)
+ {
+ // Salva tutte le impostazioni in sequenza
+ RaiseEvent(new RoutedEventArgs(SaveCookieClickedEvent, this));
+ RaiseEvent(new RoutedEventArgs(SaveSettingsClickedEvent, this));
+ RaiseEvent(new RoutedEventArgs(SaveDefaultsClickedEvent, this));
+ }
+
+ private void CancelAllSettings_Click(object sender, RoutedEventArgs e)
+ {
+ // Annulla tutte le modifiche
+ RaiseEvent(new RoutedEventArgs(CancelCookieClickedEvent, this));
+ RaiseEvent(new RoutedEventArgs(CancelSettingsClickedEvent, this));
+ RaiseEvent(new RoutedEventArgs(CancelDefaultsClickedEvent, this));
+ }
+
+ // Routed Events
+ public static readonly RoutedEvent SaveCookieClickedEvent = EventManager.RegisterRoutedEvent(
+ "SaveCookieClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
+
+ public static readonly RoutedEvent ImportCookieClickedEvent = EventManager.RegisterRoutedEvent(
+ "ImportCookieClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
+
+ public static readonly RoutedEvent CancelCookieClickedEvent = EventManager.RegisterRoutedEvent(
+ "CancelCookieClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
+
+ public static readonly RoutedEvent ExportBrowseClickedEvent = EventManager.RegisterRoutedEvent(
+ "ExportBrowseClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
+
+ public static readonly RoutedEvent SaveSettingsClickedEvent = EventManager.RegisterRoutedEvent(
+ "SaveSettingsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
+
+ public static readonly RoutedEvent CancelSettingsClickedEvent = EventManager.RegisterRoutedEvent(
+ "CancelSettingsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
+
+ public static readonly RoutedEvent SaveDefaultsClickedEvent = EventManager.RegisterRoutedEvent(
+ "SaveDefaultsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
+
+ public static readonly RoutedEvent CancelDefaultsClickedEvent = EventManager.RegisterRoutedEvent(
+ "CancelDefaultsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SettingsControl));
+
+ public event RoutedEventHandler SaveCookieClicked
+ {
+ add { AddHandler(SaveCookieClickedEvent, value); }
+ remove { RemoveHandler(SaveCookieClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler ImportCookieClicked
+ {
+ add { AddHandler(ImportCookieClickedEvent, value); }
+ remove { RemoveHandler(ImportCookieClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler CancelCookieClicked
+ {
+ add { AddHandler(CancelCookieClickedEvent, value); }
+ remove { RemoveHandler(CancelCookieClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler ExportBrowseClicked
+ {
+ add { AddHandler(ExportBrowseClickedEvent, value); }
+ remove { RemoveHandler(ExportBrowseClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler SaveSettingsClicked
+ {
+ add { AddHandler(SaveSettingsClickedEvent, value); }
+ remove { RemoveHandler(SaveSettingsClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler CancelSettingsClicked
+ {
+ add { AddHandler(CancelSettingsClickedEvent, value); }
+ remove { RemoveHandler(CancelSettingsClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler SaveDefaultsClicked
+ {
+ add { AddHandler(SaveDefaultsClickedEvent, value); }
+ remove { RemoveHandler(SaveDefaultsClickedEvent, value); }
+ }
+
+ public event RoutedEventHandler CancelDefaultsClicked
+ {
+ add { AddHandler(CancelDefaultsClickedEvent, value); }
+ remove { RemoveHandler(CancelDefaultsClickedEvent, value); }
+ }
+ }
+}
diff --git a/Mimante/Controls/StatisticsControl.xaml b/Mimante/Controls/StatisticsControl.xaml
new file mode 100644
index 0000000..3da6355
--- /dev/null
+++ b/Mimante/Controls/StatisticsControl.xaml
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Mimante/Controls/StatisticsControl.xaml.cs b/Mimante/Controls/StatisticsControl.xaml.cs
new file mode 100644
index 0000000..a2aeef0
--- /dev/null
+++ b/Mimante/Controls/StatisticsControl.xaml.cs
@@ -0,0 +1,31 @@
+using System.Windows;
+using System.Windows.Controls;
+
+namespace AutoBidder.Controls
+{
+ ///
+ /// Interaction logic for StatisticsControl.xaml
+ ///
+ public partial class StatisticsControl : UserControl
+ {
+ public StatisticsControl()
+ {
+ InitializeComponent();
+ }
+
+ private void LoadClosedAuctionsButton_Click(object sender, RoutedEventArgs e)
+ {
+ RaiseEvent(new RoutedEventArgs(LoadClosedAuctionsClickedEvent, this));
+ }
+
+ // Routed Events
+ public static readonly RoutedEvent LoadClosedAuctionsClickedEvent = EventManager.RegisterRoutedEvent(
+ "LoadClosedAuctionsClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(StatisticsControl));
+
+ public event RoutedEventHandler LoadClosedAuctionsClicked
+ {
+ add { AddHandler(LoadClosedAuctionsClickedEvent, value); }
+ remove { RemoveHandler(LoadClosedAuctionsClickedEvent, value); }
+ }
+ }
+}
diff --git a/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Browser.cs b/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Browser.cs
new file mode 100644
index 0000000..ff5e0b4
--- /dev/null
+++ b/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Browser.cs
@@ -0,0 +1,176 @@
+using System;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using Microsoft.Web.WebView2.Core;
+
+namespace AutoBidder
+{
+ ///
+ /// Browser event handlers and navigation
+ ///
+ public partial class MainWindow
+ {
+ private void BrowserBackButton_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ if (EmbeddedWebView?.CoreWebView2 != null && EmbeddedWebView.CoreWebView2.CanGoBack)
+ EmbeddedWebView.CoreWebView2.GoBack();
+ }
+ catch { }
+ }
+
+ private void BrowserForwardButton_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ if (EmbeddedWebView?.CoreWebView2 != null && EmbeddedWebView.CoreWebView2.CanGoForward)
+ EmbeddedWebView.CoreWebView2.GoForward();
+ }
+ catch { }
+ }
+
+ private void BrowserRefreshButton_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ EmbeddedWebView?.Reload();
+ }
+ catch { }
+ }
+
+ private void BrowserHomeButton_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ EmbeddedWebView?.CoreWebView2?.Navigate("https://it.bidoo.com/");
+ BrowserAddress.Text = "https://it.bidoo.com/";
+ }
+ catch { }
+ }
+
+ private void BrowserGoButton_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ var url = BrowserAddress.Text?.Trim();
+ if (string.IsNullOrEmpty(url)) return;
+ if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ url = "https://" + url;
+ EmbeddedWebView?.CoreWebView2?.Navigate(url);
+ }
+ catch { }
+ }
+
+ private void BrowserAddAuctionButton_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ var url = BrowserAddress.Text?.Trim() ?? EmbeddedWebView?.Source?.ToString();
+ if (!string.IsNullOrEmpty(url))
+ _ = AddAuctionFromUrl(url);
+ }
+ catch { }
+ }
+
+ private void EmbeddedWebView_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
+ {
+ try
+ {
+ BrowserAddress.Text = e.Uri ?? string.Empty;
+ var btn = this.FindName("BrowserAddAuctionButton") as Button;
+ if (btn != null)
+ btn.IsEnabled = IsValidAuctionUrl(e.Uri ?? string.Empty);
+ }
+ catch { }
+ }
+
+ private void EmbeddedWebView_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
+ {
+ try
+ {
+ var uri = EmbeddedWebView?.Source?.ToString() ?? BrowserAddress.Text;
+ BrowserAddress.Text = uri;
+ var btn = this.FindName("BrowserAddAuctionButton") as Button;
+ if (btn != null)
+ btn.IsEnabled = IsValidAuctionUrl(uri);
+ }
+ catch { }
+ }
+
+ private void EmbeddedWebView_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ try
+ {
+ e.Handled = true;
+ }
+ catch { }
+ }
+
+ private void CoreWebView2_ContextMenuRequested(object? sender, CoreWebView2ContextMenuRequestedEventArgs e)
+ {
+ try
+ {
+ // Prevent default native menu
+ e.Handled = true;
+
+ var target = e.ContextMenuTarget;
+ string? link = null;
+ try
+ {
+ link = target?.LinkUri;
+ if (string.IsNullOrEmpty(link)) link = target?.PageUri;
+ }
+ catch { }
+
+ // Show WPF ContextMenu on UI thread
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ try
+ {
+ var menu = new ContextMenu();
+
+ var canAdd = !string.IsNullOrEmpty(link) || IsValidAuctionUrl(EmbeddedWebView?.Source?.ToString() ?? BrowserAddress.Text);
+
+ var addItem = new MenuItem { Header = "Aggiungi Asta", IsEnabled = canAdd };
+ addItem.Click += async (s, args) =>
+ {
+ try
+ {
+ string? urlToAdd = link;
+ if (string.IsNullOrEmpty(urlToAdd))
+ urlToAdd = EmbeddedWebView?.Source?.ToString() ?? BrowserAddress.Text;
+
+ if (!string.IsNullOrEmpty(urlToAdd))
+ {
+ await AddAuctionFromUrl(urlToAdd);
+ }
+ }
+ catch (Exception ex)
+ {
+ Log($"[ERRORE] Aggiungi Asta dal menu: {ex.Message}");
+ }
+ };
+
+ menu.Items.Add(addItem);
+
+ var copyLink = new MenuItem { Header = "Copia link", IsEnabled = !string.IsNullOrEmpty(link) };
+ copyLink.Click += (s, args) =>
+ {
+ try { if (!string.IsNullOrEmpty(link)) Clipboard.SetText(link); }
+ catch { }
+ };
+ menu.Items.Add(copyLink);
+
+ menu.Placement = System.Windows.Controls.Primitives.PlacementMode.MousePoint;
+ menu.IsOpen = true;
+ }
+ catch { }
+ }));
+ }
+ catch { }
+ }
+ }
+}
diff --git a/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Export.cs b/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Export.cs
new file mode 100644
index 0000000..5c2a2b2
--- /dev/null
+++ b/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Export.cs
@@ -0,0 +1,349 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Xml.Linq;
+using AutoBidder.Utilities;
+
+namespace AutoBidder
+{
+ ///
+ /// Export functionality event handlers
+ ///
+ public partial class MainWindow
+ {
+ private CancellationTokenSource? _exportCts;
+
+ private void LoadExportSettings()
+ {
+ try
+ {
+ var s = SettingsManager.Load();
+ if (s != null)
+ {
+ ExportPathTextBox.Text = s.ExportPath ?? string.Empty;
+ if (!string.IsNullOrEmpty(s.LastExportExt))
+ {
+ var ext = s.LastExportExt.ToLowerInvariant();
+ if (ext == ".json") ExtJson.IsChecked = true;
+ else if (ext == ".xml") ExtXml.IsChecked = true;
+ else ExtCsv.IsChecked = true;
+ }
+ else
+ {
+ ExtCsv.IsChecked = true;
+ }
+
+ try { var cbOpen = this.FindName("ExportOpenToolbar") as System.Windows.Controls.CheckBox; if (cbOpen != null) cbOpen.IsChecked = s.ExportOpen; } catch { }
+ try { var cbClosed = this.FindName("ExportClosedToolbar") as System.Windows.Controls.CheckBox; if (cbClosed != null) cbClosed.IsChecked = s.ExportClosed; } catch { }
+ try { var cbUnknown = this.FindName("ExportUnknownToolbar") as System.Windows.Controls.CheckBox; if (cbUnknown != null) cbUnknown.IsChecked = s.ExportUnknown; } catch { }
+
+ try { IncludeUsedBids.IsChecked = s.IncludeOnlyUsedBids; } catch { }
+ try { IncludeLogs.IsChecked = s.IncludeLogs; } catch { }
+ try { IncludeUserBids.IsChecked = s.IncludeUserBids; } catch { }
+ try { IncludeMetadata.IsChecked = s.IncludeMetadata; } catch { }
+ try { RemoveAfterExport.IsChecked = s.RemoveAfterExport; } catch { }
+ try { OverwriteExisting.IsChecked = s.OverwriteExisting; } catch { }
+ }
+ }
+ catch { }
+ // Note: Progress bar rimosso con refactoring Statistics
+ }
+
+ private async void ExportAllButton_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ var settings = SettingsManager.Load();
+ string ext = ExtJson.IsChecked == true ? ".json" : ExtXml.IsChecked == true ? ".xml" : ".csv";
+ var dlg = new Microsoft.Win32.SaveFileDialog() { FileName = "auctions_export" + ext, Filter = "CSV files|*.csv|JSON files|*.json|XML files|*.xml|All files|*.*" };
+ if (dlg.ShowDialog(this) != true) return;
+ var path = dlg.FileName;
+
+ var all = _auctionMonitor.GetAuctions();
+ var includeOpen = (this.FindName("ExportOpenToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
+ var includeClosed = (this.FindName("ExportClosedToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
+ var includeUnknown = (this.FindName("ExportUnknownToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
+
+ var selection = all.Where(a =>
+ (includeOpen && a.IsActive) ||
+ (includeClosed && !a.IsActive) ||
+ (includeUnknown && ((a.BidHistory == null || a.BidHistory.Count == 0) && (a.BidderStats == null || a.BidderStats.Count == 0)))
+ ).ToList();
+
+ if (selection.Count == 0)
+ {
+ MessageBox.Show(this, "Nessuna asta da esportare.", "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Information);
+ return;
+ }
+
+ Log("[INFO] Esportazione in corso...", LogLevel.Info);
+
+ await Task.Run(() =>
+ {
+ if (path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
+ {
+ var json = System.Text.Json.JsonSerializer.Serialize(selection, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
+ File.WriteAllText(path, json, Encoding.UTF8);
+ }
+ else if (path.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
+ {
+ var doc = new XDocument(new XElement("Auctions",
+ from a in selection
+ select new XElement("Auction",
+ new XElement("AuctionId", a.AuctionId),
+ new XElement("Name", a.Name),
+ new XElement("OriginalUrl", a.OriginalUrl ?? string.Empty)
+ )
+ ));
+ doc.Save(path);
+ }
+ else
+ {
+ CsvExporter.ExportAllAuctions(selection, path);
+ }
+ });
+
+ try { ExportPreferences.SaveLastExportExtension(Path.GetExtension(path)); } catch { }
+
+ MessageBox.Show(this, "Esportazione completata.", "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Information);
+ Log($"[EXPORT] Aste esportate -> {path}", LogLevel.Success);
+ }
+ catch (Exception ex)
+ {
+ Log($"[ERRORE] Esportazione massiva: {ex.Message}", LogLevel.Error);
+ MessageBox.Show(this, "Errore durante esportazione: " + ex.Message, "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ private async void ExportToolbarButton_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ var settings = SettingsManager.Load();
+ var chosenExt = ExtJson.IsChecked == true ? ".json" : ExtXml.IsChecked == true ? ".xml" : ".csv";
+
+ var includeOpen = (this.FindName("ExportOpenToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
+ var includeClosed = (this.FindName("ExportClosedToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
+ var includeUnknown = (this.FindName("ExportUnknownToolbar") as System.Windows.Controls.CheckBox)?.IsChecked == true;
+
+ var all = _auctionMonitor.GetAuctions();
+ var selection = all.Where(a =>
+ (includeOpen && a.IsActive) ||
+ (includeClosed && !a.IsActive) ||
+ (includeUnknown && ((a.BidHistory == null || a.BidHistory.Count == 0) && (a.BidderStats == null || a.BidderStats.Count == 0)))
+ ).ToList();
+
+ if (selection.Count == 0)
+ {
+ MessageBox.Show(this, "Nessuna asta da esportare.", "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Information);
+ return;
+ }
+
+ string folder;
+ if (!string.IsNullOrWhiteSpace(settings?.ExportPath) && Directory.Exists(settings.ExportPath))
+ {
+ folder = settings.ExportPath!;
+ }
+ else
+ {
+ MessageBox.Show(this, "Percorso export non configurato o non valido.\nConfigura il percorso nelle Impostazioni.", "Percorso Export", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+
+ var confirm = MessageBox.Show(this, $"Esportare {selection.Count} asta/e in:\n{folder}\n\nFormato: {chosenExt.ToUpperInvariant()}\n(Un file separato per ogni asta)", "Conferma Esportazione", MessageBoxButton.YesNo, MessageBoxImage.Question);
+ if (confirm != MessageBoxResult.Yes) return;
+
+ Log("[INFO] Esportazione in corso...", LogLevel.Info);
+
+ int exported = 0;
+ int skipped = 0;
+
+ await Task.Run(() =>
+ {
+ foreach (var a in selection)
+ {
+ try
+ {
+ var filename = $"auction_{a.AuctionId}{chosenExt}";
+ var path = Path.Combine(folder, filename);
+
+ if (File.Exists(path) && settings != null && settings.OverwriteExisting != true)
+ {
+ skipped++;
+ Log($"[SKIP] File gi esistente: {filename}", LogLevel.Warn);
+ continue;
+ }
+
+ if (chosenExt.Equals(".json", StringComparison.OrdinalIgnoreCase))
+ {
+ var obj = new
+ {
+ AuctionId = a.AuctionId,
+ Name = a.Name,
+ OriginalUrl = a.OriginalUrl,
+ MinPrice = a.MinPrice,
+ MaxPrice = a.MaxPrice,
+ TimerClick = a.TimerClick,
+ DelayMs = a.DelayMs,
+ IsActive = a.IsActive,
+ IsPaused = a.IsPaused,
+ BidHistory = a.BidHistory,
+ Bidders = a.BidderStats.Values.ToList(),
+ AuctionLog = a.AuctionLog.ToList()
+ };
+ var json = System.Text.Json.JsonSerializer.Serialize(obj, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
+ File.WriteAllText(path, json, Encoding.UTF8);
+ }
+ else if (chosenExt.Equals(".xml", StringComparison.OrdinalIgnoreCase))
+ {
+ var doc = new XDocument(
+ new XElement("AuctionExport",
+ new XElement("Metadata",
+ new XElement("AuctionId", a.AuctionId),
+ new XElement("Name", a.Name ?? string.Empty),
+ new XElement("OriginalUrl", a.OriginalUrl ?? string.Empty),
+ new XElement("MinPrice", a.MinPrice),
+ new XElement("MaxPrice", a.MaxPrice),
+ new XElement("TimerClick", a.TimerClick),
+ new XElement("DelayMs", a.DelayMs),
+ new XElement("IsActive", a.IsActive),
+ new XElement("IsPaused", a.IsPaused)
+ ),
+ new XElement("FinalPrice", a.BidHistory?.LastOrDefault()?.Price.ToString("F2", CultureInfo.InvariantCulture) ?? string.Empty),
+ new XElement("TotalBids", a.BidHistory?.Count ?? 0),
+ new XElement("Bidders",
+ from b in a.BidderStats.Values.Where(x => x.BidCount > 0)
+ select new XElement("Bidder",
+ new XAttribute("Username", b.Username ?? string.Empty),
+ new XAttribute("BidCount", b.BidCount),
+ new XElement("LastBidTime", b.LastBidTimeDisplay ?? string.Empty)
+ )
+ ),
+ new XElement("AuctionLog",
+ from l in a.AuctionLog
+ select new XElement("Entry", l)
+ ),
+ new XElement("BidHistory",
+ from bh in a.BidHistory
+ select new XElement("Entry",
+ new XElement("Timestamp", bh.Timestamp.ToString("o")),
+ new XElement("EventType", bh.EventType),
+ new XElement("Bidder", bh.Bidder),
+ new XElement("Price", bh.Price.ToString("F2", CultureInfo.InvariantCulture)),
+ new XElement("Timer", bh.Timer.ToString("F2", CultureInfo.InvariantCulture)),
+ new XElement("LatencyMs", bh.LatencyMs),
+ new XElement("Success", bh.Success),
+ new XElement("Notes", bh.Notes)
+ )
+ )
+ )
+ );
+ doc.Save(path);
+ }
+ else
+ {
+ using var sw = new StreamWriter(path, false, Encoding.UTF8);
+ sw.WriteLine("Field,Value");
+ sw.WriteLine($"AuctionId,{a.AuctionId}");
+ sw.WriteLine($"Name,\"{EscapeCsv(a.Name)}\"");
+ sw.WriteLine($"OriginalUrl,\"{EscapeCsv(a.OriginalUrl)}\"");
+ sw.WriteLine($"MinPrice,{a.MinPrice}");
+ sw.WriteLine($"MaxPrice,{a.MaxPrice}");
+ sw.WriteLine($"TimerClick,{a.TimerClick}");
+ sw.WriteLine($"DelayMs,{a.DelayMs}");
+ sw.WriteLine($"IsActive,{a.IsActive}");
+ sw.WriteLine($"IsPaused,{a.IsPaused}");
+ sw.WriteLine();
+ sw.WriteLine("--Auction Log--");
+ sw.WriteLine("Message");
+ foreach (var l in a.AuctionLog)
+ {
+ sw.WriteLine($"\"{EscapeCsv(l)}\"");
+ }
+ sw.WriteLine();
+ sw.WriteLine("--Bidders--");
+ sw.WriteLine("Username,BidCount,LastBidTime");
+ foreach (var b in a.BidderStats.Values)
+ {
+ sw.WriteLine($"\"{EscapeCsv(b.Username)}\",{b.BidCount},\"{EscapeCsv(b.LastBidTimeDisplay)}\"");
+ }
+ sw.WriteLine();
+ sw.WriteLine("--BidHistory--");
+ sw.WriteLine("Timestamp,EventType,Bidder,Price,Timer,LatencyMs,Success,Notes");
+ foreach (var bh in a.BidHistory)
+ {
+ sw.WriteLine($"\"{EscapeCsv(bh.Timestamp.ToString("o"))}\",{bh.EventType},\"{EscapeCsv(bh.Bidder)}\",{bh.Price:F2},{bh.Timer:F2},{bh.LatencyMs},{bh.Success},\"{EscapeCsv(bh.Notes)}\"");
+ }
+ }
+
+ exported++;
+ Log($"[EXPORT] Asta esportata -> {path}", LogLevel.Success);
+ }
+ catch (Exception ex)
+ {
+ Log($"[ERRORE] Export asta {a.AuctionId}: {ex.Message}", LogLevel.Error);
+ skipped++;
+ }
+ }
+ });
+
+ try { ExportPreferences.SaveLastExportExtension(chosenExt); } catch { }
+
+ MessageBox.Show(this, $"Esportazione completata.\n\nEsportate: {exported}\nIgnorate: {skipped}\nPercorso: {folder}", "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Information);
+ Log($"[EXPORT] Completato: {exported} esportate, {skipped} ignorate -> {folder}", LogLevel.Success);
+
+ if ((this.FindName("RemoveAfterExport") as System.Windows.Controls.CheckBox)?.IsChecked == true && selection.Count > 0)
+ {
+ Dispatcher.Invoke(() =>
+ {
+ foreach (var a in selection)
+ {
+ try
+ {
+ _auctionMonitor.RemoveAuction(a.AuctionId);
+ var vm = _auctionViewModels.FirstOrDefault(x => x.AuctionId == a.AuctionId);
+ if (vm != null)
+ {
+ _auctionViewModels.Remove(vm);
+ }
+ }
+ catch (Exception ex)
+ {
+ Log($"[WARN] Errore rimozione asta {a.AuctionId}: {ex.Message}", LogLevel.Warn);
+ }
+ }
+
+ SaveAuctions();
+ UpdateTotalCount();
+ });
+ }
+ }
+ catch (Exception ex)
+ {
+ Log($"[ERRORE] Esportazione toolbar: {ex.Message}", LogLevel.Error);
+ MessageBox.Show(this, "Errore durante esportazione: " + ex.Message, "Esporta Aste", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ private void ExportBrowseButton_Click(object sender, RoutedEventArgs e)
+ {
+ var dlg = new Microsoft.Win32.SaveFileDialog() { FileName = "export.csv", Filter = "CSV files|*.csv|All files|*.*" };
+ if (dlg.ShowDialog(this) == true)
+ {
+ ExportPathTextBox.Text = Path.GetDirectoryName(dlg.FileName) ?? string.Empty;
+ }
+ }
+
+ private string EscapeCsv(string? value)
+ {
+ if (string.IsNullOrEmpty(value)) return string.Empty;
+ return value.Replace("\"", "\"\"");
+ }
+ }
+}
diff --git a/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Settings.cs b/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Settings.cs
new file mode 100644
index 0000000..cbf7ee9
--- /dev/null
+++ b/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Settings.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Linq;
+using System.Windows;
+using AutoBidder.Utilities;
+
+namespace AutoBidder
+{
+ ///
+ /// Settings and configuration event handlers
+ ///
+ public partial class MainWindow
+ {
+ private void SaveCookieButton_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ var cookie = SettingsCookieTextBox.Text?.Trim();
+ if (string.IsNullOrEmpty(cookie))
+ {
+ MessageBox.Show(this, "Inserisci un cookie valido", "Errore", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+
+ _auctionMonitor.InitializeSessionWithCookie(cookie, string.Empty);
+ var success = _auctionMonitor.UpdateUserInfoAsync().GetAwaiter().GetResult();
+ var session = _auctionMonitor.GetSession();
+
+ if (success && session != null)
+ {
+ Services.SessionManager.SaveSession(session);
+ SetUserBanner(session.Username ?? string.Empty, session.RemainingBids);
+ UsernameText.Text = session.Username ?? string.Empty;
+ StartButton.IsEnabled = true;
+ Log($"[OK] Sessione salvata per: {session.Username}");
+ MessageBox.Show(this, $"Cookie valido!\nUtente: {session.Username}\nPuntate disponibili: {session.RemainingBids}", "Sessione Salvata", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ else
+ {
+ Log($"[WARN] Cookie non valido o scaduto", LogLevel.Warn);
+ MessageBox.Show(this, "Cookie non valido o scaduto.\nVerifica che il cookie sia corretto.", "Errore Cookie", MessageBoxButton.OK, MessageBoxImage.Warning);
+ }
+ }
+ catch (Exception ex)
+ {
+ Log($"[ERRORE] Salvataggio cookie: {ex.Message}", LogLevel.Error);
+ MessageBox.Show(this, "Errore durante salvataggio cookie: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ private void ImportCookieFromBrowserButton_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ if (EmbeddedWebView?.CoreWebView2 == null)
+ {
+ MessageBox.Show(this, "Browser non inizializzato", "Errore", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+
+ var cookies = EmbeddedWebView.CoreWebView2.CookieManager.GetCookiesAsync("https://it.bidoo.com").GetAwaiter().GetResult();
+ var stattrb = cookies.FirstOrDefault(c => c.Name == "__stattrb");
+
+ if (stattrb != null)
+ {
+ SettingsCookieTextBox.Text = stattrb.Value;
+ Log("[OK] Cookie importato dal browser");
+ MessageBox.Show(this, "Cookie importato con successo!\nClicca 'Salva Cookie' per confermare.", "Importa Cookie", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ else
+ {
+ Log("[WARN] Cookie __stattrb non trovato nel browser", LogLevel.Warn);
+ MessageBox.Show(this, "Cookie __stattrb non trovato.\nAssicurati di aver effettuato il login su bidoo.com nella scheda Browser.", "Cookie Non Trovato", MessageBoxButton.OK, MessageBoxImage.Warning);
+ }
+ }
+ catch (Exception ex)
+ {
+ Log($"[ERRORE] Importazione cookie: {ex.Message}", LogLevel.Error);
+ MessageBox.Show(this, "Errore durante importazione cookie: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ private void CancelCookieButton_Click(object sender, RoutedEventArgs e)
+ {
+ SettingsCookieTextBox.Text = string.Empty;
+ }
+
+ private void SaveSettingsButton_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ var lastExt = ExtJson.IsChecked == true ? ".json" : ExtXml.IsChecked == true ? ".xml" : ".csv";
+ var scope = "All";
+ var cbClosed = this.FindName("ExportClosedToolbar") as System.Windows.Controls.CheckBox;
+ var cbUnknown = this.FindName("ExportUnknownToolbar") as System.Windows.Controls.CheckBox;
+ var cbOpen = this.FindName("ExportOpenToolbar") as System.Windows.Controls.CheckBox;
+
+ if (cbClosed != null && cbClosed.IsChecked == true) scope = "Closed";
+ else if (cbUnknown != null && cbUnknown.IsChecked == true) scope = "Unknown";
+ else if (cbOpen != null && cbOpen.IsChecked == true) scope = "Open";
+
+ var s = new AppSettings()
+ {
+ ExportPath = ExportPathTextBox.Text,
+ LastExportExt = lastExt,
+ ExportScope = scope,
+ IncludeOnlyUsedBids = IncludeUsedBids.IsChecked == true,
+ IncludeLogs = IncludeLogs.IsChecked == true,
+ IncludeUserBids = IncludeUserBids.IsChecked == true
+ };
+
+ SettingsManager.Save(s);
+ ExportPreferences.SaveLastExportExtension(s.LastExportExt);
+ MessageBox.Show(this, "Impostazioni salvate.", "Salva", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(this, "Errore salvataggio impostazioni: " + ex.Message, "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ private void CancelSettingsButton_Click(object sender, RoutedEventArgs e)
+ {
+ LoadExportSettings();
+ }
+
+ private void SaveDefaultsButton_Click(object sender, RoutedEventArgs e)
+ {
+ MessageBox.Show(this, "Funzionalit non ancora implementata", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+
+ private void CancelDefaultsButton_Click(object sender, RoutedEventArgs e)
+ {
+ MessageBox.Show(this, "Funzionalit non ancora implementata", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ }
+}
diff --git a/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Stats.cs b/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Stats.cs
new file mode 100644
index 0000000..59b6cc8
--- /dev/null
+++ b/Mimante/Core/EventHandlers/MainWindow.EventHandlers.Stats.cs
@@ -0,0 +1,271 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Xml.Linq;
+using AutoBidder.Models;
+using AutoBidder.Utilities;
+using Microsoft.EntityFrameworkCore;
+
+namespace AutoBidder
+{
+ ///
+ /// Statistics and closed auctions event handlers
+ /// NOTA: Funzionalit statistiche temporaneamente disabilitate - in sviluppo
+ ///
+ public partial class MainWindow
+ {
+ private void ExportStatsButton_Click(object sender, RoutedEventArgs e)
+ {
+ MessageBox.Show(this, "Funzionalit statistiche in sviluppo", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+
+ private async void LoadClosedAuctionsButton_Click(object sender, RoutedEventArgs e)
+ {
+ MessageBox.Show(this, "Funzionalit statistiche in sviluppo", "Info", MessageBoxButton.OK, MessageBoxImage.Information);
+
+ /* CODICE TEMPORANEAMENTE DISABILITATO - Statistiche in sviluppo
+ try
+ {
+ StatsStatusText.Text = "Avvio caricamento statistiche...";
+ var settings = Utilities.SettingsManager.Load();
+ if (settings == null || string.IsNullOrWhiteSpace(settings.ExportPath) || !Directory.Exists(settings.ExportPath))
+ {
+ MessageBox.Show(this, "Percorso export non configurato o non valido. Configuralo nelle impostazioni.", "Carica Statistiche", MessageBoxButton.OK, MessageBoxImage.Warning);
+ StatsStatusText.Text = "Percorso export non valido";
+ return;
+ }
+
+ ExportProgressBar.Visibility = Visibility.Visible;
+ ExportProgressText.Visibility = Visibility.Visible;
+ ExportProgressText.Text = "Caricamento statistiche...";
+
+ var folder = settings.ExportPath!;
+ var files = Directory.GetFiles(folder, "auction_*.*");
+ if (files.Length == 0)
+ {
+ MessageBox.Show(this, "Nessun file di aste trovato nella cartella di export.", "Carica Statistiche", MessageBoxButton.OK, MessageBoxImage.Information);
+ StatsStatusText.Text = "Nessun file trovato";
+ ExportProgressBar.Visibility = Visibility.Collapsed;
+ ExportProgressText.Visibility = Visibility.Collapsed;
+ return;
+ }
+
+ var aggregated = new Dictionary>(StringComparer.OrdinalIgnoreCase);
+
+ await Task.Run(() =>
+ {
+ foreach (var f in files)
+ {
+ try
+ {
+ var ext = Path.GetExtension(f).ToLowerInvariant();
+ if (ext == ".json")
+ {
+ var txt = File.ReadAllText(f, Encoding.UTF8);
+ try
+ {
+ var rec = System.Text.Json.JsonSerializer.Deserialize(txt);
+ if (rec != null)
+ {
+ var key = (rec.ProductName ?? ExtractProductFromFilename(f) ?? "").Trim();
+ if (!aggregated.ContainsKey(key)) aggregated[key] = new List();
+ aggregated[key].Add(rec);
+ continue;
+ }
+ }
+ catch { }
+
+ try
+ {
+ var arr = System.Text.Json.JsonSerializer.Deserialize>(txt);
+ if (arr != null)
+ {
+ foreach (var r in arr)
+ {
+ var key = (r.ProductName ?? ExtractProductFromFilename(f) ?? "").Trim();
+ if (!aggregated.ContainsKey(key)) aggregated[key] = new List();
+ aggregated[key].Add(r);
+ }
+ }
+ }
+ catch { }
+ }
+ else if (ext == ".xml")
+ {
+ try
+ {
+ var doc = XDocument.Load(f);
+ var auctionElems = doc.Descendants("Auction");
+ if (!auctionElems.Any()) auctionElems = doc.Descendants("AuctionExport");
+ foreach (var n in auctionElems)
+ {
+ var name = n.Descendants("Name").FirstOrDefault()?.Value ?? n.Descendants("ProductName").FirstOrDefault()?.Value;
+ double d = 0; double.TryParse(n.Descendants("FinalPrice").FirstOrDefault()?.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out d);
+ int bids = 0; int.TryParse(n.Descendants("TotalBids").FirstOrDefault()?.Value, out bids);
+ var winner = n.Descendants("Winner").FirstOrDefault()?.Value ?? string.Empty;
+ var url = n.Descendants("OriginalUrl").FirstOrDefault()?.Value ?? string.Empty;
+ var rec = new ClosedAuctionRecord { ProductName = name, FinalPrice = d == 0 ? null : (double?)d, Winner = winner, BidsUsed = bids, AuctionUrl = url };
+ var key = (rec.ProductName ?? ExtractProductFromFilename(f) ?? "").Trim();
+ if (!aggregated.ContainsKey(key)) aggregated[key] = new List();
+ aggregated[key].Add(rec);
+ }
+ }
+ catch { }
+ }
+ else // CSV or text
+ {
+ try
+ {
+ var lines = File.ReadAllLines(f, Encoding.UTF8);
+ string product = ExtractProductFromFilename(f) ?? "";
+ double? price = null; int? bids = null; string winner = string.Empty; string url = string.Empty;
+ foreach (var l in lines)
+ {
+ var line = l.Trim();
+ if (line.StartsWith("Name,") || line.StartsWith("ProductName,"))
+ {
+ var parts = line.Split(',', 2);
+ if (parts.Length == 2) product = parts[1].Trim('"');
+ }
+ else if (line.StartsWith("FinalPrice", StringComparison.OrdinalIgnoreCase) || line.StartsWith("Price,"))
+ {
+ var parts = line.Split(',', 2);
+ if (parts.Length == 2 && double.TryParse(parts[1].Trim('"').Replace('', ' ').Trim(), NumberStyles.Any, CultureInfo.InvariantCulture, out var p)) price = p;
+ }
+ else if (line.StartsWith("TotalBids", StringComparison.OrdinalIgnoreCase) || line.StartsWith("BidsUsed", StringComparison.OrdinalIgnoreCase))
+ {
+ var parts = line.Split(',', 2);
+ if (parts.Length == 2 && int.TryParse(parts[1].Trim('"'), out var b)) bids = b;
+ }
+ else if (line.StartsWith("Winner", StringComparison.OrdinalIgnoreCase))
+ {
+ var parts = line.Split(',', 2);
+ if (parts.Length == 2) winner = parts[1].Trim('"');
+ }
+ else if (line.StartsWith("OriginalUrl", StringComparison.OrdinalIgnoreCase) || line.StartsWith("AuctionUrl", StringComparison.OrdinalIgnoreCase))
+ {
+ var parts = line.Split(',', 2);
+ if (parts.Length == 2) url = parts[1].Trim('"');
+ }
+ }
+ var rec = new ClosedAuctionRecord { ProductName = product, FinalPrice = price, BidsUsed = bids, Winner = winner, AuctionUrl = url };
+ var key = (rec.ProductName ?? ExtractProductFromFilename(f) ?? "").Trim();
+ if (!aggregated.ContainsKey(key)) aggregated[key] = new List();
+ aggregated[key].Add(rec);
+ }
+ catch { }
+ }
+ }
+ catch { }
+ }
+ });
+
+ var stats = new List