Files
wiki/tasks/enduro-trails/BRD_WAYPOINT_SEARCH.md
2026-05-06 01:00:01 +03:00

198 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BRD: Enduro Trails — Поиск точек маршрута
**Версия:** 1.1
**Дата:** 2026-05-05
**Автор:** Стрим 🌊
**Статус:** 🔄 В разработке
---
## 1. Контекст и проблема
Сейчас добавить точку маршрута можно только тапом на карту. Если нужно попасть в конкретное место (деревня, перекрёсток, POI) — нужно сначала найти его на карте, потом тапнуть. Это неудобно, особенно на мобиле.
Верхний search bar решал задачу навигации по карте, но не интегрирован с маршрутом — найденное место просто центрировало карту, не добавляя точку. Плюс bar занимал место и мешал карте.
---
## 2. Цель
Убрать верхний search bar. Добавить поиск прямо в список точек маршрута — inline, для каждой точки отдельно. При первом открытии режима маршрута — онбординг с полями ввода старта и финиша.
---
## 3. Что меняется
### 3.1 Убрать верхний search bar
- Удалить `#search-bar` и `#search-results` из HTML
- Удалить все CSS стили (`#search-bar`, `#search-input`, `#search-results`, `.search-result-item`, `.sri-*`)
- Удалить вызов `initSearch()` из JS
- Кнопку темы (`#btn-theme`) перенести в `#map-controls-r` (рядом с компасом и геолокацией)
### 3.2 Онбординг при активации режима маршрута
Когда пользователь тапает «Маршрут» в toolbar и точек ещё нет — показывать вместо пустого списка:
```
┌─────────────────────────────────┐
│ 🔍 Откуда? │ ← поле поиска старта
│ или тапни на карте │ ← подсказка
└─────────────────────────────────┘
```
После выбора старта автоматически появляется:
```
┌─────────────────────────────────┐
│ S Название старта │ ← добавленная точка
├─────────────────────────────────┤
│ 🔍 Куда? │ ← поле поиска финиша
│ или тапни на карте │
└─────────────────────────────────┘
```
После выбора финиша — поля исчезают, маршрут строится, показывается обычный `wl-list`.
**Детали:**
- Поля поиска используют тот же Nominatim что и inline поиск в wl-item
- Подсказка «или тапни на карте» — мелкий текст `var(--text3)` под полем
- Тап на карте тоже работает — добавляет точку и переходит к следующему полю
- Если точки уже есть — онбординг не показывается, сразу wl-list
### 3.3 Inline поиск в каждой точке маршрута
В каждом `wl-item` добавить кнопку-лупу. Тап → разворачивается inline панель поиска прямо под точкой.
**UX flow:**
1. Пользователь видит список точек маршрута (S → 1 → F)
2. Тапает иконку 🔍 рядом с нужной точкой
3. Под точкой появляется поле ввода с автофокусом
4. Вводит название места → через 400ms debounce → Nominatim поиск
5. Выбирает результат → точка обновляется, карта летит к ней, маршрут перестраивается
6. Панель закрывается
**Детали:**
- Одновременно открыта только одна панель поиска (остальные закрываются)
- Поиск через Nominatim (`countrycodes=ru`, `accept-language=ru`, limit=5)
- Результат: название + подзаголовок (область/район)
- После выбора: `routeWaypoints[idx]` обновляется, `rebuildWaypointMarkers()` + `renderWaypointsList()` + `debounceBuildRoute()`
- `map.flyTo()` к выбранному месту (zoom 13, duration 600ms)
---
## 4. UI компоненты
### Кнопка лупы в wl-item
- Иконка: Lucide `search` SVG (15×15)
- Цвет: `var(--text3)` → hover `var(--accent)`
- Позиция: между `.wl-info` и `.wl-drag-handle`
### Inline панель поиска
- Фон: `var(--surface)`, бордер сверху `var(--border)`
- Input: `var(--surface2)` фон, `var(--border)` бордер, focus → `var(--accent)` бордер
- Результаты: max-height 180px, scroll, hover `var(--surface2)`
- Каждый результат: название (bold) + подзаголовок (мелкий, `var(--text2)`)
### Кнопка темы (перенос)
- Переносится в `#map-controls-r` как `.map-btn`
- `updateThemeButtonIcon()` продолжает работать — находит `#btn-theme` по id
---
## 5. Технические детали
| Аспект | Решение |
|--------|---------|
| Поиск API | Nominatim `/search` (уже используется в проекте) |
| Debounce | 400ms |
| Состояние | `wpSearchTimeout` — глобальная переменная для debounce |
| Закрытие панели | Тап на лупу повторно — закрывает; открытие другой — закрывает текущую |
| После выбора | `routeWaypoints[idx]` = `{lat, lon}`, rebuild + route |
| Кнопка «Добавить точку» | `wl-add` остаётся без изменений |
---
## 6. Баги после редизайна (требуют исправления)
| Баг | Описание | Вероятная причина |
|-----|---------|------------------|
| Метки в верхнем левом углу | Попап метки отображается не на позиции маркера, а в углу экрана | CSS `position` маркера сбивает позиционирование MapLibre |
| Метки линейки в верхнем углу | Попап точки линейки отображается не на треке, а в углу | Та же причина — CSS позиционирование маркера |
| Подсказка «Тапни на карте» пропала | `#route-status` не виден когда точек нет | Скрыт или перекрыт после редизайна |
**Вероятная причина багов с маркерами:** в редизайне изменились глобальные CSS стили для `position`, `transform` или `z-index` — это сбивает позиционирование MapLibre маркеров. Маркер MapLibre должен иметь `position: absolute` без переопределения в глобальных стилях.
**Фикс:** проверить CSS для `.maplibregl-marker`, `.marker-flag`, `.ruler-marker` — убрать любой `position: fixed/relative/static` который может переопределять поведение маркера.
---
## 7. Definition of Done
| Критерий | Статус |
|----------|--------|
| Верхний search bar отсутствует | ⬜ |
| Кнопка темы работает в map-controls-r | ⬜ |
| При активации маршрута без точек — поле поиска старта | ⬜ |
| После старта — поле поиска финиша | ⬜ |
| Подсказка «или тапни на карте» видна | ⬜ |
| Иконка лупы в каждом wl-item | ⬜ |
| Тап на лупу → inline поиск открывается | ⬜ |
| Поиск находит места через Nominatim | ⬜ |
| Выбор результата → waypoint обновляется | ⬜ |
| Маршрут перестраивается после выбора | ⬜ |
| Карта летит к выбранному месту | ⬜ |
| Одновременно открыта только одна панель | ⬜ |
| Метки отображаются на позиции маркера (не в углу) | ⬜ |
| Метки линейки отображаются на треке | ⬜ |
| Деплой + health check OK | ⬜ |
---
## 8. Файлы
| Файл | Изменения |
|------|-----------|
| `prototype/static/index.html` | Удалить `#search-bar`, `#search-results`; перенести `#btn-theme` в `#map-controls-r` |
| `prototype/static/app.css` | Удалить стили search bar; добавить `.wl-search-*`; исправить позиционирование маркеров |
| `prototype/static/app.js` | Удалить `initSearch()`; добавить онбординг, `openWaypointSearch()`, `doWaypointSearch()`, `selectWaypointSearchResult()` |
---
---
## 9. Дополнительные замечания (05.05.2026 — пакет 2)
| # | Замечание | Детали |
|---|-----------|--------|
| 1 | Иконка колеса | Заменить на нормальное мотокросс колесо |
| 2 | Флажок финиша | Чёрно-белая шахматная раскраска как финишный флаг |
| 3 | Спиннер в основном листе | Вращающееся колесо пока строится маршрут (как в мини-баре) |
| 4 | Тёмная карта | При тёмной теме — тёмный стиль карты, при светлой — светлый |
| 5 | Автозум на маршрут | Плавный `fitBounds` после построения маршрута, маршрут по центру без выхода за границы |
| 6 | Мини-бар перекрывает кнопки справа | Исправить z-index/позиционирование `#sheet-route-mini` |
### Детали по пунктам
**П.1 Иконка колеса:**
Заменить текущую SVG на нормальное мотокросс колесо — спицы, кноблинг, центральная втулка. Использовать Lucide `bike` или нарисовать SVG колеса с спицами.
**П.2 Флажок финиша:**
В `waypointPinSvg('F', ...)` для финишной точки использовать шахматный узор (SVG `pattern` чёрные/белые клетки) вместо красного цвета.
**П.3 Спиннер в основном листе:**
В `#route-cards` показывать skeleton/спиннер пока идёт запрос к OSRM. Тот же спиннер что в мини-баре.
**П.4 Тёмная карта:**
`map.setStyle()` при смене темы. Тёмный стиль: `/style-dark.json` (или текущий тёмный), светлый: `/style-light.json` (или текущий светлый). После `setStyle` пересоздать слои маршрутов через `map.on('style.load', onMapStyleLoad)`.
**П.5 Автозум:**
После `drawRouteResults()` вызывать:
```js
const coords = routeResults[0].geometry.coordinates;
const bounds = coords.reduce((b, c) => b.extend(c), new maplibregl.LngLatBounds(coords[0], coords[0]));
map.fitBounds(bounds, { padding: 60, duration: 1000, maxZoom: 14 });
```
**П.6 Мини-бар:**
`#sheet-route-mini` перекрывает `#map-controls-r`. Исправить `right` или `width` мини-бара чтобы не залезал на кнопки справа.