- 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
124 lines
3.4 KiB
TypeScript
124 lines
3.4 KiB
TypeScript
"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>Возможная пойма — река/канал в <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>
|
||
);
|
||
}
|