feat(admin): cadastre bulk harvest UI (#168 PR4/5) #172

Merged
lekss361 merged 1 commit from feat/cadastre-bulk-admin-ui into main 2026-05-15 10:46:07 +00:00
lekss361 commented 2026-05-15 10:39:15 +00:00 (Migrated from github.com)

Summary

  • PR 4/5 of #168 Bulk cadastre ingest ЕКБ
  • Adds admin page /admin/scrape/cadastre for triggering bulk cadastre harvest + monitoring jobs
  • Backend API (PR #171) already merged; this PR is pure frontend

Files

File Lines Purpose
frontend/src/app/admin/scrape/cadastre/page.tsx ~960 New admin page (clone of /admin/scrape/geo)
frontend/src/app/admin/layout.tsx +1 tab "Cadastre bulk harvest" link

UI components

Trigger form

  • Scope radio: Pilot (50 ЕКБ кварталов) / Full ЕКБ (2 408) / Manual list
  • Manual list mode: textarea для cad numbers (one per line)
  • Optional fields: name (для labelling job), limit (override default count)
  • Client-side guard: manual_list требует ≥1 quarter
  • On submit: alert Job #N создан, targets=X, estimate=Yмин

Live jobs table

  • Auto-refresh 5s via useQuery({ refetchInterval: 5000 })
  • Columns: ID, Name, Scope, Status (colored badge), Progress bar, Targets done/total (fail), Errors, Started, Heartbeat, Actions
  • Status colors:
    • queued grey, running blue, done green, failed red, zombie darker red, cancelled purple, paused amber
  • WAF column: red text + ⚠ icon when waf_blocked_count > 5
  • Heartbeat staleness: red "STALE (Xмин)" when status=running and now - heartbeat_at > 10min

Per-row expand

  • Click row → expand details panel
  • Shows: phase_state pretty-printed JSON, computed req/s rate, full stats grid, error text
  • Cancel button if status ∈ {queued, running}
  • Resume button if status ∈ {failed, cancelled, paused} AND phase_state.phase !== 'done'

TanStack Query

useQuery({ queryKey: ['admin','cadastre','jobs', token], refetchInterval: 5000 })
useMutation для createJob (invalidate jobsKey)
useMutation для cancelJob (invalidate jobKey + jobsKey)
useMutation для resumeJob (invalidate jobKey + jobsKey)

Token persistence

localStorage.getItem("admin_token") — same key as existing /admin/scrape/geo для shared session.

Type safety

  • 3 interfaces (CadastreJob, CreateCadastreJobRequest, CreateCadastreJobResponse) — fully typed
  • НЕТ any, all unknown narrowed
  • tsc --noEmit , ESLint

Test plan

  • cd frontend && npx tsc --noEmit -p tsconfig.json — no errors
  • cd frontend && npm run lint — clean
  • После merge + deploy → manual smoke на https://gendsgn.ru/admin/scrape/cadastre:
    • Введи admin token → jobs table должен загрузиться (initially empty)
    • Создай job scope=manual_list quarters=['66:41:0303161'] → alert + новый row
    • Expand row → phase_state visible (initially null)
    • Job переходит в running после backend enqueue_cadastre_harvest pickup

Dependencies

  • PR #171 backend MERGED — endpoints доступны на main

What's NOT in this PR

  • Backend (готово PR #171)
  • Pilot real run (PR 5 — manual через эту UI)
  • Helsink integration into site-finder analyze (cutover PR 7+)
  • Помещения / парковка UI (Q3 deferred)

Notes

  • Reuse cardStyle, inputStyle, labelStyle, td, th из @/lib/adminStyles
  • Reuse apiFetch из @/lib/api
  • Pattern полностью клонирован с working /admin/scrape/geo

Closes 4/5 of #168.

## Summary - **PR 4/5** of [#168 Bulk cadastre ingest ЕКБ](https://github.com/lekss361/-gendesign/issues/168) - Adds admin page `/admin/scrape/cadastre` for triggering bulk cadastre harvest + monitoring jobs - Backend API (PR #171) already merged; this PR is pure frontend ## Files | File | Lines | Purpose | |---|---|---| | `frontend/src/app/admin/scrape/cadastre/page.tsx` | ~960 | New admin page (clone of `/admin/scrape/geo`) | | `frontend/src/app/admin/layout.tsx` | +1 tab | "Cadastre bulk harvest" link | ## UI components ### Trigger form - **Scope radio**: Pilot (50 ЕКБ кварталов) / Full ЕКБ (2 408) / Manual list - **Manual list mode**: textarea для cad numbers (one per line) - **Optional fields**: name (для labelling job), limit (override default count) - Client-side guard: manual_list требует ≥1 quarter - On submit: alert `Job #N создан, targets=X, estimate=Yмин` ### Live jobs table - Auto-refresh **5s** via `useQuery({ refetchInterval: 5000 })` - Columns: ID, Name, Scope, Status (colored badge), Progress bar, Targets `done/total (fail)`, Errors, Started, Heartbeat, Actions - Status colors: - `queued` grey, `running` blue, `done` green, `failed` red, `zombie` darker red, `cancelled` purple, `paused` amber - **WAF column**: red text + ⚠ icon when `waf_blocked_count > 5` - **Heartbeat staleness**: red "STALE (Xмин)" when status=`running` and `now - heartbeat_at > 10min` ### Per-row expand - Click row → expand details panel - Shows: `phase_state` pretty-printed JSON, computed req/s rate, full stats grid, error text - Cancel button if status ∈ {`queued`, `running`} - Resume button if status ∈ {`failed`, `cancelled`, `paused`} AND `phase_state.phase !== 'done'` ## TanStack Query ```typescript useQuery({ queryKey: ['admin','cadastre','jobs', token], refetchInterval: 5000 }) useMutation для createJob (invalidate jobsKey) useMutation для cancelJob (invalidate jobKey + jobsKey) useMutation для resumeJob (invalidate jobKey + jobsKey) ``` ## Token persistence `localStorage.getItem("admin_token")` — same key as existing `/admin/scrape/geo` для shared session. ## Type safety - 3 interfaces (`CadastreJob`, `CreateCadastreJobRequest`, `CreateCadastreJobResponse`) — fully typed - НЕТ `any`, all unknown narrowed - `tsc --noEmit` ✅, ESLint ✅ ## Test plan - [x] `cd frontend && npx tsc --noEmit -p tsconfig.json` — no errors - [x] `cd frontend && npm run lint` — clean - [ ] После merge + deploy → manual smoke на https://gendsgn.ru/admin/scrape/cadastre: - Введи admin token → jobs table должен загрузиться (initially empty) - Создай job scope=`manual_list` quarters=`['66:41:0303161']` → alert + новый row - Expand row → phase_state visible (initially null) - Job переходит в `running` после backend enqueue_cadastre_harvest pickup ## Dependencies - **PR #171 backend** ✅ MERGED — endpoints доступны на main ## What's NOT in this PR - Backend (готово PR #171) - Pilot real run (PR 5 — manual через эту UI) - Helsink integration into site-finder analyze (cutover PR 7+) - Помещения / парковка UI (Q3 deferred) ## Notes - Reuse `cardStyle`, `inputStyle`, `labelStyle`, `td`, `th` из `@/lib/adminStyles` - Reuse `apiFetch` из `@/lib/api` - Pattern полностью клонирован с working `/admin/scrape/geo` Closes 4/5 of #168.
lekss361 commented 2026-05-15 10:45:53 +00:00 (Migrated from github.com)

Final review — CI , agent verdict approve

Cross-checked агентом — page works end-to-end against documented backend, контракт exact, нет hidden real gaps.

Contract alignment (verified by agent)

TS CadastreJob interface ↔ backend _serialize_job — exact match: job_id, name, job_kind, scope, status, triggered_by, rate_ms, created_at, started_at, finished_at, heartbeat_at, targets_total, targets_done, targets_failed, targets_skipped, requests_count, waf_blocked_count, error, phase_state, progress_pct, estimate_minutes. Никакой field drift (PR #147 pattern).
status typed как string literal union — все 7 enum values покрыты (queued/running/done/failed/zombie/cancelled/paused).
POST request/response shapes match backend exactly.

Admin token + lifecycle

Все 5 API calls send X-Admin-Token через apiFetch
localStorage["admin_token"] shared с /admin/scrape/geo — session consistent
refetchInterval: 5000 для live updates
Status badges + colored progress bar + WAF column + heartbeat staleness check

UI safety gating

Cancel button gated by status ∈ {queued, running}
Resume gated by status ∈ {failed, cancelled, paused} AND phase_state.phase !== 'done'
Client-side validation: manual_list требует ≥1 quarter
phase_state rendered safely via JSON.stringify(..., null, 2)

Минор (cosmetic / non-blocking — agent classified)

  1. React.Fragment key: <> shorthand в .map вместо <React.Fragment key={...}> — dev console warning, не runtime bug
  2. Polling не stops на terminal jobs (done/failed/cancelled) — wasted GET, не функциональная проблема
  3. No confirm() на Cancel — admin tool, low risk + действие reversible через resume
  4. Cancel/resume mutation errors silently swallowed — error eventually visible через next refetch иначе UI отстанет от state

These are genuine improvements но classified агентом как "не real gaps" — page works end-to-end. Recommend follow-up в PR 5/5 (pilot run) когда UI exercise'нется в реальных условиях.

Pattern checks

apiFetch + useQuery + useMutation reuse из существующих admin pages
Reuse cardStyle, inputStyle, labelStyle, td, th из @/lib/adminStyles
tsc + ESLint clean per PR body
Backend + frontend CI SUCCESS

approve merge

## Final review — CI ✅, agent verdict approve Cross-checked агентом — page works end-to-end against documented backend, контракт exact, нет hidden real gaps. ### Contract alignment (verified by agent) ✅ **TS `CadastreJob` interface ↔ backend `_serialize_job`** — exact match: `job_id, name, job_kind, scope, status, triggered_by, rate_ms, created_at, started_at, finished_at, heartbeat_at, targets_total, targets_done, targets_failed, targets_skipped, requests_count, waf_blocked_count, error, phase_state, progress_pct, estimate_minutes`. Никакой field drift (PR #147 pattern). ✅ **`status` typed как string literal union** — все 7 enum values покрыты (queued/running/done/failed/zombie/cancelled/paused). ✅ **POST request/response shapes** match backend exactly. ### Admin token + lifecycle ✅ Все 5 API calls send `X-Admin-Token` через `apiFetch` ✅ `localStorage["admin_token"]` shared с `/admin/scrape/geo` — session consistent ✅ `refetchInterval: 5000` для live updates ✅ Status badges + colored progress bar + WAF column + heartbeat staleness check ### UI safety gating ✅ Cancel button gated by status ∈ {queued, running} ✅ Resume gated by status ∈ {failed, cancelled, paused} AND `phase_state.phase !== 'done'` ✅ Client-side validation: manual_list требует ≥1 quarter ✅ `phase_state` rendered safely via `JSON.stringify(..., null, 2)` ### Минор (cosmetic / non-blocking — agent classified) 1. **React.Fragment key**: `<>` shorthand в `.map` вместо `<React.Fragment key={...}>` — dev console warning, не runtime bug 2. **Polling не stops** на terminal jobs (done/failed/cancelled) — wasted GET, не функциональная проблема 3. **No `confirm()` на Cancel** — admin tool, low risk + действие reversible через resume 4. **Cancel/resume mutation errors silently swallowed** — error eventually visible через next refetch иначе UI отстанет от state These are genuine improvements но classified агентом как "не real gaps" — page works end-to-end. Recommend follow-up в PR 5/5 (pilot run) когда UI exercise'нется в реальных условиях. ### Pattern checks ✅ `apiFetch` + `useQuery` + `useMutation` reuse из существующих admin pages ✅ Reuse `cardStyle`, `inputStyle`, `labelStyle`, `td`, `th` из `@/lib/adminStyles` ✅ tsc + ESLint clean per PR body ✅ Backend + frontend CI SUCCESS approve merge
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#172
No description provided.