gendesign/frontend/src/components/site-finder/HydrologyBlock.tsx
Light1YT f7773c71d4
All checks were successful
CI / openapi-codegen-check (pull_request) Successful in 1m46s
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 48s
fix(site-finder): P3 a11y/UI batch from audit #1871
- add <h1> page title to analysis page (иерархия стартовала с h2 — a11y)
- emoji → Lucide во всех вердикт/баннер/статус UI (ui-conventions: emoji
  запрещены): GateVerdictBanner (VerdictColor.icon string→ComponentType,
  sourceHint→ReactNode, SectionLabels), + найдены ещё 3 файла:
  OverviewTab (⚠ ЛЭП/трамвай), GeotechRiskBlock (🟡 вечная мерзлота → Snowflake,
  ⚠ загрязнение), HydrologyBlock (⚠ паводок, 🏞💦🪷🚣💧 subtype-map → Waves/Droplet).
  Все иконки декоративные → aria-hidden, семантика дублируется текстом.
  Семантические цвета через var(--success/--warn/--danger) + hex-fallback.
- расшифровка аббревиатур при первом упоминании (ui-microcopy):
  Section2 «строительство в охранной зоне (ОЗ)... зоне с особыми условиями
  использования территорий (ЗОУИТ), тип 5»; Section4 title-tooltip для ЗОУИТ.
- ForecastChart: axis-chrome hex (:65/67/68) задокументирован как canvas-renderer
  исключение (echarts canvas не резолвит CSS var() — var() сломал бы chrome,
  как и VIZ-series). Цвета не тронуты, только комментарий.

НЕ в scope: ForecastChart CI-band (±p25/p75 — backend не отдаёт, отдельная
задача); ★ в WeightProfilePanel <option> (SVG в option невозможен);
токенизация legacy-hex в Geotech/Hydrology (отдельный pass).

138 frontend vitest passed, tsc/lint clean. lucide-react уже в deps.

Refs #1871
2026-06-23 12:52:32 +05:00

124 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import type { ComponentType } from "react";
import { AlertTriangle, Droplet, Waves, type LucideProps } from "lucide-react";
import type { Hydrology, HydrologyNearest } from "@/types/site-finder";
interface Props {
hydrology: Hydrology;
}
// Decorative water glyph per subtype — the text label already names the body
// (река / ручей / озеро / канал), so the icon is aria-hidden. Lucide has no
// distinct lake/canal glyph; flowing bodies use Waves, the rest use Droplet.
const SUBTYPE_ICON: Record<string, ComponentType<LucideProps>> = {
river: Waves,
stream: Droplet,
lake_or_pond: Waves,
canal: Waves,
};
function subtypeLabel(subtype: HydrologyNearest["subtype"]): string {
switch (subtype) {
case "river":
return "река";
case "stream":
return "ручей";
case "lake_or_pond":
return "озеро/пруд";
case "canal":
return "канал";
default:
return "водоём";
}
}
export function HydrologyBlock({ hydrology }: Props) {
const top5 = hydrology.nearest.slice(0, 5);
return (
<div
style={{
borderRadius: 10,
padding: "14px 16px",
background: "#f0f9ff",
border: "1px solid #bae6fd",
display: "flex",
flexDirection: "column",
gap: 10,
}}
>
<div style={{ fontSize: 13, fontWeight: 700, color: "#0369a1" }}>
Гидрология
</div>
{/* Flood risk banner */}
{hydrology.flood_risk_flag && (
<div
style={{
borderRadius: 8,
padding: "10px 14px",
background: "#fecaca",
border: "1px solid #f87171",
fontSize: 13,
fontWeight: 600,
color: "#b91c1c",
display: "flex",
alignItems: "center",
gap: 6,
}}
>
<AlertTriangle
size={16}
strokeWidth={1.5}
aria-hidden
style={{ flexShrink: 0 }}
/>
<span>Возможная пойма река/канал в &lt;200м</span>
</div>
)}
{/* Nearest water bodies */}
{top5.length === 0 ? (
<div style={{ fontSize: 13, color: "#6b7280" }}>
Поблизости водных объектов нет
</div>
) : (
<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
{top5.map((item, i) => {
const Icon = SUBTYPE_ICON[item.subtype ?? ""] ?? Droplet;
const label = subtypeLabel(item.subtype);
const name = item.name ?? "б/н";
return (
<div
key={i}
style={{
display: "flex",
alignItems: "center",
gap: 6,
fontSize: 13,
color: "#374151",
}}
>
<Icon
size={16}
strokeWidth={1.5}
aria-hidden
style={{ flexShrink: 0, color: "#0369a1" }}
/>
<span>
{name} {label} {Math.round(item.distance_m)} м
</span>
</div>
);
})}
</div>
)}
{/* Note */}
{hydrology.note && (
<div style={{ fontSize: 11, color: "#9ca3af" }}>{hydrology.note}</div>
)}
</div>
);
}