Some checks failed
CI / changes (push) Successful in 7s
CI / frontend-tests (push) Successful in 1m4s
CI / changes (pull_request) Successful in 15s
CI / openapi-codegen-check (push) Failing after 2m9s
CI / frontend-tests (pull_request) Successful in 1m12s
CI / openapi-codegen-check (pull_request) Failing after 1m46s
CI / backend-tests (push) Failing after 9m1s
CI / backend-tests (pull_request) Failing after 9m2s
68 lines
2.6 KiB
TypeScript
68 lines
2.6 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect } from "react";
|
|
|
|
import { useParcelAnalyzeQuery } from "@/lib/site-finder-api";
|
|
import type { ParcelAnalysis } from "@/types/site-finder";
|
|
import type { CompareColumn } from "./CompareTable";
|
|
|
|
interface Props {
|
|
cad: string;
|
|
onResult: (col: CompareColumn) => void;
|
|
}
|
|
|
|
/**
|
|
* Headless loader: runs `useParcelAnalyzeQuery` for a single cad and lifts the
|
|
* flattened metrics up to the compare page via `onResult`. Rendered one-per-cad
|
|
* inside a `.map`, so each instance owns a stable hook order (rules-of-hooks
|
|
* safe) — the page itself never calls the hook in a loop. Reuses the exact same
|
|
* analyze endpoint + 202-poll logic as the single-parcel analysis page, so a
|
|
* parcel already analyzed there is served warm from the shared query cache.
|
|
*
|
|
* Renders nothing; `useEffect` here only mirrors query state into parent state,
|
|
* it does not perform HTTP (the hook does, via TanStack Query).
|
|
*/
|
|
export function ParcelCompareLoader({ cad, onResult }: Props) {
|
|
const { data, isLoading, error } = useParcelAnalyzeQuery(cad);
|
|
|
|
useEffect(() => {
|
|
if (isLoading) {
|
|
onResult({ cad, status: "loading" });
|
|
return;
|
|
}
|
|
if (error || !data) {
|
|
onResult({
|
|
cad,
|
|
status: "error",
|
|
errorMsg:
|
|
error instanceof Error ? error.message : "Ошибка загрузки анализа",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// ParcelAnalyzeResponse is a structural subset of ParcelAnalysis (same
|
|
// /analyze endpoint); the richer fields (velocity, gate_verdict, confidence,
|
|
// success_recommendation) are optional and may be absent on thin parcels.
|
|
const a = data as unknown as ParcelAnalysis;
|
|
|
|
onResult({
|
|
cad,
|
|
status: "ready",
|
|
districtName: a.district?.district_name ?? null,
|
|
verdictLabel: a.gate_verdict?.verdict_label ?? null,
|
|
score: a.score ?? null,
|
|
scoreLabel: a.score_label ?? null,
|
|
velocityScore: a.velocity?.velocity_score ?? null,
|
|
// SF#17 / issue #1422: explicit false (конкуренты есть, данных Objective
|
|
// нет → velocity_score=0 — sentinel) гасит ячейку и исключает колонку из
|
|
// подсветки «лучшее». undefined/null трактуется как «данные есть».
|
|
velocityDataAvailable: a.velocity?.velocity_data_available ?? null,
|
|
medianPricePerM2: a.district?.median_price_per_m2 ?? null,
|
|
pipeline24mo: a.pipeline_24mo?.flats_total ?? null,
|
|
confidenceLabel: a.confidence_label ?? null,
|
|
confidenceValue: a.confidence ?? null,
|
|
});
|
|
}, [cad, data, isLoading, error, onResult]);
|
|
|
|
return null;
|
|
}
|