Files
wiki/tasks/enduro-trails/DEV_TASK_PHASE5.md
2026-05-05 08:10:01 +03:00

12 KiB
Raw Blame History

Dev Task: Enduro Trails — Фаза 5 «Редизайн»

Приоритет: HIGH
Проект: enduro-trails
BRD: BRD_PHASE5.md
Дата: 2026-05-05


Контекст

Фазы 34 реализованы: роутинг A→B, альтернативы, Разведка, Связка, Красивый маршрут, Линейка, Метки.
UI уже частично обновлён (bottom sheets, toolbar, SVG, тёмная тема), но BRD Phase 5 требует довести до продакшн-качества — мобильный first, две темы с авто-режимом по восходу/закату, адаптив, анимации.

⚠️ Бэкенд (app.py) НЕ ТРОГАТЬ. Только фронтенд: index.html, app.css, app.js.


Деплой

SSH бинарник не работает в контейнере (glibc 2.36 < 2.38). Используй Node.js ssh2:

const { Client } = require('/tmp/node_modules/ssh2');

Шаблон: /tmp/deploy_app2.js. Статику заливаем через SFTP в /home/slin/enduro-trails/prototype/static/, потом docker cp в контейнер + docker restart.


Что уже есть vs что нужно

Компонент Сейчас (Phase 4) Надо (Phase 5)
Тема Только тёмная, кнопка ☀️/🌙 но не работает Три режима: Авто (SunCalc восход/закат), Светлая, Тёмная
Цвета CSS vars есть, но только тёмные Два набора CSS vars, переключение через body.theme-dark/body.theme-light
Кнопка темы Есть #btn-theme в search-bar Циклическое переключение Авто→Светлая→Тёмная→Авто, подпись режима
Search bar Есть, position:fixed Остается, но цвета под тему
Bottom toolbar 6 кнопок, SVG, #toolbar Остается, но: активная кнопка = оранжевый фон (не только цвет текста), label появляется под иконкой
Bottom sheets 4 sheets (route/recon/scenic/link) Остаются, но: цвета под тему, skeleton loading, свайп вниз для закрытия
Map buttons #map-controls-r (2 кнопки) Остаются, цвета под тему
Адаптив Mobile only, без десктопа Десктоп (≥768px): боковая панель 320px, кнопки слева
Анимации Есть sheet slide + skeleton loading, + кнопка scale(0.94) при tap, + маркер scale появление
Иконки SVG inline (lucide-style) ОК, остаются. НЕ emoji
Карта style Один style.json Переключение dark/light style при смене темы

Задача 1: Дизайн-система — Цвета и Тема

1.1 CSS Custom Properties — две темы

На :root определить ВСЕ переменные для тёмной (по умолчанию). На body.theme-light — переопределить.

Тёмная (default):

:root {
  --bg:         #0D1117;
  --surface:    #161B22;
  --surface2:   #21262D;
  --border:     #30363D;
  --text:       #E6EDF3;
  --text2:      #8B949E;
  --text3:      #484F58;
  --accent:     #FF6B00;
  --accent-hover:#FF8A33;
  --gold:       #FFD700;
  --red:        #FF3B1F;
  --success:    #2EA043;
  --shadow:     rgba(0,0,0,0.5);
  --overlay:    rgba(0,0,0,0.6);
}

Светлая:

body.theme-light {
  --bg:         #F5F5F0;
  --surface:    #FFFFFF;
  --surface2:   #F0F0EA;
  --border:     #D0CFC8;
  --text:       #1A1A1A;
  --text2:      #6B6B6B;
  --text3:      #9A9A9A;
  --accent:     #E55A00;
  --accent-hover:#CC4F00;
  --gold:       #C89B00;
  --red:        #CC2200;
  --success:    #1A7A30;
  --shadow:     rgba(0,0,0,0.15);
  --overlay:    rgba(0,0,0,0.3);
}

Все компоненты (search-bar, toolbar, bottom-sheet, map-btn, cards, pills, ruler-info, marker-dialog, search-results) должны использовать ТОЛЬКО CSS vars — никаких хардкоженных цветов.

1.2 Переключатель темы — три режима

Состояния:

  • themeMode = 'auto' — по умолчанию, тема определяется по восходу/закату
  • themeMode = 'light' — принудительно светлая
  • themeMode = 'dark' — принудительно тёмная

Циклическое переключение: тап на #btn-themeauto → light → dark → auto

UI кнопки:

  • В авто-режиме: иконка динамическая (☀️ если день, 🌙 если ночь)
  • В ручном светлом: всегда ☀️
  • В ручном тёмном: всегда 🌙
  • Подпись под иконкой (или рядом, маленький текст): «Авто» / «День» / «Ночь»

Сохранение: localStorage.setItem('enduro-theme-mode', themeMode) — при загрузке читать оттуда.

1.3 SunCalc — авто-режим по восходу/закату

Подключить в index.html:

<script src="https://unpkg.com/suncalc@1.9.0/suncalc.min.js"></script>

(до app.js)

Логика:

function applyAutoTheme() {
  if (themeMode !== 'auto') return;
  const now = new Date();
  // Геолокация: если есть — используем, иначе Москва
  const lat = userLat || 55.75;
  const lon = userLon || 37.62;
  const times = SunCalc.getTimes(now, lat, lon);
  const isDay = now >= times.sunrise && now < times.sunset;
  document.body.className = isDay ? 'theme-light' : 'theme-dark';
  updateThemeButtonIcon();
}

// Проверять раз в минуту
setInterval(applyAutoTheme, 60000);

