Aggiunta scheda "Storia Puntate" con aggiornamento live

Introdotta una nuova scheda "Storia Puntate" nel pannello
dell'asta selezionata, che mostra la cronologia delle ultime
puntate in tempo reale. La scheda utilizza un `TabControl`
con due `TabItem`: uno per gli utenti e uno per la storia
delle puntate.

- Creata la classe `BidHistoryEntry` per rappresentare una
  singola puntata, con proprietà come `Price`, `BidType`,
  `Timestamp`, e calcoli formattati.
- Aggiunte proprietà `RecentBids` in `AuctionInfo` e
  `RecentBidsHistory` in `AuctionState` per gestire i dati
  della cronologia.
- Modificato il parsing API in `BidooApiClient` per includere
  la cronologia delle puntate.
- Aggiornato il monitor delle aste (`AuctionMonitor.cs`) per
  sincronizzare i dati della cronologia con il backend.
- Aggiunta la proprietà `BidHistoryEntries` in
  `AuctionViewModel` per il binding della griglia.
- Modificata la UI (`AuctionMonitorControl.xaml`) per
  includere la nuova scheda e personalizzare gli stili.
- Aggiornata la logica di aggiornamento UI in
  `MainWindow.xaml.cs` per gestire i dati della cronologia.
- Documentata la funzionalità in `FEATURE_BID_HISTORY_TAB.md`.
- Aggiunto uno screenshot (`Screenshot 2025-11-25 113552.png`).

Questa funzionalità migliora la trasparenza e fornisce agli
utenti informazioni dettagliate sulle attività recenti,
aiutandoli a prendere decisioni strategiche durante le aste.
This commit is contained in:
2025-11-25 14:35:09 +01:00
parent 6795282993
commit d99b5ec923
11 changed files with 960 additions and 58 deletions
+6
View File
@@ -248,6 +248,12 @@ namespace AutoBidder.Services
}
auction.PollingLatencyMs = state.PollingLatencyMs;
// ? NUOVO: Aggiorna storia puntate da API
if (state.RecentBidsHistory != null && state.RecentBidsHistory.Count > 0)
{
auction.RecentBids = state.RecentBidsHistory;
}
if (state.Status == AuctionStatus.EndedWon ||
state.Status == AuctionStatus.EndedLost ||
+83 -4
View File
@@ -273,8 +273,12 @@ namespace AutoBidder.Services
return null;
}
var auctionData = mainData.Substring(bracketStart + 1, bracketEnd - bracketStart - 1);
var firstSeparator = auctionData.IndexOfAny(new[] { '|', ',' });
var coreData = firstSeparator > 0 ? auctionData.Substring(0, firstSeparator) : auctionData;
// Separa dati principali dalla storia puntate
var pipeIndex = auctionData.IndexOf('|');
var coreData = pipeIndex > 0 ? auctionData.Substring(0, pipeIndex) : auctionData;
var historyData = pipeIndex > 0 ? auctionData.Substring(pipeIndex + 1) : "";
var fields = coreData.Split(';');
if (fields.Length < 5)
{
@@ -304,9 +308,16 @@ namespace AutoBidder.Services
state.LastBidder = fields[4].Trim();
state.IsMyBid = !string.IsNullOrEmpty(_session.Username) &&
state.LastBidder.Equals(_session.Username, StringComparison.OrdinalIgnoreCase);
// ✅ NUOVO: Parse storia puntate
// Formato: 42;fedekikka2323;3,42;fedekikka2323;1764068204;3|41;chamorro1984;1764068194;3|...
if (!string.IsNullOrEmpty(historyData))
{
state.RecentBidsHistory = ParseBidHistory(historyData, fields[3]);
}
state.ParsingSuccess = true;
// Log only summary on success
Log($"[PARSE SUCCESS] Timer: {state.Timer:F2}s, Price: €{state.Price:F2}, Bidder: {state.LastBidder}, Status: {state.Status}", auctionId);
Log($"[PARSE SUCCESS] Timer: {state.Timer:F2}s, Price: €{state.Price:F2}, Bidder: {state.LastBidder}, Status: {state.Status}, History: {state.RecentBidsHistory?.Count ?? 0} bids", auctionId);
return state;
}
catch (Exception ex)
@@ -315,6 +326,74 @@ namespace AutoBidder.Services
return null;
}
}
/// <summary>
/// Parse la storia delle ultime puntate dalla risposta API
/// Formato: 41;chamorro1984;1764068194;3|40;fedekikka2323;1764068184;3|...
/// </summary>
private List<BidHistoryEntry>? ParseBidHistory(string historyData, string currentPriceStr)
{
try
{
var entries = new List<BidHistoryEntry>();
// Il primo record è spesso il prezzo corrente con dati duplicati, lo saltiamo
var records = historyData.Split('|');
// Parsing prezzo corrente per calcolare i prezzi precedenti
if (!int.TryParse(currentPriceStr, out var currentPriceIndex))
return null;
foreach (var record in records)
{
if (string.IsNullOrWhiteSpace(record))
continue;
var parts = record.Split(';');
if (parts.Length < 4)
continue;
// Formato: priceIndex;username;timestamp;bidType
// Es: 41;chamorro1984;1764068194;3
if (!int.TryParse(parts[0], out var priceIndex))
continue;
var username = parts[1].Trim();
if (!long.TryParse(parts[2], out var timestamp))
continue;
var bidTypeCode = parts.Length > 3 ? parts[3].Trim() : "0";
// Determina tipo puntata: 3 = Auto, 1 = Manuale
string bidType = bidTypeCode switch
{
"3" => "Auto",
"1" => "Manuale",
_ => "Auto"
};
var entry = new BidHistoryEntry
{
Price = priceIndex * 0.01m,
BidType = bidType,
Timestamp = timestamp,
Username = username,
IsMyBid = !string.IsNullOrEmpty(_session.Username) &&
username.Equals(_session.Username, StringComparison.OrdinalIgnoreCase)
};
entries.Add(entry);
}
return entries.Count > 0 ? entries : null;
}
catch
{
return null;
}
}
public async Task<bool> UpdateUserInfoAsync()
{