auto-sync: 2026-05-05 22:30:01
This commit is contained in:
133
tasks/enduro-trails/DEV_TASK_PHASE5_UX3.md
Normal file
133
tasks/enduro-trails/DEV_TASK_PHASE5_UX3.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Dev Task: Enduro Trails — UX маршрута дополнение (drag + дистанции)
|
||||
|
||||
**Приоритет:** HIGH
|
||||
**Проект:** enduro-trails
|
||||
**Дата:** 2026-05-05
|
||||
**Дополнение к:** DEV_TASK_PHASE5_UX2.md
|
||||
|
||||
---
|
||||
|
||||
## Задача 5: Drag точек в основном sheet — не закрывать sheet
|
||||
|
||||
### Проблема
|
||||
Сейчас в `_initWaypointDragHandles` после drop вызывается `debounceBuildRoute()` — это нормально. Но где-то в цепочке sheet закрывается.
|
||||
|
||||
### Нужное поведение
|
||||
При перетаскивании точек в `#waypoints-list` (основной sheet открыт):
|
||||
- Sheet остаётся открытым
|
||||
- Маршрут пересчитывается в фоне (колесо в мини-баре)
|
||||
- После пересчёта — обновить карточки маршрута прямо в открытом sheet
|
||||
|
||||
### Изменения
|
||||
|
||||
В `endDrag()` внутри `_initWaypointDragHandles`:
|
||||
```js
|
||||
function endDrag(finalClientY) {
|
||||
if (dragIdx < 0) return;
|
||||
clearHighlights();
|
||||
const dy = Math.abs(finalClientY - startY);
|
||||
|
||||
if (dragging && dy > 30 && lastOverEl !== null) {
|
||||
const dropIdx = parseInt(lastOverEl.dataset.idx, 10);
|
||||
let insertAt = lastOverPos === 'top' ? dropIdx : dropIdx + 1;
|
||||
const moved = routeWaypoints.splice(dragIdx, 1)[0];
|
||||
if (insertAt > dragIdx) insertAt--;
|
||||
routeWaypoints.splice(insertAt, 0, moved);
|
||||
rebuildWaypointMarkers();
|
||||
renderWaypointsList();
|
||||
// НЕ закрывать sheet — просто пересчитать маршрут
|
||||
if (routeWaypoints.length >= 2) {
|
||||
showMiniRouteLoading(); // колесо в мини-баре
|
||||
debounceBuildRoute();
|
||||
}
|
||||
updateMiniRouteCard();
|
||||
}
|
||||
|
||||
dragIdx = -1;
|
||||
dragging = false;
|
||||
lastOverEl = null;
|
||||
lastOverPos = null;
|
||||
}
|
||||
```
|
||||
|
||||
В `buildRoute()` — после успешного построения, если основной sheet открыт, обновить карточки без закрытия:
|
||||
```js
|
||||
// После drawRouteResults(routeResults, 0):
|
||||
const sheetOpen = document.getElementById('sheet-route')?.classList.contains('open');
|
||||
if (sheetOpen) {
|
||||
renderRouteCards(routeResults); // обновить карточки в открытом sheet
|
||||
} else {
|
||||
showMiniRouteSheet(); // показать мини-бар
|
||||
}
|
||||
hideMiniRouteLoading();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Задача 6: Расстояние между точками в wl-item
|
||||
|
||||
### Нужное поведение
|
||||
В каждом `wl-item` (кроме первой точки) показывать расстояние от предыдущей точки.
|
||||
|
||||
Формат:
|
||||
- < 1 км → "130 м"
|
||||
- ≥ 1 км → "2,3 км" (запятая как разделитель)
|
||||
|
||||
### Реализация
|
||||
|
||||
Добавить функцию форматирования дистанции между точками:
|
||||
```js
|
||||
function formatSegmentDist(m) {
|
||||
if (m < 1000) return Math.round(m) + ' м';
|
||||
return (m / 1000).toFixed(1).replace('.', ',') + ' км';
|
||||
}
|
||||
```
|
||||
|
||||
Использовать haversine для расчёта расстояния между соседними точками:
|
||||
```js
|
||||
function haversineM(a, b) {
|
||||
const R = 6371000;
|
||||
const dLat = (b.lat - a.lat) * Math.PI / 180;
|
||||
const dLon = (b.lon - a.lon) * Math.PI / 180;
|
||||
const s = Math.sin(dLat/2)**2 + Math.cos(a.lat*Math.PI/180) * Math.cos(b.lat*Math.PI/180) * Math.sin(dLon/2)**2;
|
||||
return R * 2 * Math.atan2(Math.sqrt(s), Math.sqrt(1-s));
|
||||
}
|
||||
```
|
||||
|
||||
В `renderWaypointsList()` добавить дистанцию в каждый wl-item (кроме первого):
|
||||
```js
|
||||
routeWaypoints.map((wp, i) => {
|
||||
// ...
|
||||
const distStr = i > 0 ? formatSegmentDist(haversineM(routeWaypoints[i-1], wp)) : '';
|
||||
return `<div class="wl-item" id="wl-item-${i}" data-idx="${i}">
|
||||
<div class="wl-pin">${waypointPinSvg(label, color)}</div>
|
||||
<div class="wl-info">
|
||||
<span class="wl-label" id="wl-label-${i}">${coordText}</span>
|
||||
${distStr ? `<span class="wl-dist">${distStr}</span>` : ''}
|
||||
</div>
|
||||
<div class="wl-drag-handle" data-idx="${i}">${gripSvg}</div>
|
||||
<button class="wl-remove" onclick="removeWaypoint(${i})" ...>...</button>
|
||||
</div>`;
|
||||
})
|
||||
```
|
||||
|
||||
CSS для `.wl-dist`:
|
||||
```css
|
||||
.wl-info { display: flex; flex-direction: column; flex: 1; min-width: 0; }
|
||||
.wl-label { font-size: 13px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.wl-dist { font-size: 11px; color: var(--text3); margin-top: 1px; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Деплой
|
||||
```
|
||||
/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-зависимостей
|
||||
- Реализовать поверх изменений из DEV_TASK_PHASE5_UX2.md (они уже в коде или деплоятся параллельно)
|
||||
Reference in New Issue
Block a user