"use client"; // 01 ПАРАМЕТРЫ КВАРТИРЫ — left card of the /trade-in/v2 "МЕРА Оценка" port. // Faithful markup port (МЕРА Оценка.dc.html lines 146-240). Wire phase: this is // now a CONTROLLED form. Each field is local useState producing a // TradeInEstimateInput that is handed to onSubmit; the visual markup / layout / // styles are UNCHANGED — only the data plumbing differs (display
s became // s styled identically, dropdowns now feed real enum values). RU dropdown // labels <-> API enum values go through HOUSE_TYPE_*/REPAIR_* maps in ./mappers. // РАДИУС is now wired (radius_m on submit + the outer map ring scales with it); // the CRM dropdown still has no backend → kept visually but disabled. Hover/active // + @keyframes live in a pp-prefixed local {/* HUD corner brackets */}
{/* header */}
01 ПАРАМЕТРЫ КВАРТИРЫ
ШАГ 1/1
{/* MAP */}
{/* zoomable map content — scaled by the +/− controls. The map controls and corner bracket below are SIBLINGS (outside this layer) so they never scale. zoom=1 → scale() identity → pixel-identical to design. */}
{/* center pin */}
{address.trim() || "Адрес квартиры"}
{/* analog price pins — projected from the real estimate via mapMarkers(). Empty markers (no estimate yet) → no price pins, just the subject pin + radius rings (Finding #2: never the fabricated fixture prices/dots). */} {markers.map((m, i) => (
{m.label}
{m.sub}
))}
{/* map controls */}
setZoom((z) => Math.min(2.5, z + 0.25))} > +
setZoom((z) => Math.max(0.6, z - 0.25))} > −
{/* map corner bracket */}
{/* radius row */}
РАДИУС АНАЛИЗА {/* РАДИУС — selectable analysis radius (wired: parsed to radius_m on submit). Trigger markup unchanged; now toggles a Dd-style option panel and uses the shared pp-dd-trigger hover cue. */}
toggle("radius")} style={{ display: "flex", alignItems: "center", gap: 8, background: tokens.surface.w60, borderRadius: 5, padding: "6px 10px", fontSize: 12, fontFamily: tokens.font.mono, cursor: "pointer", }} > {radius}
{openDd === "radius" && (
{RADIUS_OPTIONS.map((o) => (
pick(setRadius)(o)} style={{ padding: "7px 10px", fontSize: 12, fontFamily: tokens.font.mono, borderRadius: 4, cursor: "pointer", color: o === radius ? tokens.accent : tokens.ink, fontWeight: o === radius ? 600 : 400, }} > {o}
))}
)}
Объявления Сделки
{/* INPUTS */}
АДРЕС (YANDEX / NOMINATIM)
{/* Address autocomplete (geocode suggest, ЕКБ viewbox). Typed text is debounced into `addressQuery`; the dropdown below mirrors the HUD
panel styling. Picking an item fills the full address and captures lat/lon for the estimate payload. */} handleAddressChange(e.target.value)} onFocus={() => { if (address.trim().length >= 3) setSuggestOpen(true); }} onBlur={() => setSuggestOpen(false)} onKeyDown={(e) => { if (e.key === "Enter") handleSubmit(); if (e.key === "Escape") setSuggestOpen(false); }} placeholder="Город, улица, дом" autoComplete="off" style={addressField} /> {suggestOpen && addressQuery.trim().length >= 3 && (
{suggest.data && suggest.data.length > 0 ? ( suggest.data.map((s, i) => (
{ e.preventDefault(); pickSuggestion(s); }} style={suggestOpt} >
{s.label}
{s.full_address !== s.label && (
{s.full_address}
)}
)) ) : suggest.isFetching ? (
Поиск…
) : (
Ничего не найдено
)}
)}
ПЛОЩАДЬ, М²
setArea(e.target.value.replace(/[^\d.,]/g, "")) } onKeyDown={(e) => { if (e.key === "Enter") handleSubmit(); }} placeholder="напр. 54" style={inputField} />
КОМНАТ
toggle("rooms")} value={rooms} options={dropdownOptions.rooms} onSelect={pick(setRooms)} mono triggerFontSize={14} optionFontSize={13} />
ЭТАЖ если знаешь
setFloor(e.target.value.replace(/\D/g, ""))} onKeyDown={(e) => { if (e.key === "Enter") handleSubmit(); }} placeholder="—" style={inputField} />
ВСЕГО ЭТАЖЕЙ если знаешь
setTotalFloors(e.target.value.replace(/\D/g, "")) } onKeyDown={(e) => { if (e.key === "Enter") handleSubmit(); }} placeholder="—" style={inputField} />
1-й и последний этаж снижают цену на 5–10% — заполни если знаешь
ГОД ПОСТРОЙКИ опц.
setYear(e.target.value.replace(/\D/g, ""))} onKeyDown={(e) => { if (e.key === "Enter") handleSubmit(); }} placeholder="—" style={inputField} />
ТИП ДОМА опц.
toggle("houseType")} value={houseType} options={dropdownOptions.houseType} onSelect={pick(setHouseType)} mono={false} triggerFontSize={13} optionFontSize={12} />
СОСТОЯНИЕ РЕМОНТА опц.
toggle("repair")} value={repair} options={dropdownOptions.repair} onSelect={pick(setRepair)} mono={false} triggerFontSize={13} optionFontSize={12} />
БАЛКОН / ЛОДЖИЯ
setBalcony(false)} style={{ ...balBase, ...(balcony ? balOff : balOn) }} > НЕТ
setBalcony(true)} style={{ ...balBase, ...(balcony ? balOn : balOff) }} > ДА
CRM — ДЛЯ МЕНЕДЖЕРА опц.
{/* CRM — non-functional (no backend, #395). Markup kept; open is forced false + no-op handlers so it never expands. TODO(wire). */}
undefined} value={crm} options={dropdownOptions.crm} onSelect={() => undefined} mono={false} triggerFontSize={12} optionFontSize={12} variant="dashed" openUp triggerHeight={34} triggerBg={tokens.surface.w50} triggerColor={tokens.hint} caretColor={tokens.muted4} />
{/* button */} {/* inline error (validation or server) — only when present */} {(validationError || error) && (
{validationError ?? error}
)} {/* cache status */}
КЭШ ПО АДРЕСУ — 24 Ч ГОТОВ
); }