(medium) concept/page.tsx: устаревший результат концепции против перерисованного полигона (стейл-стейт) #1411

Closed
opened 2026-06-15 05:53:42 +00:00 by bot-backend · 1 comment
Collaborator

Severity: medium · Категория: frontend · Файл: frontend/src/app/concept/page.tsx:86-95,205,257-264

page.tsx: ConceptDrawMap onChange={setPolygon} (:205) меняет polygon, но НЕ вызывает concept.reset(). reset() есть только в кадастровой ветке handleCadSubmit (:81), не при рисовании. Блок результата рендерится при concept.isSuccess && polygon с parcel={polygon} variants={concept.data.variants} (:257-264). Сценарий: нарисовал участок A → 'Рассчитать' → успех (варианты для A) → перерисовал/дорисовал участок B (setPolygon(B), мутация не сброшена). На экране остаётся прежний concept.data (здания/ТЭП/финмодель для A), но ConceptResultMap получает parcel = B → контур участка B с корпусами и цифрами, рассчитанными для A. Клиент видит несогласованную концепцию (здания не вписаны в показанный участок), цифры при этом выглядят валидными. Дополнительно: concept.reset() не отменяет уже запущенный HTTP, так что поздний resolve старого запроса тоже может отрисоваться против нового polygon. Как чинить: при onChange полигона сбрасывать результат — обернуть setPolygon в handler, вызывающий concept.reset() (как в кадастровой ветке), или скрывать блок результата, если polygon-ссылка изменилась после успешного расчёта.

Почему баг: Сценарий: нарисовал участок A → 'Рассчитать' → получил результат для A → дорисовал/перерисовал участок B (setPolygon(B), мутация не сброшена) → на экране остаётся прежний успех concept.data (варианты для A), но parcel={polygon} уже равен B. ConceptResultMap получит outline участка B и здания, рассчитанные для A — карта размещения, ТЭП и финмодель относятся к A, а контур участка к B. Клиент видит несогласованную концепцию (здания не вписаны в показанный участок), при этом цифры выглядят валидными. Аналогичная гонка возможна при in-flight мутации A и последующем resolve кадастра B: concept.reset() не отменяет уже запущенный HTTP, и пришедший позже результат A отрисуется против B.

Как чинить: Сбрасывать результат при любом изменении границ: в onChange полигона (и в ветке рисования) вызывать concept.reset(), либо рендерить результат только когда polygon идентичен тому, что отправлялся в mutate (хранить snapshot отправленного parcel в data/локальном стейте и сравнивать).


deep-audit «армия ботов 2» · verify+harden, confidence 0.8 · unit U77

**Severity:** medium · **Категория:** frontend · **Файл:** `frontend/src/app/concept/page.tsx:86-95,205,257-264` page.tsx: ConceptDrawMap onChange={setPolygon} (:205) меняет polygon, но НЕ вызывает concept.reset(). reset() есть только в кадастровой ветке handleCadSubmit (:81), не при рисовании. Блок результата рендерится при concept.isSuccess && polygon с parcel={polygon} variants={concept.data.variants} (:257-264). Сценарий: нарисовал участок A → 'Рассчитать' → успех (варианты для A) → перерисовал/дорисовал участок B (setPolygon(B), мутация не сброшена). На экране остаётся прежний concept.data (здания/ТЭП/финмодель для A), но ConceptResultMap получает parcel = B → контур участка B с корпусами и цифрами, рассчитанными для A. Клиент видит несогласованную концепцию (здания не вписаны в показанный участок), цифры при этом выглядят валидными. Дополнительно: concept.reset() не отменяет уже запущенный HTTP, так что поздний resolve старого запроса тоже может отрисоваться против нового polygon. Как чинить: при onChange полигона сбрасывать результат — обернуть setPolygon в handler, вызывающий concept.reset() (как в кадастровой ветке), или скрывать блок результата, если polygon-ссылка изменилась после успешного расчёта. **Почему баг:** Сценарий: нарисовал участок A → 'Рассчитать' → получил результат для A → дорисовал/перерисовал участок B (setPolygon(B), мутация не сброшена) → на экране остаётся прежний успех concept.data (варианты для A), но parcel={polygon} уже равен B. ConceptResultMap получит outline участка B и здания, рассчитанные для A — карта размещения, ТЭП и финмодель относятся к A, а контур участка к B. Клиент видит несогласованную концепцию (здания не вписаны в показанный участок), при этом цифры выглядят валидными. Аналогичная гонка возможна при in-flight мутации A и последующем resolve кадастра B: concept.reset() не отменяет уже запущенный HTTP, и пришедший позже результат A отрисуется против B. **Как чинить:** Сбрасывать результат при любом изменении границ: в onChange полигона (и в ветке рисования) вызывать concept.reset(), либо рендерить результат только когда polygon идентичен тому, что отправлялся в mutate (хранить snapshot отправленного parcel в data/локальном стейте и сравнивать). --- <sub>deep-audit «армия ботов 2» · verify+harden, confidence 0.8 · unit U77</sub>
bot-backend added the
week ревью 1
label 2026-06-15 05:53:42 +00:00
bot-backend added the
scope/frontend
label 2026-06-15 11:02:00 +00:00
Owner

🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)

Примечание: верифицировано против forgejo/main (canonical prod). В локальной ветке feat/cian-city-sweep-schedule рабочая копия concept/page.tsx устаревшая (TODO-заглушка) — реальная реализация в main.

Текущее состояние кода (forgejo/main): frontend/src/app/concept/page.tsx:

  • <ConceptDrawMap polygon={polygon} onChange={setPolygon} /> (≈стр. 205) — onChange напрямую вызывает setPolygon, без concept.reset().
  • concept.reset() есть только в handleCadSubmitonSuccess (≈стр. 76-78, кадастровая ветка).
  • Блок результата: {concept.isSuccess && polygon && (<ConceptVariantsResult parcel={polygon} variants={concept.data.variants} />)} (≈стр. 258-264).

Корень: Перерисовка/дорисовка полигона меняет polygon (B), но не сбрасывает мутацию → на экране остаётся concept.data для участка A, тогда как ConceptVariantsResult получает parcel = polygon = B. Здания/ТЭП/финмодель для A рендерятся против контура B — несогласованная концепция. Доп.: concept.reset() не отменяет in-flight HTTP, поздний resolve старого запроса тоже отрисуется против нового polygon.

(Тело issue называет компонент ConceptResultMap — в текущем коде это ConceptVariantsResult. Поведение то же.)

Что править:

  • Обернуть setPolygon для draw-ветки в handler, вызывающий concept.reset() (как в handleCadSubmit): onChange={(p) => { setPolygon(p); concept.reset(); }}.
  • Либо рендерить результат только если polygon идентичен отправленному в mutate — хранить snapshot отправленного parcel и сравнивать перед показом блока.
  • Для in-flight гонки — учесть, что reset() не отменяет запрос (TanStack mutation); при необходимости игнорировать поздний resolve по сравнению snapshot.

Что посмотреть:

  • Код: concept/page.tsx (handler onChange ≈205, handleCadSubmit+concept.reset() ≈76, результат-блок ≈258-264); компонент ConceptVariantsResult; хук useCreateConcept.
  • Vault: релевантных заметок не найдено.
**🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)** _Примечание: верифицировано против `forgejo/main` (canonical prod). В локальной ветке `feat/cian-city-sweep-schedule` рабочая копия `concept/page.tsx` устаревшая (TODO-заглушка) — реальная реализация в main._ **Текущее состояние кода (forgejo/main):** `frontend/src/app/concept/page.tsx`: - `<ConceptDrawMap polygon={polygon} onChange={setPolygon} />` (≈стр. 205) — `onChange` напрямую вызывает `setPolygon`, **без** `concept.reset()`. - `concept.reset()` есть только в `handleCadSubmit` → `onSuccess` (≈стр. 76-78, кадастровая ветка). - Блок результата: `{concept.isSuccess && polygon && (<ConceptVariantsResult parcel={polygon} variants={concept.data.variants} />)}` (≈стр. 258-264). **Корень:** Перерисовка/дорисовка полигона меняет `polygon` (B), но не сбрасывает мутацию → на экране остаётся `concept.data` для участка A, тогда как `ConceptVariantsResult` получает `parcel = polygon = B`. Здания/ТЭП/финмодель для A рендерятся против контура B — несогласованная концепция. Доп.: `concept.reset()` не отменяет in-flight HTTP, поздний resolve старого запроса тоже отрисуется против нового polygon. _(Тело issue называет компонент `ConceptResultMap` — в текущем коде это `ConceptVariantsResult`. Поведение то же.)_ **Что править:** - Обернуть `setPolygon` для draw-ветки в handler, вызывающий `concept.reset()` (как в `handleCadSubmit`): `onChange={(p) => { setPolygon(p); concept.reset(); }}`. - Либо рендерить результат только если `polygon` идентичен отправленному в `mutate` — хранить snapshot отправленного parcel и сравнивать перед показом блока. - Для in-flight гонки — учесть, что `reset()` не отменяет запрос (TanStack mutation); при необходимости игнорировать поздний resolve по сравнению snapshot. **Что посмотреть:** - Код: `concept/page.tsx` (handler `onChange` ≈205, `handleCadSubmit`+`concept.reset()` ≈76, результат-блок ≈258-264); компонент `ConceptVariantsResult`; хук `useCreateConcept`. - Vault: релевантных заметок не найдено.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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#1411
No description provided.