From e23dabe4f5d32836b770475acb81d3c2f8431219 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sun, 28 Jun 2026 18:03:07 +0300 Subject: [PATCH] =?UTF-8?q?fix(tradein/v2):=20wire=20=D0=A0=D0=90=D0=94?= =?UTF-8?q?=D0=98=D0=A3=D0=A1=20(backend=20radius=5Fm)=20+=20map=20+/-=20z?= =?UTF-8?q?oom=20+=20building=20image=20+=20deglue=20word+word?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live-browser audit (round 2): РАДИУС/+- dead, building photo missing. - РАДИУС: backend BE-2 — optional radius_m (int 100–5000) on TradeInEstimateInput, threaded into estimate_quality comp search (base + fallback). None preserves the exact two-tier default (1000m primary / 2000m fallback) byte-identically. FE: РАДИУС dropdown with **Авто** default (sends no radius_m → legacy behaviour, NOT a silent 500m narrowing) + 300/500/1000/2000 overrides; included as radius_m on submit; outer map ring tracks the value. - Map +/− buttons: now a real zoom (transform: scale() on the map content layer, step .25, clamp .6–2.5); controls/bracket stay unscaled. - building.png: next/image didn't apply basePath → 400; switched to plain with NEXT_PUBLIC_BASE_PATH-prefixed src (→ 200) + onError fallback. HERO АДРЕС card шоу. - deglueAddr: also splits word+word glue ('БольшаковаГеологическая'→'Большакова Геологическая'), still preserves house letters (14А, д.5К). next build green (/v2 34.3 kB); estimator None-default byte-identical (ge=100 rules out falsy-0); code-reviewer ✅ after the Авто-default fix (avoided a default-radius regression). --- tradein-mvp/backend/app/schemas/trade_in.py | 5 + tradein-mvp/backend/app/services/estimator.py | 17 ++- .../src/components/trade-in/v2/HeroBar.tsx | 17 ++- .../components/trade-in/v2/ParamsPanel.tsx | 105 ++++++++++++++++-- .../src/components/trade-in/v2/mappers.ts | 19 ++-- tradein-mvp/frontend/src/types/trade-in.ts | 3 + 6 files changed, 136 insertions(+), 30 deletions(-) diff --git a/tradein-mvp/backend/app/schemas/trade_in.py b/tradein-mvp/backend/app/schemas/trade_in.py index ea558765..2af77c3f 100644 --- a/tradein-mvp/backend/app/schemas/trade_in.py +++ b/tradein-mvp/backend/app/schemas/trade_in.py @@ -27,6 +27,11 @@ class TradeInEstimateInput(BaseModel): # geocode() (который падает на DaData-формах при мёртвом Yandex-ключе). lat: float | None = Field(default=None, ge=-90, le=90) lon: float | None = Field(default=None, ge=-180, le=180) + # #2044: опциональный радиус анализа (контрол РАДИУС на /trade-in/v2), метры. + # None → текущее дефолтное поведение (1000 м поиск аналогов, 2000 м fallback). + # Задан → и первичный, и fallback-поиск аналогов/сделок используют ровно этот + # радиус (без авто-расширения). Диапазон 100–5000 м — sane guard. + radius_m: int | None = Field(default=None, ge=100, le=5000) # CRM-поля (#395) — операционные, на расчёт оценки не влияют ownership_type: str | None = Field(default=None, max_length=100) has_mortgage: bool | None = None diff --git a/tradein-mvp/backend/app/services/estimator.py b/tradein-mvp/backend/app/services/estimator.py index fb039db5..0e4d60e0 100644 --- a/tradein-mvp/backend/app/services/estimator.py +++ b/tradein-mvp/backend/app/services/estimator.py @@ -2592,6 +2592,13 @@ async def estimate_quality( # a) 1km + ±15% area (без cohort — drop fallback) # b) 2km + ±15% area (fallback_used = True) # c) 2km + ±25% area (fallback_used = True, area_widened = True) + # + # #2044: опциональный override радиуса анализа (контрол РАДИУС). None → + # точное текущее поведение (DEFAULT 1000 м первичный, FALLBACK 2000 м + # расширение). Задан → и первичный, и fallback-поиск используют ровно этот + # радиус (он же — максимум, без авто-расширения за пределы выбранного). + base_radius_m = payload.radius_m or DEFAULT_RADIUS_M + fallback_radius_m = payload.radius_m or FALLBACK_RADIUS_M cohort_range = _target_cohort_range(target_year) if cohort_range is not None: @@ -2602,7 +2609,7 @@ async def estimate_quality( lon=geo.lon, rooms=payload.rooms, area=payload.area_m2, - radius_m=DEFAULT_RADIUS_M, + radius_m=base_radius_m, full_address=geo.full_address, target_house_id=target_house_id, year_built=target_year, @@ -2626,7 +2633,7 @@ async def estimate_quality( lon=geo.lon, rooms=payload.rooms, area=payload.area_m2, - radius_m=DEFAULT_RADIUS_M, + radius_m=base_radius_m, full_address=geo.full_address, target_house_id=target_house_id, year_built=target_year, @@ -2642,7 +2649,7 @@ async def estimate_quality( lon=geo.lon, rooms=payload.rooms, area=payload.area_m2, - radius_m=FALLBACK_RADIUS_M, + radius_m=fallback_radius_m, full_address=geo.full_address, target_house_id=target_house_id, year_built=target_year, @@ -2663,7 +2670,7 @@ async def estimate_quality( lon=geo.lon, rooms=payload.rooms, area=payload.area_m2, - radius_m=FALLBACK_RADIUS_M, + radius_m=fallback_radius_m, area_tolerance=0.25, full_address=geo.full_address, target_house_id=target_house_id, @@ -2877,7 +2884,7 @@ async def estimate_quality( lon=geo.lon, rooms=payload.rooms, area=payload.area_m2, - radius_m=DEFAULT_RADIUS_M, + radius_m=base_radius_m, ) # 6. Сохраняем в trade_in_estimates diff --git a/tradein-mvp/frontend/src/components/trade-in/v2/HeroBar.tsx b/tradein-mvp/frontend/src/components/trade-in/v2/HeroBar.tsx index 55090e3a..6864cd46 100644 --- a/tradein-mvp/frontend/src/components/trade-in/v2/HeroBar.tsx +++ b/tradein-mvp/frontend/src/components/trade-in/v2/HeroBar.tsx @@ -1,7 +1,6 @@ "use client"; import { useState, type CSSProperties } from "react"; -import Image from "next/image"; import { API_BASE_URL } from "@/lib/api"; import { safeUrl } from "@/lib/safeUrl"; @@ -13,6 +12,11 @@ import type { HeroBarData } from "./mappers"; // Default presentation data (unwired usage): the existing design fixtures. const HERO_FIXTURE: HeroBarData = { report, object }; +// next/image does NOT prepend the configured basePath ("/trade-in") to a +// literal src, so an 404s behind Caddy. A plain +// with the basePath baked in resolves to /trade-in/trade-in-v2/… → 200. +const BP = process.env.NEXT_PUBLIC_BASE_PATH ?? ""; + // estimate ids are server-issued UUIDs — reject anything else before it lands // in the PDF request path so a tampered id cannot be injected. const PDF_UUID_RE = @@ -244,14 +248,15 @@ export default function HeroBar({ }} > {!imgFailed && ( - setImgFailed(true)} style={{ + position: "absolute", + inset: 0, + width: "100%", + height: "100%", objectFit: "contain", objectPosition: "right center", filter: "saturate(.9) brightness(1.04)", diff --git a/tradein-mvp/frontend/src/components/trade-in/v2/ParamsPanel.tsx b/tradein-mvp/frontend/src/components/trade-in/v2/ParamsPanel.tsx index 40e0a779..db330cb1 100644 --- a/tradein-mvp/frontend/src/components/trade-in/v2/ParamsPanel.tsx +++ b/tradein-mvp/frontend/src/components/trade-in/v2/ParamsPanel.tsx @@ -7,8 +7,9 @@ // styles are UNCHANGED — only the data plumbing differs (display
s became // s styled identically, dropdowns now feed real enum values). RU dropdown // labels <-> API enum values go through HOUSE_TYPE_*/REPAIR_* maps in ./mappers. -// The РАДИУС and CRM dropdowns have no backend yet → kept visually but disabled -// (see TODOs). Hover/active + @keyframes live in a pp-prefixed local