gendesign/frontend/src/components/site-finder/compare/ParcelCompareLoader.tsx
Light1YT 7fd69e90cd
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
fix(site-finder/compare): thread velocityDataAvailable into compare columns (#1422)
2026-06-15 21:33:10 +05:00

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;
}