12 KiB
Active Context — Strava Frontend
Task State
- task_id: TASK-HIDE-TRACK-T1
- status: success
- parent_task: TASK-HIDE-TRACK
- summary: Backend тест
test_workout_hide_start_endдобавлен. Все 4 теста PASSED (5.13s). - next task: —
✅ CODER SUCCESS REPORT — TASK-HIDE-TRACK-T1
../backend/tests/test_api_workout.py: добавленtest_workout_hide_start_end— проверяет PATCHhide_start_end(False→True→False) с GET-верификацией на каждом шаге.- Verified:
./venv/bin/python -m pytest tests/test_api_workout.py::test_workout_hide_start_end -v→ PASSED (1.92s) - Verified:
./venv/bin/python -m pytest tests/test_api_workout.py -v→ 4 passed (5.13s)
✅ CODER SUCCESS REPORT — TASK-HIDE-TRACK-B3
../backend/app/web/v0/workout.py:WorkoutEditReq+ полеhide_start_end: bool | None = Field(None); endpoint передаётhide_start_end=req.hide_start_endвworkout_service.workout_edit(...).../backend/app/modules/charts/services/workout.py:workout_edit+ параметрhide_start_end: bool | None = None; тело:if hide_start_end is not None: params["hide_start_end"] = hide_start_end.- Verified:
./venv/bin/python -c "from app.web.v0.workout import workout_update; print('OK')"→ OK. - Verified:
./venv/bin/python -m mypy app/web/v0/workout.py app/modules/charts/services/workout.py --explicit-package-bases --no-error-summary→ 0 errors.
✅ CODER SUCCESS REPORT — TASK-HIDE-TRACK-B2
../backend/migration/versions/2025-09-25-0000-a1b2c3d4e5f6-hide-start-end.pyсоздан:revision='a1b2c3d4e5f6',down_revision='b1c2d3e4f5a6';upgrade()→op.add_column('workouts', sa.Column('hide_start_end', sa.Boolean(), server_default=sa.text('FALSE'), nullable=False));downgrade()→op.drop_column(...). БезIS_TEST-guard (колонка нужна и в тестах).- Verified:
python3 -c "import ast; ast.parse(...)"→ OK.
✅ CODER SUCCESS REPORT — TASK-HIDE-TRACK-4
src/pages/workouts/WorkoutPublicItem.vue: импортtrimTrack; в.then()приd.workoutItem.hide_start_end === true— обрезкаlineCoordinates/distances/dataчерезtrimTrack(..., totalDist)(totalDist = последний элементdistances).mapCenterостаётся неизменным.src/pages/workouts/Definitions.vue: добавлено полеhide_start_end: booleanв типWorkoutItem(восстановлено — было потеряно при git stash).- Приватный вид (
WorkoutItem.vueстраница) НЕ затронут. - Verified:
yarn buildexit 0 (vue-tsc --noEmit 0 ошибок, vite ✓ 6.86s). - Note:
yarn lint→ системная ошибкаtsutils.iterateComments is not a function(касается ВСЕХ .vue файлов, не связано с изменениями — существующий дефект окружения lint-депенденций).
✅ CODER SUCCESS REPORT — TASK-TEST-AUTH
../integration/tests/auth-flows.spec.tsсоздан: 4 теста (login success, login failure, signup+auto-login, logout)- Self-seed via
POST /api/v0/signup, Vuestic selectors (input[type=email],input[type=password]) - Verified:
npx playwright test tests/auth-flows.spec.ts→ 4 passed (19.2s)
TASK-TEST-PUBLIC: public-pages.spec.ts
Цель: Playwright spec для auth-сценариев (signup, login success/error, logout, validation).
Файл для создания: ../integration/tests/auth-flows.spec.ts
Конвенции (см. SKILL.md):
- Self-seed user via
POST /api/v0/signupinbeforeAll - Selectors:
input[type=email],input[type=password],getByRole('button', { name: 'Вход' }) - Vuestic floating labels → select by input type, NOT placeholder
- Auth mock via
page.evaluate(() => localStorage.setItem(...)) - Do NOT call
expect()insidepage.evaluate() - Base URLs:
FRONTEND(5173),BACKEND(8000) freshPage()helper: goto → clear localStorage → goto again
Тесты в spec:
- Signup:
POST /api/v0/signup→ returns token, user, profile; navigate to/auth/signup→ fill form → submit → redirect to/explore - Login success: pre-seed user via API → UI login → token in localStorage → redirect to
/explore - Login failure: wrong password → error toast appears, stays on
/auth/login - Logout: logged in → open profile dropdown → «Выход» → redirect to login, token cleared
Acceptance Criteria:
- Spec file created at
../integration/tests/auth-flows.spec.ts - All tests self-contained (self-seed data)
- Follows existing
sidebar-auth-switch.spec.tspatterns npx playwright test tests/auth-flows.spec.tspasses against integration stack
TASK-MCP-SETUP: Playwright MCP configuration
Что сделано:
- MCP-конфиг Zoo Code (
mcp_settings.json): serverweb-browser→@playwright/mcp@latest, Node 22 via nvm absolute path,--headless - Chromium
1234уже установлен в~/.cache/ms-playwright/ .mcp.jsonсоздан в проекте (project-level, для совместимости)techContext.mdобновлён: секция "UI Verification (MCP Browser)"
Как использовать (workflow):
yarn dev— запустить dev server (порт 5173)browser_navigate→http://localhost:5173/explore— проверить публичную страницуbrowser_evaluate→() => { localStorage.setItem('token', 'test'); localStorage.setItem('user', JSON.stringify({id:1,name:'Test'})); location.reload(); }— сымитировать authbrowser_navigate→ проверить auth-страницы (/workouts,/workouts/:id)browser_snapshot— проверить DOM-структуруbrowser_take_screenshot— визуальная проверкаbrowser_network_requests— проверить API-запросы
Баг (контекст)
Сайдбар показывает publicRoutes (Лента + Маршрут) после логина до F5. Корень: NavigationRoutes.ts:70 — routes вычисляется один раз при module evaluation и кэшируется. TASK-AUTH-NAV-1 уже добавил реактивный isAuthenticated в useGlobalStore (инициализация из localStorage + setAuthenticated в Login/Logout). Эта задача переключает UI на computed.
TASK-AUTH-NAV-2: реактивные маршруты (3 файла)
1. src/components/sidebar/NavigationRoutes.ts
- Сделать named exports:
export const authRoutes: INavigationRoute[]иexport const publicRoutes: INavigationRoute[](сейчас это локальныеconst, не экспортированные). - Убрать статическое поле
routesиз default export (или оставить default безroutes, т.к. оба потребителя перейдут на computed).INavigationRouteинтерфейс — не трогать. - Важно: массивы
authRoutes/publicRoutesостаются теми же по структуре/содержимому — только экспортируются.
2. src/components/sidebar/AppSidebar.vue (Options API, setup())
import { useGlobalStore } from "../../stores/global-store";+import { authRoutes, publicRoutes } from "./NavigationRoutes";(импортnavigationRoutesпо default можно оставить/убрать — но все обращенияnavigationRoutes.routesзаменить).- В
setup():const globalStore = useGlobalStore(); - computed:
const routes = computed(() => globalStore.isAuthenticated ? authRoutes : publicRoutes); - В
returnзаменитьnavigationRoutesнаroutes(тепловой шаблон уже используетnavigationRoutes.routes→ поправить наroutes.value? НЕТ — в Options API computed из setup доступно в шаблоне какroutesбез.value, т.е. в шаблонеnavigationRoutes.routes→routes). setActiveExpand:navigationRoutes.routes.map(...)→routes.value.map(...)(в setup.valueнужен).- Шаблоны:
v-for="(route, index) in navigationRoutes.routes"→in routes.
3. src/components/app-layout-navigation/AppLayoutNavigation.vue (script setup)
import { authRoutes, publicRoutes } from "../sidebar/NavigationRoutes";+import { useGlobalStore } from "../../stores/global-store";const globalStore = useGlobalStore();findRouteName(строка ~64):traverse(NavigationRoutes.routes)→traverse(globalStore.isAuthenticated ? authRoutes : publicRoutes).- Убедиться, что
itemscomputed (зависит отroute.matchedиfindRouteName) остаётся реактивным — т.к.findRouteNameчитаетglobalStore.isAuthenticatedвнутриitemscomputed, реactivity сохранится.
Acceptance Criteria (TASK-AUTH-NAV-2)
yarn lint→ 0 ошибок.yarn build→ 0 ошибок (vue-tsc --noEmit).NavigationRoutes.ts:authRoutesиpublicRoutesэкспортируются; статическогоroutesс localStorage-тернарником больше нет.AppSidebar.vue: routes = computed отglobalStore.isAuthenticated;setActiveExpandиспользуетroutes.value.AppLayoutNavigation.vue:traverseиспользует выбор поglobalStore.isAuthenticated.- Ручной сценарий (ключевой): без F5 — выход из профиля →
/explore→ логин → сайдбар сразу показывает «Мои тренировки» (authRoutes). Аут → сайдбар сразуpublicRoutes.
✅ CODER SUCCESS REPORT — TASK-AUTH-NAV-2
src/components/sidebar/NavigationRoutes.ts:export const authRoutes/export const publicRoutes; удалён статическийroutes: localStorage.getItem("token") ? ...из default export (оставлен толькоroot).src/components/sidebar/AppSidebar.vue: импортauthRoutes, publicRoutes+useGlobalStore;computed(() => globalStore.isAuthenticated ? authRoutes : publicRoutes);setActiveExpandиспользуетroutes.value.map; шаблонv-for="(route, index) in routes".src/components/app-layout-navigation/AppLayoutNavigation.vue:const globalStore = useGlobalStore();traverse(globalStore.isAuthenticated ? authRoutes : publicRoutes)внутриfindRouteName(вызывается вitemscomputed → реactivity сохранена).- Verified:
yarn lintexit 0,yarn buildexit 0 (vue-tsc --noEmit 0 ошибок, vite ✓ 7.07s).
История (закрытые задачи)
-
TASK-HIDE-TRACK-4: success —
trimTrackвWorkoutPublicItem.vueприhide_start_end;hide_start_endв типеWorkoutItem(Definitions.vue). Verified:yarn build0. (Пред. active: TASK-TEST-PUBLIC → в очереди TASK-TEST-PROTECTED.) -
TASK-AUTH-NAV-1: success —
isAuthenticated+setAuthenticatedвuseGlobalStore; LoginsetAuthenticated(true), LogoutsetAuthenticated(false). Verified lint/build 0. -
TASK-SSR-DETAIL: success — SSR /public/workouts/:id под DOM WorkoutItem.vue.
-
TASK-404: success — HTTP 404 для неизвестных страниц.
-
TASK-PUBLIC-ROUTE-FIX: success — workout_public_item в top-level router.
-
TASK-F11/F12/F13/F14: success — SEO + фиксы карты тренировки.