# 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 `
${waypointPinSvg(label, color)}
${coordText} ${distStr ? `${distStr}` : ''}
${gripSvg}
`; }) ``` 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 (они уже в коде или деплоятся параллельно)