auto-sync: 2026-05-05 22:10:01
This commit is contained in:
223
tasks/enduro-trails/DEV_TASK_PHASE5_UX2.md
Normal file
223
tasks/enduro-trails/DEV_TASK_PHASE5_UX2.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# Dev Task: Enduro Trails — UX маршрута + индикатор загрузки
|
||||
|
||||
**Приоритет:** HIGH
|
||||
**Проект:** enduro-trails
|
||||
**Дата:** 2026-05-05
|
||||
|
||||
---
|
||||
|
||||
## Контекст
|
||||
|
||||
Текущий UX маршрута неудобен:
|
||||
1. После выбора двух точек автоматически открывается основной sheet — карта скрывается
|
||||
2. «Добавить точку» в основном sheet не скрывает sheet — карта не видна для тапа
|
||||
3. Основной sheet открывается сразу, хотя достаточно мини-бара
|
||||
4. Нет индикатора что маршрут строится — пользователь не понимает что происходит
|
||||
|
||||
**Файлы:** ТОЛЬКО `app.js`, `app.css`, `index.html` (бэкенд НЕ трогать)
|
||||
|
||||
---
|
||||
|
||||
## Задача 1: Не открывать основной sheet автоматически
|
||||
|
||||
### Текущее поведение
|
||||
В `initRouteClicks` при добавлении второй точки вызывается `buildRoute()` — который внутри ничего не открывает, НО `toggleRouteMode()` при входе в режим уже открыл sheet через `openSheet('sheet-route')`.
|
||||
|
||||
### Нужное поведение
|
||||
- Вход в режим маршрута (`toggleRouteMode()`) → **НЕ открывать** `sheet-route` автоматически
|
||||
- Sheet открывается ТОЛЬКО по явному тапу на мини-бар (уже работает через `showMiniRouteSheet`)
|
||||
- Маршрут строится в фоне, карта остаётся видна, мини-бар показывает прогресс
|
||||
|
||||
### Изменения в `toggleRouteMode()`:
|
||||
|
||||
```js
|
||||
function toggleRouteMode() {
|
||||
const btn = document.getElementById('tb-route');
|
||||
|
||||
if (routeMode) {
|
||||
// Если sheet открыт — закрыть sheet (но не выходить из режима)
|
||||
const sheet = document.getElementById('sheet-route');
|
||||
if (sheet && sheet.classList.contains('open')) {
|
||||
closeSheet('sheet-route');
|
||||
return;
|
||||
}
|
||||
// Если sheet закрыт — выйти из режима и сбросить маршрут
|
||||
routeMode = false;
|
||||
btn.classList.remove('active');
|
||||
clearRoute();
|
||||
window._map.getCanvas().style.cursor = '';
|
||||
} else {
|
||||
// Войти в режим маршрута — НЕ открывать sheet
|
||||
deactivateAllModes();
|
||||
routeMode = true;
|
||||
btn.classList.add('active');
|
||||
clearRoute();
|
||||
window._map.getCanvas().style.cursor = 'crosshair';
|
||||
// sheet НЕ открываем — пользователь сам тапнет на мини-бар
|
||||
}
|
||||
updateMapModeClass();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Задача 2: «Добавить точку» скрывает sheet
|
||||
|
||||
### В `addWaypointMode()`:
|
||||
|
||||
```js
|
||||
function addWaypointMode() {
|
||||
if (routeWaypoints.length >= 10) return;
|
||||
if (!routeMode) {
|
||||
routeMode = true;
|
||||
document.getElementById('tb-route').classList.add('active');
|
||||
updateMapModeClass();
|
||||
}
|
||||
addingWaypoint = true;
|
||||
window._map.getCanvas().style.cursor = 'crosshair';
|
||||
// Скрыть основной sheet чтобы карта была видна
|
||||
closeSheet('sheet-route');
|
||||
// Показать подсказку в мини-баре
|
||||
const statsEl = document.getElementById('mini-stats');
|
||||
if (statsEl) statsEl.textContent = 'Тапни на карте для добавления точки';
|
||||
document.getElementById('sheet-route-mini').classList.add('visible');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Задача 3: После построения маршрута — только мини-бар
|
||||
|
||||
### В `buildRoute()` — убрать автоматическое открытие sheet, показывать только мини-бар:
|
||||
|
||||
```js
|
||||
async function buildRoute() {
|
||||
if (routeWaypoints.length < 2) return;
|
||||
|
||||
// Показать мини-бар с индикатором загрузки
|
||||
showMiniRouteLoading();
|
||||
|
||||
// Закрыть основной sheet если открыт
|
||||
closeSheet('sheet-route');
|
||||
|
||||
document.getElementById('route-status').textContent = 'Строю маршрут...';
|
||||
|
||||
try {
|
||||
const resp = await fetch(...);
|
||||
// ... существующая логика ...
|
||||
|
||||
drawRouteResults(routeResults, 0);
|
||||
|
||||
// После успеха — показать мини-бар с результатом (НЕ открывать sheet)
|
||||
showMiniRouteSheet();
|
||||
hideMiniRouteLoading();
|
||||
document.getElementById('route-status').textContent = `${routeResults.length} маршрут(ов)`;
|
||||
} catch(e) {
|
||||
hideMiniRouteLoading();
|
||||
document.getElementById('route-status').textContent = '❌ ' + e.message;
|
||||
// Показать ошибку в мини-баре
|
||||
const statsEl = document.getElementById('mini-stats');
|
||||
if (statsEl) statsEl.textContent = '❌ ' + e.message;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Задача 4: Индикатор загрузки — колесо мотоцикла
|
||||
|
||||
### Анимация
|
||||
|
||||
В мини-баре (`#sheet-route-mini`) показывать SVG колесо мотоцикла которое крутится пока строится маршрут.
|
||||
|
||||
**SVG колесо (кросс/эндуро стиль — спицы, грунтозацепы):**
|
||||
|
||||
```svg
|
||||
<svg class="moto-wheel" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Обод -->
|
||||
<circle cx="20" cy="20" r="17" fill="none" stroke="var(--accent)" stroke-width="2.5"/>
|
||||
<!-- Шина (внешний контур с грунтозацепами — неровный) -->
|
||||
<circle cx="20" cy="20" r="19" fill="none" stroke="var(--text2)" stroke-width="1.5" stroke-dasharray="3 2"/>
|
||||
<!-- Ступица -->
|
||||
<circle cx="20" cy="20" r="3" fill="var(--accent)"/>
|
||||
<!-- Спицы (8 штук) -->
|
||||
<line x1="20" y1="3" x2="20" y2="17" stroke="var(--text2)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<line x1="20" y1="23" x2="20" y2="37" stroke="var(--text2)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<line x1="3" y1="20" x2="17" y2="20" stroke="var(--text2)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<line x1="23" y1="20" x2="37" y2="20" stroke="var(--text2)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<line x1="7.9" y1="7.9" x2="17.5" y2="17.5" stroke="var(--text2)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<line x1="22.5" y1="22.5" x2="32.1" y2="32.1" stroke="var(--text2)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<line x1="32.1" y1="7.9" x2="22.5" y2="17.5" stroke="var(--text2)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<line x1="17.5" y1="22.5" x2="7.9" y2="32.1" stroke="var(--text2)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
```
|
||||
|
||||
**CSS анимация:**
|
||||
```css
|
||||
.moto-wheel {
|
||||
width: 32px; height: 32px;
|
||||
animation: wheelSpin 0.8s linear infinite;
|
||||
display: none;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.moto-wheel.spinning {
|
||||
display: block;
|
||||
}
|
||||
@keyframes wheelSpin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
```
|
||||
|
||||
### Где показывать
|
||||
|
||||
В `#sheet-route-mini` добавить элемент колеса рядом с `#mini-stats`:
|
||||
|
||||
```html
|
||||
<!-- В index.html, внутри #sheet-route-mini -->
|
||||
<svg id="mini-wheel" class="moto-wheel" viewBox="0 0 40 40" ...>...</svg>
|
||||
```
|
||||
|
||||
**Функции управления:**
|
||||
```js
|
||||
function showMiniRouteLoading() {
|
||||
const wheel = document.getElementById('mini-wheel');
|
||||
const statsEl = document.getElementById('mini-stats');
|
||||
if (wheel) wheel.classList.add('spinning');
|
||||
if (statsEl) statsEl.textContent = 'Строю маршрут...';
|
||||
document.getElementById('sheet-route-mini').classList.add('visible');
|
||||
}
|
||||
|
||||
function hideMiniRouteLoading() {
|
||||
const wheel = document.getElementById('mini-wheel');
|
||||
if (wheel) wheel.classList.remove('spinning');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Итоговый UX-флоу
|
||||
|
||||
```
|
||||
Тап на 🗺 → routeMode=true, cursor=crosshair, sheet НЕ открывается
|
||||
Тап на карте (точка A) → маркер A, cursor=crosshair
|
||||
Тап на карте (точка B) → маркер B, мини-бар появляется с крутящимся колесом
|
||||
Маршрут построен → колесо исчезает, мини-бар показывает "120 км · 87% грунт"
|
||||
Тап на мини-бар → открывается основной sheet с деталями
|
||||
Тап «Добавить точку» в sheet → sheet скрывается, карта видна, cursor=crosshair
|
||||
Тап на карте → точка добавлена, маршрут перестраивается (колесо в мини-баре)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Деплой
|
||||
```
|
||||
/tmp/deploy_static.js
|
||||
SSH: host=82.22.50.71, port=22, user=slin, pass=motoZ@yaz2010
|
||||
Health: curl -s http://mva154:5558/api/health
|
||||
```
|
||||
|
||||
## Ограничения
|
||||
- НЕ трогать `app.py`
|
||||
- Без npm-зависимостей
|
||||
- Сохранить всю существующую логику (drag-and-drop, geocoding, GPX, etc.)
|
||||
@@ -79,10 +79,10 @@ docker restart prototype-enduro-trails-1
|
||||
| F-08 | Исключить тротуары | footway/pedestrian/steps убрать из графа | 📋 BRD готов | 3.1 |
|
||||
| F-09 | Больше альтернатив | Penalized re-query + дедупликация | 📋 BRD готов | 3.1 |
|
||||
| F-10 | Слой препятствий | Шлагбаумы, броды, блокпосты на карте | 📋 BRD готов | 3.1 |
|
||||
| F-11 | "Красивый маршрут" | Замкнутый круг через живописные места, scenic_score, варианты | ✅ Готово | 4 |
|
||||
| F-11 | "Красивый маршрут" | Замкнутый круг через живописные места, scenic_score, варианты | ⏳ Бэклог | 4 |
|
||||
| F-12 | "Горка" | Макс набор высоты, мин дистанция (SRTM) | ⏳ Бэклог | 6 |
|
||||
| F-13 | "Связка" | Соединить два трека грунтовками | ✅ Готово | 4 |
|
||||
| F-14 | "Разведка" | Грунтовки вокруг точки, статистика по типам, POI | ✅ Готово | 4 |
|
||||
| F-13 | "Связка" | Соединить два трека грунтовками | ⏳ Бэклог | 4 |
|
||||
| F-14 | "Разведка" | Грунтовки вокруг точки, статистика по типам, POI | ⏳ Бэклог | 4 |
|
||||
| F-15 | "Народные треки" | OSM Traces, Wikiloc, Komoot, 4x4travel | ⏳ Бэклог | 8 |
|
||||
| F-16 | Тёмная тема + редизайн | Две темы (авто/светлая/тёмная), SunCalc, мобильный UI, drag-and-drop точек | ✅ Готово | 5 |
|
||||
| F-17 | PWA + офлайн | Service Worker, MBTiles, GPS-трекинг | ⏳ Бэклог | 7 |
|
||||
@@ -141,7 +141,7 @@ docker restart prototype-enduro-trails-1
|
||||
- **F-13** «Связка» — соединить два трека грунтовками, альтернативы
|
||||
- **F-14** «Разведка» — грунтовки в радиусе 20/50/100 км, статистика по типам, POI
|
||||
|
||||
### ✅ Фаза 4 — Продвинутый роутинг (04.05.2026)
|
||||
### ⏳ Фаза 4 — Продвинутый роутинг (в разработке)
|
||||
- **F-11** «Красивый маршрут» — замкнутый круг, scenic_score, варианты по дистанции (50/100/150/200 км)
|
||||
- **F-13** «Связка» — соединить два трека грунтовками, альтернативы
|
||||
- **F-14** «Разведка» — грунтовки в радиусе 20/50/100 км, статистика по типам, POI
|
||||
|
||||
Reference in New Issue
Block a user