--- type: review work_item_id: ET-002 title: "Code Review: Чекбокс показа/скрытия POI в попапе рельефа" version: 2 status: approved verdict: APPROVED created_at: 2026-05-21 authors: - "agent:reviewer" --- # Code Review — ET-002 ## Вердикт **APPROVED** — найдены только замечания уровня P3 (nice-to-have). Блокеров (P0) и must-fix (P1) нет. ## Объём ревью Проверены изменения в `src/web/app.js` и `src/web/index.html` против: - `docs/work-items/ET-002/02-trz.md` (ТЗ, v1, approved) - `docs/work-items/ET-002/03-acceptance-criteria.md` (AC, v1, approved) - `docs/work-items/ET-002/06-adr/adr-0001-poi-visibility-client-side.md` - `docs/work-items/ET-002/04-test-plan.yaml` - `CLAUDE.md` Дополнительно учтён юнит-тест `tests/unit/poi_toggle.test.js`. --- ## 1. Соответствие ТЗ | Требование | Статус | Где реализовано | |------------|--------|-----------------| | REQ-F-01 — чекбокс «POI» в `terrain-popup` после `trails-path-cb`, отделён `
` | ✅ | `index.html:56-60` | | REQ-F-02 — состояние по умолчанию: checked, POI видимы | ✅ | `index.html:58` (атрибут `checked`); `app.js:2848-2852` (`stored === null → poiOn = true`) | | REQ-F-03 — снятие чекбокса → `visibility: 'none'` для `poi-circles`, `poi-labels` | ✅ | `app.js:2833-2837` → `applyPoiVisibility(false)` → `app.js:2815-2825` | | REQ-F-04 — установка чекбокса → `visibility: 'visible'` | ✅ | те же функции, ветка `visible` | | REQ-F-05 — сохранение в `localStorage['poi-visible']` (`'1'`/`'0'`) | ✅ | `app.js:2835` | | REQ-F-06 — восстановление при загрузке (`'0'` скрыть / `'1'`\|null показать) | ✅ | `app.js:2847-2853` (`restorePoiState`) | | REQ-F-07 — синхронизация `layerState.poi` | ✅ | `app.js:2816` (`layerState.poi = visible` в общем хелпере) | | REQ-NF-01 — переключение < 50 мс, без перезагрузки тайлов | ✅ | используется `setLayoutProperty`, источник слоёв не трогается | | REQ-NF-02 — совместимость с MapLibre GL JS | ✅ | штатные API MapLibre, без экзотики | | REQ-NF-03 — мобильный touch target ≥ 44px | ⚠️ см. P3-2 | переиспользован класс `terrain-checkbox` (как предписано ТЗ §3) | | REQ-NF-04 — отсутствие регрессий | ✅ | существующие чекбоксы и их обработчики не затронуты | UI-спецификация ТЗ §3 (разметка, `id="poi-visible-cb"`, `onchange="onPoiCheckbox()"`, класс `terrain-checkbox`) воспроизведена в `index.html` дословно. **Вывод:** все функциональные требования выполнены. ## 2. Соответствие ADR (adr-0001) | Пункт решения | Статус | Комментарий | |---------------|--------|-------------| | 1. Видимость через `map.setLayoutProperty(... 'visibility' ...)` | ✅ | `app.js:2822` | | 2. Персистентность — `localStorage['poi-visible']`, `'1'`/`'0'` | ✅ | соответствует конвенции проекта (`trails-track`, `terrain-*`) | | 3. Источник истины — `layerState.poi`; `onPoiCheckbox()` синхронно обновляет `layerState`, `localStorage`, `layout.visibility` | ✅ | `onPoiCheckbox` → `setItem` + `applyPoiVisibility` (правит `layerState` и оба слоя) | | 4. Без дублирования: общий приватный хелпер, переиспользование `layerGroups.poi` | ✅ | выделен `applyPoiVisibility(visible)`, итерирует `layerGroups.poi`; используется и `onPoiCheckbox`, и `restorePoiState` — ровно как предлагает ADR | | 5. Backend/БД/API/инфраструктура без изменений | ✅ | изменения чисто клиентские | Выбран и реализован Вариант A. Решение полностью соответствует ADR. Замечание по консистентности с `toggleLayer('poi')` — см. P3-1. ## 3. Качество кода Сильные стороны: - Изменения локализованы в явно размеченном блоке `>>> ET-002 POI visibility block ... <<<` (`app.js:2800-2854`) — удобно для ревью и для тест-харнеса. - JSDoc на всех трёх функциях, есть ссылка на ADR в шапке блока. - `applyPoiVisibility` имеет guard `if (!map) return` и проверку `map.getLayer(id)` перед `setLayoutProperty` — устойчиво к раннему вызову и к смене стиля. - `restorePoiState` корректно интегрирован в существующую цепочку восстановления: `style.load → onMapStyleLoad → rebuildMapOverlays` (`app.js:131`) — POI восстанавливается и при первой загрузке, и при переключении темы. Паттерн идентичен `restoreTrailsState`. - `restorePoiState` не пишет в `localStorage` (восстановление не должно иметь побочных эффектов) — поведение задокументировано в JSDoc и покрыто тестом TP-03. - Разделение ответственности: персистентность — только в `onPoiCheckbox`, применение видимости — в общем хелпере. Замечаний P0/P1/P2 нет. ## 4. Качество тестов `tests/unit/poi_toggle.test.js` исполняет **реальный** код из `app.js` (блок извлекается по маркерам ET-002 и оборачивается через `new Function` с мок-зависимостями) — это покрывает риск рассинхрона тестов и продакшн-кода. - TP-01 — снятие чекбокса: скрытие слоёв + `setItem('poi-visible','0')` + `layerState.poi=false` ✅ - TP-02 — установка чекбокса: показ слоёв + `setItem('1')` + `layerState.poi=true` ✅ - TP-03 — `restorePoiState()` при `'0'`: скрытие + чекбокс снят + без записи в `localStorage` ✅ - TP-04 — `restorePoiState()` без ключа: дефолт «видимы» ✅ - Доп.: значение `'1'`, изоляция чужих слоёв (дух TP-08), синхронизация `layerState` без слоёв на карте. TP-05..TP-07, TP-09 (e2e) и TP-08 (integration) по своей природе не покрываются юнит-тестом — это ожидаемо и не является замечанием к данному PR. ## Findings ### P3-1 (nice-to-have) — рассинхрон `toggleLayer('poi')` с чекбоксом `toggleLayer(group)` (`app.js:386-396`) меняет `layerState.poi`, но не обновляет ни чекбокс `poi-visible-cb`, ни `localStorage`. ADR-0001 п.4 указывает, что состояние кнопки и чекбокса не должно расходиться. Смягчающие факты: `toggleLayer` не имеет ни одного вызова в кодовой базе, элемента `btn-poi` в `index.html` нет (вызов `toggleLayer('poi')` упал бы на `btn.classList`). Фактически это недостижимый код, расхождение пользователю не наблюдаемо. ADR сам относит унификацию тулбар-кнопок и popup-чекбоксов к будущему техдолгу (раздел «Технический долг»). Рекомендация (вне scope ET-002): удалить мёртвый `toggleLayer` либо оформить unified-контроллер слоёв отдельной задачей. ### P3-2 (nice-to-have) — REQ-NF-03 не проверяется по диффу Touch target ≥ 44px зависит от CSS-класса `terrain-checkbox` в `app.css`, который в рамках ET-002 не менялся (ТЗ §3 предписывает переиспользовать существующий класс). Замечания к коду нет — отметка для приёмки: подтвердить REQ-NF-03 прогоном e2e-теста TP-09. ### P3-3 (nice-to-have) — неровный отступ в IIFE `initTerrain` В `app.js:2949-2950` и `2962-2963` вызовы `restoreTrailsState()` / `restorePoiState()` имеют отступ, не совпадающий с окружающим блоком. Это предсуществующая стилевая мелочь, которую ET-002 лишь продолжил. Косметика; рекомендуется выровнять при ближайшем касании файла. ## Итог Реализация полностью соответствует ТЗ и ADR-0001, код аккуратен, юнит-тесты исполняют реальный код и покрывают TP-01..TP-04. Найдены только три замечания уровня P3, ни одно из которых не блокирует мерж. **Вердикт: APPROVED.**