# Phase 8: Results Loader **Status:** ⬜ Not Started **Parent plan:** [PLAN.md](./PLAN.md) **Domain:** fullstack **Implementer:** Sonnet (backend) + Opus (UI) **Depends on:** Phase 6 (UI patterns) ## Objective Per customer TZ §4: scrape and persist results of completed events, with a UI that allows the user to load all results in a date range OR pick specific events to load selectively. ## Tasks ### Backend (Sonnet) - [ ] `PullResultsUseCase` was scaffolded in Phase 4 — extend it here: - When `selection` is null/empty, fetch results for ALL completed events in range that don't have a stored `EventResult` yet - When `selection` provided, fetch results only for those events - Idempotent — re-running for already-loaded results is a no-op - [ ] Add `IResultsScraper`-related parser methods (or extend `IOddsScraper` with `ScrapeResultsAsync`) — implementation may already exist from Phase 3. - [ ] After persisting results, infer `WinnerSide` and update the `Event` accordingly (or store derived `WinnerSide` on `EventResult` only — implementer's choice, document in handoff). - [ ] Tests in `Marathon.Application.Tests`: - `PullResultsUseCase` with selection list pulls only those events - With null selection, pulls all completed events missing results in range - Idempotency: running twice produces no duplicates ### Frontend (Opus + frontend-design) - [ ] Create `Marathon.UI/Pages/Results/ResultsLoader.razor`: - Date range picker - Two modes: "All in range" (default) | "Selected events" - Selected events mode: searchable multi-select of completed events lacking results - "Load Results" button → invokes `PullResultsUseCase` - Progress indicator (number of events processed / total) - Result table on completion showing what was loaded (event identity, score, winner side) - [ ] Create `Marathon.UI/Pages/Results/ResultsList.razor`: - Browse already-loaded results - Filter by sport, date range, winner-side-1 / winner-side-2 / draw - Link back to event detail page (Phase 6) - [ ] Add `Results` entry to navigation drawer. - [ ] Localize all strings RU + EN. - [ ] Frontend tests: - bUnit: loader page invokes use case with correct parameters in both modes - bUnit: results list filter narrows correctly ## Files to Modify/Create - `src/Marathon.Application/UseCases/PullResultsUseCase.cs` — extend - `src/Marathon.UI/Pages/Results/ResultsLoader.razor` - `src/Marathon.UI/Pages/Results/ResultsList.razor` - `tests/Marathon.Application.Tests/UseCases/PullResultsUseCaseTests.cs` - `tests/Marathon.UI.Tests/Pages/Results/**` ## Acceptance Criteria - Compiles (Big Bang). - Selective loading respects user's selection. - Bulk loading skips events that already have results. - UI shows progress during a multi-event load. ## Notes - Big Bang: compile-only smoke check. ## Review Checklist - [ ] Idempotent — no duplicate `EventResult` rows - [ ] UI handles empty range gracefully (no events match) - [ ] All strings localized ## Handoff to Next Phase