gendesign/tradein-mvp/frontend/src/components/trade-in/HouseAnalyticsKpiRow.tsx
lekss361 7eeffe353a feat(tradein): house analytics section на trade-in странице
Backend: GET /api/v1/trade-in/estimate/{id}/house-analytics — resolves target
house_ids через нормализованный адрес или geo-fallback 100m, expand до 300m
если в самом доме <8 hist rows. Возвращает:
- price_history: median ₽/м² по году (из house_placement_history)
- recent_sold: последние 12 мес снятые с фактической ценой + торгом
- kpi: median exposure_days, median bargain %, sold_rate %

Frontend: HouseAnalyticsSection (под PlacementHistoryCard) c 3 блоками:
- PriceHistoryChart (Recharts line, median ₽/м²)
- HouseAnalyticsKpiRow (3 KPI cards)
- RecentSoldList (таблица снятых лотов)

Использует уже собранные данные house_placement_history (15k+ rows от
backfill scripts: avito_imv + yandex_valuation). Новых таблиц нет.
2026-05-24 21:00:42 +03:00

66 lines
1.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import type { HouseAnalyticsKpi } from "@/types/trade-in";
type Props = { kpi: HouseAnalyticsKpi };
function KpiCard({
label,
value,
sub,
}: {
label: string;
value: string;
sub?: string;
}) {
return (
<div className="card" style={{ padding: 12, flex: 1, minWidth: 0 }}>
<div
style={{ fontSize: 11, color: "var(--muted, #6b7280)", marginBottom: 4 }}
>
{label}
</div>
<div style={{ fontSize: 20, fontWeight: 700 }}>{value}</div>
{sub && (
<div
style={{
fontSize: 11,
color: "var(--muted, #6b7280)",
marginTop: 2,
}}
>
{sub}
</div>
)}
</div>
);
}
export function HouseAnalyticsKpiRow({ kpi }: Props) {
return (
<div style={{ display: "flex", gap: 12, marginTop: 8 }}>
<KpiCard
label="Средняя экспозиция"
value={
kpi.median_exposure_days != null
? `${kpi.median_exposure_days} дн.`
: "—"
}
sub="по архивным объявлениям"
/>
<KpiCard
label="Средний торг"
value={
kpi.median_bargain_pct != null
? `${kpi.median_bargain_pct.toFixed(1)}%`
: "—"
}
sub="от start_price до last_price"
/>
<KpiCard
label="Доля снятых"
value={`${kpi.sold_rate_pct.toFixed(0)}%`}
sub={`${kpi.sold_count} из ${kpi.total_lots}`}
/>
</div>
);
}