auto-sync: 2026-05-05 22:00:01
This commit is contained in:
29
memory/2026-05-05.md
Normal file
29
memory/2026-05-05.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# 2026-05-05 — Дневник
|
||||
|
||||
## Enduro Trails — работа над прототипом
|
||||
|
||||
### Кнопка «Поделиться» → «Скачать GPX»
|
||||
- Слава попросил убрать кастомный диалог «Поделиться» (Telegram/WhatsApp)
|
||||
- Оставить только скачивание GPX через `downloadGPX()`
|
||||
- Иконка заменена на download-стрелку (стрелка вниз + линия) в том же стиле что и другие кнопки header
|
||||
- HTML уже обновлён: `onclick="downloadGPX()"`, иконка download
|
||||
- Из app.js удалены функции: `shareRoute`, `closeShareDialog`, `shareTelegram`, `shareWhatsApp`, `shareNative`
|
||||
- Из app.css удалены стили: `.share-dialog`, `.sd-*`, `#share-dialog`
|
||||
|
||||
### Drag-and-drop для точек маршрута
|
||||
- Слава попросил добавить иконку «перетаскивания» (grip) в каждый wl-item
|
||||
- Дев-агент добавил grip SVG (6 кружков) и touch-drag логику
|
||||
- Логика вынесена в `_initWaypointDragHandles(list)` с общими функциями `startDrag/moveDrag/endDrag`
|
||||
- **Проблема:** на десктопе не работало — дев сделал только touch-события
|
||||
- **Фикс:** добавлены mouse-события (mousedown/mousemove/mouseup) с `document`-level listeners для корректного завершения drag за пределами списка
|
||||
|
||||
### Баг: «Добавить точку» не работала в баре маршрутов
|
||||
- Причина: `addWaypointMode()` не устанавливала `routeMode = true`
|
||||
- При закрытом/свайпнутом sheet `routeMode` мог быть `false`
|
||||
- Клик на карте проверяет `if (!routeMode) return` — и игнорировал добавление
|
||||
- Фикс: в `addWaypointMode()` добавить `routeMode = true` перед установкой `addingWaypoint = true`
|
||||
|
||||
## Технические заметки
|
||||
- Web Share API требует HTTPS — на HTTP молча падает, даже на Android Chrome
|
||||
- Кастомные диалоги через DOM работают всегда, но Слава предпочёл простоту (только скачать)
|
||||
- Mouse drag: важно вешать mousemove/mouseup на `document`, а не на `list` — иначе drag ломается при выходе курсора за пределы элемента
|
||||
@@ -1,565 +1,127 @@
|
||||
# BRD: Enduro Trails — Фаза 5 «Редизайн»
|
||||
|
||||
**Версия:** 1.0
|
||||
**Дата:** 2026-05-04
|
||||
**Версия:** 2.0
|
||||
**Дата:** 2026-05-05
|
||||
**Автор:** Стрим 🌊
|
||||
**Статус:** На согласовании
|
||||
**Статус:** ✅ Реализовано (с дополнениями)
|
||||
|
||||
---
|
||||
|
||||
## 1. Контекст и проблема
|
||||
|
||||
Текущий UI — функциональный прототип, не готовый к реальному использованию. Основные боли:
|
||||
|
||||
- **Не мобильный** — панели перекрывают карту, мелкие кнопки, тяжело нажимать в перчатках
|
||||
- **Нет стиля** — белый фон, emoji-иконки, выглядит как dev-прототип
|
||||
- **Всё поверх карты** — карта не видна когда работают панели
|
||||
- **Нет режима вождения** — на мотоцикле невозможно пользоваться
|
||||
Фазы 3–4 дали полный функционал (роутинг, разведка, связка, красивый маршрут), но UI был dev-прототипом: белый фон, emoji-иконки, панели перекрывали карту, не работало на мобиле в перчатках.
|
||||
|
||||
## 2. Цель
|
||||
|
||||
Создать мобильный UI уровня onX Offroad / Locus Map — тёмный, эндуро-стильный, thumb-friendly. Карта — главный герой. UI — минималистичный HUD.
|
||||
Мобильный UI уровня onX Offroad / Locus Map — тёмный, эндуро-стильный, thumb-friendly. Карта — главный герой. UI — минималистичный HUD.
|
||||
|
||||
## 3. Дизайн-система
|
||||
## 3. Что реализовано в Фазе 5
|
||||
|
||||
### 3.1 Цветовая палитра — две темы
|
||||
### ✅ Дизайн-система
|
||||
|
||||
Тема переключается кнопкой ☀️/🌙 в search bar. **Три режима:**
|
||||
- **Две темы:** тёмная (`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)`
|
||||
|
||||
| Режим | Описание |
|
||||
|-------|----------|
|
||||
| **Авто** (по умолчанию) | Тема определяется по реальному восходу/закату солнца для текущей геолокации. Если браузер отдал геопозицию — используем её; иначе — запасной город (Москва, 55.75°N). День = светлая, ночь = тёмная. Переключение происходит без перезагрузки. |
|
||||
| **Светлая** | Принудительно светлая тема, независимо от времени суток. |
|
||||
| **Тёмная** | Принудительно тёмная тема. |
|
||||
### ✅ Layout и компоненты
|
||||
|
||||
**Реализация авто-режима:**
|
||||
- Используется SunCalc (MIT, ~3KB) через CDN: `https://unpkg.com/suncalc@latest`
|
||||
- `SunCalc.getTimes(date, lat, lng)` → `sunrise`, `sunset`
|
||||
- Текущее время между `sunrise` и `sunset` → светлая тема; иначе — тёмная
|
||||
- При изменении геолокации — пересчёт восхода/заката
|
||||
- Проверка раз в минуту (на случай, если сессия длительная)
|
||||
- **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-анимация в карточках маршрутов пока идёт запрос
|
||||
|
||||
**UI переключателя:** тап на ☀️/🌙 циклически переключает: Авто → Светлая → Тёмная → Авто. Текущий режим показан маленькой подписью под иконкой (или tooltip на десктопе): «Авто», «День», «Ночь». В авто-режиме иконка динамическая: ☀️ если сейчас день, 🌙 если ночь.
|
||||
### ✅ Анимации
|
||||
|
||||
**Тёмная (ночная езда):**
|
||||
```
|
||||
--bg: #0D1117
|
||||
--surface: #161B22
|
||||
--surface2: #21262D
|
||||
--border: #30363D
|
||||
--text: #E6EDF3
|
||||
--text2: #8B949E
|
||||
--accent: #FF6B00
|
||||
--gold: #FFD700
|
||||
--red: #FF3B1F
|
||||
--success: #2EA043
|
||||
```
|
||||
- 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 появление
|
||||
|
||||
**Светлая (дневная езда):**
|
||||
```
|
||||
--bg: #F5F5F0 (бежевый, не слепит на солнце)
|
||||
--surface: #FFFFFF
|
||||
--surface2: #F0F0EA
|
||||
--border: #D0CFC8
|
||||
--text: #1A1A1A
|
||||
--text2: #6B6B6B
|
||||
--accent: #E55A00 (оранжевый чуть темнее — виден на белом)
|
||||
--gold: #C89B00
|
||||
--red: #CC2200
|
||||
--success: #1A7A30
|
||||
```
|
||||
### ✅ Свайп вниз для закрытия sheet
|
||||
|
||||
Реализация через CSS custom properties на `:root` + класс `body.theme-dark` / `body.theme-light`.
|
||||
Стиль карты MapLibre меняется соответственно (тёмный/светлый style.json — уже существуют).
|
||||
- `initSheetSwipe()` — touch-обработка на всех `.bottom-sheet`
|
||||
- Свайп > 80px → `closeSheet()`
|
||||
- Во время свайпа: `translateY(dy)` для визуального feedback
|
||||
|
||||
### 3.2 Типографика
|
||||
### ✅ Адаптив — десктоп
|
||||
|
||||
```
|
||||
Font: system-ui, -apple-system, 'SF Pro Display', 'Segoe UI', sans-serif
|
||||
Заголовок: 16px, 700, #E6EDF3
|
||||
Подзаголовок: 13px, 600, #8B949E, uppercase, letter-spacing: 0.08em
|
||||
Текст: 14px, 400, #E6EDF3
|
||||
Мелкий: 12px, 400, #8B949E
|
||||
Цифры: font-variant-numeric: tabular-nums
|
||||
```
|
||||
- `@media (min-width: 768px)` — toolbar вертикально слева, sheet с max-width 400px
|
||||
|
||||
### 3.3 Иконки
|
||||
### ✅ Промежуточные точки — drag-and-drop (добавлено в ходе фазы)
|
||||
|
||||
**НЕ emoji!** SVG-иконки через inline SVG или icon font.
|
||||
Источник: **Lucide Icons** (MIT, 24px stroke, line-cap: round).
|
||||
- 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()`
|
||||
|
||||
Ключевые иконки:
|
||||
- 🗺 Route: `map` (lucide)
|
||||
- 📍 Recon: `search` или `radar`
|
||||
- 🔗 Link: `git-merge`
|
||||
- 🎨 Scenic: `sparkles`
|
||||
- 📏 Ruler: `ruler`
|
||||
- 📌 Marker: `map-pin`
|
||||
- 🎯 Locate: `navigation`
|
||||
- 🧭 Compass: `compass`
|
||||
- ⬇ Download/GPX: `download`
|
||||
- ✕ Close: `x`
|
||||
- ➕ Add: `plus`
|
||||
- 🔥 Difficulty: `flame`
|
||||
- 💧 Water: `droplets`
|
||||
- 👁 View: `eye`
|
||||
### ✅ Кнопка «Скачать GPX» (изменение в ходе фазы)
|
||||
|
||||
### 3.4 Компоненты
|
||||
- Кнопка «Поделиться» (share dialog с Telegram/WhatsApp) убрана по запросу Славы
|
||||
- Заменена на прямую кнопку «Скачать GPX» с download-иконкой (стрелка вниз)
|
||||
- `onclick="downloadGPX()"` — без промежуточных диалогов
|
||||
|
||||
#### Кнопка карты (FAB style)
|
||||
```css
|
||||
.map-btn {
|
||||
width: 48px; height: 48px;
|
||||
background: #161B22;
|
||||
border: 1px solid #30363D;
|
||||
border-radius: 12px;
|
||||
color: #E6EDF3;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.5);
|
||||
transition: all 0.15s;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.map-btn:active { background: #21262D; transform: scale(0.94); }
|
||||
.map-btn.active { background: #FF6B00; color: #fff; border-color: #FF6B00; }
|
||||
```
|
||||
### ✅ Мини-бар маршрута
|
||||
|
||||
#### Bottom Sheet (панели)
|
||||
```css
|
||||
.bottom-sheet {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
background: #161B22;
|
||||
border-radius: 20px 20px 0 0;
|
||||
border-top: 1px solid #30363D;
|
||||
padding: 0 16px 32px; /* 32px = safe area снизу */
|
||||
z-index: 100;
|
||||
max-height: 75vh;
|
||||
overflow-y: auto;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s cubic-bezier(0.32, 0, 0.15, 1);
|
||||
}
|
||||
.bottom-sheet.open { transform: translateY(0); }
|
||||
|
||||
/* Drag handle */
|
||||
.sheet-handle {
|
||||
width: 36px; height: 4px;
|
||||
background: #30363D;
|
||||
border-radius: 2px;
|
||||
margin: 12px auto 16px;
|
||||
}
|
||||
```
|
||||
|
||||
#### Карточка маршрута
|
||||
```css
|
||||
.route-card {
|
||||
background: #21262D;
|
||||
border: 1.5px solid #30363D;
|
||||
border-radius: 12px;
|
||||
padding: 12px 14px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
.route-card.active {
|
||||
border-color: #FF6B00;
|
||||
box-shadow: 0 0 0 1px #FF6B00;
|
||||
}
|
||||
```
|
||||
|
||||
#### Stat pill (статистика)
|
||||
```css
|
||||
.stat-pill {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
background: #0D1117;
|
||||
border: 1px solid #30363D;
|
||||
border-radius: 20px;
|
||||
padding: 3px 10px;
|
||||
font-size: 12px; font-weight: 600;
|
||||
color: #E6EDF3;
|
||||
}
|
||||
.stat-pill.dirt { border-color: #FFD700; color: #FFD700; }
|
||||
.stat-pill.asphalt { border-color: #8B949E; color: #8B949E; }
|
||||
```
|
||||
|
||||
## 4. Layout — Mobile First
|
||||
|
||||
### Основной экран
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ [🔍 Поиск...........] [☰] │ ← search bar, 44px, top: safe
|
||||
│ │
|
||||
│ │
|
||||
│ КАРТА │ ← 100% экрана
|
||||
│ │
|
||||
│ [🧭] │
|
||||
│ [🎯] │ ← кнопки справа, 48×48
|
||||
│ ────────────────────────────────│
|
||||
│ [🗺][🔗][🎨][📍][📏][📌] │ ← bottom toolbar, 64px
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Bottom toolbar**: 6 режимов, иконки 24px, tap target 48px minimum.
|
||||
Активный режим — оранжевый фон + label появляется под иконкой.
|
||||
|
||||
### При активном режиме (пример: Маршрут)
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ [🔍 Поиск...] [☰] │
|
||||
│ │
|
||||
│ КАРТА │
|
||||
│ │
|
||||
│ [🧭] │
|
||||
│ [🎯] │
|
||||
├─────────────────────────────────┤
|
||||
│ ▬▬▬ drag handle ▬▬▬ │ ← Bottom Sheet
|
||||
│ 🗺 МАРШРУТ [✕] │
|
||||
│ ─────────────────────────────── │
|
||||
│ [A: Хоруговино ] [B: ...] │ ← точки (горизонтально)
|
||||
│ [+ Точка] [Сбросить] [GPX⬇] │
|
||||
│ │
|
||||
│ Строю маршрут... │
|
||||
│ ┌───────────────────────────┐ │
|
||||
│ │● Вариант 1 1013 км 14ч │ │
|
||||
│ │ ████████░░ 97% грунт │ │
|
||||
│ └───────────────────────────┘ │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 5. Компоненты — детальный дизайн
|
||||
|
||||
### 5.1 Toolbar (нижняя панель)
|
||||
|
||||
```html
|
||||
<nav id="toolbar">
|
||||
<button class="tb-btn active" data-mode="route" onclick="toggleRouteMode()">
|
||||
<svg><!-- map icon --></svg>
|
||||
<span>Маршрут</span>
|
||||
</button>
|
||||
<button class="tb-btn" data-mode="link" onclick="toggleLinkMode()">
|
||||
<svg><!-- git-merge --></svg>
|
||||
<span>Связка</span>
|
||||
</button>
|
||||
<button class="tb-btn" data-mode="scenic" onclick="toggleScenicMode()">
|
||||
<svg><!-- sparkles --></svg>
|
||||
<span>Красивый</span>
|
||||
</button>
|
||||
<button class="tb-btn" data-mode="recon" onclick="toggleReconMode()">
|
||||
<svg><!-- radar --></svg>
|
||||
<span>Разведка</span>
|
||||
</button>
|
||||
<button class="tb-btn" data-mode="ruler" onclick="toggleRuler()">
|
||||
<svg><!-- ruler --></svg>
|
||||
<span>Линейка</span>
|
||||
</button>
|
||||
<button class="tb-btn" data-mode="marker" onclick="toggleMarkerMode()">
|
||||
<svg><!-- map-pin --></svg>
|
||||
<span>Метка</span>
|
||||
</button>
|
||||
</nav>
|
||||
```
|
||||
|
||||
```css
|
||||
#toolbar {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
height: 72px;
|
||||
background: #161B22;
|
||||
border-top: 1px solid #30363D;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
padding: 0 4px;
|
||||
padding-bottom: env(safe-area-inset-bottom, 0px);
|
||||
z-index: 200;
|
||||
}
|
||||
.tb-btn {
|
||||
flex: 1;
|
||||
display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center;
|
||||
gap: 3px;
|
||||
height: 56px;
|
||||
border: none; background: none;
|
||||
color: #8B949E;
|
||||
font-size: 10px; font-weight: 600;
|
||||
text-transform: uppercase; letter-spacing: 0.04em;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: color 0.15s, background 0.15s;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.tb-btn svg { width: 22px; height: 22px; stroke-width: 1.8; }
|
||||
.tb-btn.active { color: #FF6B00; }
|
||||
.tb-btn.active svg { stroke: #FF6B00; }
|
||||
```
|
||||
|
||||
### 5.2 Bottom Sheet — Маршрут
|
||||
|
||||
Заменяет существующий `#route-panel`.
|
||||
|
||||
**Секция точек (горизонтальный scroll если много):**
|
||||
```html
|
||||
<div class="waypoints-row">
|
||||
<div class="wp-chip wp-start">
|
||||
<div class="wp-dot" style="background:#2EA043"></div>
|
||||
<span class="wp-label">Хоруговино</span>
|
||||
</div>
|
||||
<div class="wp-arrow">›</div>
|
||||
<div class="wp-chip wp-end">
|
||||
<div class="wp-dot" style="background:#FF3B1F"></div>
|
||||
<span class="wp-label">Корак-Чурачки</span>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Карточка маршрута:**
|
||||
```html
|
||||
<div class="route-card active">
|
||||
<div class="rc-header">
|
||||
<span class="rc-dot" style="background:#0066ff"></span>
|
||||
<span class="rc-title">Основной</span>
|
||||
<span class="rc-km">1013 км</span>
|
||||
<span class="rc-time">14ч 22м</span>
|
||||
</div>
|
||||
<div class="rc-bar">
|
||||
<div class="rc-bar-dirt" style="width:97%"></div>
|
||||
<div class="rc-bar-asphalt" style="width:3%"></div>
|
||||
</div>
|
||||
<div class="rc-stats">
|
||||
<span class="stat-pill dirt">🟡 97% грунт</span>
|
||||
<span class="stat-pill asphalt">⬜ 3% асфальт</span>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 5.3 Bottom Sheet — Разведка
|
||||
|
||||
При нажатии открывается sheet с радиус-контролом. После клика на карте — обновляется.
|
||||
|
||||
```html
|
||||
<div class="bottom-sheet" id="sheet-recon">
|
||||
<div class="sheet-handle"></div>
|
||||
<div class="sheet-header">
|
||||
<svg><!-- radar icon --></svg>
|
||||
<h2>Разведка</h2>
|
||||
<button class="sheet-close" onclick="toggleReconMode()">✕</button>
|
||||
</div>
|
||||
|
||||
<p class="sheet-hint">Тапни точку на карте — узнаешь сколько грунтовок рядом</p>
|
||||
|
||||
<div class="radius-selector">
|
||||
<button class="radius-btn active" onclick="setReconRadius(20)">20 км</button>
|
||||
<button class="radius-btn" onclick="setReconRadius(50)">50 км</button>
|
||||
<button class="radius-btn" onclick="setReconRadius(100)">100 км</button>
|
||||
</div>
|
||||
|
||||
<div id="recon-results" style="display:none">
|
||||
<div class="recon-section">
|
||||
<div class="section-label">ГРУНТОВКИ</div>
|
||||
<div class="recon-grid">
|
||||
<div class="recon-stat">
|
||||
<div class="rs-value" id="r-total-km">—</div>
|
||||
<div class="rs-label">км всего</div>
|
||||
</div>
|
||||
<div class="recon-stat">
|
||||
<div class="rs-value rs-gold" id="r-lev12-km">—</div>
|
||||
<div class="rs-label">км Lev1-2</div>
|
||||
</div>
|
||||
<div class="recon-stat">
|
||||
<div class="rs-value rs-red" id="r-lev345-km">—</div>
|
||||
<div class="rs-label">км Lev3-5</div>
|
||||
</div>
|
||||
<div class="recon-stat">
|
||||
<div class="rs-value" id="r-path-km">—</div>
|
||||
<div class="rs-label">км тропы</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="recon-section">
|
||||
<div class="section-label">POI В РАДИУСЕ</div>
|
||||
<div class="poi-list" id="r-poi-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 5.4 Bottom Sheet — Красивый маршрут
|
||||
|
||||
```html
|
||||
<div class="bottom-sheet" id="sheet-scenic">
|
||||
<div class="sheet-handle"></div>
|
||||
<div class="sheet-header">
|
||||
<svg><!-- sparkles --></svg>
|
||||
<h2>Красивый маршрут</h2>
|
||||
<button class="sheet-close" onclick="toggleScenicMode()">✕</button>
|
||||
</div>
|
||||
|
||||
<div id="scenic-start-prompt">
|
||||
<p class="sheet-hint">Тапни точку старта на карте</p>
|
||||
</div>
|
||||
|
||||
<div id="scenic-config" style="display:none">
|
||||
<div class="section-label">ДИСТАНЦИЯ</div>
|
||||
<div class="dist-row">
|
||||
<button class="dist-btn" data-km="50">50</button>
|
||||
<button class="dist-btn active" data-km="100">100</button>
|
||||
<button class="dist-btn" data-km="150">150</button>
|
||||
<button class="dist-btn" data-km="200">200</button>
|
||||
<input type="number" id="dist-custom" placeholder="км" min="20" max="500">
|
||||
</div>
|
||||
|
||||
<button class="btn-primary" onclick="buildScenicRoute()">
|
||||
<svg><!-- sparkles --></svg>
|
||||
Построить маршрут
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="scenic-cards"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 5.5 Кнопки карты (правый столбец)
|
||||
|
||||
```html
|
||||
<div id="map-controls-r">
|
||||
<button class="map-btn" title="Компас" id="btn-compass" onclick="toggleCompass()">
|
||||
<svg><!-- compass --></svg>
|
||||
</button>
|
||||
<button class="map-btn" title="Моё местоположение" onclick="locateMe()">
|
||||
<svg><!-- navigation --></svg>
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 5.6 Search Bar (верхняя строка)
|
||||
|
||||
```html
|
||||
<div id="search-bar">
|
||||
<svg><!-- search icon --></svg>
|
||||
<input type="text" id="search-input" placeholder="Поиск места..." autocomplete="off">
|
||||
<button id="btn-menu" onclick="toggleMenu()">
|
||||
<svg><!-- menu --></svg>
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
```css
|
||||
#search-bar {
|
||||
position: fixed;
|
||||
top: env(safe-area-inset-top, 12px);
|
||||
left: 12px; right: 12px;
|
||||
height: 48px;
|
||||
background: #161B22;
|
||||
border: 1px solid #30363D;
|
||||
border-radius: 14px;
|
||||
display: flex; align-items: center;
|
||||
padding: 0 14px;
|
||||
gap: 10px;
|
||||
z-index: 200;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.4);
|
||||
}
|
||||
#search-input {
|
||||
flex: 1;
|
||||
background: none; border: none;
|
||||
color: #E6EDF3; font-size: 15px;
|
||||
outline: none;
|
||||
}
|
||||
#search-input::placeholder { color: #484F58; }
|
||||
```
|
||||
|
||||
## 6. Анимации и микроинтерактивность
|
||||
|
||||
- **Bottom sheet**: `transform: translateY` + `cubic-bezier(0.32, 0, 0.15, 1)`, 280ms
|
||||
- **Кнопки**: `transform: scale(0.94)` при tap (active state), 100ms
|
||||
- **Карточки маршрутов**: `border-color` transition 150ms
|
||||
- **Loading**: пульсирующий skeleton (opacity animation) вместо spinner
|
||||
- **Маркеры на карте**: появление через `transform: scale(0) → scale(1)`, 200ms
|
||||
|
||||
## 7. Адаптив
|
||||
|
||||
- **Мобила (< 768px)**: bottom sheet, toolbar снизу
|
||||
- **Десктоп (≥ 768px)**: боковая панель 320px слева, кнопки сохраняются
|
||||
- **Landscape мобила**: toolbar справа вертикально, sheet с max-height: 80vh
|
||||
|
||||
## 8. Тест-кейсы
|
||||
|
||||
### P0 — Критично (должно работать идеально)
|
||||
|
||||
| ID | Сценарий | Устройство | Шаги | Ожидаемый результат |
|
||||
|----|----------|-----------|------|---------------------|
|
||||
| T01 | Открытие приложения | iPhone SE (375px) | Открыть URL | Карта 100% экрана, toolbar снизу виден, search bar сверху |
|
||||
| T02 | Нажать режим Маршрут | iPhone | Тап на иконку 🗺 | Bottom sheet выезжает снизу, иконка оранжевая |
|
||||
| T03 | Установить A и B точки | iPhone | Тап A, тап B | Маркеры на карте, карта НЕ перекрыта |
|
||||
| T04 | Маршрут построен | iPhone | После T03 | Карточки маршрутов в sheet, карта видна |
|
||||
| T05 | Тап по карточке | iPhone | Тап на вариант 2 | Маршрут 2 активен (оранжевый), карточка выделена |
|
||||
| T06 | Закрыть sheet | iPhone | Тап ✕ или свайп вниз | Sheet уходит, toolbar возвращается, карта чистая |
|
||||
| T07 | Разведка — тап на карту | iPhone | Включить Разведку, тапнуть | Круг на карте, sheet со статистикой |
|
||||
| T08 | Переключение радиуса | iPhone | В sheet Разведки нажать 50км | Круг обновился, статистика пересчиталась |
|
||||
| T09 | Красивый маршрут | iPhone | Включить, тапнуть, нажать «Построить» | Кольцевой маршрут на карте, карточки в sheet |
|
||||
| T10 | Связка | iPhone | Включить, тапнуть 2 точки | Маршрут между точками, карточки |
|
||||
| T11 | Поиск места | iPhone | Тапнуть search bar, ввести «Тверь» | Результаты поиска, тап → карта летит |
|
||||
| T12 | GPX скачать | iPhone | Построить маршрут → Download | Файл скачался |
|
||||
|
||||
### P1 — Важно
|
||||
|
||||
| ID | Сценарий | Устройство | Шаги | Ожидаемый результат |
|
||||
|----|----------|-----------|------|---------------------|
|
||||
| T13 | Тема — авто по умолчанию | Любое | Открыть (днём) | Фон светлый (бежевый #F5F5F0), иконка ☀️, подпись «Авто» |
|
||||
| T13a | Тема — авто ночью | Любое | Открыть (после заката) | Фон тёмный #0D1117, иконка 🌙, подпись «Авто» |
|
||||
| T13b | Тема — ручной цикл | Любое | Тап ☀️/🌙 3 раза | Авто → Светлая → Тёмная → Авто, при каждом тапе тема и подпись меняются |
|
||||
| T13c | Тема — ручная не зависит от времени | Любое | Переключить на «Светлая» ночью | Фон бежевый, подпись «День», иконка ☀️ |
|
||||
| T13d | Тема — авто пересчитывает при геолокации | Любое | В авто-режиме, разрешить геолокацию | Восход/закат пересчитаны по реальным координатам, тема соответствует |
|
||||
| T14 | Толстые пальцы в перчатках | iPhone | Нажимать кнопки | Все кнопки min 48×48px, не промахиваешься |
|
||||
| T15 | Landscape поворот | iPhone | Повернуть телефон | UI перестроился, карта видна |
|
||||
| T16 | Десктоп Chrome | MacBook | Открыть | Боковая панель 320px, кнопки слева |
|
||||
| T17 | Свайп вниз для закрытия | iPhone | Свайп вниз по handle | Sheet закрывается |
|
||||
| T18 | Два режима не активны одновременно | iPhone | Открыть Маршрут, потом Разведку | Маршрут закрылся, Разведка открылась |
|
||||
| T19 | Геолокация | iPhone | Нажать 🎯 | Запрос разрешения, потом маркер на карте |
|
||||
| T20 | Компас | iPhone | Нажать 🧭 | Карта вращается, кнопка активна |
|
||||
| T21 | Метка — добавить | iPhone | Включить 📌, тапнуть | Диалог выбора типа метки (popup) |
|
||||
| T22 | Метка — иконка на карте | iPhone | После T21 | Метка видна, тап открывает popup с опциями |
|
||||
| T23 | Линейка | iPhone | Включить 📏, тапнуть несколько точек | Линия + дистанция |
|
||||
| T24 | Анимация sheet | iPhone | Открыть/закрыть | Плавная, 280ms, без дёрганий |
|
||||
| T25 | Skeleton loading | iPhone | Построить длинный маршрут | Skeleton в карточках пока грузится |
|
||||
| T26 | Ошибка маршрута | iPhone | Поставить точки в море | Понятное сообщение об ошибке в sheet |
|
||||
|
||||
### P2 — Nice to have
|
||||
|
||||
| ID | Сценарий | Шаги | Ожидаемый результат |
|
||||
|----|----------|------|---------------------|
|
||||
| T27 | Safari iOS | Открыть | Safe area работает, нет обрезания снизу |
|
||||
| T28 | Медленный интернет | Throttle 3G | Skeleton, потом данные |
|
||||
| T29 | Очень длинное название места | Поставить точку у «деревня Нижние Бородавки» | Название обрезается с ellipsis |
|
||||
| T30 | 3 альтернативных маршрута | Построить маршрут | 3 карточки, можно переключать |
|
||||
| T31 | Scenic score визуализация | В карточке Красивого | Звёздочки или bar для scenic_score |
|
||||
| T32 | POI на маршруте в Красивом | В карточке | Иконки POI с названиями |
|
||||
| T33 | Кнопки режима вместе видны | На экране 375px | Все 6 иконок в toolbar без обрезания |
|
||||
| T34 | Статус бар iOS | iPhone | Нет перекрытия search bar статус-баром |
|
||||
|
||||
## 9. Definition of Done
|
||||
|
||||
- [ ] Все P0 тест-кейсы прошли на iPhone SE (375px)
|
||||
- [ ] Все P0 + P1 тест-кейсы прошли на iPhone 14 Pro (393px)
|
||||
- [ ] Bottom sheet плавно открывается/закрывается
|
||||
- [ ] Toolbar: все 6 режимов, min 48px tap target
|
||||
- [ ] Тема: авто (по восходу/закату SunCalc) + ручной переключатель (3 режима: Авто/Светлая/Тёмная)
|
||||
- [ ] Тёмная тема везде, нет белых вспышек; светлая тема без ослепляющего белого
|
||||
- [ ] SVG иконки (Lucide), никаких emoji в UI-элементах
|
||||
- [ ] Карта видна при активных панелях
|
||||
- [ ] Safe area корректная (notch, home indicator)
|
||||
- [ ] Десктоп: боковая панель, не ломается layout
|
||||
- [ ] GPX скачивание работает
|
||||
- [ ] Деплой + health check OK
|
||||
|
||||
## 10. Технические ограничения
|
||||
|
||||
- Менять бэкенд (app.py) нельзя — только фронт
|
||||
- MapLibre GL остаётся
|
||||
- Без новых npm-зависимостей (только inline CSS/JS)
|
||||
- Lucide иконки — подключить через CDN: `https://unpkg.com/lucide@latest`
|
||||
- SunCalc — подключить через CDN: `https://unpkg.com/suncalc@latest` (MIT, ~3KB, для расчёта восхода/заката)
|
||||
- Деплой через ssh2 (стандартная схема)
|
||||
- `#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 с другими | Унифицированы, затем диалог полностью убран |
|
||||
|
||||
---
|
||||
|
||||
## 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) | ✅ |
|
||||
| Деплой + 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 cp → docker restart
|
||||
|
||||
---
|
||||
|
||||
## 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 рельеф.*
|
||||
|
||||
@@ -79,12 +79,12 @@ 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 | "Красивый маршрут" | Замкнутый круг через водоёмы, виды, заброшки | ⏳ Бэклог | 4 |
|
||||
| F-11 | "Красивый маршрут" | Замкнутый круг через живописные места, scenic_score, варианты | ✅ Готово | 4 |
|
||||
| F-12 | "Горка" | Макс набор высоты, мин дистанция (SRTM) | ⏳ Бэклог | 6 |
|
||||
| F-13 | "Связка" | Соединить два трека грунтовками | ⏳ Бэклог | 4 |
|
||||
| F-14 | "Разведка" | Грунтовки вокруг точки | ⏳ Бэклог | 4 |
|
||||
| F-13 | "Связка" | Соединить два трека грунтовками | ✅ Готово | 4 |
|
||||
| F-14 | "Разведка" | Грунтовки вокруг точки, статистика по типам, POI | ✅ Готово | 4 |
|
||||
| F-15 | "Народные треки" | OSM Traces, Wikiloc, Komoot, 4x4travel | ⏳ Бэклог | 8 |
|
||||
| F-16 | Тёмная тема | День/ночь + эндуро-дизайн | ⏳ Бэклог | 5 |
|
||||
| F-16 | Тёмная тема + редизайн | Две темы (авто/светлая/тёмная), SunCalc, мобильный UI, drag-and-drop точек | ✅ Готово | 5 |
|
||||
| F-17 | PWA + офлайн | Service Worker, MBTiles, GPS-трекинг | ⏳ Бэклог | 7 |
|
||||
|
||||
---
|
||||
@@ -136,14 +136,26 @@ docker restart prototype-enduro-trails-1
|
||||
|
||||
**Порядок:** F-07+F-08 вместе (одна пересборка) → F-09 → F-10
|
||||
|
||||
### ⏳ Фаза 4 — Продвинутый роутинг
|
||||
- F-11 «Красивый маршрут» — замкнутый круг через живописные места (озёра, виды, заброшки)
|
||||
- Оценка аттрактивности: близость к воде +10, перепад высот +15, viewpoints +20
|
||||
- F-13 «Связка» — соединить два трека грунтовками
|
||||
- F-14 «Разведка» — все грунтовки в радиусе X км от точки
|
||||
### ✅ Фаза 4 — Продвинутый роутинг (04.05.2026)
|
||||
- **F-11** «Красивый маршрут» — замкнутый круг через живописные места, scenic_score, карточки с вариантами
|
||||
- **F-13** «Связка» — соединить два трека грунтовками, альтернативы
|
||||
- **F-14** «Разведка» — грунтовки в радиусе 20/50/100 км, статистика по типам, POI
|
||||
|
||||
### ⏳ Фаза 5 — Редизайн
|
||||
- F-16 Тёмная тема + эндуро-стиль + адаптив под мобилку
|
||||
### ✅ Фаза 4 — Продвинутый роутинг (04.05.2026)
|
||||
- **F-11** «Красивый маршрут» — замкнутый круг, scenic_score, варианты по дистанции (50/100/150/200 км)
|
||||
- **F-13** «Связка» — соединить два трека грунтовками, альтернативы
|
||||
- **F-14** «Разведка» — грунтовки в радиусе 20/50/100 км, статистика по типам, POI
|
||||
- Мини-бар маршрута (`#mini-route-bar`) — компактный HUD поверх карты
|
||||
|
||||
### ✅ Фаза 5 — Редизайн (05.05.2026)
|
||||
**BRD:** `BRD_PHASE5.md`
|
||||
- F-16 Тёмная + светлая тема, авто-режим по SunCalc (восход/закат)
|
||||
- Bottom sheets, toolbar, SVG Lucide-иконки, skeleton loading
|
||||
- Свайп вниз для закрытия sheet
|
||||
- Drag-and-drop точек маршрута (touch + mouse)
|
||||
- Кнопка «Скачать GPX» (share dialog убран)
|
||||
- Фикс: «Добавить точку» из мини-бара
|
||||
- Десктоп-адаптив (toolbar слева, sheet max-width 400px)
|
||||
|
||||
### ⏳ Фаза 6 — SRTM рельеф
|
||||
- F-12 «Горка» — макс набор высоты, мин дистанция
|
||||
@@ -190,8 +202,10 @@ docker restart prototype-enduro-trails-1
|
||||
| `DEV_TASK.md` | Стабилизация v0.1 (архив) |
|
||||
| `BRD_PHASE3.md` | Бизнес-требования Фазы 3 (согласовано) |
|
||||
| `BRD_PHASE3.1.md` | Бизнес-требования Фазы 3.1 (на согласовании) |
|
||||
| `BRD_PHASE5.md` | Бизнес-требования Фазы 5 (✅ реализовано) |
|
||||
| `TEST_CASES_PHASE3.md` | 56 тест-кейсов |
|
||||
| `DEV_TASK_PHASE3.md` | ТЗ для Dev-агента Фаза 3 |
|
||||
| `DEV_TASK_PHASE5.md` | ТЗ для Dev-агента Фаза 5 |
|
||||
| `reports/` | Отчёты о тестировании |
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user