feat(tradein): /scrapers/avito — 5-я секция City sweep (auto + cancel + runs list) #478

Merged
lekss361 merged 1 commit from feat/tradein-frontend-city-sweep into main 2026-05-23 14:40:11 +00:00
Owner

Summary

UI для backend city sweep (parallel PR feat/tradein-avito-city-sweep) — добавляет 5-ю секцию на /trade-in/scrapers/avito с формой запуска, polling списка runs, и кнопкой отмены.

LOC: 2 files (1 modify page.tsx + 1 append CSS).

Changes

app/scrapers/avito/page.tsx

  • 3 новых hooks (useStartCitySweep, useCitySweepRuns, useCancelCitySweep):
    • Start mutation → POST /admin/scrape/avito-city-sweep (returns run_id, invalidates runs query)
    • Runs query → GET /admin/scrape/avito-city-sweep/runs?limit=10, auto-poll каждые 5 sec, staleTime=0
    • Cancel mutation → POST /admin/scrape/avito-city-sweep/{run_id}/cancel
  • Local TS interfaces: CitySweepInput, CitySweepStartResp, CitySweepCounters, ScrapeRunRow
  • 5-я секция "Полный обход ЕКБ (auto sweep)":
    • Form: pages_per_anchor (1-10), detail_top_n (0-30), request_delay_sec (3-15), radius_m, enrich_houses checkbox
    • Кнопка "Запустить sweep" → form submit
    • Result panel с JSON response (success/error)
    • Recent runs table (auto-refresh 5s):
      • Колонки: ID, статус badge, старт, heartbeat, anchors_done/total, lots (+inserted), houses_enriched/unique, detail_enriched/attempted, errors_count
      • "Отменить" button per running run (disabled во время cancel pending)
  • Helper functions: translateStatus (RU labels: выполняется/готово/ошибка/отменено/...), formatTime (ru-RU локаль)

trade-in.css

  • .scraper-section--highlight — синий border + accent-soft background для city sweep
  • .city-sweep-runs — секция с recent runs
  • .runs-table — tabular-nums, sticky header, compact layout
  • .run-badge--{running|done|failed|cancelled|zombie} — цветные status badges
  • .cancel-btn — danger color, hover

Verify

  • tsc --noEmit clean
  • next build pass — /scrapers/avito 4.86 kB, all 9 routes generated

Test plan

  • tsc strict
  • next build
  • Deploy parallel backend PR + this PR → visit /trade-in/scrapers/avito → fill sweep form → submit → verify run_id returned + runs table показывает новый row с status=running
  • Verify heartbeat updates каждые ~30 sec (per-anchor) + counters растут
  • Click "Отменить" → verify status badge changes на "отменено" в течение 1-2 polls
  • Verify cron на VPS запускает auto sweep ночью (02:00-05:00 UTC), новые runs появляются в таблице утром

Behavior

State UI
No runs yet "Нет запусков пока."
Running Badge синий "выполняется", "Отменить" button enabled
Done Badge зелёный "готово", no cancel button
Cancelled Badge оранжевый "отменено"
Failed Badge красный "ошибка" + error text below

Refs

  • Backend contract: parallel PR feat/tradein-avito-city-sweep (3 endpoints + scrape_runs tracking)
