Commit Graph

23 Commits

Author SHA1 Message Date
7507030f72 Task 3: add NewListingView with drop zone, thumbnail strip, and AI analyse flow 2026-04-16 01:37:53 +01:00
e9c5464df0 feat: add Post to eBay button and toast to SavedListingsView 2026-04-16 01:35:19 +01:00
e3827d97e8 feat: add CategoryId/Condition/Format/Postcode to SavedListing for draft posting 2026-04-16 01:30:14 +01:00
Peter Foster
edbce97a74 Add .gitattributes to normalise line endings
Fixes Windows git showing modified files after Linux commits.
LF stored in repo; CRLF on Windows checkout for all source files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 02:45:17 +01:00
Peter Foster
f65521b9ab 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>
2026-04-15 02:44:12 +01:00
Peter Foster
da0efc1374 Replace IE WebBrowser with WebView2 for eBay OAuth login
- Add Microsoft.Web.WebView2 package
- New EbayLoginWindow using Edge (WebView2) instead of IE — handles modern eBay login pages
- Intercept ThirdPartyAuthSucessFailure redirect to extract auth code
- Remove invalid sell.listing scope that caused immediate invalid_scope error
- Add detailed browser navigation logging for diagnostics

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 17:06:39 +01:00
Peter Foster
1ff9d3d78b Add eBay credentials, edit listings feature, fix category service token 2026-04-14 11:45:15 +01:00
Peter Foster
f4e7854297 Revert to Light.Indigo theme; restore original grey values for light bg
Dark theme experiment abandoned due to persistent contrast issues.
All Gray3/Gray4 muted text reverted to Gray5/Gray6 (darker = readable
on light background). Gray2 headings reverted to Gray4/Gray5.
App.xaml switched back to Light.Indigo.xaml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 03:21:10 +01:00
Peter Foster
56e0be83d2 Fix low-contrast price and date text in saved listings cards
Price text changed from Accent brush (~3.4:1) to ThemeForeground (white)
for WCAG AA compliance. Date text changed from Gray5 (~3.2:1) to Gray3.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 03:13:41 +01:00
Peter Foster
5cf7f1b8c6 Switch theme to Dark.Cobalt for readable accent colour
Indigo 500 (#3F51B5) is too dark on black backgrounds. Cobalt (#0050EF)
is a bright electric blue that meets contrast requirements on dark
surfaces without needing lighter fallback shades.

Replaced all hardcoded #9FA8DA (Indigo 200 workaround) with
{DynamicResource MahApps.Brushes.Accent} now that the accent is
naturally readable — section headings, price labels, detail labels,
listing URL link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 03:05:06 +01:00
Peter Foster
15726a4f18 Fix dark theme contrast across all views
All text using Gray5/Gray6 (too dark — fails WCAG AA) promoted to
Gray3/Gray4 as appropriate. SectionHeading styles using Accent brush
(Indigo 500, ~2.8:1 on dark bg) changed to #9FA8DA (Indigo 200, ~7:1).

MainWindow: overlay body text Gray5→Gray3, status bar text Gray5→Gray3.
PhotoAnalysisView: SectionHeading Accent→#9FA8DA; 13 text nodes updated
  from Gray5/Gray6 to Gray3/Gray4 (hints, labels, counters, live price
  status, price reasoning, range bar labels).
SingleItemView: SectionHeading Accent→#9FA8DA; title/desc counters,
  photo drop hint, listing URL all updated.
BulkImportView: empty state heading Gray4→Gray2; hint text Gray6→Gray4.
SavedListingsView: DetailLabel Accent→#9FA8DA; search icon, empty state
  headings, meta row text/icons all promoted from Gray4-6 to Gray2-4.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 03:02:05 +01:00
Peter Foster
bd59db724a Apply dark theme (Dark.Indigo) across all views
Switch MahApps.Metro base theme from Light.Indigo to Dark.Indigo in
App.xaml. Fix hardcoded light-specific colours in all five views:

- MainWindow: overlay backgrounds from hardcoded light-blue gradients
  to ThemeBackground; overlay text updated to ThemeForeground / Gray5
- SingleItemView: listing URL colour from hardcoded #1565C0 (invisible
  on dark) to Accent brush
- PhotoAnalysisView / BulkImportView / SavedListingsView: dynamic
  resource brushes already used; no structural changes needed

Semantic colours (green/red/amber indicators, AI gradient buttons,
white text on coloured backgrounds) left unchanged as they read
correctly on dark surfaces.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 02:54:01 +01:00
Peter Foster
48c6049dfc Track appsettings.json in git; remove from gitignore
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 02:48:56 +01:00
Peter Foster
ffba3ce1b6 Make appsettings.json optional; support appsettings.local.json override
App no longer crashes on startup when appsettings.json is absent (e.g.
fresh clone). Configuration is loaded from appsettings.json if present,
then overridden by appsettings.local.json if that exists. Both files
are gitignored to keep secrets out of the repo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 02:48:18 +01:00
Peter Foster
d3bdcc1061 Fix all listing blockers: real policy IDs, merchant location, shared HttpClient
Business policies: EnsurePoliciesAndLocationAsync fetches fulfillment,
payment and return policy IDs from the seller's eBay Account API on
first post and caches them for the session. Uses the first policy of
each type; gives a clear error pointing to My eBay → Business policies
if none are configured.

