diff --git a/frontend/src/components/site-finder/analysis/Section7Concept.tsx b/frontend/src/components/site-finder/analysis/Section7Concept.tsx index b80c5228..883cb004 100644 --- a/frontend/src/components/site-finder/analysis/Section7Concept.tsx +++ b/frontend/src/components/site-finder/analysis/Section7Concept.tsx @@ -212,6 +212,14 @@ export function Section7Concept({ analysis }: Props) { //
via the summary; onToggle keeps `expanded` in sync when reopened. const [expanded, setExpanded] = useState(true); + // Одноразовый авторетрай (fix 2026-07-03): на узких/мелких участках дефолтная + // программа (3 корпуса comfort 21×18) честно не влезает — движок отвечает 422 + // «программа застройки не вместила ни одной секции». Показывать это ошибкой + // при ЗАГРУЗКЕ отчёта нельзя (прод-кейс 66:41:0702033:20) — пробуем один раз + // densify=true: greedy-раскладка сама вмещает, что физически влезает. + // Не влезло и так → честная ошибка остаётся. Ручные запуски не ретраятся. + const autoRetriedRef = useRef(false); + useEffect(() => { if (autoRanRef.current || !expanded) return; if (!polygon || catalog.length === 0) return; @@ -233,6 +241,27 @@ export function Section7Concept({ analysis }: Props) { }); }, [expanded, polygon, catalog, concept, seeded, handleGenerate]); + useEffect(() => { + if (!autoRanRef.current || autoRetriedRef.current) return; + if (!concept.isError || !polygon || catalog.length === 0) return; + if (!/не вместила/i.test(concept.error.message)) return; + const def = pickDefaultType(catalog); + if (!def) return; + autoRetriedRef.current = true; + handleGenerate({ + corpuses: CORPUSES_DEFAULT, + floors: def.default_floors > 0 ? def.default_floors : FLOORS_FALLBACK, + floor_height_m: 3.0, + section_type: def.section_type, + footprint_w_m: def.footprint_w_m, + footprint_d_m: def.footprint_d_m, + housing_class: seeded.housing_class, + development_type: seeded.development_type, + land_cost_rub: seeded.land_cost_rub, + densify: true, // greedy max-FAR: вместить, что физически влезает + }); + }, [concept, polygon, catalog, seeded, handleGenerate]); + const handleToggle = useCallback( (e: React.SyntheticEvent) => { if (e.currentTarget.open) setExpanded(true);