---
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» документа (наследие)
