# Active Context — Strava Frontend
## Task State
- task_id: TASK-ROUTE-FIX
- status: success
- parent_task: —
- summary: **FIX: Route.vue — багфикс GPX-генератора (for...in, дубликаты, фейковый ele) + фильтр близлежащих точек 5м. Snap to road убран (GraphHopper 401, риск блокировки OSM в РФ).**
- next task: —
## ✅ CODER SUCCESS REPORT — TASK-ROUTE-FIX (final)
- Изменён файл: `src/pages/routes/Route.vue`
- `toGPX()`: `var date` → `const date`, `for...in` → index-based loop, `${i}` → `0`
- `handleMapClick`: синхронная (не async), фильтр < 5м до последней точки (игнор клика), push без snap
- `SNAP_THRESHOLD_M` → `MIN_DISTANCE_M`
- Убран import `snapToRoad` (файл `src/services/roadSnap.ts` остался в репо, но не используется)
- Поведение: клик < 5м от последней точки → игнорируется; клик в другом месте → точка добавляется как есть; GPX генерируется корректно (index loop, ele=0)
- `yarn lint`: exit 0
- `yarn build`: exit 0 (vue-tsc --noEmit 0 ошибок, vite build ✓ 6.58s)
## TASK-LINK1 — TODO (delegate to Coder)
- [x] `src/pages/workouts/components/WorkoutItem.vue`, секция «Ссылки на описание» (~строка 183): добавить `v-if="dzenLink || isPrivate"` на внешний `div.workout-item-params` секции. Внутренняя логика без изменений: `v-if="dzenLink"` → `Дзен` + `VaIcon edit v-if="isPrivate"`; `v-else` → «Добавить» (теперь достижим только при isPrivate, т.к. секция скрыта иначе).
- [x] Файлы трогать ДРУГИЕ НЕЛЬЗЯ: только `src/pages/workouts/components/WorkoutItem.vue`.
- [x] Acceptance: `yarn lint` exit 0, `yarn build` exit 0. Поведение: публичная страница без ссылки → секции нет вообще; публичная со ссылкой → видна ссылка «Дзен» без иконки edit; приватная без ссылки → «Добавить» (открывает модалку, PATCH работает); приватная со ссылкой → «Дзен» + иконка edit.
## ✅ CODER SUCCESS REPORT — TASK-LINK1
- Изменен файл (единственный): `src/pages/workouts/components/WorkoutItem.vue` — одна строка: на внешний `div.workout-item-params` секции «Ссылки на описание» (стр. 183) добавлен `v-if="dzenLink || isPrivate"`. Внутренняя логика секции не тронута.
- Поведение после фикса: публичная страница без ссылки → секция полностью скрыта; публичная со ссылкой → только «Дзен» (иконка edit уже была `v-if="isPrivate"`); приватная без ссылки → «Добавить» (модалка `v-if="isPrivate"` доступна, PATCH работает); приватная со ссылкой → «Дзен» + иконка edit.
- `yarn lint`: exit 0 (prettier: файлы без изменений, eslint 0 ошибок).
- `yarn build`: exit 0 (`vue-tsc --noEmit` 0 ошибок, `vite build` ✓ built in 5.41s; warning о chunk >500kB — предсуществующий).
## TASK-F1 — TODO
- [x] `src/pages/workouts/Definitions.vue`: тип `WorkoutPhoto` + `photos?: Array` в `WorkoutItem` (fetch менять не нужно — бэкенд уже присылает `workout.photos`).
- [x] `src/pages/workouts/components/WorkoutItem.vue`: `photos` ref (из `workoutItem?.photos`), `photosWithCoords` computed, `` для фото с координатами (порядок `[lon, lat]` как у `clickCoordinates`, `color: 'blue'`, onClick → `window.open(photo.url, "_blank")`).
- [x] `src/pages/workouts/components/WorkoutItem.vue`: секция `#workout-photos` под `#workout-container` — grid `
`, «Добавить фото» (`v-if="isPrivate"`, скрытый `input[type=file] accept="image/*" multiple`, `axiosAuth.post` multipart поле `file`, `Promise.allSettled`, ответ в `photos`, VaButton `:loading`), «Удалить» (`v-if="isPrivate"`, `axiosAuth.delete` → убрать из массива), ошибки/успех — `useToast`.
- [x] `src/pages/workouts/components/WorkoutItem.vue`: `isPrivate` добавлен в деструктуризацию `defineProps`; стили `.workout-photos-*` (grid auto-fill 160px, img 120px object-fit cover, кнопка удаления absolute).
- [x] `WorkoutListItem.vue` НЕ тронут (по ТЗ).
- [x] Верификация: `npm run build` exit 0 (vue-tsc --noEmit + vite, ~8–12s), `npm run lint` exit 0 (prettier+eslint --fix).
## ✅ CODER SUCCESS REPORT — TASK-F1
- Изменены файлы: `src/pages/workouts/Definitions.vue` (+8 строк: тип `WorkoutPhoto`, поле `photos?`), `src/pages/workouts/components/WorkoutItem.vue` (+163 строки: маркеры фото, галерея, upload/delete, стили).
- `photos` — локальный `ref>` (иниц. из пропса), все мутации только по нему; `axiosAuth` — существующий `inject("axiosAuth")`.
- Маркеры: `` с `coordinates: [lon, lat] as LngLat` (тот же порядок, что у `clickCoordinates`), `color: 'blue'` для отличия от серых маркеров кликов; onClick → `window.open(photo.url, "_blank")`.
- Загрузка: `Promise.allSettled` по каждому файлу (multipart, поле `file`); успех → push в `photos` + toast success; частичная неудача → toast error «Не удалось загрузить часть фото.»; `:loading` на кнопке.
- Удаление: `axiosAuth.delete` → filter из `photos`; ошибка → toast «Не удалось удалить фото.»
- Кнопки «Добавить фото»/«Удалить» и `v-if` секции только при `isPrivate` (владелец); на публичной странице (`WorkoutPublicItem.vue`, `isPrivate=false`) фото видны только-чтение без управления.
- `npm run build`: exit 0, `✓ built in 11.71s` (vue-tsc 0 ошибок; warning о chunk >500kB — предсуществующий).
- `npm run lint`: exit 0 (prettier: `WorkoutItem.vue` переформатирован, eslint 0 ошибок).
- Ручные проверки (сценарии для QA): фото с GPS → в галерее + синий маркер на карте; фото без GPS → только в галерее; добавление нескольких файлов сразу → все добавлены, `:loading` на кнопке во время; удаление → исчезло из галерее и с карты; публичная страница → фото видны, кнопок нет; клик по фото/маркеру → opens в новой вкладке.
## W3 acceptance criteria
- `eslint` 9.x + `typescript-eslint` 8.x in yarn.lock; legacy `@typescript-eslint/eslint-plugin@6` + `@typescript-eslint/parser@6` removed (superseded by the `typescript-eslint` 8 meta-package); NO other package moved to a new major
- `yarn lint` 0 errors + `yarn build` green
- `eslint.config.mjs` adjustments minimal and documented (flat config already in use — migration should be trivial)
- Memory Bank: status `success` + Success Report
## W3 notes for the Coder
- Current state: `eslint@8.57.1` (flat config works on 8.57), `typescript-eslint@7.18.0`, legacy `@typescript-eslint/eslint-plugin@^6.11.0` + `@typescript-eslint/parser@^6.11.0` still in package.json (redundant with the meta-package but currently unused by eslint.config.mjs).
- Target: `yarn add -D eslint@^9 typescript-eslint@^8`, then `yarn remove @typescript-eslint/eslint-plugin @typescript-eslint/parser` ONLY if eslint.config.mjs does not import them directly (check first).
- eslint 9 removes some legacy config options — if `eslint.config.mjs` breaks, apply the minimal migration (documented).
- `eslint-plugin-prettier`/`@vue/eslint-config-prettier` are NOT part of W3 — leave versions as-is.
## W2d acceptance criteria
- `package.json`: no `storybook`/`@storybook/*`/`eslint-plugin-storybook` deps, no `storybook`/`build-storybook` scripts
- yarn.lock: no `@storybook/vue3-vite` entry, no nested `@vitejs/plugin-vue@4` install
- `yarn lint` 0 errors + `yarn build` green
- `eslint.config.mjs` — no references to storybook plugin (verify; add minimal fix if present)
- Memory Bank: status `success` + Success Report (removed list, install-size delta if measurable)
## W2b acceptance criteria
- `vue-tsc` resolved to 2.x and `typescript` to 5.8.x in yarn.lock; NO other package moved to a new major
- `yarn lint` — 0 errors; `yarn build` — green (vue-tsc 2 type-check 0 errors, bundle built)
- Minimal type-only fixes in `src/` allowed only if required by the stricter checker; each documented with justification
- Memory Bank: status `success` + Success Report (old/new versions, full list of src/ fixes with justification)
## Wave plan (each wave = one atomic task, verified independently)
- **W0a (TASK-DEPS-UPDATE-W0a, success)**: Fix everything that hard-blocks `yarn build`:
- `LineWithLineChart.ts(41)` TS2532 "Object is possibly 'undefined'" (labels can be undefined) — fix via non-null assertion or guarded length.
- SFC parsing errors: `src/pages/workouts/components/WorkoutItem.vue:260` (`Unexpected token {`) and `WorkoutListItem.vue:60` (`'interface' is reserved`). Root-cause candidates: TS syntax in a plain `