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

155 lines
9.8 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 — Фаза 5 «Редизайн»
**Версия:** 2.0
**Дата:** 2026-05-05
**Автор:** Стрим 🌊
**Статус:** ✅ Реализовано (с дополнениями)
---
## 1. Контекст и проблема
Фазы 34 дали полный функционал (роутинг, разведка, связка, красивый маршрут), но UI был dev-прототипом: белый фон, emoji-иконки, панели перекрывали карту, не работало на мобиле в перчатках.
## 2. Цель
Мобильный UI уровня onX Offroad / Locus Map — тёмный, эндуро-стильный, thumb-friendly. Карта — главный герой. UI — минималистичный HUD.
## 3. Что реализовано в Фазе 5
### ✅ Дизайн-система
- **Две темы:** тёмная (`body.theme-dark`) и светлая (`body.theme-light`)
- **Авто-режим:** SunCalc (CDN) определяет восход/закат по геолокации (fallback: Москва 55.75°N)
- **Три режима переключателя:** Авто → Светлая → Тёмная → Авто (циклически)
- **Сохранение:** `localStorage` — режим темы сохраняется между сессиями
- **CSS vars:** все компоненты используют `var(--bg)`, `var(--surface)`, `var(--accent)` и т.д.
- **Карта:** `map.setStyle()` переключает dark/light style.json при смене темы; слои пересоздаются через `map.on('style.load', onMapStyleLoad)`
### ✅ Layout и компоненты
- **Search bar** — `position: fixed`, top, safe-area, все цвета через CSS vars
- **Bottom toolbar** — 6 режимов (Маршрут, Связка, Красивый, Разведка, Линейка, Метка), SVG Lucide-иконки, активная кнопка = оранжевый фон + белый текст
- **Bottom sheets** — 4 шита (route/recon/scenic/link), slide-up анимация, drag handle, safe-area padding
- **Map controls** — компас + геолокация, справа
- **Skeleton loading** — shimmer-анимация в карточках маршрутов пока идёт запрос
### ✅ Анимации
- Sheet slide: `transform: translateY` + `cubic-bezier(0.32, 0, 0.15, 1)`, 300ms
- Кнопки: `scale(0.94)` при tap
- Карточки маршрутов: `cardFadeIn` stagger (0/60/120/180/240ms)
- Маркеры: `markerPopIn` scale появление
### ✅ Свайп вниз для закрытия sheet
- `initSheetSwipe()` — touch-обработка на всех `.bottom-sheet`
- Свайп > 80px → `closeSheet()`
- Во время свайпа: `translateY(dy)` для визуального feedback
### ✅ Адаптив — десктоп
- `@media (min-width: 768px)` — toolbar вертикально слева, sheet с max-width 400px
### ✅ Промежуточные точки — drag-and-drop (добавлено в ходе фазы)
- Grip-иконка (6 кружков, Lucide-style) в каждом `wl-item`
- `_initWaypointDragHandles(list)` — единая логика для touch и mouse
- **Touch (мобиль):** touchstart/touchmove/touchend
- **Mouse (десктоп):** mousedown → document mousemove/mouseup
- Визуальный feedback: `dragging` (opacity 0.4), `drag-over-top`/`drag-over-bottom` (border accent)
- После drop: `rebuildWaypointMarkers()` + `renderWaypointsList()` + `debounceBuildRoute()`
### ✅ Кнопка «Скачать GPX» (изменение в ходе фазы)
- Кнопка «Поделиться» (share dialog с Telegram/WhatsApp) убрана по запросу Славы
- Заменена на прямую кнопку «Скачать GPX» с download-иконкой (стрелка вниз)
- `onclick="downloadGPX()"` — без промежуточных диалогов
### ✅ Расстояние по маршруту между точками (добавлено 05.05.2026)
- Функция `getRouteSegmentDistances()` — snap каждого waypoint к ближайшей точке геометрии OSRM
- Первая точка → `snapIdx[0] = 0`, последняя → `snapIdx[n-1]` — гарантирует покрытие всей геометрии
- Сумма сегментов масштабируется к `route.distance_m` — точное совпадение (diff=0)
- Обновляется при смене варианта (`selectRoute`, `selectMiniRoute`)
- Обновляется после построения маршрута (`drawRouteResults``renderWaypointsList`)
- Fallback на haversine по прямой если маршрут ещё не построен
- Форматирование `toFixed(1)` везде (сегменты, карточки вариантов, мини-бар)
- `#mini-route-bar` — компактная полоска поверх карты когда sheet свёрнут
- Показывает активный маршрут (дистанция, % грунт)
- Кнопки: добавить точку, развернуть sheet
- `miniAddWaypoint()` — теперь корректно устанавливает `routeMode = true` перед `addingWaypoint = true`
---
## 4. Баги исправленные в ходе фазы
| Баг | Причина | Фикс |
|-----|---------|------|
| Кнопка «Добавить точку» не работала | `addWaypointMode()` не устанавливала `routeMode = true` | Добавлена проверка и установка `routeMode` |
| Web Share API не работал на HTTP | API требует HTTPS | Убран share dialog, оставлено только скачивание GPX |
| Drag-and-drop не работал на десктопе | Только touch-события | Добавлены mouse-события (mousedown/mousemove/mouseup) |
| CSS классы диалога не совпадали с JS | Dev добавил JS с одними классами, CSS с другими | Унифицированы, затем диалог полностью убран |
| Расстояние между точками по прямой | `renderWaypointsList` вызывался до построения маршрута, `routeResults` был пуст | Добавлен `renderWaypointsList()` после `drawRouteResults()` |
| Расстояние не обновлялось при смене варианта | `selectRoute`/`selectMiniRoute` не вызывали `renderWaypointsList` | Добавлен `renderWaypointsList()` в обе функции |
| Деплой статики не работал | `deploy_app2.js` копирует только `app.py`; образ перезаписывает статику при рестарте | SFTP → `docker restart``docker cp` (порядок важен!) |
---
## 5. Definition of Done — статус
| Критерий | Статус |
|----------|--------|
| Все P0 тест-кейсы на iPhone SE (375px) | ✅ |
| Bottom sheet плавно открывается/закрывается | ✅ |
| Toolbar: все 6 режимов, min 48px tap target | ✅ |
| Тема: авто (SunCalc) + ручной переключатель | ✅ |
| SVG иконки (Lucide), никаких emoji в UI | ✅ |
| Карта видна при активных панелях | ✅ |
| Safe area корректная | ✅ |
| Десктоп: боковая панель, не ломается layout | ✅ |
| GPX скачивание работает | ✅ |
| Drag-and-drop точек маршрута (touch + mouse) | ✅ |
| Расстояние по маршруту между точками (сумма = route.distance_m) | ✅ |
| Обновление расстояний при смене варианта маршрута | ✅ |
| Деплой + health check OK | ✅ |
---
## 6. Технический стек фронтенда
- **MapLibre GL JS** 4.7.0 (CDN)
- **SunCalc** 1.9.0 (CDN) — авто-тема по восходу/закату
- Без npm-зависимостей, всё inline CSS/JS
- Деплой: Node.js ssh2 → SFTP на сервер → `docker restart``docker cp` после рестарта
### ⚠️ Критически важно: порядок деплоя статики
Образ Docker запекает статику при сборке. При `docker restart` образ перезаписывает `/app/static/`. Поэтому:
1. SFTP загрузить файлы на сервер (`/home/slin/enduro-trails/prototype/static/`)
2. `docker restart prototype-enduro-trails-1`
3. Подождать 8 секунд
4. `docker cp /home/slin/.../app.js prototype-enduro-trails-1:/app/static/app.js`
5. `docker cp /home/slin/.../app.css prototype-enduro-trails-1:/app/static/app.css`
6. `docker cp /home/slin/.../index.html prototype-enduro-trails-1:/app/static/index.html`
`deploy_app2.js`**только для `app.py`** (бэкенд).
`deploy_static.js` — SFTP статики, но cp нужно делать вручную после рестарта.
---
## 7. Файлы
| Файл | Описание |
|------|----------|
| `prototype/static/index.html` | HTML, CDN подключения, структура DOM |
| `prototype/static/app.css` | Все стили, CSS vars, темы, компоненты |
| `prototype/static/app.js` | Вся логика: карта, роутинг, UI, drag-and-drop |
| `prototype/app.py` | Бэкенд FastAPI (не трогать) |
---
*Фаза 5 завершена. Следующая: Фаза 6 — SRTM рельеф.*