User: 'плюс проверь какие данн чтобы было то что хочет макс хотя бы похожи
что не старше года иначе жк не конкурент'.
Анализ /analyze для 66:41:0204016:10:
Total competitors: 20
Строящиеся: 6
Сданные: 14 (12 из них ready_dt >1y → стейл)
Maxim spec: ЖК старше года не конкурент. Активные = строящиеся
ИЛИ ready_dt в будущем ИЛИ сданы ≤1 года назад.
MarketTab фильтрует data.competitors в relevantCompetitors:
- site_status === 'Строящиеся' → активен
- ready_dt в будущем → активен
- ready_dt в пределах последнего года → активен
- иначе скрыт
activeCount + CompetitorTable теперь используют отфильтрованный список.
Section label показывает 'N из M — фильтр' для прозрачности.
После deploy для тестового cad: activeCount упадёт с 20 → 9
(6 строящихся + 1 future ≤1y + 2 past ≤1y).
Бэкенд /api/v1/parcels/{cad}/analyze возвращает много новых B5 fields
(egrn, utilities, gate_verdict, nspd_zouit_overlaps, ...), но старая Обзор
вкладка показывает только subset (district, score, POI). User просит чтобы
legacy показывал не только старые данные, ближе к Maxim-style макету.
Добавил 2 conditional блока в OverviewTab после District:
- EGRN compact: address / ВРИ / категория / статус / право (если есть)
- Сети рядом: substation/pipeline/water/... с distances + охранная зона
ЛЭП warning
Type-only: ParcelAnalysis в types/site-finder.ts расширен полями
egrn (8 optional fields) + utilities (summary array + охранная зона flag).
Поля nullable optional — graceful empty state per cad.
Не трогает новый SF (/site-finder/*) и не меняет существующие блоки
Обзора (district / score / POI / isochrones).
User reports on /legacy/site-finder/[cad]:
Велосити 0.0 м²/мес · Срок продаж —
which is contradictory — if velocity were truly zero, sales period
would be infinity. Root cause: backend returns 0 (not null) when
competitors are unmapped to Objective ground truth (OBJ-3). The
condition for 'unavailable' must catch both null and zero.
Fix: getSalesPeriodMonths already treats velocitySqm <= 0 as null
(line 41), but the HeadlineBar + KPI card read raw velocity and
display '0.0 м²/мес' for zero. Same coercion applied at the source:
velocityPerMonth = (raw != null && raw > 0) ? raw : null.
Both HeadlineBar and KPI card now show '—' for zero velocity,
matching the '—' shown for Срок продаж.
User asked для доступа к старой версии перед SF redesign (A1-A11 in progress).
Файл backup лежал в frontend/src/app/_legacy/site-finder-page-tabs.tsx —
папка с префиксом _ Next.js игнорирует как route, доступа через URL нет.
Перенос в frontend/src/app/legacy/site-finder/page.tsx делает его accessible
как /legacy/site-finder. Все импорты компонентов (OverviewTab, EnvironmentTab,
LandTab, MarketTab, KpiCard, CadInput, FetchingState, SiteMap, WeightProfilePanel)
существуют — старая страница рабочая без модификаций.
После полной миграции на новый SF (A12 done + acceptance audit) папку
frontend/src/app/legacy/ можно удалить.
Wire analysis/[cad]/page.tsx: replace SidebarPlaceholder with
AnalysisSidebar (sticky, IntersectionObserver scrollspy on sections
1/2/3/3.1/3.2/3.3/4/5), header with AnalysisBreadcrumb (SiteFinder
→ cad → Анализ) and UserAvatar (gd_org_id from localStorage, SSR-safe).
Add lucide-react to package.json (was used in A1 but missing from deps).
When Objective mapping coverage falls below 50% of competitors in the radius,
fall back to rosreestr_deals JOIN on the parcel's cadastral quarter. Audit shows
237/237 EKB quarters (100%) have rosreestr data for the last 12 months, compared
to <20% Objective coverage before bulk mapping.
- velocity.py: add _compute_rosreestr_fallback(), _OBJECTIVE_COVERAGE_MIN_RATIO
constant, velocity_source field on VelocityResult (objective/rosreestr_fallback/none)
- parcels.py: extract cad_quarter from cad_num, pass to compute_velocity
- site-finder.ts: add velocity_source field to Velocity interface
- VelocityBlock.tsx: badge "Источник: квартальные сделки" when rosreestr_fallback
Epic #271 item #17
- v_bucket_success_score HAVING >= 30 → >= 15 so sparse districts
(Кировский etc.) are no longer silently excluded from the block
- Backend: SUCCESS_REC_MIN_DEALS=15, SUCCESS_REC_STRONG_DEALS=30;
data_confidence='weak' (15-29 deals) or 'strong' (≥30) in response
- TS type ParcelSuccessRecommendation: add data_confidence literal
- SuccessRecommendationBlock: amber badge for weak, all hex → CSS tokens,
emoji star removed (ui-ux.md), empty-state text min 30→15
Add «Срок продажи (мес)» column (supply / velocity, colored by urgency)
and click-to-expand drill-in row showing competitor_obj_ids as DOM.РФ
linked badges. Both fields were already present in TopLayoutRow type.
Closes#271 item 11
Replace INNER JOIN on objective_complex_mapping with LEFT JOIN approach:
competitors without mapping now return velocity=0 with
velocity_data_available=False instead of being silently dropped.
UI: VelocityBlock shows 'нет данных velocity' badge when flag is False
and hides the gauge (meaningless zero). TS type updated (additive optional
field, backward compat).
Audit (EKB, region_cd=66): mapped=129, unmapped=1387, total=1516.
Closes#271 item #10
_build_recommendation зеркалило рынок через чистый Hamilton apportionment.
Добавлен _cap_and_redistribute: bucket >35% clamp к 35%, surplus
пропорционально перераспределяется в остальные bucket'ы (Hamilton финальный
pass гарантирует sum=100). Pathological case (все bucket'ы >35%) — warning
в лог, cap пропускается. Frontend: warning banner при maxPct>60 в
RecommendationCard (Tailwind amber utilities, без inline hex).
ekb_districts.median_price_per_m2 was populated from a 24-month window.
LEFT JOIN mv_quarter_price_per_m2 on the cad quarter and COALESCE(median_12m,
fallback) so the API returns the 12-month median where available.
Frontend OverviewTab adds a "(12 мес)" hint next to the value.
Closes#271 item 7
Marketing landing «gendsgn — аналитика спроса для девелоперов».
Self-contained HTML (50KB, inline assets) — будет доступен через
Next.js public/ serving по URL https://gendsgn.ru/landing.html
После backend PR #276 (site_status в /analyze) — frontend показывает
статус строящиеся/сданные с цветным badge и фильтр-tabs наверху таблицы.
TS type extended manually (backward compat) — codegen подхватит после
backend deploy.
Closes (epic part) #271 item 3