Merchant location: checks for existing locations via
GET /sell/inventory/v1/location; if none found, creates a 'home'
location using the seller's postcode. Location key is cached so the
check only runs once. Cache cleared on disconnect so it works
correctly after switching accounts.

CreateOfferAsync now sends real fulfillmentPolicyId / paymentPolicyId /
returnPolicyId instead of hardcoded policy name strings, and uses the
resolved merchantLocationKey instead of the hardcoded "home" string.

Removed BuildListingPolicies (inline shipping service codes no longer
needed; shipping is governed by the fulfillment policy).

Shared HttpClient: replaced BuildClient() (which returned a new
HttpClient per call) with a static _http client and MakeRequest()
helper that creates a pre-authorised HttpRequestMessage. UploadSinglePhotoAsync
likewise uses a static _photoHttp client and HttpRequestMessage instead
of new HttpClient() per photo.

Removed placeholder ExternalPictureURL from Trading API SOAP body.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 02:24:06 +01:00
Peter Foster
6efa5df2c6 Add drag-to-reorder photo ordering in New Listing tab
Photos can now be dragged to any position — cursor changes to a move
cursor to signal draggability, the drop target dims to show the
insertion point, and the list rebuilds immediately after the drop.

First photo gets a 'Cover' badge since eBay uses it as the gallery
hero image. All add/remove/clear operations now go through
RebuildPhotoThumbnails() so the panel always reflects the true order
in PhotoPaths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 19:52:37 +01:00
Peter Foster
426089fb3e Apply second code review fixes: robustness and correctness
- AiAssistantService: 90s HTTP timeout; RefineWithCorrections uses 29.99 example
  price and explicit "Never return 0" instruction; treat returned 0 as missing and
  keep original price instead
- EbayAuthService: shared static HttpClient for all auth/token calls; SemaphoreSlim
  double-checked locking on GetAppTokenAsync to prevent concurrent token fetches
- EbayPriceResearchService: 15s timeout; require ≥5 samples before suggesting;
  trim top/bottom 10% outliers; BEST_MATCH sort avoids spam/broken listing bias
- PhotoAnalysisView: spinner-show inside try (Issue 1); decimal→double casts use
  Math.Round to avoid 19.99→19.989... drift (Issue 6); Dispatcher.CheckAccess guard
  for off-thread callers (Issue 7); save toast always restarts instead of silently
  dropping rapid saves, eliminating any stuck-flag path (Issue 8)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:14:00 +01:00
Peter Foster
551bed6814 Add live eBay Browse API price lookup to photo analysis
After photo analysis completes, fires a background request to the
eBay Browse API (app-level auth, no user login needed) to fetch
current Buy It Now prices for similar items on eBay UK.

- EbayAuthService.GetAppTokenAsync(): client credentials OAuth flow,
  cached for the token lifetime
- EbayPriceResearchService: searches /buy/browse/v1/item_summary/search
  filtered to FIXED_PRICE + EBAY_GB, returns min/max/median/suggested
  (suggested = 40th percentile — competitive but not cheapest)
- PhotoAnalysisView: shows spinner + "Checking live eBay UK prices…",
  then updates price badge, range bar and PriceOverride with real data,
  plus a "Based on N live listings (£X – £Y)" label

Silently degrades if eBay credentials are not configured.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:05:16 +01:00
Peter Foster
1bdc8783f9 Fix: update price badge display after AI refinement
PriceSuggestedText was not being refreshed when Refine with AI ran,
so the badge still showed the original price even though PriceOverride
had been updated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 17:55:50 +01:00
Peter Foster
c02c69aaa6 Add AI corrections/refinement to Photo Analysis view
Adds a Corrections box and "Refine with AI" button below the
description. User types what the AI got wrong (e.g. "earrings are
white gold with diamonds, not silver and zirconium") and clicks
Refine — the AI rewrites the title, description and price while
keeping all other analysis fields intact.

AiAssistantService.RefineWithCorrectionsAsync() sends the current
listing text + corrections as a single text prompt and parses the
JSON response back into the title/description/price fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 17:53:06 +01:00
Peter Foster
41b88bca45 Fix remaining invalid PackIconMaterialKind names
TableRowsPlusAfter, InformationOutline and LinkOff don't exist in
IconPacks 4.11.0. Replaced with TablePlus, AlertCircleOutline,
LinkVariantOff. Audited all XAML files via DLL reflection — no
further invalid icon names remain.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 17:39:58 +01:00
Peter Foster
d7fcb82179 Fix invalid PackIconMaterialKind names for IconPacks 4.11.0
CameraSearch, ShoppingSearch, TagSearchOutline, ImagePlusOutline,
and FolderOpenOutline don't exist in this version of the package.
Replaced with valid equivalents: Camera, CartOutline, TagOutline,
ImagePlus, FolderOpen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 17:37:23 +01:00
Peter Foster
9fad0f2ac0 Initial commit: EbayListingTool WPF application
C# WPF desktop app for creating eBay UK listings with AI-powered
photo analysis. Features: multi-photo vision analysis via OpenRouter
(Claude), local listing save/export, saved listings browser,
single item listing form, bulk import from CSV/Excel, and eBay
OAuth authentication.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 17:33:27 +01:00