## Summary UI для backend city sweep (parallel PR `feat/tradein-avito-city-sweep`) — добавляет 5-ю секцию на `/trade-in/scrapers/avito` с формой запуска, polling списка runs, и кнопкой отмены. LOC: 2 files (1 modify page.tsx + 1 append CSS). ## Changes ### `app/scrapers/avito/page.tsx` - 3 новых hooks (`useStartCitySweep`, `useCitySweepRuns`, `useCancelCitySweep`): - Start mutation → POST `/admin/scrape/avito-city-sweep` (returns run_id, invalidates runs query) - Runs query → GET `/admin/scrape/avito-city-sweep/runs?limit=10`, **auto-poll каждые 5 sec**, staleTime=0 - Cancel mutation → POST `/admin/scrape/avito-city-sweep/{run_id}/cancel` - Local TS interfaces: `CitySweepInput`, `CitySweepStartResp`, `CitySweepCounters`, `ScrapeRunRow` - **5-я секция** "Полный обход ЕКБ (auto sweep)": - Form: pages_per_anchor (1-10), detail_top_n (0-30), request_delay_sec (3-15), radius_m, enrich_houses checkbox - Кнопка "Запустить sweep" → form submit - Result panel с JSON response (success/error) - **Recent runs table** (auto-refresh 5s): - Колонки: ID, статус badge, старт, heartbeat, anchors_done/total, lots (+inserted), houses_enriched/unique, detail_enriched/attempted, errors_count - "Отменить" button per running run (disabled во время cancel pending) - Helper functions: `translateStatus` (RU labels: выполняется/готово/ошибка/отменено/...), `formatTime` (ru-RU локаль) ### `trade-in.css` - `.scraper-section--highlight` — синий border + accent-soft background для city sweep - `.city-sweep-runs` — секция с recent runs - `.runs-table` — tabular-nums, sticky header, compact layout - `.run-badge--{running|done|failed|cancelled|zombie}` — цветные status badges - `.cancel-btn` — danger color, hover ## Verify - ✅ `tsc --noEmit` clean - ✅ `next build` pass — `/scrapers/avito` 4.86 kB, all 9 routes generated ## Test plan - [x] tsc strict - [x] next build - [ ] Deploy parallel backend PR + this PR → visit `/trade-in/scrapers/avito` → fill sweep form → submit → verify run_id returned + runs table показывает новый row с status=running - [ ] Verify heartbeat updates каждые ~30 sec (per-anchor) + counters растут - [ ] Click "Отменить" → verify status badge changes на "отменено" в течение 1-2 polls - [ ] Verify cron на VPS запускает auto sweep ночью (02:00-05:00 UTC), новые runs появляются в таблице утром ## Behavior | State | UI | |---|---| | No runs yet | "Нет запусков пока." | | Running | Badge синий "выполняется", "Отменить" button enabled | | Done | Badge зелёный "готово", no cancel button | | Cancelled | Badge оранжевый "отменено" | | Failed | Badge красный "ошибка" + error text below | ## Refs - Backend contract: parallel PR `feat/tradein-avito-city-sweep` (3 endpoints + scrape_runs tracking)
lekss361 added 1 commit 2026-05-23 14:35:15 +00:00
- City sweep form: pages_per_anchor (1-10), detail_top_n (0-30), request_delay_sec (3-15),
  radius_m, enrich_houses checkbox -> POST /admin/scrape/avito-city-sweep
- 'Запустить sweep' button -> returns run_id (background task)
- Recent runs table (auto-poll 5s): status badge, started/heartbeat, counters
  (anchors_done/total, lots, houses_enriched/unique, detail_enriched/attempted, errors)
- 'Отменить' button per running run -> POST /scrape/avito-city-sweep/{run_id}/cancel
- Status badges с цветами (running/done/failed/cancelled/zombie)
- translateStatus helper (RU labels), formatTime helper (ru-RU locale)
- CSS: .scraper-section--highlight (синий border для city-sweep section),
  .city-sweep-runs, .runs-table с tabular-nums, .run-badge variants, .cancel-btn

Backend контракт: 3 endpoints из parallel PR (avito-city-sweep start/runs/cancel).
TS strict + next build pass.
Author
Owner

Deep code review — verdict APPROVE.

Files: 2 (page.tsx +303/-1, trade-in.css +106). TS strict clean, no any/@ts-ignore. Local TS interfaces match backend Pydantic shapes из PR #477 1-в-1 (URL /api/v1/admin/scrape/avito-city-sweep[/runs|/{id}/cancel], payload fields, response keys, status enum). apiFetch корректно prefixes через API_BASE_URL = BASE_PATH ?? "" (basePath-aware). Existing scraper hooks в этом же файле уже используют /api/v1/admin/scrape/* path style — consistency сохранена.

Backend contract dependency: hooks call 3 endpoints из parallel PR #477. Если #478 merged без #477useCitySweepRuns вернёт 404 → красная панель "Ошибка загрузки runs", остальные 4 секции работают. Graceful degradation OK, но рекомендую merge order #477#478 чтобы users не видели transient 404.

