diff --git a/frontend/src/components/site-finder/ptica/reports/PticaReports.tsx b/frontend/src/components/site-finder/ptica/reports/PticaReports.tsx index b0c5e76d..0dbce1a8 100644 --- a/frontend/src/components/site-finder/ptica/reports/PticaReports.tsx +++ b/frontend/src/components/site-finder/ptica/reports/PticaReports.tsx @@ -31,6 +31,7 @@ import { API_BASE_URL } from "@/lib/api"; import { triggerDownload } from "@/lib/download"; import { useParcelForecastQuery } from "@/lib/site-finder-api"; import type { ParcelAnalysis } from "@/types/site-finder"; +import { StatusRow } from "@/components/site-finder/ptica/drawers/DrawerPrimitives"; import styles from "@/app/site-finder/analysis/[cad]/ptica/ptica.module.css"; interface Props { @@ -345,6 +346,19 @@ export function PticaReports({ cad, analysis }: Props) { {error}

)} + + {/* ── Параметры — что войдёт в сгенерированный отчёт ─────────────── */} +
+

+ Параметры OPTIONS +

+ + + {/* ── Report-section checklist ──────────────────────────────────── */} diff --git a/frontend/src/components/site-finder/ptica/scenarios/FutureSupply.tsx b/frontend/src/components/site-finder/ptica/scenarios/FutureSupply.tsx new file mode 100644 index 00000000..26cea25c --- /dev/null +++ b/frontend/src/components/site-finder/ptica/scenarios/FutureSupply.tsx @@ -0,0 +1,84 @@ +/** + * FutureSupply — 6.6 «Будущее предложение и конкуренты». Renders + * §9.7 forward-looking competitors (report.future_market.future_competitors): + * one row per forecast competitor — ЖК / проект · класс · квартир · дистанция · + * темп поглощения. Distinct from the market_now competitors block: these are the + * top-N projects ranked by `relevance_weight` at the horizon that will compete + * for the same demand. + * + * The prototype's «Срок сдачи» column has NO backing field in the §22 report, so + * it is dropped in favour of the real `velocity_per_month` (темп поглощения) — + * every value traces to a real forecast field. Rows are sorted by distance asc + * (closest competitors first). Nullable fields render «нет данных», never a + * fabricated 0. Empty/absent list → honest empty-state, NOT a hidden section. + */ + +import styles from "@/app/site-finder/analysis/[cad]/ptica/ptica.module.css"; +import type { FutureCompetitor } from "@/types/forecast"; +import { fmtInt, fmtNum } from "./scenario-helpers"; + +interface Props { + competitors: FutureCompetitor[] | undefined; +} + +/** Distance in metres → «445 м» / «1.20 км» (mirrors PticaMapInner.formatMeters). */ +function formatMeters(m: number): string { + if (m >= 1000) return `${(m / 1000).toFixed(2)} км`; + return `${Math.round(m)} м`; +} + +export function FutureSupply({ competitors }: Props) { + const rows = (competitors ?? []) + .slice() + .sort((a, b) => a.distance_m - b.distance_m); + + return ( + <> +
+ 6.6 · будущее предложение и конкуренты +
+
+ {rows.length === 0 ? ( +
+ Нет данных по конкурирующему предложению +
+ ) : ( + <> + + + + + + + + + + + + {rows.map((c) => ( + + + + + + + + ))} + +
ЖК / проектКлассКвартирДистанцияПоглощение
{c.comm_name ?? "нет данных"}{c.obj_class ?? "нет данных"} + {c.flats_total != null + ? `${fmtInt(c.flats_total)} кв` + : "нет данных"} + {formatMeters(c.distance_m)} + {`${fmtNum(c.velocity_per_month, 1)} кв/мес`} +
+

+ Конкурирующие проекты в горизонте прогноза, отранжированные по + близости. Поглощение — темп продаж проекта (квартир в месяц). +

+ + )} +
+ + ); +} diff --git a/frontend/src/components/site-finder/ptica/scenarios/PticaScenarios.tsx b/frontend/src/components/site-finder/ptica/scenarios/PticaScenarios.tsx index 4a3d1c9e..4bd66a62 100644 --- a/frontend/src/components/site-finder/ptica/scenarios/PticaScenarios.tsx +++ b/frontend/src/components/site-finder/ptica/scenarios/PticaScenarios.tsx @@ -24,6 +24,7 @@ import { ScenarioCompareTable } from "./ScenarioCompareTable"; import { ConfidencePanel } from "./ConfidencePanel"; import { RecommendedProduct } from "./RecommendedProduct"; import { ScoringTransparency } from "./ScoringTransparency"; +import { FutureSupply } from "./FutureSupply"; interface Props { cad: string; @@ -148,6 +149,8 @@ export function PticaScenarios({ cad, horizon, onHorizonChange }: Props) { {report.scoring && } + + ); }