feat(sf-fe-a6): Section 2 «Сети и точки подключения» — real B5 utilities data #354

Merged
lekss361 merged 1 commit from feat/sf-fe-a6-section2-networks into main 2026-05-18 00:27:55 +00:00
Owner

Summary

SF Frontend Wave 2 §A6: Section 2 «Сети и точки подключения» на analysis page. Реальные данные через useParcelAnalyzeQuery (B5 endpoint analyze.utilities).

Files

NEW frontend/src/components/site-finder/analysis/Section2NetworksUtilities.tsx (~526 lines):

  • "use client" — TanStack Query через existing useParcelAnalyzeQuery
  • 4 states: loading skeleton / no-data (NSPD absent) / error / full table
  • UtilitiesTable: 5-column grid (icon / label / nearest_m / count_within_2km / presence badge)
  • Subtype → Lucide icon: substation / power_lineZap, pipelineFlame, water_intakeDroplet, pumping_stationPipette, unknown → Info
  • Presence badge from distance: ≤200m = success / ≤500m = accent / ≤1km = warn / >1km = danger
  • power_line_охранная_зона_flag: trueAlertTriangle danger banner
  • HeadlineBar subtitle auto-builds: «электричество 59 м · газ 320 м»
  • Anchor id="section-2" для scrollspy

Types frontend/src/lib/site-finder-api.ts:

  • UtilitySubtype, UtilitySummaryItem, UtilitiesData
  • utilities?: UtilitiesData | null на ParcelAnalyzeResponse

Page wire frontend/src/app/site-finder/analysis/[cad]/page.tsx — Section 2 placeholder → <Section2NetworksUtilities cad={cad} />

Mock fixture frontend/src/lib/mocks/parcel-analyze.jsonutilities block для dev (USE_MOCKS) matches prod shape

Real data flow

  • 66:41:0204016:10utilities.summary = substation 59m / pipeline 320m / power_line 630m
  • 66:41:0701045:42utilities: {} → graceful no-data state
  • НЕТ mock fallback в prod codepath; mock fixture только для local dev USE_MOCKS=true

