265 lines
16 KiB
Markdown
265 lines
16 KiB
Markdown
---
|
||
type: data-requirements
|
||
work_item_id: ET-014
|
||
title: "Требования к данным — ET-014: Z-index фикс — terrain-popup уступает sheet'у"
|
||
version: 1
|
||
status: approved
|
||
created_at: 2026-06-04
|
||
authors:
|
||
- "agent:architect"
|
||
---
|
||
|
||
# Требования к данным — ET-014
|
||
|
||
## 1. Резюме
|
||
|
||
ET-014 — **pure client UI ordering change**. Никаких изменений в данных:
|
||
ни в БД, ни в файлах на диске, ни в localStorage, ни в API-контрактах,
|
||
ни в конфигурациях.
|
||
|
||
Меняется **порядок вызова двух уже существующих UI-функций** в
|
||
`src/web/app.js`: при открытии любого `.bottom-sheet` теперь
|
||
принудительно вызывается helper `closeTerrainPopup()`, который скрывает
|
||
`#terrain-popup` (если он открыт) и снимает класс `.active` с
|
||
`#terrain-toggle`.
|
||
|
||
**Меняется:**
|
||
|
||
- Порядок DOM-операций при `openSheet(id)` (1 дополнительный вызов).
|
||
- Видимое состояние `#terrain-popup` в момент открытия любого
|
||
bottom-sheet (теперь скрывается; раньше оставался открытым → визуальный
|
||
баг ET-014).
|
||
|
||
**Не меняется:**
|
||
|
||
- Содержимое и схема БД `centralfederal.sqlite`, `gps_tracks.sqlite`.
|
||
- Содержимое и формат PNG-тайлов в `data/terrain/*`.
|
||
- Контракты API (`/api/gps-tracks/*`, `/terrain/*`, `/api/route/*`,
|
||
`/api/health`, прочие).
|
||
- Ключи `localStorage` (`terrain-hillshade`, `terrain-tri`,
|
||
`gps-tracks-enabled`, gps-фильтры, theme, units и т. д.).
|
||
- `style.json`, `style-dark.json`.
|
||
- `config/*.yaml`.
|
||
- `src/web/index.html`, `src/web/gps_tracks.js`, `src/web/app.css`.
|
||
|
||
## 2. Архитектурные границы данных
|
||
|
||
| Слой данных | Тип | Расположение | Изменения в ET-014 |
|
||
|-----------------------------------|----------------|----------------------------------------------|-------------------------------------------------|
|
||
| OSM-vector (`trails`) | существующий | `/app/data/centralfederal.sqlite` | **нет** |
|
||
| Личные GPX треки (ET-006) | существующий | браузер (memory) | **нет** |
|
||
| Публичные GPS треки (ET-008) | существующий | `/app/data/gps_tracks.sqlite` | **нет** |
|
||
| OSRM-граф | существующий | `/app/data/enduro.osrm.*` | **нет** |
|
||
| Terrain hillshade/TRI/hypso PNG | существующий | `data/terrain/*` | **нет** |
|
||
| User UI state | существующий | `localStorage` | **нет** новых ключей, нет миграции |
|
||
| MapLibre client tile cache | существующий | браузер (LRU MapLibre) | **нет** |
|
||
| Серверный кэш | не предусмотрен | n/a | **нет** |
|
||
| DOM-state `#terrain-popup` | runtime UI | браузер (DOM) | **меняется**: `display:none` при `openSheet()` |
|
||
| DOM-state `#terrain-toggle` | runtime UI | браузер (DOM) | **меняется**: класс `.active` снимается |
|
||
| DOM-state `.bottom-sheet` | runtime UI | браузер (DOM) | **не меняется** (та же логика `.open`) |
|
||
| DOM-state `#sheet-backdrop` | runtime UI | браузер (DOM) | **не меняется** (та же логика `.visible`) |
|
||
| `closeTerrainOnOutside` listener | runtime UI | браузер (event listener на `document`) | **снимается** через `removeEventListener` |
|
||
|
||
## 3. Серверные данные
|
||
|
||
### 3.1 БД
|
||
|
||
**Без изменений vs ET-013/ET-008.**
|
||
|
||
- `centralfederal.sqlite` — read-only для ET-014.
|
||
- `gps_tracks.sqlite` — read-only для ET-014.
|
||
- Никаких ALTER/CREATE/INSERT/UPDATE/DELETE.
|
||
- Никаких миграций.
|
||
|
||
### 3.2 Тайлы на диске
|
||
|
||
**Без изменений.** `data/terrain/*`, `data/osm/*`, `data/osrm/*` — не
|
||
трогаются.
|
||
|
||
### 3.3 Статика `src/web/`
|
||
|
||
| Файл | Изменение |
|
||
|-----------------------|-----------------------------------------------------------------|
|
||
| `src/web/app.js` | +1 helper-функция `closeTerrainPopup()` (~7 строк), +1 вызов в `openSheet()` |
|
||
| `src/web/app.css` | **нет** |
|
||
| `src/web/index.html` | **нет** |
|
||
| `src/web/gps_tracks.js` | **нет** |
|
||
| `src/web/gpx.js` | **нет** |
|
||
| `src/web/units.js` | **нет** |
|
||
| `src/web/style.json` | **нет** |
|
||
| `src/web/style-dark.json` | **нет** |
|
||
|
||
Дельта размера `app.js`: ~+300 байт (helper-функция + комментарий +
|
||
вызов). Пренебрежимо.
|
||
|
||
## 4. Клиентские данные
|
||
|
||
### 4.1 localStorage
|
||
|
||
**Без изменений.** Используются существующие ключи (read-only для
|
||
ET-014):
|
||
|
||
| Ключ | Назначение | Изменения в ET-014 |
|
||
|----------------------------|---------------------------------------------|--------------------|
|
||
| `terrain-hillshade` | `'1' | '0'` — чекбокс «Тени рельефа» | **нет** |
|
||
| `terrain-tri` | `'1' | '0'` — чекбокс «Перепады» | **нет** |
|
||
| `gps-tracks-enabled` | публичные треки on/off | **нет** |
|
||
| `gps-filter-*` | состояние фильтров публичных треков | **нет** |
|
||
| `theme` | `'dark' | 'light'` | **нет** |
|
||
| `units` | `'km' | 'mi'` | **нет** |
|
||
| `base-layer` | подложка | **нет** |
|
||
|
||
Никакой миграции. Существующие сессии при следующей загрузке
|
||
автоматически получают исправленное UI-поведение.
|
||
|
||
### 4.2 MapLibre LRU (browser-side)
|
||
|
||
Без изменений. Тайловый кэш не задействован — мы не меняем тайлы,
|
||
zoom-уровни, source.minzoom, или paint properties.
|
||
|
||
### 4.3 DOM runtime state
|
||
|
||
Ниже — единственное место, где ET-014 «меняет данные» (в runtime
|
||
браузера, не на диске):
|
||
|
||
#### `#terrain-popup`
|
||
|
||
- **До ET-014**: при клике на `#public-tracks-filters-btn` popup
|
||
остаётся `display: block`, z=500.
|
||
- **После ET-014**: при любом `openSheet(id)`, если
|
||
`popup.style.display !== 'none'`, popup переключается в
|
||
`display: none`.
|
||
|
||
#### `#terrain-toggle`
|
||
|
||
- **До ET-014**: при открытии sheet'а сохраняет класс `.active`.
|
||
- **После ET-014**: при `openSheet(id)` класс `.active` снимается
|
||
(синхронно с popup'ом).
|
||
|
||
#### Event listener `closeTerrainOnOutside` на `document`
|
||
|
||
- **До ET-014**: добавлен в `toggleTerrainPopup()` через
|
||
`addEventListener('click', closeTerrainOnOutside)`. Удаляется в двух
|
||
местах: повторный клик по `#terrain-toggle` и срабатывание самого
|
||
`closeTerrainOnOutside`.
|
||
- **После ET-014**: дополнительно удаляется внутри
|
||
`closeTerrainPopup()`, который вызывается из `openSheet()`. Двойной
|
||
`removeEventListener` безвреден (DOM-спека: removeEventListener на
|
||
отсутствующий listener — no-op).
|
||
|
||
### 4.4 In-memory constants
|
||
|
||
**Нет.** Никаких новых JS-констант (в отличие от ET-013 с
|
||
`HILLSHADE_PAINT` / `TRI_PAINT`). Только новая функция и вызов.
|
||
|
||
## 5. Контракты API
|
||
|
||
### 5.1 Backend endpoints
|
||
|
||
**Без изменений.** ET-014 — чистый клиент. Никаких новых вызовов,
|
||
никакого изменения параметров запросов, никакого изменения частоты
|
||
запросов.
|
||
|
||
| Endpoint | До ET-014 | После ET-014 |
|
||
|-----------------------------------------|-------------|--------------|
|
||
| `GET /api/gps-tracks/tiles/{z}/{x}/{y}.mvt` | без изменений | без изменений |
|
||
| `GET /api/gps-tracks?bbox=…` | без изменений | без изменений |
|
||
| `GET /api/gps-tracks/{id}/download` | без изменений | без изменений |
|
||
| `GET /api/gps-tracks/health` | без изменений | без изменений |
|
||
| `GET /terrain/{layer}/{z}/{x}/{y}.png` | без изменений | без изменений |
|
||
| `GET /api/route/*` | без изменений | без изменений |
|
||
| `GET /api/trails/*` | без изменений | без изменений |
|
||
|
||
### 5.2 Frontend internal API (`src/web/app.js`)
|
||
|
||
| Функция | До ET-014 | После ET-014 |
|
||
|-------------------------------|-------------------------------------------------|------------------------------------------------------------------------------|
|
||
| `openSheet(id)` | публичный (вызывается из всех `toggle*Sheet`) | публичный, контракт сохранён; добавлен внутренний вызов `closeTerrainPopup()` |
|
||
| `closeSheet(id)` | публичный | без изменений |
|
||
| `closeAllSheets()` | публичный | без изменений |
|
||
| `toggleTerrainPopup()` | публичный | без изменений |
|
||
| `closeTerrainOnOutside(e)` | публичный (выставляется как event handler) | без изменений (опциональный TD-1 рефакторинг описан в ADR-019) |
|
||
| `closeTerrainPopup()` | **отсутствует** | **новая** publish-функция (для возможного reuse) |
|
||
|
||
Контракт `openSheet(id)` совместим со всеми существующими вызовами:
|
||
|
||
```bash
|
||
$ grep -n 'openSheet(' src/web/*.js
|
||
```
|
||
|
||
- `app.js:openSheet(...)` — собственная реализация.
|
||
- `app.js:openSheet('sheet-route')`, `openSheet('sheet-recon')`,
|
||
`openSheet('sheet-scenic')`, `openSheet('sheet-link')`,
|
||
`openSheet('sheet-gpx')` — все продолжают работать как раньше.
|
||
- `gps_tracks.js:openSheet('sheet-gps-filters')` — продолжает работать;
|
||
дополнительно теперь корректно закрывает popup.
|
||
|
||
## 6. Миграции
|
||
|
||
**Нет.** Никаких миграций БД, миграций localStorage, миграций конфигов.
|
||
|
||
При деплое в test:
|
||
- `data/*` — без изменений.
|
||
- БД — без изменений.
|
||
- localStorage — старые ключи интерпретируются как раньше.
|
||
- MapLibre LRU — самоочищается при reload браузера; явной инвал. не нужно.
|
||
|
||
## 7. Тестовые данные
|
||
|
||
### 7.1 Для unit-тестов
|
||
|
||
В ET-014 **новых python unit-тестов не добавляется** — поведение
|
||
исключительно UI и тестируется через Playwright.
|
||
|
||
Опционально (cleanup, не обязательно): тест на статический grep по
|
||
`src/web/app.js`, что:
|
||
- Есть функция `closeTerrainPopup`.
|
||
- В теле `openSheet` есть вызов `closeTerrainPopup()`.
|
||
|
||
Если такой тест добавляется, формат — как `test_terrain_paint.py` в
|
||
ET-013 (`tests/unit/test_ui_z_index_fix.py`, regex по исходнику без
|
||
JS-runtime). Это **не блокирующий гейт** ET-014.
|
||
|
||
### 7.2 Для integration-тестов
|
||
|
||
Не применимо. ET-014 не трогает API endpoints, integration-тесты не нужны.
|
||
|
||
### 7.3 Для UI-тестов (Playwright)
|
||
|
||
`04b-ui-test-cases.md` — TC-UI-01..TC-UI-08:
|
||
|
||
- Запускается на test-среде `https://openclaw.mva154.duckdns.org/enduro/`.
|
||
- Данные — реальные (БД, тайлы) на mva154.
|
||
- Скриншоты в `tests/e2e/__screenshots__/ET-014/`.
|
||
- Не пиксельный diff; визуальная приёмка оператором + DOM-assertion'ы
|
||
(`classList.contains('open')`, `elementFromPoint`,
|
||
`getBoundingClientRect`).
|
||
|
||
## 8. Резервные копии и DR
|
||
|
||
**Без изменений.** ET-014 не пишет данных. RPO = 0.
|
||
|
||
## 9. Privacy / Compliance
|
||
|
||
| Аспект | Требование |
|
||
|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||
| PII | **Нет.** ET-014 не собирает, не обрабатывает, не передаёт никаких данных |
|
||
| Licensing | Не применимо |
|
||
| Attribution | MapLibre attribution control — без изменений |
|
||
| GDPR / 152-ФЗ | Не применимо |
|
||
|
||
## 10. Связанные документы
|
||
|
||
- `01-brd.md` §1 (бизнес-контекст), §3 (бизнес-цель), §4 (BR-01..BR-06)
|
||
- `02-trz.md` §1.1 (DOM-структура), §1.2 (стек z-index), §1.3 (корень),
|
||
§2 (REQ-F, REQ-NF), §3 (варианты)
|
||
- `03-acceptance-criteria.md` AC-01..AC-14
|
||
- `04b-ui-test-cases.md` TC-UI-01..TC-UI-08
|
||
- `06-adr/ADR-019-terrain-popup-yields-to-sheet.md`
|
||
- `07-infra-requirements.md`
|
||
- `10-tech-risks.md`
|
||
- `docs/work-items/ET-013/08-data-requirements.md` — образец «read-only
|
||
data» документа (наследие)
|
||
- `docs/work-items/ET-012/08-data-requirements.md` — образец «read-pattern
|
||
change» документа (наследие)
|