feat(site-finder): прогрессивное раскрытие секций отчёта §1–§6 («Раскрыть всё») (#2116)
This commit is contained in:
parent
663f04b777
commit
e52dc47067
7 changed files with 413 additions and 346 deletions
|
|
@ -21,6 +21,7 @@ import { NspdRegulationCard } from "./NspdRegulationCard";
|
||||||
import { NspdVerifyLink } from "./NspdVerifyLink";
|
import { NspdVerifyLink } from "./NspdVerifyLink";
|
||||||
import { PoiList2Gis } from "./PoiList2Gis";
|
import { PoiList2Gis } from "./PoiList2Gis";
|
||||||
import { ExportButtons } from "./ExportButtons";
|
import { ExportButtons } from "./ExportButtons";
|
||||||
|
import { StageDetails } from "./StageDetails";
|
||||||
import {
|
import {
|
||||||
adaptEgrn,
|
adaptEgrn,
|
||||||
formatEncumbrance,
|
formatEncumbrance,
|
||||||
|
|
@ -295,6 +296,7 @@ export function Section1ParcelInfo({ cad }: Props) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<StageDetails>
|
||||||
{/* 2-column grid: map (left) + EGRN + POI (right) */}
|
{/* 2-column grid: map (left) + EGRN + POI (right) */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -351,6 +353,7 @@ export function Section1ParcelInfo({ cad }: Props) {
|
||||||
>
|
>
|
||||||
<ExportButtons cad={cad} analyzeData={data} egrn={egrn} />
|
<ExportButtons cad={cad} analyzeData={data} egrn={egrn} />
|
||||||
</div>
|
</div>
|
||||||
|
</StageDetails>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ import {
|
||||||
type UtilityInfrastructureResponse,
|
type UtilityInfrastructureResponse,
|
||||||
} from "@/hooks/useUtilityInfrastructure";
|
} from "@/hooks/useUtilityInfrastructure";
|
||||||
import { buildUtilityTableRows, type UtilityTableRow } from "./utility-table";
|
import { buildUtilityTableRows, type UtilityTableRow } from "./utility-table";
|
||||||
|
import { StageDetails } from "./StageDetails";
|
||||||
|
|
||||||
// ── Connection-points map (#1746) ─────────────────────────────────────────────
|
// ── Connection-points map (#1746) ─────────────────────────────────────────────
|
||||||
// Высота тайла карты для drill-in уровня раздела. Легенда + CpLayerControlPanel
|
// Высота тайла карты для drill-in уровня раздела. Легенда + CpLayerControlPanel
|
||||||
|
|
@ -770,6 +771,7 @@ export function Section2NetworksUtilities({ cad }: Props) {
|
||||||
<HeadlineBar title="Сети" subtitle={headlineSubtitle} />
|
<HeadlineBar title="Сети" subtitle={headlineSubtitle} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<StageDetails>
|
||||||
{/* Power line охранная зона warning */}
|
{/* Power line охранная зона warning */}
|
||||||
{powerLineZone && (
|
{powerLineZone && (
|
||||||
<div
|
<div
|
||||||
|
|
@ -794,8 +796,9 @@ export function Section2NetworksUtilities({ cad }: Props) {
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
Участок находится в охранной зоне ЛЭП ≥35 кВ. Капитальное
|
Участок находится в охранной зоне ЛЭП ≥35 кВ. Капитальное
|
||||||
строительство в охранной зоне (ОЗ) запрещено (СП 36.13330). Участок
|
строительство в охранной зоне (ОЗ) запрещено (СП 36.13330).
|
||||||
в зоне с особыми условиями использования территорий (ЗОУИТ), тип 5.
|
Участок в зоне с особыми условиями использования территорий
|
||||||
|
(ЗОУИТ), тип 5.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -876,11 +879,16 @@ export function Section2NetworksUtilities({ cad }: Props) {
|
||||||
<Info
|
<Info
|
||||||
size={14}
|
size={14}
|
||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
style={{ flexShrink: 0, marginTop: 1, color: "var(--fg-tertiary)" }}
|
style={{
|
||||||
|
flexShrink: 0,
|
||||||
|
marginTop: 1,
|
||||||
|
color: "var(--fg-tertiary)",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<span>{note}</span>
|
<span>{note}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</StageDetails>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import type {
|
||||||
ParcelAnalysis,
|
ParcelAnalysis,
|
||||||
ParcelAnalysisCompetitor,
|
ParcelAnalysisCompetitor,
|
||||||
} from "@/types/site-finder";
|
} from "@/types/site-finder";
|
||||||
|
import { StageDetails } from "./StageDetails";
|
||||||
|
|
||||||
// ── Types ─────────────────────────────────────────────────────────────────────
|
// ── Types ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -799,6 +800,7 @@ export function Section3SettingsAndCompetitors({ cad, data }: Props) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<StageDetails>
|
||||||
{/* Sub-sections */}
|
{/* Sub-sections */}
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
|
||||||
<Section31Settings filters={filters} onFiltersChange={setFilters} />
|
<Section31Settings filters={filters} onFiltersChange={setFilters} />
|
||||||
|
|
@ -833,6 +835,7 @@ export function Section3SettingsAndCompetitors({ cad, data }: Props) {
|
||||||
{competitorCount} конкурентов
|
{competitorCount} конкурентов
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</StageDetails>
|
||||||
|
|
||||||
{/* Competitor detail drawer */}
|
{/* Competitor detail drawer */}
|
||||||
<CompetitorDetailDrawer
|
<CompetitorDetailDrawer
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import { GeotechRiskBlock } from "@/components/site-finder/GeotechRiskBlock";
|
||||||
import { NspdZouitOverlapsBlock } from "@/components/site-finder/NspdZouitOverlapsBlock";
|
import { NspdZouitOverlapsBlock } from "@/components/site-finder/NspdZouitOverlapsBlock";
|
||||||
import { SuccessRecommendationBlock } from "@/components/site-finder/SuccessRecommendationBlock";
|
import { SuccessRecommendationBlock } from "@/components/site-finder/SuccessRecommendationBlock";
|
||||||
import { useParcelAnalyzeQuery } from "@/lib/site-finder-api";
|
import { useParcelAnalyzeQuery } from "@/lib/site-finder-api";
|
||||||
|
import { StageDetails } from "./StageDetails";
|
||||||
import type { ParcelAnalysis } from "@/types/site-finder";
|
import type { ParcelAnalysis } from "@/types/site-finder";
|
||||||
import type { FactorContribution, ScoreGroupTotal } from "@/types/site-finder";
|
import type { FactorContribution, ScoreGroupTotal } from "@/types/site-finder";
|
||||||
|
|
||||||
|
|
@ -187,6 +188,7 @@ export function Section4Estimate({ cad }: Props) {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<StageDetails>
|
||||||
{/* ── Risk blocks grid ─────────────────────────────────────────────── */}
|
{/* ── Risk blocks grid ─────────────────────────────────────────────── */}
|
||||||
{(analysis.geology || analysis.hydrology || analysis.geotech_risk) && (
|
{(analysis.geology || analysis.hydrology || analysis.geotech_risk) && (
|
||||||
<div
|
<div
|
||||||
|
|
@ -253,6 +255,7 @@ export function Section4Estimate({ cad }: Props) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</StageDetails>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import { Wind, Thermometer, Droplets } from "lucide-react";
|
||||||
|
|
||||||
import { HeadlineBar } from "@/components/ui/HeadlineBar";
|
import { HeadlineBar } from "@/components/ui/HeadlineBar";
|
||||||
import { SeasonalWeatherBlock } from "@/components/site-finder/SeasonalWeatherBlock";
|
import { SeasonalWeatherBlock } from "@/components/site-finder/SeasonalWeatherBlock";
|
||||||
|
import { StageDetails } from "./StageDetails";
|
||||||
import { useParcelAnalyzeQuery } from "@/lib/site-finder-api";
|
import { useParcelAnalyzeQuery } from "@/lib/site-finder-api";
|
||||||
import type { ParcelAnalysis } from "@/types/site-finder";
|
import type { ParcelAnalysis } from "@/types/site-finder";
|
||||||
import type {
|
import type {
|
||||||
|
|
@ -625,6 +626,7 @@ export function Section5Atmosphere({ cad }: Props) {
|
||||||
|
|
||||||
{/* Seasonal weather (climate normals) */}
|
{/* Seasonal weather (climate normals) */}
|
||||||
{hasSeasonal && (
|
{hasSeasonal && (
|
||||||
|
<StageDetails>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -644,6 +646,7 @@ export function Section5Atmosphere({ cad }: Props) {
|
||||||
</div>
|
</div>
|
||||||
<SeasonalWeatherBlock seasonal={analysis.seasonal_weather} />
|
<SeasonalWeatherBlock seasonal={analysis.seasonal_weather} />
|
||||||
</div>
|
</div>
|
||||||
|
</StageDetails>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import { ForecastScoringBlock } from "./ForecastScoringBlock";
|
||||||
import { ForecastFutureSupplyBlock } from "./ForecastFutureSupplyBlock";
|
import { ForecastFutureSupplyBlock } from "./ForecastFutureSupplyBlock";
|
||||||
import { ForecastMetaLine } from "./ForecastMetaLine";
|
import { ForecastMetaLine } from "./ForecastMetaLine";
|
||||||
import { ForecastExportButtons } from "./ForecastExportButtons";
|
import { ForecastExportButtons } from "./ForecastExportButtons";
|
||||||
|
import { StageDetails } from "./StageDetails";
|
||||||
import {
|
import {
|
||||||
CONFIDENCE_RU,
|
CONFIDENCE_RU,
|
||||||
deficitPegState,
|
deficitPegState,
|
||||||
|
|
@ -373,6 +374,7 @@ function ForecastReady({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<StageDetails>
|
||||||
{/* Траектория индекса дефицита (chart-then-table, выше 6.1). Причину
|
{/* Траектория индекса дефицита (chart-then-table, выше 6.1). Причину
|
||||||
схлопывания сценариев показываем в 6.2 под заголовком «Почему один
|
схлопывания сценариев показываем в 6.2 под заголовком «Почему один
|
||||||
сценарий, а не три» (#1963) — здесь дубль убран. */}
|
сценарий, а не три» (#1963) — здесь дубль убран. */}
|
||||||
|
|
@ -468,6 +470,7 @@ function ForecastReady({
|
||||||
{ADVISORY_DISCLAIMER}
|
{ADVISORY_DISCLAIMER}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</StageDetails>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
interface StageDetailsProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
openLabel?: string;
|
||||||
|
closeLabel?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function StageDetails({
|
||||||
|
children,
|
||||||
|
openLabel = "Раскрыть всё",
|
||||||
|
closeLabel = "Свернуть",
|
||||||
|
}: StageDetailsProps) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-expanded={open}
|
||||||
|
onClick={() => setOpen((prev) => !prev)}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
padding: "10px 14px",
|
||||||
|
border: "1px solid var(--border-card)",
|
||||||
|
borderRadius: 8,
|
||||||
|
background: "var(--bg-card-alt)",
|
||||||
|
color: "var(--accent)",
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: 500,
|
||||||
|
cursor: "pointer",
|
||||||
|
textAlign: "center",
|
||||||
|
marginTop: 16,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{open ? closeLabel : openLabel}
|
||||||
|
{open ? " ▴" : " ▾"}
|
||||||
|
</button>
|
||||||
|
{open && <div style={{ marginTop: 16 }}>{children}</div>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue