diff --git a/docs/work-items/ET-002/06-adr/adr-0001-poi-visibility-client-side.md b/docs/work-items/ET-002/06-adr/adr-0001-poi-visibility-client-side.md new file mode 100644 index 0000000..fffc5a1 --- /dev/null +++ b/docs/work-items/ET-002/06-adr/adr-0001-poi-visibility-client-side.md @@ -0,0 +1,139 @@ +--- +type: adr +work_item_id: ET-002 +adr_id: adr-0001 +title: "ADR-0001: Управление видимостью POI — клиентское решение на localStorage" +status: accepted +created_at: 2026-05-21 +authors: + - "agent:architect" +supersedes: [] +superseded_by: [] +labels: [] +--- + +# ADR-0001 — Управление видимостью POI: клиентское решение на localStorage + +## Статус + +Accepted + +## Контекст + +ET-002 добавляет в попап кнопки «Рельеф» (`terrain-popup`) чекбокс «POI», +позволяющий пользователю скрывать маркеры POI (слои `poi-circles`, +`poi-labels`), чтобы не загромождать карту при планировании маршрута. + +Существующее состояние кодовой базы (`src/web/app.js`): + +- объект `layerState = { tracks, paths, poi, basemap }` — внутренняя модель + видимости слоёв (строка 377), `poi: true` по умолчанию; +- карта групп слоёв `poi: ['poi-circles', 'poi-labels']` (строка 381); +- функция `toggleLayer(group)` уже переключает видимость через + `map.setLayoutProperty(..., 'visibility', ...)` (строки 385–389), но + завязана на кнопку-тулбар (`btn.classList.toggle('active', ...)`), а не на + чекбокс попапа; +- сложился устойчивый паттерн персистентности в `localStorage` со + значениями `'1'`/`'0'`: ключи `terrain-hillshade`, `terrain-tri`, + `trails-track`, `trails-path`, `enduro-theme-mode`, `MARKERS_KEY`. + +Backend (FastAPI) и БД (SQLite/Spatialite) к видимости POI отношения не +имеют — POI отдаются как часть тайлов/источника, видимость регулируется +исключительно на уровне рендера MapLibre. + +## Рассматриваемые варианты + +### Вариант A — Клиентское решение: `setLayoutProperty` + `localStorage` (выбран) + +Видимость переключается через `map.setLayoutProperty()` для слоёв +`poi-circles` и `poi-labels`; состояние хранится в `localStorage` под +ключом `poi-visible`; `layerState.poi` остаётся единственным источником +истины в рантайме. + +- **Плюсы:** нулевые изменения backend/БД/инфраструктуры; переключение + мгновенное (тайлы не перезагружаются — слой остаётся в источнике, + меняется только `layout.visibility`); полностью консистентно с уже + существующими чекбоксами попапа и принципом «минимум зависимостей». +- **Минусы:** настройка не синхронизируется между устройствами/браузерами + (для данной фичи это приемлемо и зафиксировано как Out of scope в BRD). + +### Вариант B — Хранение настройки на backend (профиль пользователя) + +Видимость POI сохраняется через новый API-эндпоинт в SQLite. + +- **Плюсы:** синхронизация между устройствами. +- **Минусы:** требует модели пользователя/сессий (в проекте отсутствует), + новый эндпоинт, миграцию БД, сетевой round-trip — несопоставимо + избыточно для одного UI-флага. Противоречит принципам BRD (минимум + зависимостей, нет необходимости в server-state). + +### Вариант C — Удаление/повторное добавление слоёв POI + +Скрытие через `map.removeLayer()`, показ через `map.addLayer()`. + +- **Плюсы:** нет «невидимых» слоёв в стиле. +- **Минусы:** дороже по производительности, риск рассинхрона порядка + слоёв и обработчиков событий (`map.on('click', 'poi-circles', ...)`, + строка 1515), усложняет код. `setLayoutProperty` — штатный механизм + MapLibre именно для этого сценария. + +## Решение + +Принимается **Вариант A**. + +1. **Видимость** слоёв `poi-circles` и `poi-labels` переключается через + `map.setLayoutProperty(layerId, 'visibility', 'visible' | 'none')`. +2. **Персистентность** — `localStorage`, ключ `poi-visible`, значения + `'1'` / `'0'`, в соответствии с уже сложившейся конвенцией проекта. +3. **Источник истины в рантайме** — `layerState.poi`. Обработчик + `onPoiCheckbox()` обязан синхронно обновлять `layerState.poi`, + `localStorage` и `layout.visibility` обоих слоёв. +4. **Без дублирования логики.** Логика установки видимости группы POI + должна переиспользовать существующую карту групп слоёв + (`poi: ['poi-circles','poi-labels']`). Допускается выделение общего + приватного хелпера (например `applyPoiVisibility(visible)`), + вызываемого как из `onPoiCheckbox()`, так и из восстановления + состояния при загрузке. `toggleLayer('poi')` (тулбар-кнопка) и + `onPoiCheckbox()` (чекбокс попапа) должны оставаться консистентны + через общий `layerState.poi` — недопустимо, чтобы кнопка и чекбокс + показывали разное состояние. +5. **Backend, БД, API, инфраструктура — без изменений.** Состав + компонентов C4 не меняется, обновление C4-диаграмм не требуется. + +## Последствия + +### Положительные + +- Изменение полностью клиентское: deploy без миграций и без изменения + состава контейнеров. +- Переключение < 50 мс (REQ-NF-01) — тайлы не перезагружаются. +- Конвенция `localStorage` остаётся единообразной — ниже когнитивная + нагрузка при поддержке. + +### Отрицательные / ограничения + +- Настройка локальна для браузера: при очистке `localStorage` или смене + устройства сбрасывается на дефолт (POI включены). Принято осознанно. +- В стиле карты остаются слои с `visibility: none` — это штатно для + MapLibre и не влияет на производительность. + +### Технический долг + +- `toggleLayer()` и `onPoiCheckbox()` частично дублируют намерение + «переключить группу слоёв». Если в будущих фазах появятся ещё + popup-чекбоксы для слоёв, стоит унифицировать тулбар-кнопки и + popup-чекбоксы в единый контроллер слоёв. На ET-002 — вне scope. + +## Классификация изменения + +Minor change. Новые сервисы/БД/эндпоинты не вводятся, состав компонентов +не меняется. Лейбл `arch:major-change` **не требуется**. Обязательного +архитектурного approve не требуется. + +## Связанные документы + +- `docs/work-items/ET-002/01-brd.md` +- `docs/work-items/ET-002/02-trz.md` +- `docs/work-items/ET-002/03-acceptance-criteria.md` +- `docs/work-items/ET-002/07-infra-requirements.md` +- `docs/architecture/README.md` diff --git a/docs/work-items/ET-002/07-infra-requirements.md b/docs/work-items/ET-002/07-infra-requirements.md new file mode 100644 index 0000000..0dced72 --- /dev/null +++ b/docs/work-items/ET-002/07-infra-requirements.md @@ -0,0 +1,94 @@ +--- +type: infra-requirements +work_item_id: ET-002 +title: "Инфраструктурные требования — ET-002: Чекбокс показа/скрытия POI" +version: 1 +status: approved +created_at: 2026-05-21 +authors: + - "agent:architect" +--- + +# Инфраструктурные требования — ET-002 + +## 1. Резюме + +ET-002 — изменение **исключительно фронтенда** (`src/web/index.html`, +`src/web/app.js`). Новой инфраструктуры не требуется. Документ +зафиксирован для полноты work-item и явно подтверждает отсутствие +инфра-воздействия (см. ADR-0001). + +## 2. Контейнеры и сервисы + +| Аспект | Требование | +|--------|------------| +| Новые контейнеры | Нет | +| Изменения существующих сервисов (api, osrm, nginx) | Нет | +| Изменения `docker-compose.yml` | Нет | +| Изменения `Dockerfile` | Нет | + +## 3. Сеть + +| Аспект | Требование | +|--------|------------| +| Новые порты | Нет | +| Изменения reverse proxy (nginx, `/enduro/`) | Нет | +| Новые внешние домены / DNS | Нет | +| Исходящие сетевые вызовы из фронтенда | Нет (вся логика локальная) | + +## 4. Хранилища данных + +| Аспект | Требование | +|--------|------------| +| Изменения схемы SQLite/Spatialite | Нет | +| Миграции БД (`migrations/`) | Нет | +| Серверное хранилище состояния | Нет | +| Клиентское хранилище | `localStorage`, ключ `poi-visible` (`'1'`/`'0'`), ≈ 1 байт полезной нагрузки на браузер | + +## 5. Конфигурация и секреты + +| Аспект | Требование | +|--------|------------| +| Новые переменные окружения | Нет | +| Новые секреты | Нет | +| Изменения конфигурации FastAPI / uvicorn | Нет | + +## 6. Зависимости + +| Аспект | Требование | +|--------|------------| +| Новые npm/Python пакеты | Нет | +| Новые внешние сервисы | Нет | +| Версия MapLibre GL JS | Без изменений (`setLayoutProperty` — штатный API) | + +## 7. Сборка и деплой + +- **Pipeline:** существующий Gitea Actions без изменений (`lint`, `test`, + `build`). +- **Артефакт:** статические ассеты фронтенда (`src/web/`). Деплой — + штатная пересборка/перевыкладка и `docker compose up -d` на mva154. +- **Простой (downtime):** нет — изменение только в статике фронтенда. +- **План отката:** обратный коммит (revert) и повторный деплой; + миграций/состояния, требующих отдельного отката, нет. + +## 8. Ресурсы (CPU / RAM / диск) + +Воздействие отсутствует. Переключение `layout.visibility` слоёв +выполняется в браузере клиента; тайлы не перезапрашиваются (REQ-NF-01). + +## 9. Наблюдаемость + +Новые метрики, логи и алерты не требуются. Поведение проверяется +e2e-тестами фронтенда согласно `04-test-plan.yaml`. + +## 10. Влияние на C4 + +Состав компонентов системы не меняется. Обновление +`docs/architecture/c4-*.mmd` не требуется (диаграммы C4 в репозитории +на данный момент отсутствуют — только `docs/architecture/README.md`). + +## 11. Вывод + +Инфраструктурных, сетевых, конфигурационных изменений и изменений БД +**нет**. ET-002 безопасен для деплоя в рамках обычного релизного цикла +фронтенда. Эскалация `arch:major-change` не требуется.