feat(sf-fe-a5): Section 1 Инфо об участке — HeadlineBar + KPI + EGRN + POI + Export #346

Merged
lekss361 merged 2 commits from feat/sf-fe-a5-section1-parcel-info into main 2026-05-17 22:55:05 +00:00

2 commits

Author SHA1 Message Date
lekss361
47d0374386 fix(sf-fe-a5): address review #346 — CSV injection + area_m2 NaN fallback + nits
🟠 HIGH (security): ExportButtons.escapeCsvCell now prefixes cells starting
with =/+/-/@/tab/CR with a leading apostrophe (OWASP CWE-1236). Prevents
Excel/Calc/Sheets from evaluating attacker-controllable strings (egrn.address,
cad_num) as formulas (=cmd|... / =HYPERLINK / DDE).

🟡 MEDIUM: buildFallbackEgrn now returns area_m2: NaN instead of 0; KPI 'Площадь'
and EgrnPropertyTable 'Площадь' row both gate on Number.isFinite + >0, rendering
'—' consistently when EGRN absent (previously asymmetric: KPI '—' / table '0 м²').

🟢 LOW: scoreVerdict collapsed dead branch (both >=80 and >=65 returned same
ПОДХОДИТ label). MiniMap toSiteMapData narrows source via explicit ternary
instead of unchecked 'as' cast.
2026-05-18 01:53:09 +03:00
lekss361
6fe8d637fe feat(sf-fe-a5): Section 1 "Инфо об участке" — HeadlineBar + KPI + EGRN + POI + Export
- Section1ParcelInfo: wrapper с HeadlineBar вердикт, 4 KpiCard (Площадь/Район/Медиана/Score), 2-col grid (MiniMap + EgrnPropertyTable + PoiList2Gis), ExportButtons. Anchor id="section-1" для scrollspy.
- MiniMap: dynamic import SiteMap (ssr:false), 400×320, адаптер ParcelAnalyzeResponse → ParcelAnalysis.
- EgrnPropertyTable: 10 EGRN полей key-value, monospace для cad_num/area_m2, zebra-stripe.
- PoiList2Gis: top-7 POI weighted score, Lucide иконки (Train/TreePine/GraduationCap/Baby/ShoppingBag/Hospital), distance + weight Badge, fallback из score_breakdown.
- ExportButtons: PDF blob download (B7 /snapshot.pdf) + frontend CSV из analyze response, loading state, error display.
- site-finder-api.ts: useParcelAnalyzeQuery (B5) + useParcelPoiScoreQuery (B6) с MOCK_ANALYZE/MOCK_POI_SCORE fallback.
- mocks: parcel-analyze.json + poi-score.json fixtures для dev.
- page.tsx: Section 1 placeholder заменён на <Section1ParcelInfo cad={cad} />.
2026-05-18 01:43:11 +03:00