Constraints

  • TS strict, "use client" только где нужно
  • TanStack Query (reuse), no useEffect+HTTP
  • Real backend data в prod (B5 уже live)
  • CSS tokens (--bg-card, --accent, etc.) из globals.css (#353)
  • Lucide icons, no emoji
  • pre-commit prettier + lint pass

Visual verify (post-deploy)

  • /site-finder/analysis/66:41:0204016:10 → Section 2 HeadlineBar + 3-row table + badges
  • /site-finder/analysis/66:41:0701045:42 → graceful «нет данных»
  • Любой parcel с power_line_охранная_зона_flag: true → red AlertTriangle banner

Part of plan vault code/patterns/SiteFinder_Frontend_Migration_Plan_May18.md §A6.

## Summary SF Frontend Wave 2 §A6: Section 2 «Сети и точки подключения» на analysis page. Реальные данные через `useParcelAnalyzeQuery` (B5 endpoint `analyze.utilities`). ## Files **NEW** `frontend/src/components/site-finder/analysis/Section2NetworksUtilities.tsx` (~526 lines): - `"use client"` — TanStack Query через existing `useParcelAnalyzeQuery` - 4 states: loading skeleton / no-data (NSPD absent) / error / full table - `UtilitiesTable`: 5-column grid (icon / label / `nearest_m` / `count_within_2km` / presence badge) - Subtype → Lucide icon: `substation` / `power_line` → `Zap`, `pipeline` → `Flame`, `water_intake` → `Droplet`, `pumping_station` → `Pipette`, unknown → `Info` - Presence badge from distance: ≤200m = success / ≤500m = accent / ≤1km = warn / >1km = danger - `power_line_охранная_зона_flag: true` → `AlertTriangle` danger banner - `HeadlineBar` subtitle auto-builds: «электричество 59 м · газ 320 м» - Anchor `id="section-2"` для scrollspy **Types** `frontend/src/lib/site-finder-api.ts`: - `UtilitySubtype`, `UtilitySummaryItem`, `UtilitiesData` - `utilities?: UtilitiesData | null` на `ParcelAnalyzeResponse` **Page wire** `frontend/src/app/site-finder/analysis/[cad]/page.tsx` — Section 2 placeholder → `<Section2NetworksUtilities cad={cad} />` **Mock fixture** `frontend/src/lib/mocks/parcel-analyze.json` — `utilities` block для dev (USE_MOCKS) matches prod shape ## Real data flow - `66:41:0204016:10` → `utilities.summary` = substation 59m / pipeline 320m / power_line 630m - `66:41:0701045:42` → `utilities: {}` → graceful no-data state - НЕТ mock fallback в prod codepath; mock fixture только для local dev USE_MOCKS=true ## Constraints - ✅ TS strict, "use client" только где нужно - ✅ TanStack Query (reuse), no useEffect+HTTP - ✅ Real backend data в prod (B5 уже live) - ✅ CSS tokens (`--bg-card`, `--accent`, etc.) из globals.css (#353) - ✅ Lucide icons, no emoji - ✅ pre-commit prettier + lint pass ## Visual verify (post-deploy) - `/site-finder/analysis/66:41:0204016:10` → Section 2 HeadlineBar + 3-row table + badges - `/site-finder/analysis/66:41:0701045:42` → graceful «нет данных» - Любой parcel с `power_line_охранная_зона_flag: true` → red AlertTriangle banner Part of plan vault `code/patterns/SiteFinder_Frontend_Migration_Plan_May18.md` §A6.
lekss361 added 1 commit 2026-05-18 00:22:52 +00:00
- Add Section2NetworksUtilities component (HeadlineBar + utilities table)
- Map backend utilities.summary subtypes to Lucide icons (Zap/Flame/Droplet/Pipette)
- Presence status badges: 200m / 500m / 1km / далее 1km with semantic colors
- AlertTriangle warning when power_line_охранная_зона_flag is true
- Graceful no-data state when utilities absent from NSPD snapshot
- Add UtilitiesData / UtilitySummaryItem types to ParcelAnalyzeResponse
- Update parcel-analyze.json mock fixture with utilities sample data
- Wire Section2 into analysis/[cad]/page.tsx replacing placeholder
Author
Owner

Deep Code Review — verdict APPROVE

Status: APPROVE

Files reviewed: 4 (P2: 1 page wire + 1 types + 1 mock, P3: 1 new component) +616/-6

Critical / High / Medium: none

Cross-file contract check (the key concern)

  • Cyrillic key match VERIFIED: backend app/api/v1/parcels.py:1750 literally emits "power_line_охранная_зона_flag" (snake_case + Cyrillic identifier). Frontend UtilitiesData and the destructure in Section2NetworksUtilities.tsx:464 use the same literal key. No silent feature loss.
  • Backend always sets utilities = None on failure (parcels.py:1762), never {}. The component guard !utilities || utilities.summary.length === 0 is correct — summary is always emitted when utilities object exists (parcels.py:1748).
  • note typed string | null is slightly loose (backend always emits a non-null string in this branch), but safe.
  • subtype union ... | string keeps type-safety while covering future OSM road_class values; getUtilityConfig default branch handles unknowns with Info icon + raw label.

State machine (4 states)

  • isLoading → Skeleton; isError || !data → Error; !utilities || summary.length === 0 → NoData; otherwise → full render with optional banner + note.
  • Anchor id="section-2" preserved in all four states — scrollspy from A4 won't break.

Conventions per .claude/rules/ui-ux.md

  • All colors from token allowlist (--bg-card, --bg-card-alt, --border-card, --border-soft, --border-strong, --fg-*, --accent, --success/-soft, --warn/-soft, --danger/-soft, --viz-2, --viz-4). No raw hex.
  • Lucide icons only (no emoji). h2 left-align. HeadlineBar dark verdict strip present.
  • "use client" placed correctly for TanStack Query hook.
  • TS strict, no any, TanStack Query reused (no bare fetch).

Lockfile / deps

  • No package.json / package-lock.json changes. lucide-react, @tanstack/react-query, HeadlineBar all pre-existing. Clean.

Mock fixture

  • JSON valid (trailing comma after last_updated, then utilities block). 3 entries cover substation/pipeline/power_line. power_line_охранная_зона_flag: false exercises the non-banner path; for visual smoke on banner path you'd toggle to true locally.

Nits (non-blocking, optional follow-up)

  • buildSubtitle only surfaces electricity/gas/water — канализация (pumping_station) never shows in subtitle. Acceptable per design; consider extending later if useful for развития.
  • 564 lines in one file — Skeleton / NoData / Error / Table sub-components could split into _components/ if reused elsewhere. Not blocking.

Merging.

## Deep Code Review — verdict APPROVE ### Status: APPROVE ### Files reviewed: 4 (P2: 1 page wire + 1 types + 1 mock, P3: 1 new component) +616/-6 ### Critical / High / Medium: none ### Cross-file contract check (the key concern) - **Cyrillic key match VERIFIED**: backend `app/api/v1/parcels.py:1750` literally emits `"power_line_охранная_зона_flag"` (snake_case + Cyrillic identifier). Frontend `UtilitiesData` and the destructure in `Section2NetworksUtilities.tsx:464` use the same literal key. No silent feature loss. - Backend always sets `utilities = None` on failure (parcels.py:1762), never `{}`. The component guard `!utilities || utilities.summary.length === 0` is correct — `summary` is always emitted when `utilities` object exists (parcels.py:1748). - `note` typed `string | null` is slightly loose (backend always emits a non-null string in this branch), but safe. - `subtype` union `... | string` keeps type-safety while covering future OSM `road_class` values; `getUtilityConfig` default branch handles unknowns with Info icon + raw label. ### State machine (4 states) - `isLoading` → Skeleton; `isError || !data` → Error; `!utilities || summary.length === 0` → NoData; otherwise → full render with optional banner + note. - Anchor `id="section-2"` preserved in all four states — scrollspy from A4 won't break. ### Conventions per `.claude/rules/ui-ux.md` - All colors from token allowlist (`--bg-card`, `--bg-card-alt`, `--border-card`, `--border-soft`, `--border-strong`, `--fg-*`, `--accent`, `--success/-soft`, `--warn/-soft`, `--danger/-soft`, `--viz-2`, `--viz-4`). No raw hex. - Lucide icons only (no emoji). h2 left-align. HeadlineBar dark verdict strip present. - "use client" placed correctly for TanStack Query hook. - TS strict, no `any`, TanStack Query reused (no bare fetch). ### Lockfile / deps - No `package.json` / `package-lock.json` changes. `lucide-react`, `@tanstack/react-query`, `HeadlineBar` all pre-existing. Clean. ### Mock fixture - JSON valid (trailing comma after `last_updated`, then utilities block). 3 entries cover substation/pipeline/power_line. `power_line_охранная_зона_flag: false` exercises the non-banner path; for visual smoke on banner path you'd toggle to `true` locally. ### Nits (non-blocking, optional follow-up) - `buildSubtitle` only surfaces electricity/gas/water — канализация (`pumping_station`) never shows in subtitle. Acceptable per design; consider extending later if `useful` for развития. - 564 lines in one file — Skeleton / NoData / Error / Table sub-components could split into `_components/` if reused elsewhere. Not blocking. Merging.
lekss361 merged commit 307a75f333 into main 2026-05-18 00:27:55 +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#354
No description provided.