При получении геолокации (кнопка 🎯): обновить userLat/userLon и пересчитать тему.

При переключении карты: MapLibre.setStyle() на соответствующий style.json (тёмный/светлый).

1.4 Карта — стиль под тему

Уже есть два style.json: тёмный и светлый. При смене темы:

map.setStyle(isDark ? 'style-dark.json' : 'style-light.json');

Или если стили инлайн — переключить map.setStyle() с нужным объектом.

⚠️ Важно: после setStyle нужно пересоздать все слои (routes, markers, circles). Использовать событие map.on('style.load', ...).


Задача 2: Компоненты — все на CSS vars

  • Фон: var(--surface), бордер: var(--border), тень с var(--shadow)
  • Placeholder: var(--text3)
  • Кнопка темы: иконка + подпись

2.2 Toolbar

  • Фон: var(--surface), бордер: var(--border)
  • Неактивная кнопка: var(--text2) цвет
  • Активная кнопка: var(--accent) фон, белый цвет текста/иконки, border-radius: 10px
  • Активная кнопка: подпись под иконкой видна

2.3 Bottom sheets

  • Фон: var(--surface), бордер: var(--border)
  • Handle: var(--border) цвет
  • Заголовок: var(--text), подзаголовок/label: var(--text2)
  • Close button: var(--text2) → hover var(--text)
  • Waypoint chips: var(--surface2) фон, var(--border) бордер
  • Route cards: var(--surface2) фон, var(--border) бордер
  • Stat pills: var(--bg) фон, var(--border) бордер
  • Hint text: var(--text3)

2.4 Map buttons

  • Фон: var(--surface), бордер: var(--border), тень с var(--shadow)
  • Active: var(--accent) фон

2.5 Search results

  • Фон: var(--surface), бордер: var(--border)
  • Hover: var(--surface2)

2.6 Marker dialog

  • Фон: var(--surface), внутренности var(--surface2), бордер var(--border)

Задача 3: Skeleton Loading

При загрузке маршрутов/разведки показывать skeleton вместо «Строю маршрут...»:

.skeleton {
  background: linear-gradient(90deg, var(--surface2) 25%, var(--border) 50%, var(--surface2) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: 8px;
  height: 64px;
  margin-bottom: 8px;
}
@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

Использовать в #route-cards, #scenic-cards, #link-cards пока идёт запрос к API.


Задача 4: Свайп вниз для закрытия sheet

Touch-обработка:

// На каждом .bottom-sheet
let startY = 0;
sheet.addEventListener('touchstart', e => {
  if (e.target.closest('.sheet-handle') || e.touches[0].clientY < sheet.getBoundingClientRect().top + 40) {
    startY = e.touches[0].clientY;
  }
});
sheet.addEventListener('touchmove', e => {
  const dy = e.touches[0].clientY - startY;
  if (dy > 0) sheet.style.transform = `translateY(${dy}px)`;
});
sheet.addEventListener('touchend', e => {
  const dy = e.changedTouches[0].clientY - startY;
  if (dy > 80) closeSheet(sheet.id);
  else sheet.style.transform = '';
});

Задача 5: Адаптив — Десктоп

При width >= 768px:

@media (min-width: 768px) {
  #toolbar {
    position: fixed;
    bottom: auto; left: 0; top: 60px;
    width: 72px; height: auto;
    flex-direction: column;
    border-top: none;
    border-right: 1px solid var(--border);
  }
  .bottom-sheet {
    left: 72px;
    max-width: 400px;
    border-radius: 20px 20px 0 0;
  }
  #map-controls-r {
    left: 72px;
  }
  #search-bar {
    left: 84px;
  }
}

Задача 6: Микро-анимации

  1. Кнопки toolbar при tap: transform: scale(0.94), 100ms
  2. Маркеры на карте при появлении: transform: scale(0) → scale(1), 200ms (MapLibre marker animation)
  3. Карточки при появлении: opacity: 0 → 1, 200ms stagger

Порядок реализации

  1. Задача 1 — Дизайн-система: CSS vars для двух тем + SunCalc + переключатель
  2. Задача 2Все компоненты на CSS vars (замена хардкода)
  3. Задача 3 — Skeleton loading
  4. Задача 4 — Свайп для закрытия sheets
  5. Задача 5 — Десктоп-адаптив
  6. Задача 6 — Микро-анимации
  7. Деплой + проверка

Проверка после деплоя

  1. Открыть приложение → проверить тему (если день — светлая, если ночь — тёмная, подпись «Авто»)
  2. Тапнуть кнопку темы 3 раза → Авто→Светлая→Тёмная→Авто
  3. В светлом режиме — бежевый фон, тёмный текст, нет белых вспышек
  4. В тёмном режиме — тёмный фон, светлый текст
  5. Открыть Маршрут → sheet, карточки, всё в цветах текущей темы
  6. Переключить тему с открытым sheet → цвета обновились
  7. Десктоп (широкий экран) → toolbar слева вертикально, sheet с max-width
  8. Skeleton при построении маршрута
  9. Свайп вниз по sheet → закрывается
  10. Геолокация → тема пересчитывается по реальным координатам

Технические ограничения

  • Бэкенд (app.py) НЕ ТРОГАТЬ — только index.html, app.css, app.js
  • MapLibre GL остаётся
  • Без npm-зависимостей (inline CSS/JS)
  • CDN: https://unpkg.com/suncalc@1.9.0/suncalc.min.js (добавить в index.html)
  • CDN: https://unpkg.com/maplibre-gl@4.7.0/... (уже есть)
  • Деплой через ssh2 (шаблон /tmp/deploy_app2.js)