Add layered price lookup and category auto-fill

- PriceLookupService: eBay live data → saved listing history → AI estimate,
  each result labelled by source so the user knows how reliable it is
- Revalue row: new "Check eBay" button fetches suggestion and pre-populates
  the price field; shows source label beneath (or "No suggestion available")
- Category auto-fill: AutoFillCategoryAsync takes the top eBay category
  suggestion and fills the field automatically after photo analysis or AI
  title generation; dropdown stays visible so user can override

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Peter Foster
2026-04-15 02:44:12 +01:00
parent da0efc1374
commit f65521b9ab
5 changed files with 340 additions and 69 deletions

View File

@@ -18,6 +18,7 @@ public partial class SingleItemView : UserControl
private ListingDraft _draft = new();
private System.Threading.CancellationTokenSource? _categoryCts;
private bool _suppressCategoryLookup;
private string _suggestedPriceValue = "";
// Photo drag-reorder
@@ -50,7 +51,7 @@ public partial class SingleItemView : UserControl
}
/// <summary>Pre-fills the form from a Photo Analysis result.</summary>
public void PopulateFromAnalysis(PhotoAnalysisResult result, IReadOnlyList<string> imagePaths, decimal price)
public async void PopulateFromAnalysis(PhotoAnalysisResult result, IReadOnlyList<string> imagePaths, decimal price)
{
// Q6: reset form directly — calling NewListing_Click shows a confirmation dialog which
// is unexpected when arriving here automatically from the Photo Analysis tab.
@@ -71,9 +72,9 @@ public partial class SingleItemView : UserControl
TitleBox.Text = result.Title;
DescriptionBox.Text = result.Description;
PriceBox.Value = (double)price;
CategoryBox.Text = result.CategoryKeyword;
_draft.CategoryName = result.CategoryKeyword;
// Auto-fill the top eBay category from the analysis keyword; user can override
await AutoFillCategoryAsync(result.CategoryKeyword);
// Q1: load all photos from analysis
var validPaths = imagePaths.Where(p => !string.IsNullOrEmpty(p) && File.Exists(p)).ToArray();
@@ -117,6 +118,10 @@ public partial class SingleItemView : UserControl
{
var title = await _aiService.GenerateTitleAsync(current, condition);
TitleBox.Text = title.Trim().TrimEnd('.').Trim('"');
// Auto-fill category from the generated title if not already set
if (string.IsNullOrWhiteSpace(_draft.CategoryId))
await AutoFillCategoryAsync(TitleBox.Text);
}
catch (Exception ex)
{
@@ -129,6 +134,8 @@ public partial class SingleItemView : UserControl
private async void CategoryBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (_suppressCategoryLookup) return;
_categoryCts?.Cancel();
_categoryCts?.Dispose();
_categoryCts = new System.Threading.CancellationTokenSource();
@@ -211,6 +218,38 @@ public partial class SingleItemView : UserControl
}
}
/// <summary>
/// Fetches the top eBay category suggestion for <paramref name="keyword"/> and auto-fills
/// the category fields. The suggestions list is shown so the user can override.
/// </summary>
private async Task AutoFillCategoryAsync(string keyword)
{
if (_categoryService == null || string.IsNullOrWhiteSpace(keyword)) return;
try
{
var suggestions = await _categoryService.GetCategorySuggestionsAsync(keyword);
if (suggestions.Count == 0) return;
var top = suggestions[0];
_suppressCategoryLookup = true;
try
{
_draft.CategoryId = top.CategoryId;
_draft.CategoryName = top.CategoryName;
CategoryBox.Text = top.CategoryName;
CategoryIdLabel.Text = $"ID: {top.CategoryId}";
}
finally { _suppressCategoryLookup = false; }
// Show the full list so user can see alternatives and override
CategorySuggestionsList.ItemsSource = suggestions;
CategorySuggestionsList.Visibility = suggestions.Count > 1
? Visibility.Visible : Visibility.Collapsed;
}
catch { /* non-critical — leave category blank if lookup fails */ }
}
// ---- Condition ----
private void ConditionBox_SelectionChanged(object sender, SelectionChangedEventArgs e)