feat(site-finder): NSPD frontend integration в LandTab (#202) #212
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#212
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/202-nspd-ui-landtab"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Issue #202 — frontend integration NSPD данных. Backend поставил поля в
/analyzeчерез PR #109/#110/#111/#116, но LandTab показывал hardcoded "API Росреестра закрыт, см. PKK" → false impression. Этот PR заменяет на live data.Backend response (verified live)
nspd_zoning: {zone_code, zone_name, source, raw_props}(поляpermitted_use/max_floorsиз issue spec — отсутствуют, использован реальный shape)nspd_zouit_overlaps: [{group_key, layer, subcategory, name, raw_props}](severity derived изgroup_key)nspd_engineering_nearby: [{name, type, distance_m, raw_props}]nspd_dump: {available, stale, fetched_at_utc, harvest_triggered}Frontend additions (~800 LOC, 8 files)
types/nspd.tstypes/site-finder.tsParcelAnalysisextended с nspd_* optional fieldshooks/useConnectionPoints.tsuseQueryдля/api/v1/parcels/{cad}/connection-pointscomponents/site-finder/NspdZoningBlock.tsxcomponents/site-finder/NspdZouitOverlapsBlock.tsxcomponents/site-finder/NspdEngineeringNearbyBlock.tsxcomponents/site-finder/NspdFreshnessBadge.tsxcomponents/site-finder/LandTab.tsxAsync harvest UX
harvest_triggered=true && available=false— skeleton + текст «Загружаем НСПД, 15-30с»Test plan
tsc --noEmit+npm run lint+npm run build— все clean/site-finder?cad=66:41:0603016:194→ LandTab → 4 NSPD blocks visiblezone_code/zone_name(не "API закрыт")/connection-pointsfetched on page loadCloses part of #202. Phase 2 (auto-polling) — follow-up.
Related: PR #109/#110/#111/#116 (backend NSPD harvest), PR #190 (connection-points endpoint).
Code Review verdict — PR #212
Summary
components/site-finder/**,hooks/**,types/**) — ALLOWED for auto-merge per.claude/rules/git-pr.mde5de841Critical issues
None.
Minor observations (non-blocking)
NspdEngineeringNearbyBlock.tsx:46-52— dedupe key usesMath.round(distanceM), so two items at 12.4 m and 12.6 m collide and one is dropped. Acceptable for informational table (sub-meter precision irrelevant for UX), but worth a follow-up if a real duplicate-detection bug surfaces.useConnectionPoints.ts— error state not surfaced inNspdEngineeringNearbyBlock(onlyisPendingis read). 404/500 from/connection-pointswould silently degrade to "no analyze items" instead of showing a hint. Low priority — backend currently returns 200 with empty arrays when dump absent (dump_available=false), so error path is rare..claude/rules/frontend.md) says "Tailwind 4 утилитарные классы; inline styles только для динамических значений". This PR uses inline styles throughout, but is consistent with existingsite-finder/blocks (NeighborsBlock, GeologyBlock, GeometrySuitabilityBlock all do the same). Not a regression — refactor would be a separate sweep.Positive observations
raw_propsrendered via React children (String(v ?? "—")) — auto-escaped, nodangerouslySetInnerHTMLanywhere → no XSS surface even if backend returns HTML-looking values. PKK URLs useencodeURIComponent(cadNum). Schemes are hardcodedhttps://(allowlisted) —safeUrl()not needed.ConnectionPointsResponse/EngineeringStructure/ConnectionPointsSummaryTS shapes mirrorbackend/app/schemas/parcel.pyexactly (verified:nearest_structure_distance_m: float | None,dump_fetched_at: str | None,intersects_parcel: bool). Noany, usesRecord<string, unknown>for opaque dicts.ParcelAnalysis.nspd_*все optional → старый бэк (без полей) не ломает UI.hasContentcheck корректно расширен.NspdFreshnessBadgeчисто покрывает все 5 branches (no dump / harvesting / no available / stale / fresh).NspdZoningBlockпоказывает skeleton + «Загружаем НСПД, 15–30 с» приharvest_triggered && !available && !data— соответствует issue spec.NspdZoningBlockприdata == null(не удалён вместе с hardcoded блоком).staleTime: 5 min,enabled: !!cadNum && enabled— корректно. DefaultgcTime(5 min) подходит для этого use case.cadNum!non-null assertion безопасен из-заenabledguard.NspdZouitOverlapsBlock: okn/protected → red (block), engineering → yellow (warn), natural → blue (low), other → gray — разумный default.Auto-polling (Phase 2)
Known follow-up — отдельный PR. Текущая реализация показывает skeleton без авто-refetch; user должен перезагрузить страницу через ~30s. Acceptable для MVP.
Recommended next steps
66:41:0603016:194+ check Network tab на/connection-pointsfetch.