fix(sf-legacy): фильтр стейл competitors (>1 года Сданные → не конкурент) — Maxim spec #365

Merged
lekss361 merged 1 commit from fix/sf-legacy-filter-stale-competitors into main 2026-05-18 04:33:42 +00:00
Owner

Summary

User: «плюс проверь какие данн чтобы было то что хочет Макс хотя бы похожи — что не старше года иначе ЖК не конкурент».

Анализ data

POST /api/v1/parcels/66:41:0204016:10/analyze:

  • Total competitors: 20
  • Строящиеся: 6
  • Сданные: 14 → из них 12 ready_dt > 1 года назад (стейл)
  • Релевантных (Maxim spec): 9 = 6 строящихся + 1 future ≤1y + 2 past ≤1y

Старая activeCount = data.competitors.length (20) — misleading.

Fix

MarketTab.tsx фильтрует data.competitors → relevantCompetitors:

  • site_status === 'Строящиеся' → активен
  • ready_dt в будущем → активен (готовность не наступила)
  • ready_dt в пределах последнего года → активен (свежие продажи)
  • иначе (Сданные >1y назад) → скрыт

activeCount + CompetitorTable теперь используют отфильтрованный список. Section label: «N из M — фильтр: строящиеся или сданы ≤1 года».

Verify

  • /legacy/site-finder → 66:41:0204016:10 → KPI «Активные ЖК: 9» (не 20)
  • HeadlineBar: «9 строящихся в 3км · велосити … · рекомендуемый mix …»
  • Конкуренты table: 9 rows, label «9 из 20»
## Summary User: «плюс проверь какие данн чтобы было то что хочет Макс хотя бы похожи — что не старше года иначе ЖК не конкурент». ## Анализ data `POST /api/v1/parcels/66:41:0204016:10/analyze`: - Total competitors: **20** - Строящиеся: **6** - Сданные: 14 → из них **12 ready_dt > 1 года назад** (стейл) - Релевантных (Maxim spec): **9** = 6 строящихся + 1 future ≤1y + 2 past ≤1y Старая `activeCount = data.competitors.length` (20) — misleading. ## Fix `MarketTab.tsx` фильтрует `data.competitors → relevantCompetitors`: - `site_status === 'Строящиеся'` → активен - `ready_dt` в будущем → активен (готовность не наступила) - `ready_dt` в пределах последнего года → активен (свежие продажи) - иначе (Сданные >1y назад) → скрыт `activeCount` + `CompetitorTable` теперь используют отфильтрованный список. Section label: «N из M — фильтр: строящиеся или сданы ≤1 года». ## Verify - `/legacy/site-finder` → 66:41:0204016:10 → KPI «Активные ЖК: 9» (не 20) - HeadlineBar: «9 строящихся в 3км · велосити … · рекомендуемый mix …» - Конкуренты table: 9 rows, label «9 из 20»
lekss361 added 1 commit 2026-05-18 04:31:06 +00:00
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).
Author
Owner

APPROVE — inline review (без агента)

Rationale (3 sentences):

  1. Filter correctness verified. Three-branch predicate: site_status === "Строящиеся" → true, future ready_dt → true, ready_dt ≤ 365 days ago → true, иначе (включая null/NaN) → false. Number.isNaN(ts) правильно (не global isNaN), null guard на !c.ready_dt стабилен.

  2. UI hygiene clean. Conditional render обновлён data.competitors.length > 0relevantCompetitors.length > 0 (нет пустой таблицы когда все стейл). SectionLabel прозрачно «N из M — фильтр: строящиеся или сданы ≤1 года» даёт пользователю reason. Устаревший // Note: site_status field pending fix #2/#3 удалён — predicate теперь честно проверяет.

  3. Scope surgical. 1 file, +22/-6, нет XSS, нет regression в Wave 4 sections (#358/#361). Maxim spec implemented.

Non-blocking nit: 365 * 24 * 60 * 60 * 1000 не учитывает leap year (366 дней). Для freshness business logic — irrelevant. Если HeadlineBar формирует "N строящихся" copy используя activeCount — copy может быть misleading (relevantCompetitors включает future-ready и sold-recently, не только "Строящиеся"). Flag для follow-up если нужна точность лейбла.

Merging.

✅ **APPROVE — inline review (без агента)** **Rationale (3 sentences):** 1. **Filter correctness verified.** Three-branch predicate: `site_status === "Строящиеся"` → true, future `ready_dt` → true, `ready_dt` ≤ 365 days ago → true, иначе (включая null/NaN) → false. `Number.isNaN(ts)` правильно (не global `isNaN`), null guard на `!c.ready_dt` стабилен. 2. **UI hygiene clean.** Conditional render обновлён `data.competitors.length > 0` → `relevantCompetitors.length > 0` (нет пустой таблицы когда все стейл). SectionLabel прозрачно «N из M — фильтр: строящиеся или сданы ≤1 года» даёт пользователю reason. Устаревший `// Note: site_status field pending fix #2/#3` удалён — predicate теперь честно проверяет. 3. **Scope surgical.** 1 file, +22/-6, нет XSS, нет regression в Wave 4 sections (#358/#361). Maxim spec implemented. **Non-blocking nit:** `365 * 24 * 60 * 60 * 1000` не учитывает leap year (366 дней). Для freshness business logic — irrelevant. Если HeadlineBar формирует "N строящихся" copy используя `activeCount` — copy может быть misleading (relevantCompetitors включает future-ready и sold-recently, не только "Строящиеся"). Flag для follow-up если нужна точность лейбла. Merging.
lekss361 merged commit 7f3734e062 into main 2026-05-18 04:33:42 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#365
No description provided.