UI-аудит инструментами (axe/lighthouse) требует отрендеренную контентную
страницу. Live trade-in за auth-gate, локальный фронт без бэкенда = белый экран
(RouteGuard висит на pending /me). Добавлен оффлайн preview «денежного экрана».
- src/app/ui-preview/estimate/{page,fixture}.tsx — рендерит реальные компоненты
результата (HeroSummary + Cian + Distribution + Deals + StreetDeals + Exposure
+ PriceTrend) на статичной фикстуре AggregatedEstimate, без сети: свой
QueryClient с засеянным фейковым /me (useMe/useBrand резолвятся оффлайн).
- RouteGuard: bypass /ui-preview/* под флагом (RBAC к preview не применяется).
- Гейт: NEXT_PUBLIC_ENABLE_PREVIEW=1 (иначе notFound) — в проде по умолчанию off.
Папка ui-preview (НЕ _-prefix: Next трактует _* как private/non-routable).
Проверено локально (NEXT_PUBLIC_ENABLE_PREVIEW=1): страница рендерится оффлайн,
axe (wcag2a/aa) дал baseline — 2 serious: color-contrast (teal #14b8a6 на светлом
2.32<4.5) + svg-img-alt (recharts scatter без accessible text). tsc clean.
Refs #801. Followup: фиксы найденных axe-дефектов отдельными issue.
189 lines
5.1 KiB
TypeScript
189 lines
5.1 KiB
TypeScript
// #801: статичная фикстура результата оценки для preview-маршрута (a11y/lighthouse).
|
||
// Полностью оффлайн — никакой сети. Значения правдоподобны (ЕКБ, 2-к вторичка),
|
||
// заполнены ВСЕ surface'ы «денежного экрана»: asking + expected-sold + Avito IMV +
|
||
// ДКП-коридор + cian valuation + price_trend + аналоги/сделки.
|
||
// photo_url=null у всех лотов — намеренно, чтобы не тянуть внешние картинки
|
||
// (чистый оффлайн-рендер для lighthouse/axe).
|
||
|
||
import type { AggregatedEstimate, AnalogLot, TradeInEstimateInput } from "@/types/trade-in";
|
||
|
||
const analogs: AnalogLot[] = [
|
||
{
|
||
address: "ул. Репина, 73",
|
||
area_m2: 54,
|
||
rooms: 2,
|
||
floor: 7,
|
||
total_floors: 16,
|
||
price_rub: 9_700_000,
|
||
price_per_m2: 179_629,
|
||
listing_date: "2026-05-12",
|
||
days_on_market: 18,
|
||
photo_url: null,
|
||
source: "avito",
|
||
source_url: "https://www.avito.ru/ekaterinburg/kvartiry/preview-1",
|
||
distance_m: 120,
|
||
tier: null,
|
||
lat: 56.8401,
|
||
lon: 60.5702,
|
||
},
|
||
{
|
||
address: "ул. Викулова, 33",
|
||
area_m2: 57,
|
||
rooms: 2,
|
||
floor: 4,
|
||
total_floors: 10,
|
||
price_rub: 9_950_000,
|
||
price_per_m2: 174_561,
|
||
listing_date: "2026-05-20",
|
||
days_on_market: 10,
|
||
photo_url: null,
|
||
source: "cian",
|
||
source_url: "https://www.cian.ru/sale/flat/preview-2",
|
||
distance_m: 340,
|
||
tier: null,
|
||
lat: 56.8389,
|
||
lon: 60.5681,
|
||
},
|
||
{
|
||
address: "ул. Кирова, 28",
|
||
area_m2: 53,
|
||
rooms: 2,
|
||
floor: 9,
|
||
total_floors: 12,
|
||
price_rub: 9_400_000,
|
||
price_per_m2: 177_358,
|
||
listing_date: "2026-05-04",
|
||
days_on_market: 31,
|
||
photo_url: null,
|
||
source: "avito",
|
||
source_url: "https://www.avito.ru/ekaterinburg/kvartiry/preview-3",
|
||
distance_m: 510,
|
||
tier: null,
|
||
lat: 56.8372,
|
||
lon: 60.5749,
|
||
},
|
||
];
|
||
|
||
const actualDeals: AnalogLot[] = [
|
||
{
|
||
address: "ул. Репина",
|
||
area_m2: 55,
|
||
rooms: 2,
|
||
floor: null,
|
||
total_floors: null,
|
||
price_rub: 8_800_000,
|
||
price_per_m2: 160_000,
|
||
listing_date: "2026-01-01",
|
||
days_on_market: null,
|
||
photo_url: null,
|
||
source: "rosreestr",
|
||
source_url: null,
|
||
distance_m: null,
|
||
tier: "T1_per_street",
|
||
lat: null,
|
||
lon: null,
|
||
},
|
||
{
|
||
address: "ул. Викулова",
|
||
area_m2: 58,
|
||
rooms: 2,
|
||
floor: null,
|
||
total_floors: null,
|
||
price_rub: 9_280_000,
|
||
price_per_m2: 160_000,
|
||
listing_date: "2025-10-01",
|
||
days_on_market: null,
|
||
photo_url: null,
|
||
source: "rosreestr",
|
||
source_url: null,
|
||
distance_m: null,
|
||
tier: "T1_per_street",
|
||
lat: null,
|
||
lon: null,
|
||
},
|
||
];
|
||
|
||
export const FIXTURE_ESTIMATE: AggregatedEstimate = {
|
||
estimate_id: "preview-0000-0000-0000-000000000000",
|
||
median_price_rub: 9_850_000,
|
||
range_low_rub: 9_100_000,
|
||
range_high_rub: 10_600_000,
|
||
median_price_per_m2: 178_000,
|
||
expected_sold_price_rub: 8_900_000,
|
||
expected_sold_range_low_rub: 8_300_000,
|
||
expected_sold_range_high_rub: 9_500_000,
|
||
expected_sold_per_m2: 160_800,
|
||
asking_to_sold_ratio: 0.9,
|
||
ratio_basis: "per_rooms",
|
||
confidence: "high",
|
||
confidence_explanation: "12 аналогов в радиусе 800 м, разброс цен ±9%.",
|
||
n_analogs: 12,
|
||
insufficient_data: false,
|
||
period_months: 24,
|
||
analogs,
|
||
actual_deals: actualDeals,
|
||
expires_at: "2026-06-30T00:00:00Z",
|
||
target_address: "Екатеринбург, ул. Репина, 75/2",
|
||
target_lat: 56.84,
|
||
target_lon: 60.57,
|
||
sources_used: ["avito", "cian", "rosreestr"],
|
||
data_freshness_minutes: 35,
|
||
last_scraped_at: "2026-05-30T12:00:00Z",
|
||
est_days_on_market: 48,
|
||
address_precision: "house",
|
||
area_m2: 55.3,
|
||
rooms: 2,
|
||
floor: 5,
|
||
total_floors: 11,
|
||
year_built: 2018,
|
||
house_type: "monolith",
|
||
repair_state: "good",
|
||
has_balcony: true,
|
||
cian_valuation: {
|
||
sale_price_rub: 9_700_000,
|
||
rent_price_rub: 42_000,
|
||
chart: [
|
||
{ date: "2025-11", price: 9_300_000 },
|
||
{ date: "2025-12", price: 9_420_000 },
|
||
{ date: "2026-01", price: 9_510_000 },
|
||
{ date: "2026-02", price: 9_560_000 },
|
||
{ date: "2026-03", price: 9_640_000 },
|
||
{ date: "2026-04", price: 9_700_000 },
|
||
],
|
||
chart_change_pct: 3.2,
|
||
chart_change_direction: "increase",
|
||
},
|
||
avito_imv: {
|
||
recommended_price: 10_200_000,
|
||
lower_price: 9_400_000,
|
||
higher_price: 11_000_000,
|
||
market_count: 7,
|
||
},
|
||
dkp_corridor: {
|
||
count: 9,
|
||
low_ppm2: 150_000,
|
||
median_ppm2: 162_000,
|
||
high_ppm2: 178_000,
|
||
period_months: 24,
|
||
},
|
||
price_trend: [
|
||
{ month: "2025-11", ppm2: 168_000 },
|
||
{ month: "2025-12", ppm2: 170_500 },
|
||
{ month: "2026-01", ppm2: 172_000 },
|
||
{ month: "2026-02", ppm2: 174_000 },
|
||
{ month: "2026-03", ppm2: 176_500 },
|
||
{ month: "2026-04", ppm2: 178_000 },
|
||
],
|
||
};
|
||
|
||
export const FIXTURE_INPUT: TradeInEstimateInput = {
|
||
address: "Екатеринбург, ул. Репина, 75/2",
|
||
area_m2: 55.3,
|
||
rooms: 2,
|
||
floor: 5,
|
||
total_floors: 11,
|
||
year_built: 2018,
|
||
house_type: "monolith",
|
||
repair_state: "good",
|
||
has_balcony: true,
|
||
};
|