"use client"; /* История оценок (#399). */ import { useEffect, useState } from "react"; import "@/components/trade-in/trade-in.css"; import { API_BASE_URL } from "@/lib/api"; interface HistoryRow { id: string; address: string; rooms: number; area_m2: number; median_price: number; confidence: string; n_analogs: number; created_at: string; } export default function HistoryPage() { const [rows, setRows] = useState([]); const [loading, setLoading] = useState(true); // #770 finding #25: не маскировать ошибку пустым списком — иначе «нет оценок» // показывается и при реальном сбое загрузки. const [error, setError] = useState(false); useEffect(() => { fetch(`${API_BASE_URL}/api/v1/trade-in/history?limit=100`) .then((r) => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); }) .then((d) => setRows(d as HistoryRow[])) .catch(() => setError(true)) .finally(() => setLoading(false)); }, []); return (

← К оценке

История оценок

{loading ? (

Загрузка…

) : error ? (

Не удалось загрузить историю оценок. Обновите страницу или попробуйте позже.

) : rows.length === 0 ? (

Оценок пока нет.

) : ( {rows.map((r) => ( ))}
Дата Адрес Комн. Площадь Медиана, ₽ Точность
{new Date(r.created_at).toLocaleDateString("ru-RU")} {r.address} {r.rooms} {r.area_m2} м² {Number(r.median_price).toLocaleString("ru-RU")} {/* #1871 P1.2 — ghost-anchor defensive depth: n_analogs=0 → low */} {r.n_analogs === 0 ? "low" : r.confidence}
)}
); }