diff --git a/tradein-mvp/backend/app/api/v1/trade_in.py b/tradein-mvp/backend/app/api/v1/trade_in.py index 219d0221..522ec816 100644 --- a/tradein-mvp/backend/app/api/v1/trade_in.py +++ b/tradein-mvp/backend/app/api/v1/trade_in.py @@ -299,7 +299,9 @@ def get_estimate( SELECT id, median_price, range_low, range_high, median_price_per_m2, confidence, confidence_explanation, n_analogs, analogs, actual_deals, sources_used, data_freshness_minutes, - expires_at, address, lat, lon + expires_at, address, lat, lon, + area_m2, rooms, floor, total_floors, + year_built, house_type, repair_state, has_balcony FROM trade_in_estimates WHERE id = CAST(:id AS uuid) AND expires_at > NOW() @@ -335,6 +337,14 @@ def get_estimate( target_lon=row.lon, sources_used=row.sources_used or [], data_freshness_minutes=row.data_freshness_minutes, + area_m2=row.area_m2, + rooms=row.rooms, + floor=row.floor, + total_floors=row.total_floors, + year_built=row.year_built, + house_type=row.house_type, + repair_state=row.repair_state, + has_balcony=row.has_balcony, ) diff --git a/tradein-mvp/backend/app/schemas/trade_in.py b/tradein-mvp/backend/app/schemas/trade_in.py index b0e70835..88f353fd 100644 --- a/tradein-mvp/backend/app/schemas/trade_in.py +++ b/tradein-mvp/backend/app/schemas/trade_in.py @@ -66,6 +66,16 @@ class AggregatedEstimate(BaseModel): sources_used: list[str] = Field(default_factory=list) # ['avito', 'cian', 'rosreestr'] data_freshness_minutes: int | None = None # сколько минут назад был самый свежий парсинг est_days_on_market: int | None = None # прогноз срока продажи (медиана по аналогам) + # ── Параметры оценённой квартиры — нужны, чтобы восстановить карточку + # при открытии оценки по ссылке (?id=), когда формы-инпута уже нет ── + area_m2: float | None = None + rooms: int | None = None + floor: int | None = None + total_floors: int | None = None + year_built: int | None = None + house_type: str | None = None + repair_state: str | None = None + has_balcony: bool | None = None class PhotoMeta(BaseModel): diff --git a/tradein-mvp/backend/app/services/estimator.py b/tradein-mvp/backend/app/services/estimator.py index d319891b..f4f81cdb 100644 --- a/tradein-mvp/backend/app/services/estimator.py +++ b/tradein-mvp/backend/app/services/estimator.py @@ -288,6 +288,14 @@ async def estimate_quality( sources_used=sources_used, data_freshness_minutes=freshness_min, est_days_on_market=_estimate_days_on_market(listings_clean, deals), + area_m2=payload.area_m2, + rooms=payload.rooms, + floor=payload.floor, + total_floors=payload.total_floors, + year_built=target_year, + house_type=target_house_type, + repair_state=payload.repair_state, + has_balcony=payload.has_balcony, ) diff --git a/tradein-mvp/frontend/src/components/trade-in/HeroSummary.tsx b/tradein-mvp/frontend/src/components/trade-in/HeroSummary.tsx index 85ecf6d0..1126e8f4 100644 --- a/tradein-mvp/frontend/src/components/trade-in/HeroSummary.tsx +++ b/tradein-mvp/frontend/src/components/trade-in/HeroSummary.tsx @@ -58,6 +58,17 @@ export function HeroSummary({ estimate, input }: Props) { const medianPctRaw = span > 0 ? ((m - lo) / span) * 100 : 50; const medianPct = Math.max(5, Math.min(95, medianPctRaw)); + // Параметры квартиры берём из estimate (надёжно при восстановлении по ?id=), + // с откатом на форму input — у свежей оценки оба источника совпадают. + const floor = estimate.floor ?? input.floor; + const totalFloors = estimate.total_floors ?? input.total_floors; + const areaM2 = estimate.area_m2 ?? input.area_m2; + const rooms = estimate.rooms ?? input.rooms; + const yearBuilt = estimate.year_built ?? input.year_built; + const houseType = estimate.house_type ?? input.house_type; + const repairState = estimate.repair_state ?? input.repair_state; + const hasBalcony = estimate.has_balcony ?? input.has_balcony; + return (
@@ -103,48 +114,48 @@ export function HeroSummary({ estimate, input }: Props) { {estimate.target_address ?? input.address}
- ЭТАЖ {input.floor}/{input.total_floors} - {input.year_built ? ` · ${input.year_built}` : ""} + ЭТАЖ {floor}/{totalFloors} + {yearBuilt ? ` · ${yearBuilt}` : ""}
- {input.year_built && ( + {yearBuilt && (
Год постройки - {input.year_built} + {yearBuilt}
)} - {input.house_type && ( + {houseType && (
Тип дома - {HOUSE_TYPE_LABELS[input.house_type] ?? input.house_type} + {HOUSE_TYPE_LABELS[houseType] ?? houseType}
)}
Этаж - {input.floor} / {input.total_floors} + {floor} / {totalFloors}
Площадь - {input.area_m2} м² + {areaM2} м²
Планировка - {input.rooms === 0 ? "Студия" : `${input.rooms}-к`}, классическая + {rooms === 0 ? "Студия" : `${rooms}-к`}, классическая
- {input.repair_state && ( + {repairState && (
Состояние - {REPAIR_LABELS[input.repair_state] ?? input.repair_state} + {REPAIR_LABELS[repairState] ?? repairState}
)}
Балкон - {input.has_balcony ? "есть" : "нет"} + {hasBalcony ? "есть" : "нет"}
Аналогов diff --git a/tradein-mvp/frontend/src/types/trade-in.ts b/tradein-mvp/frontend/src/types/trade-in.ts index 859bb478..77ddcb36 100644 --- a/tradein-mvp/frontend/src/types/trade-in.ts +++ b/tradein-mvp/frontend/src/types/trade-in.ts @@ -65,4 +65,14 @@ export interface AggregatedEstimate { target_lon: number | null; sources_used: string[]; // ['avito', 'cian', 'rosreestr'] data_freshness_minutes: number | null; // «обновлено N минут назад» + est_days_on_market: number | null; // прогноз срока продажи + // ── Параметры оценённой квартиры (для восстановления карточки по ?id=) ── + area_m2: number | null; + rooms: number | null; + floor: number | null; + total_floors: number | null; + year_built: number | null; + house_type: HouseType | null; + repair_state: RepairState | null; + has_balcony: boolean | null; }