Polling load: refetchInterval: 5_000, staleTime: 0 → 12 req/min на admin user. Endpoint list_recent(limit=10) — single indexed SELECT, negligible. TanStack Query v5 default пауза при document.hidden. Не оптимизировано: продолжает poll даже когда все runs в terminal state (done/failed/cancelled). Minor — admin tool, low concurrent users.

Minor nits (non-blocking, post-merge polish):

  • parseInt(sweepPages, 10) без Number.isNaN guard → "abc" в input даёт NaN → JSON.stringify → null → backend 422. Admin-only UI, acceptable.
  • Cancel button без optimistic UI — badge меняется через 1-2 polls.
  • formatTime — absolute timestamp ru-RU, без "5 мин назад". Achievable для long-running 1h+ runs.
  • .cancel-btn:disabled background --border-strong + white text → low contrast.
  • Conditional polling: (q) => q.state.data?.some(r => r.status === 'running') ? 5000 : 30000 — экономит requests когда idle.
  • Table без overflow-x: auto wrapper — 10 cols на mobile узко (admin desktop OK).

Status enum coverage: translateStatus маппит все 7 значений из 051 миграции CHECK constraint (running/done/failed/cancelled/zombie/skipped/banned) + default: return s fallback на новые backend статусы.

Risk: Low. Reversibility: Easy revert (2 файла, append-only, no migration). Deploy trigger tradein-mvp/frontend/** в deploy-tradein.yml сработает.

Merging via deep-code-reviewer.

Deep code review — verdict APPROVE. **Files**: 2 (page.tsx +303/-1, trade-in.css +106). TS strict clean, no `any`/`@ts-ignore`. Local TS interfaces match backend Pydantic shapes из PR #477 1-в-1 (URL `/api/v1/admin/scrape/avito-city-sweep[/runs|/{id}/cancel]`, payload fields, response keys, status enum). `apiFetch` корректно prefixes через `API_BASE_URL = BASE_PATH ?? ""` (basePath-aware). Existing scraper hooks в этом же файле уже используют `/api/v1/admin/scrape/*` path style — consistency сохранена. **Backend contract dependency**: hooks call 3 endpoints из parallel PR #477. Если #478 merged без #477 — `useCitySweepRuns` вернёт 404 → красная панель "Ошибка загрузки runs", остальные 4 секции работают. Graceful degradation OK, но рекомендую merge order **#477 → #478** чтобы users не видели transient 404. **Polling load**: `refetchInterval: 5_000`, `staleTime: 0` → 12 req/min на admin user. Endpoint `list_recent(limit=10)` — single indexed SELECT, negligible. TanStack Query v5 default пауза при `document.hidden`. Не оптимизировано: продолжает poll даже когда все runs в terminal state (done/failed/cancelled). Minor — admin tool, low concurrent users. **Minor nits (non-blocking, post-merge polish)**: - `parseInt(sweepPages, 10)` без `Number.isNaN` guard → "abc" в input даёт `NaN` → JSON.stringify → null → backend 422. Admin-only UI, acceptable. - Cancel button без optimistic UI — badge меняется через 1-2 polls. - `formatTime` — absolute timestamp ru-RU, без "5 мин назад". Achievable для long-running 1h+ runs. - `.cancel-btn:disabled` background `--border-strong` + white text → low contrast. - Conditional polling: `(q) => q.state.data?.some(r => r.status === 'running') ? 5000 : 30000` — экономит requests когда idle. - Table без `overflow-x: auto` wrapper — 10 cols на mobile узко (admin desktop OK). **Status enum coverage**: `translateStatus` маппит все 7 значений из 051 миграции CHECK constraint (running/done/failed/cancelled/zombie/skipped/banned) + `default: return s` fallback на новые backend статусы. **Risk**: Low. **Reversibility**: Easy revert (2 файла, append-only, no migration). Deploy trigger `tradein-mvp/frontend/**` в deploy-tradein.yml сработает. Merging via deep-code-reviewer.
lekss361 merged commit dd24bd8136 into main 2026-05-23 14:40:11 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#478
No description provided.