feat(tradein/frontend): pass suggest fias_id as target_fias_id in estimate request #2186
4 changed files with 39 additions and 2 deletions
|
|
@ -22,6 +22,9 @@ interface SuggestItem {
|
|||
lat: number;
|
||||
lon: number;
|
||||
kind: string;
|
||||
// ФИАС-идентификатор дома (заполнен только для house-level кандидатов
|
||||
// DaData-тира; иначе null/absent). Пробрасывается наверх через onPickFias.
|
||||
fias_id?: string | null;
|
||||
}
|
||||
|
||||
interface SuggestResponse {
|
||||
|
|
@ -43,6 +46,9 @@ interface Props {
|
|||
value: string;
|
||||
onChange: (val: string) => void;
|
||||
onPickCoords?: (coords: { lat: number; lon: number } | null) => void;
|
||||
// ФИАС-ключ выбранного дома. Зеркалит поведение onPickCoords: значение при
|
||||
// выборе house-level подсказки, null при ручной правке текста.
|
||||
onPickFias?: (fiasId: string | null) => void;
|
||||
placeholder?: string;
|
||||
inputId?: string;
|
||||
}
|
||||
|
|
@ -81,10 +87,11 @@ function presetsToItems(): SuggestItem[] {
|
|||
lat: 0,
|
||||
lon: 0,
|
||||
kind: p.kind,
|
||||
fias_id: null,
|
||||
}));
|
||||
}
|
||||
|
||||
export function AddressInput({ value, onChange, onPickCoords, placeholder, inputId = "addr" }: Props) {
|
||||
export function AddressInput({ value, onChange, onPickCoords, onPickFias, placeholder, inputId = "addr" }: Props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [items, setItems] = useState<SuggestItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
|
@ -150,6 +157,7 @@ export function AddressInput({ value, onChange, onPickCoords, placeholder, input
|
|||
item.lat !== 0 &&
|
||||
item.lon !== 0;
|
||||
onPickCoords?.(hasCoords ? { lat: item.lat, lon: item.lon } : null);
|
||||
onPickFias?.(item.fias_id ?? null);
|
||||
setOpen(false);
|
||||
setShowPresets(false);
|
||||
setItems([]);
|
||||
|
|
@ -197,6 +205,7 @@ export function AddressInput({ value, onChange, onPickCoords, placeholder, input
|
|||
onChange(e.target.value);
|
||||
lastPickedRef.current = ""; // юзер начал редактировать — снимаем guard
|
||||
onPickCoords?.(null); // ручное редактирование сбрасывает ранее выбранные coords
|
||||
onPickFias?.(null); // ...и ранее выбранный ФИАС (иначе stale перебьёт новый адрес)
|
||||
setShowPresets(false);
|
||||
}}
|
||||
onFocus={() => {
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ function validate(f: FormState): string | null {
|
|||
function toPayload(
|
||||
f: FormState,
|
||||
coords: { lat: number; lon: number } | null,
|
||||
fias: string | null,
|
||||
): TradeInEstimateInput {
|
||||
const out: TradeInEstimateInput = {
|
||||
address: f.address.trim(),
|
||||
|
|
@ -89,6 +90,9 @@ function toPayload(
|
|||
out.lat = coords.lat;
|
||||
out.lon = coords.lon;
|
||||
}
|
||||
// Только когда адрес выбран из подсказки (иначе fias === null): даём бэкенду
|
||||
// детерминированный ключ дома вместо/до геокодинга строки.
|
||||
if (fias) out.target_fias_id = fias;
|
||||
if (f.year_built !== "") out.year_built = Number(f.year_built);
|
||||
const ht = asHouseType(f.house_type);
|
||||
if (ht) out.house_type = ht;
|
||||
|
|
@ -111,6 +115,10 @@ export function EstimateForm({
|
|||
}: Props) {
|
||||
const [form, setForm] = useState<FormState>(INITIAL);
|
||||
const [coords, setCoords] = useState<{ lat: number; lon: number } | null>(null);
|
||||
// ФИАС-ключ дома из выбранной подсказки. Живёт рядом с coords и сбрасывается
|
||||
// по тем же событиям: ручная правка адреса (внутри AddressInput → onPickFias(null))
|
||||
// и выбор точки на карте (MapPicker onPick ниже).
|
||||
const [fias, setFias] = useState<string | null>(null);
|
||||
const [touched, setTouched] = useState(false);
|
||||
const [mapOpen, setMapOpen] = useState(false);
|
||||
// CRM-блок свёрнут по умолчанию — экономит ~190px высоты, чтобы форма влезала
|
||||
|
|
@ -132,7 +140,7 @@ export function EstimateForm({
|
|||
e.preventDefault();
|
||||
setTouched(true);
|
||||
if (canSubmit) {
|
||||
onSubmit(toPayload(form, coords));
|
||||
onSubmit(toPayload(form, coords, fias));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,6 +171,7 @@ export function EstimateForm({
|
|||
value={form.address}
|
||||
onChange={(val) => setForm((s) => ({ ...s, address: val }))}
|
||||
onPickCoords={setCoords}
|
||||
onPickFias={setFias}
|
||||
placeholder="ул. Малышева, 30 · Куйбышева, 48…"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -522,6 +531,7 @@ export function EstimateForm({
|
|||
onPick={(addr, mapCoords) => {
|
||||
setForm((s) => ({ ...s, address: addr }));
|
||||
setCoords(mapCoords ?? null);
|
||||
setFias(null); // точка с карты ≠ house-level ФИАС → сбрасываем
|
||||
}}
|
||||
onClose={() => setMapOpen(false)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -542,6 +542,12 @@ export default function ParamsPanel({
|
|||
? { lat: initialValues.lat, lon: initialValues.lon }
|
||||
: null,
|
||||
);
|
||||
// ФИАС-ключ дома из выбранной подсказки. Живёт рядом с coords и сбрасывается
|
||||
// по тем же событиям (ручная правка адреса → handleAddressChange). Отправляется
|
||||
// только когда адрес выбран из suggest — иначе stale fias перебил бы новый адрес.
|
||||
const [fias, setFias] = useState<string | null>(
|
||||
initialValues?.target_fias_id ?? null,
|
||||
);
|
||||
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
// БАЛКОН radiogroup focus targets (roving tabindex).
|
||||
const balNoRef = useRef<HTMLButtonElement>(null);
|
||||
|
|
@ -551,6 +557,7 @@ export default function ParamsPanel({
|
|||
const handleAddressChange = (v: string) => {
|
||||
setAddress(v);
|
||||
setCoords(null); // a manual edit invalidates a previously picked point
|
||||
setFias(null); // ...and the previously picked house ФИАС (stale-guard)
|
||||
setSuggestOpen(v.trim().length >= 3);
|
||||
setAddressActive(-1); // a new query invalidates the previous highlight
|
||||
if (fieldErrors.address)
|
||||
|
|
@ -563,6 +570,7 @@ export default function ParamsPanel({
|
|||
setAddress(s.full_address);
|
||||
setAddressQuery(s.full_address);
|
||||
setCoords({ lat: s.lat, lon: s.lon });
|
||||
setFias(s.fias_id ?? null);
|
||||
setSuggestOpen(false);
|
||||
setAddressActive(-1);
|
||||
};
|
||||
|
|
@ -696,6 +704,8 @@ export default function ParamsPanel({
|
|||
has_balcony: balcony ?? undefined,
|
||||
lat: coords?.lat ?? null,
|
||||
lon: coords?.lon ?? null,
|
||||
// Детерминированный ключ дома — только из выбранной подсказки (иначе null).
|
||||
target_fias_id: fias,
|
||||
// "Авто" → null → backend keeps its two-tier default (1000 m primary /
|
||||
// 2000 m fallback). A fixed value overrides both. (Не отправлять 500 по
|
||||
// умолчанию — это сузило бы поиск аналогов и убрало fallback.)
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ export interface TradeInEstimateInput {
|
|||
// Радиус поиска аналогов/сделок в метрах (опциональный override дефолта бэкенда).
|
||||
// Зеркалит новое поле request-схемы; absent → бэкенд применяет свой дефолт.
|
||||
radius_m?: number | null;
|
||||
// ФИАС-идентификатор дома из выбранной подсказки автокомплита (house-level,
|
||||
// DaData-тир). Детерминированный ключ дома — бэкенд использует его ПЕРВЫМ,
|
||||
// до геокодинга строки. Отправляется ТОЛЬКО когда адрес выбран из подсказки;
|
||||
// сбрасывается в null при ручной правке адреса или сдвиге пина на карте.
|
||||
target_fias_id?: string | null;
|
||||
}
|
||||
|
||||
export interface AnalogLot {
|
||||
|
|
@ -424,6 +429,9 @@ export interface GeocodeSuggestion {
|
|||
lat: number;
|
||||
lon: number;
|
||||
kind: string; // тип объекта (house / street / …)
|
||||
// ФИАС-идентификатор дома (заполнен только для house-level кандидатов
|
||||
// DaData-тира; для street/locality и не-DaData-источников — null/absent).
|
||||
fias_id?: string | null;
|
||||
}
|
||||
|
||||
export interface GeocodeSuggestResponse {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue