242 lines
31 KiB
Markdown
242 lines
31 KiB
Markdown
# Progress — Strava Frontend
|
||
|
||
## 2026-09-25 — TASK-HIDE-TRACK-B3: hide_start_end в PATCH endpoint + service (backend)
|
||
|
||
- `../backend/app/web/v0/workout.py` — `WorkoutEditReq` + `hide_start_end: bool | None = Field(None)`; PATCH `/workouts/{workout_id}` передаёт `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` перед `repository.update`.
|
||
- 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.
|
||
|
||
## 2026-09-25 — TASK-HIDE-TRACK-B2: Alembic migration hide_start_end (backend)
|
||
|
||
- `../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('workouts', 'hide_start_end')`. Без `IS_TEST`-guard (это колонка, не index/FK — нужна в тестовой БД тоже).
|
||
- Verified: `python3 -c "import ast; ast.parse(open(...).read())"` → OK (exit 0).
|
||
|
||
## 2026-09-25 — TASK-HIDE-TRACK-4: trimTrack в публичном виде тренировки
|
||
|
||
- `src/pages/workouts/WorkoutPublicItem.vue` — импорт `trimTrack` (`./components/TrimTrack`); в `.then()` `initWorkout`: при `d.workoutItem.hide_start_end` → `trimTrack(d.lineCoordinates, d.distances, d.data, totalDist)`, где `totalDist = d.distances[d.distances.length - 1] ?? 0`; иначе значения присваиваются как есть. `mapCenter` не меняется. Приватный вид не затронут.
|
||
- `src/pages/workouts/Definitions.vue` — `WorkoutItem` + поле `hide_start_end: boolean` (восстановлено после потери в git stash).
|
||
- Verified: `yarn build` exit 0 (vue-tsc 0 errors, vite ✓ 6.86s).
|
||
- Known issue: `yarn lint` сломан системно — `tsutils.iterateComments is not a function` на всех .vue файлах (дефект окружения, не от этих изменений).
|
||
|
||
## 2026-09-24 — UI-testing skill + mandatory rule
|
||
|
||
- `.roo/skills/ui-testing/SKILL.md` — how to test UI via (A) MCP browser ad-hoc and (B) the Playwright integration stand: run commands, spec conventions, selectors, gotchas.
|
||
- `.roo/rules/ui-testing.md` — MANDATORY rule: after any `src/` change affecting a visible route/interaction the task is not done until `yarn lint` + `yarn build` + browser verification (MCP ad-hoc for quick/visual, Playwright spec for must-not-regress) + green `npx playwright test`. Includes run cheat-sheet and required reporting format.
|
||
|
||
## 2026-09-24 — Integration UI-test stand (Playwright + docker-compose)
|
||
|
||
- Created `../integration/` (sibling of `frontend/`, `backend/`):
|
||
- `docker-compose.yml` — 3 services: `db` (postgres:15, host:5433, schema `strava` via `db-init/01-schema.sql`), `backend` (python:3.12, `pip install -r requirements.txt` + `alembic upgrade head` + `python -m app.web.__main__`, port 8000, `DB_URI=postgresql+asyncpg://svcuser:svcpass@db:5432/svc`), `frontend` (node:20-alpine, `yarn install --frozen-lockfile --ignore-scripts` + `yarn dev --host 0.0.0.0 --port 5173`, env `VITE_HOST=http://localhost:8000`, `HUSKY=0`).
|
||
- `package.json` + `playwright.config.ts` (`@playwright/test` ^1.54, baseURL `FRONTEND_URL||localhost:5173`, `API_URL`).
|
||
- `tests/sidebar-auth-switch.spec.ts` — TASK-AUTH-NAV regression: publicRoutes (no «Тренировки») → UI login (`/auth/login`, `input[type=email]` + `input[type=password]` + button «Вход») → authRoutes («Тренировки» appears, no reload) → UI logout (profile dropdown → «Выход») → publicRoutes. Self-seeds a unique user via `POST /api/v0/signup` in `beforeAll`.
|
||
- `README.md` — run instructions (`yarn up`, wait for backend, `yarn test`).
|
||
- Frontend change: `src/main.ts` `HOST = import.meta.env.VITE_HOST || "https://cycle-rider.ru"` (production unaffected — env unset → falls back to prod host).
|
||
- Verified: full stack up (frontend 200, backend 200 on `/api/v0/html_test/login`, db healthy); `npx playwright test tests/sidebar-auth-switch.spec.ts` → **1 passed (9.9s)** on Node 22 (nvm `~/.nvm/versions/node/v22.14.0/bin`).
|
||
- Gotchas: (1) npm install in frontend container fails on `@unhead/vue` peer-dep (wants vite>=6, project vite 5) → must use **yarn** (`yarn.lock`), and `node:20-alpine` already ships yarn (do NOT `npm i -g yarn` → EEXIST); (2) backend health path is `/api/v0/html_test/login` (router_public prefix `/api/v0`, no `/auth`); (3) Vuestic `VaInput` uses floating labels → no usable placeholder, select login fields by `input[type=email]` / `input[type=password]`; (4) Playwright/MCP need Node ≥20 (system node is 18) → run via nvm Node 22.
|
||
|
||
## 2026-09-24 — TASK-AUTH-NAV-2: реактивные маршруты в сайдбаре и хлебных крошках
|
||
|
||
- `NavigationRoutes.ts`: named exports `authRoutes`/`publicRoutes`, удалён module-level `localStorage.getItem("token")` тернарник.
|
||
- `AppSidebar.vue`: computed `routes` от `globalStore.isAuthenticated`; `AppLayoutNavigation.vue`: `traverse` по `globalStore.isAuthenticated ? authRoutes : publicRoutes`.
|
||
- Verified: `yarn lint` exit 0, `yarn build` exit 0 (vue-tsc 0 ошибок).
|
||
|
||
## 2026-09-24 — TASK-AUTH-NAV-1: auth-состояние в Pinia (реактивный isAuthenticated)
|
||
|
||
- `src/stores/global-store.ts` — state: `isAuthenticated` (SSR-guard `typeof window !== "undefined"`), action `setAuthenticated(v: boolean)`.
|
||
- `src/pages/auth/Login.vue` — `import { useGlobalStore }`; в `.then` после `localStorage.setItem(...)` и до `push({ name: "explore" })`: `globalStore.setAuthenticated(true)`.
|
||
- `src/pages/auth/Logout.vue` — `import { useGlobalStore }`; после `localStorage.clear()` и до `push({ name: "login" })`: `globalStore.setAuthenticated(false)`.
|
||
- Verified: `yarn lint` exit 0, `yarn build` exit 0 (vue-tsc 0 errors, vite ✓ 7.17s).
|
||
|
||
## 2026-09-19 — TASK-404: HTTP 404 для несуществующих страниц (SEO)
|
||
|
||
- `server/index.ts` — catch-all 404 handler (`app.use` без path, перед `app.listen`): GET → `renderTemplate` (SEO-мета, canonical `/404`), non-GET → `res.status(404).send("Not Found")`.
|
||
- `nginx.conf` — SPA whitelist location `~ ^/(workouts|auth|preferences|404)(/|$)` → `try_files $uri /index.html`; catch-all `location /` → `proxy_pass http://127.0.0.1:3001` (SSR 404 для неизвестных URL).
|
||
- `src/router/index.ts` — catch-all redirect: `explore` → `404`.
|
||
- `src/router/seo.ts` — `SEO_MAP["404"]` добавлен.
|
||
- Verified: `yarn lint` exit 0, `yarn build` exit 0 (vue-tsc 0, vite ✓ 7.02s).
|
||
|
||
## 2026-09-19 — TASK-F11: SEO @unhead/vue dynamic title + meta description per route
|
||
|
||
- `package.json` — `@unhead/vue` ^3.4.1 added to dependencies (`npm install @unhead/vue --legacy-peer-deps`; vite transitively bumped 4.5.5→5.4.21).
|
||
- Created `src/router/seo.ts` — `setupSeo(router, head: Unhead)`: `SEO_MAP` keyed by route `name` (+ `""` for `/`), `DEFAULT_SEO` fallback, `head.push({ title, meta: [{ name: "description", content }] })` + `router.afterEach(() => update())`. Covers: `""`, `explore`, `routes`, `list_workouts`, `upload_workouts`, `workout_item`, `workout_public_item`, `preferences`, `login`, `signup`, `recover-password`.
|
||
- `src/main.ts` — `import { createHead } from "unhead/client"`, `import { headSymbol } from "@unhead/vue"`; `const head = createHead(); app.provide(headSymbol, head); setupSeo(router, head);` right after `app.use(router)`. (NOT `app.use(head)` — `ClientUnhead` is not a Vue plugin; `@unhead/vue` v3 installs via `app.provide(headSymbol, head)`.)
|
||
- `index.html` — `<title>` set to «Cycle Rider — платформа для анализа велотренировок: мощность, пульс, скорость, каденс»; added `<meta name="description">` in `<head>` as fallback; moved Yandex.Metrika counter block from `<head>` to top of `<body>` (Vite 5 parse5 rejects `<div>/<img>` inside `<noscript>` within `<head>` → `disallowed-content-in-noscript-in-head`).
|
||
- Verified: `npm run build` exit 0 (vue-tsc 0 errors, vite build ✓ 6.65s); `npm run lint` exit 0.
|
||
- Tech note: `@unhead/vue` v3 API — `createUnhead` is in `unhead` core (`createHead` from `unhead/client` adds DOM renderer); the `Unhead` type is exported from `unhead/types`. `@unhead/vue` itself only re-exports `useHead`/`useSeoMeta`/`injectHead`/`headSymbol` — no `createHead` from it.
|
||
|
||
## 2026-09-19 — TASK-PUBLIC-ROUTE-FIX: workout_public_item moved to top-level (no auth)
|
||
|
||
- `src/router/index.ts`: removed nested `workout_public_item` from `workouts` children; added top-level route `/public/workouts/:id` before catch-all, outside AppLayout.
|
||
- Result: `/public/workouts/:id` accessible without auth, aligns with SSR endpoint + nginx proxy.
|
||
- Verified: `yarn lint` exit 0, `yarn build` exit 0.
|
||
|
||
## 2026-09-18 — TASK-SSR-3: Sitemap, robots.txt, nginx, Dockerfile, guards
|
||
|
||
- Modified: `server/index.ts` (+49 lines: `GET /sitemap.xml` — XML with 3 static URLs + workout URLs from API, `Content-Type: application/xml`; `GET /robots.txt` — Disallow `/auth/ /workouts/ /preferences/ /admin/`, Sitemap line, `Content-Type: text/plain`).
|
||
- Modified: `nginx.conf` — full rewrite: SSR proxy locations for `/explore|/routes|/sitemap.xml|/robots.txt` and `/public/workouts/`; `location = /` with `hascookie token` (no cookie → proxy, cookie → static); `/assets/` with 30d immutable cache; SPA fallback `try_files $uri /index.html`.
|
||
- Modified: `Dockerfile` — multi-stage (node:20-alpine): build stage (yarn install + yarn build), runtime stage (nginx + node, copies dist/, server/, node_modules/, package.json; CMD `npx tsx server/index.ts & nginx -g 'daemon off;'`).
|
||
- Modified: `run.sh` — `cd /app && npx tsx server/index.ts & nginx -g 'daemon off;'`.
|
||
- Modified: `package.json` — `tsx` moved from devDependencies to dependencies.
|
||
- Modified: `src/stores/user-store.ts` — `typeof window === "undefined"` guard in `state()` (returns empty state on SSR).
|
||
- Modified: `src/main.ts` — `typeof window !== "undefined" &&` guard before `localStorage.getItem("token")` (line 110).
|
||
- Verified: `yarn lint` exit 0, `yarn build` exit 0 (vue-tsc 0 errors, vite 6.68s). Curl: `/robots.txt` → 200 + correct text; `/sitemap.xml` → 200 + valid XML (3 static + 2 workout URLs); `/health` → ok.
|
||
|
||
## 2026-09-18 — TASK-SSR-2: SSR route handlers
|
||
|
||
- Modified: `server/index.ts` — 4 route handlers: `GET /` (landing, 302→/explore if `token` cookie), `GET /explore` (public feed via `getPublicWorkouts()`), `GET /public/workouts/:id` (detail via `getPublicWorkout()`, 404 on API 404, 500 on other errors), `GET /routes` (static SEO). `app.disable("x-powered-by")`. Helpers: `getAssetTagsSafe()` (falls back to dev script tag if no `dist/index.html`), `escapeHtml`, `formatDate` (DD.MM.YYYY), `formatDuration` (X ч. Y мин.), `formatDistance` (m→km 1 decimal), `formatSpeed` (m/s→km/h rounded). Canonical base: `SSR_BASE_URL` || `https://cycle-rider.ru`.
|
||
- Verified: `yarn lint` exit 0, `yarn build` exit 0 (vue-tsc 0 errors). Curl: `/` → `<h1>Cycle Rider — анализ велотренировок</h1>`; `/routes` → `<h1>Конструктор маршрутов</h1>`; `/public/workouts/test` → 500 (API unreachable/404→500 acceptable locally); `/explore` → 200; `/` with `token` cookie → 302 → `/explore`; `/health` → ok.
|
||
|
||
## 2026-09-18 — TASK-SSR-1: SSR server skeleton
|
||
|
||
- Created: `server/index.ts`, `server/template.ts`, `server/api.ts`.
|
||
- Modified: `package.json` (+express, +@types/express, +tsx, +3 scripts).
|
||
- `tsconfig.json` unchanged (server/ already excluded via `include` glob).
|
||
- Verified: `yarn lint` exit 0, `yarn build` exit 0, `npx tsx server/index.ts` + curl /health OK.
|
||
|
||
## What works (verified at Memory Bank init)
|
||
|
||
- `yarn dev` / `yarn build` / `yarn lint` scripts defined; build runs `vue-tsc --noEmit` type-check.
|
||
- App boots: axios providers, Pinia, router, i18n, Vuestic, Yandex Maps, optional GTM (`src/main.ts`).
|
||
- Auth flow: JWT Bearer interceptor, 401 -> logout redirect, startup `/api/v0/auth/check`.
|
||
- Pages: workout feed (dashboard), workout list/detail/public detail, upload, routes, preferences, auth pages, 404.
|
||
- Charts (Chart.js) and Yandex Maps route rendering for workout data.
|
||
|
||
## Known issues / tech debt (observed, not yet fixed)
|
||
|
||
- `HOST`, Yandex Maps API key and GTM keys hardcoded in `src/main.ts` (no env-driven base URL).
|
||
- `GetWorkout.ts` uses mutable module-level `let` variables shared across calls (state leakage risk) — refactor to pure functions returning state if touched.
|
||
- `user-store.ts` embeds a huge base64 avatar blob in source.
|
||
- No test suite; no global router auth guard (relies on 401 interceptor).
|
||
- Mixed callback (`.then/.catch`) and imperative styles in pages.
|
||
- ~~**BLOCKER (2026-09-04, TASK-DEPS-UPDATE-W1)**~~ — **RESOLVED by W0a (build) + W0b (lint)**: both `yarn build` and `yarn lint` are green; W1/W2/W3 unblocked.
|
||
- `vue/no-mutating-props` in `workouts/components/WorkoutItem.vue` (checkbox/name edits mutate the prop object in place) — suppressed file-targeted in `eslint.config.mjs`; proper fix = local state extraction, needs its own task.
|
||
- `src/main.ts` axios interceptors loosely typed (`any`) — suppressed file-targeted; pre-existing documented debt.
|
||
- ~~**`typescript` capped at 5.4.5** (TASK-DEPS-UPDATE-W1)~~ — **RESOLVED by W2b**: `typescript` now 5.8.3 (`"5.8"` pinned no-caret in package.json), `vue-tsc` 2.2.12 (`^2`). vue-tsc 2.x officially supports TS up to 5.8 — do NOT go to TS 5.9.
|
||
- **`sass` left at 1.69.5** (TASK-DEPS-UPDATE-W1): latest 1.x (1.104.0) requires node ≥20.19.0. Docker is now node:22, so a sass bump is viable — verify the local dev node version first; small follow-up task.
|
||
- **Stale `package-lock.json`** in repo (npm workflow abandoned for yarn in W2c-FIX) — remove as a small cleanup task.
|
||
- **yarn v1 nested-stale dirs**: after major bumps, `node_modules` can keep leftover nested packages (observed: `@typescript-eslint/*@7` under `typescript-eslint/`) causing transient CJS resolution glitches — fix with a clean `rm -rf node_modules && yarn install`; not reproducible from the lockfile.
|
||
- Installed yarn 1.22.22 binary lacks the `up` alias — use `yarn upgrade` (verified equivalent).
|
||
|
||
## Milestones
|
||
|
||
### 2026-09-19 — SSR PHASE COMPLETE (TASK-SSR-0 → SSR-3 + PUBLIC-ROUTE-FIX)
|
||
|
||
- **SSR-0**: Renamed `/dashboard` → `/explore` across router, nav, auth pages, layout guard. Legacy redirect `/dashboard` → `explore`.
|
||
- **SSR-1**: Created `server/` (Express on :3001): `index.ts` (app + /health), `template.ts` (SEO HTML shell with JSON-LD, OG, canonical), `api.ts` (getPublicWorkouts/getPublicWorkout).
|
||
- **SSR-2**: 4 SSR route handlers: `GET /` (landing/302), `GET /explore`, `GET /public/workouts/:id`, `GET /routes`. Helpers for formatting.
|
||
- **SSR-3**: `GET /sitemap.xml`, `GET /robots.txt`. Nginx rewrite (SSR proxy + SPA fallback). Dockerfile multi-stage (node:20-alpine + nginx). tsx → dependencies. SSR guards in user-store/main.ts.
|
||
- **PUBLIC-ROUTE-FIX**: `workout_public_item` moved to top-level router (outside AppLayout) — accessible without auth, aligns with SSR + nginx.
|
||
- **Final state**: SSR renders `/`, `/explore`, `/routes`, `/public/workouts/:id`, `/sitemap.xml`, `/robots.txt` with full SEO meta + JSON-LD. Nginx proxies these to :3001; all else → SPA. Docker: multi-stage, `npx tsx server/index.ts & nginx`.
|
||
- Verified: lint 0, build 0, docker build 0, curl all SSR endpoints 200.
|
||
|
||
### 2026-09-05 — TASK-DEPS-UPDATE-W0a: baseline `yarn build` green (uncommitted)
|
||
|
||
- Replaced `// @ts-ignore` with non-null assertions (`data!.labels!`) in `LineWithLineChart.ts` `draw()` — eliminates the latent TS2532 and the `ban-ts-comment` lint error for that file.
|
||
- Verified: `vue-tsc --noEmit` 0 errors, `vite build` OK (1309 modules).
|
||
- The two SFC "parsing errors" were confirmed eslint-only (vite/vue-tsc parse both files fine) — deferred to W0b along with remaining lint errors.
|
||
- Note: on the clean tree the build was already green (TS2532 was suppressed by the `@ts-ignore`); the fix is still required because stricter TS/vue-tsc in W2 would re-surface it without the suppression.
|
||
|
||
### 2026-09-05 — TASK-DEPS-UPDATE-W0b: `yarn lint` green (uncommitted)
|
||
|
||
- Rewrote `eslint.config.mjs` (flat config):
|
||
- TS parser wired into `.vue` files (`languageOptions.parserOptions.parser = tsParser` from `typescript-eslint` CJS default import) — this was the root cause of all ~20 SFC parse errors (vue flat preset leaves espree as inner parser).
|
||
- `@typescript-eslint/no-unused-vars` global `argsIgnorePattern: "^_"`.
|
||
- File-targeted overrides only (no inline eslint-disable anywhere): `src/main.ts` (any + unused, documented exception), `src/pages/**/*.vue` (multi-word names), `Logout.vue` (valid-template-root), `components/WorkoutItem.vue` (no-mutating-props).
|
||
- Code fixes (21 files, type-only / dead-code / unused-cleanup): unused import in `router/index.ts`; `any`→`unknown`/`Event`/structural types across `services/utils.ts`, auth pages, workouts pages, `AppLayoutNavigation.vue`; `@ts-ignore`→`@ts-expect-error` in `LineWithLineChart.ts`; removed dead code (`VuesticLogo` computed, `PreferencesHeader.readFile`, `Login.HOST`, `AppNavbarActions.t`, `Logout.push`); removed redundant `v-if` on `v-for` in `Feed.vue`/`WorkoutList.vue`; null-safe error handling in `CheckTheEmail.vue` (fixes TS18048/TS18046 surfaced by the new strict typing); `AppSidebar` name → `AppSidebar`; added `:key` to `v-for` marker in `components/WorkoutItem.vue`.
|
||
- Prettier auto-reformatted ~30 `src/` files via `prelint` — formatting changes are part of the intended clean diff.
|
||
- Verified: `yarn lint` 0 errors (exit 0), `yarn build` green (`vue-tsc --noEmit` 0 errors, vite build OK).
|
||
|
||
### 2026-09-05 — TASK-DEPS-UPDATE-W1: patch/minor dependency refresh via `yarn upgrade` (uncommitted)
|
||
|
||
- Ran `yarn upgrade <all direct deps except sass>` (yarn 1.22.22 binary has no `up` alias). Baseline was green before the run (lint 0 errors, build green).
|
||
- Result: `package.json` unchanged; `yarn.lock` refreshed — 61 direct deps verified, none moved to a new major. Notable bumps: typescript 5.2.2→5.4.5 (capped), postcss 8.4.31→8.5.28, axios 1.7.7→1.20.0, chart.js 4.4.4→4.5.1, eslint 8.57.0→8.57.1, typescript-eslint 7.6.0→7.18.0, @typescript-eslint/\* 6.11.0→6.21.0, prettier 3.1.0→3.9.6, tailwindcss 3.4.1→3.4.19, vite 4.5.5→4.5.14, vue-i18n 9.6.5→9.14.5, vue-router 4.2.5→4.6.4, vue-yandex-maps 2.1.4→2.3.3, vuestic-ui 1.9.0→1.10.3, storybook suite →7.6.24, pinia 2.1.7→2.3.1.
|
||
- Two caps applied (rule 5): `typescript` pinned to 5.4.5 in the lock (vue-tsc 1.8.27 incompatible with TS ≥5.5; `yarn add -D typescript@5.4.5` then `git checkout -- package.json` + 1-line lock key rename `typescript@5.4.5`→`typescript@^5.2.2`); `sass` excluded from the upgrade (engine node ≥20.19.0 vs baseline 18.19.1).
|
||
- Verified after upgrade: `yarn install --frozen-lockfile` clean; `yarn lint` 0 errors; `yarn build` green (vue-tsc 0 errors, vite build OK ~7.3s).
|
||
- No `src/` changes in W1; no HOST/Yandex/GTM changes.
|
||
|
||
### 2026-09-05 — TASK-DEPS-UPDATE-W2a: vue 3.3.9 → 3.5.42 + pinia 2 → 3.0.4 (uncommitted)
|
||
|
||
- `yarn add vue@^3.5 pinia@^3`: `package.json` changed exactly two lines (`vue: 3.3.9 → ^3.5`, `pinia: ^2.1.7 → ^3`); lockfile resolved `vue@3.5.42`, `pinia@3.0.4`. No other direct dep moved to a new major (new lock entries are only the vue 3.5 / pinia 3 subtrees).
|
||
- Vue 3.5 template type-check surfaced 6× TS2322 (`number[]` → ymaps `LngLat` tuple) in the `vue-yandex-maps` `:settings` props of `src/pages/routes/Route.vue` and `src/pages/workouts/components/WorkoutItem.vue`. Fixed type-only: `ref<LngLat>` in Route.vue; `import type { LngLat }` + 4 template `as LngLat`/`as LngLat[]` casts + `clickCoordinates` typed `ref<LngLat[]>` in components/WorkoutItem.vue. No runtime/prop-signature changes.
|
||
- Verified: `yarn lint` 0 errors; `yarn build` green (vue-tsc 0 errors, vite build ~7.5s); `yarn dev` smoke-check OK (boot + key modules serve 200, no console/compile errors).
|
||
|
||
### 2026-09-05 — TASK-DEPS-UPDATE-W2b: typescript 5.4.5 → 5.8.3 + vue-tsc 1.8.27 → 2.2.12 (uncommitted)
|
||
|
||
- `yarn add -D typescript@5.8 vue-tsc@^2`: `package.json` changed exactly the two lines (`typescript: ^5.2.2 → "5.8"`, `vue-tsc: ^1.8.22 → ^2`); lock resolved `typescript@5.8.3`, `vue-tsc@2.2.12`. No other direct dep moved to a new major (new lock entries are only the vue-tsc 2.x subtree: `@vue/language-core@2.2.12`, `@volar/*@2.4.15`, `muggle-string`, `alien-signals`, `vscode-uri`, `@vue/compiler-vue2`).
|
||
- vue-tsc 2 + TS 5.8 surfaced 4 type errors in 2 file-upload pages (TS 5.8 `Blob` gained required `bytes` property + definite-assignment TS2454 on the uninitialized `let file`). Fixed type-only: legacy 14-line inline structural `file` type → `let file: File | undefined = undefined` in both files; `formData.append("file", file!)` non-null assertion in `PreferencesHeader.vue` (`WorkoutUpload.vue` already guards `if (file == undefined) return`). No runtime/prop-signature changes.
|
||
- Verified: baseline green before update; after update `yarn lint` 0 errors, `yarn build` green (vue-tsc 2.2.12 --noEmit 0 errors, vite build ~7.3s).
|
||
|
||
### 2026-09-05 — TASK-DEPS-UPDATE-W2c: vite 4.5.14 → 5.4.21 + @vitejs/plugin-vue 4.6.2 → 5.2.4 (uncommitted)
|
||
|
||
- `yarn add -D vite@^5 @vitejs/plugin-vue@^5`: `package.json` changed exactly the two lines (`vite: ^4.4.6 → ^5`, `@vitejs/plugin-vue: ^4.2.3 → ^5`); lock resolved `vite@5.4.21`, `@vitejs/plugin-vue@5.2.4`. No other direct dep moved to a new major — new lock entries are only the vite 5 subtree (`rollup@4.63.1` + `@rollup/*` platform binaries, `esbuild@0.21.5` + `@esbuild/*` platform binaries, `@napi-rs/lzma-linux-x64-gnu`); storybook pin `@vitejs/plugin-vue@^4.0.0` still resolves 4.6.2 (separate lock entry); `esbuild@0.18.20`/`rollup@3.30.0` retained for storybook.
|
||
- No `src/` or `vite.config.ts` changes — config API-compatible between vite 4 and 5.
|
||
- Warnings: CJS Node API deprecation (vite 5, expected, documented, not fixed); NO sass legacy-API warnings appeared (sass 1.69.5 untouched, per task rule).
|
||
- Verified: baseline green before update; after update `yarn lint` 0 errors, `yarn build` green (vue-tsc 0 errors, vite 5.4.21 build ~6.5s, 1396 modules); `yarn dev` smoke-check OK (VITE v5.4.21 ready 372 ms; `GET /`, `/src/main.ts`, `/src/App.vue`, `/src/pages/workouts/Feed.vue` all 200; server stopped after check).
|
||
|
||
### 2026-09-05 — TASK-DEPS-UPDATE-W2c-FIX: Dockerfile switched to yarn (uncommitted)
|
||
|
||
- `Dockerfile` only (2 lines): `COPY package.json package-lock.json ./` → `COPY package.json yarn.lock ./`; `RUN npm install` → `RUN yarn install --frozen-lockfile`. Root cause fixed: the Docker build ran `npm install` against a stale `package-lock.json` while dev uses yarn; npm's strict peer validation failed on `@storybook/vue3-vite@7.6.20` → `@vitejs/plugin-vue@^4` vs root `^5`. yarn 1 resolves this via a nested plugin-vue 4.6.2 for storybook (existing separate lock entry).
|
||
- `node:18` base image ships yarn 1.22.22 out of the box (verified via `docker run --rm node:18 yarn --version`) — no `npm i -g yarn@1` line added.
|
||
- `package-lock.json` intentionally left in the repo (removal = separate decision, out of scope).
|
||
- Verified: `yarn install --frozen-lockfile` clean locally (lockfiles untouched per `git status`); `yarn lint` 0 errors; `yarn build` green (vue-tsc 0 errors, vite 5.4.21 ~6.4s); **full `docker build` green** (image `strava-frontend-w2cfix` built: yarn install + npm run build + nginx steps all OK).
|
||
|
||
### 2026-09-05 — TASK-DEPS-UPDATE-W2d: dead Storybook deps removed (uncommitted)
|
||
|
||
- `yarn remove` of 9 packages: `storybook`, `@storybook/addon-essentials`, `@storybook/addon-interactions`, `@storybook/addon-links`, `@storybook/blocks`, `@storybook/testing-library`, `@storybook/vue3`, `@storybook/vue3-vite`, `eslint-plugin-storybook` + manual removal of the 2 scripts (`storybook`, `build-storybook`) from `package.json`.
|
||
- `eslint.config.mjs` verified: zero storybook references — untouched.
|
||
- `yarn.lock` pruned: `grep -c storybook` → 0; nested `@vitejs/plugin-vue@4.6.2` (storybook peer-conflict workaround) gone — only `@vitejs/plugin-vue@^5` → 5.2.4 remains.
|
||
- Baseline green before removal; after: `yarn install` clean, `yarn lint` 0 errors, `yarn build` green (vue-tsc 0 errors, vite 5.4.21 ~6.3s, 1396 modules).
|
||
- No `src/` changes; `package-lock.json` left stale (separate decision).
|
||
|
||
### 2026-09-05 — TASK-DEPS-UPDATE-W3 (FINAL): eslint 8→9 + typescript-eslint 7→8 (uncommitted)
|
||
|
||
- `package.json` devDeps: `eslint` ^8.57.0→**^9** (resolved 9.39.5), `typescript-eslint` ^7.6.0→**^8** (resolved 8.69.0); REMOVED `@typescript-eslint/eslint-plugin@^6.11.0` + `@typescript-eslint/parser@^6.11.0` (not imported by `eslint.config.mjs` — verified). No other package moved to a new major (lock checked: vue 3.5.42, pinia 3, TS 5.8.3, vue-tsc 2.2.12, vite 5.4.21, sass 1.69.5).
|
||
- `eslint.config.mjs`: **unchanged** — flat config (CJS default-import of the `typescript-eslint` meta-package, `configs.recommended` spread, `tseslintPkg.parser`) works as-is on eslint 9 / typescript-eslint 8.
|
||
- `Dockerfile`: base image `node:18` → `node:22` (transitive `brace-expansion@5.0.9` / `eslint-visitor-keys@5` engines require node ≥20; node 18 EOL). Local dev verified on node 22.14.0.
|
||
- Verified: baseline green at wave start; after — `yarn lint` 0 errors, `yarn build` green (vue-tsc 0 errors, vite 5.4.21 ~5.3s), `docker build --pull --no-cache` green (image `strava-frontend-w3`, `yarn install --frozen-lockfile` clean inside image).
|
||
- No `src/` changes; `vite.config.ts`, i18n, HOST/keys untouched. W3 = FINAL wave → TASK-DEPS-UPDATE ready to close.
|
||
|
||
### 2026-09-05 — TASK-DEPS-UPDATE CLOSED (parent)
|
||
|
||
- All waves W0a → W3 completed and Architect-verified. Final stack: vue 3.5.42, pinia 3.0.4, TS 5.8.3, vue-tsc 2.2.12, vite 5.4.21, @vitejs/plugin-vue 5.2.4, eslint 9.39.5, typescript-eslint 8.69.0, axios 1.20.0, tailwind 3.4.19, vuestic 1.10.3, node:22 (Docker). Storybook fully removed.
|
||
- End state: `yarn lint` 0 errors, `yarn build` green, `docker build` green. Runtime behavior unchanged (type-level + config + lockfile only).
|
||
- Lessons captured in `systemPatterns.md`: TS parser for `.vue` in flat eslint config; `LngLat` typing contract for vue-yandex-maps; vue-tsc 2 ↔ TS ≤5.8 ceiling; yarn-only workflow; `ban-ts-comment` enforcement.
|
||
|
||
## Left to build (small follow-ups, no urgency)
|
||
|
||
- Remove stale `package-lock.json` from the repo (npm workflow abandoned in W2c-FIX).
|
||
- Bump `sass` to latest 1.x — Docker (node:22) is ready; check local dev node ≥20.19 first.
|
||
- Consider vite 6/7 after the ecosystem settles (own breaking changes).
|
||
- `GetWorkout.ts` module-level `let` refactor to pure functions returning state.
|
||
- `components/WorkoutItem.vue` prop mutation → local state extraction (unblock the `no-mutating-props` suppression).
|
||
|
||
### 2026-09-12 — TASK-F1: workout photos gallery + upload/delete + map markers (uncommitted)
|
||
|
||
- `src/pages/workouts/Definitions.vue`: `WorkoutPhoto` type (`{id, url, size, latitude: number|null, longitude: number|null}`) + `photos?: Array<WorkoutPhoto>` in `WorkoutItem`. No fetch changes (backend already sends `workout.photos` via `GET /workouts/{id}` / public / list).
|
||
- `src/pages/workouts/components/WorkoutItem.vue`: local `photos` ref initialized from `workoutItem?.photos`; `photosWithCoords` computed (non-null lat/lon); photo markers as `<yandex-map-default-marker>` with `coordinates: [lon, lat] as LngLat` (same order as `clickCoordinates`), `color: 'blue'`, `onClick` → `window.open(photo.url, "_blank")`.
|
||
- Same file: `#workout-photos` section under the map/short-data container — grid of `<img :src="photo.url">` (auto-fill 160px, 120px cover), «Добавить фото» (`v-if="isPrivate"`, hidden `input[type=file] accept="image/*" multiple`, `axiosAuth.post` multipart field `file`, `Promise.allSettled`, success → push to `photos` + toast, partial failure → error toast, VaButton `:loading`), per-photo «Удалить» (`v-if="isPrivate"`, `axiosAuth.delete` → filter from `photos`, error toast). `isPrivate` added to `defineProps` destructure. Styles: `.workout-photos-*` block.
|
||
- `WorkoutListItem.vue` untouched (per scope).
|
||
- Verified: `npm run build` exit 0 (vue-tsc --noEmit 0 errors, vite `✓ built in 11.71s`; pre-existing >500kB chunk warning), `npm run lint` exit 0 (prettier reformatted the component, eslint 0 errors).
|
||
|
||
### 2026-09-17 — TASK-LINK1: «Ссылки на описание» скрыта на публичной странице без ссылки (uncommitted)
|
||
|
||
- `src/pages/workouts/components/WorkoutItem.vue` (единственный файл, 1 строка): на внешний `div.workout-item-params` секции «Ссылки на описание» добавлен `v-if="dzenLink || isPrivate"`. Поведение: публичная без ссылки → секция скрыта; публичная со ссылкой → только «Дзен»; приватная без ссылки → «Добавить» (модалка доступна); приватная со ссылкой → «Дзен» + иконка edit. Внутренняя логика секции не тронута.
|
||
- Verified: `yarn lint` exit 0, `yarn build` exit 0 (vue-tsc --noEmit 0 ошибок, vite `✓ built in 5.41s`; pre-existing >500kB chunk warning).
|
||
|
||
### 2026-09-18 — TASK-SSR-0: rename /dashboard → /explore + legacy redirect (uncommitted)
|
||
|
||
- `src/router/index.ts`: маршрут `name: "explore"`, `path: "explore"` (Feed.vue); catch-all и admin redirect → `{ name: "explore" }`; новый legacy redirect `{ path: "/dashboard", redirect: { name: "explore" } }` (до catch-all).
|
||
- `src/components/sidebar/NavigationRoutes.ts`: `name: "dashboard"` → `"explore"` в `authRoutes` и `publicRoutes` (i18n-ключ `menu.dashboard` сохранён).
|
||
- `src/components/app-layout-navigation/AppLayoutNavigation.vue`: breadcrumb `:to="{ name: 'explore' }"`.
|
||
- `src/layouts/AppLayout.vue`: auth-guard `useRoute().path != "/explore"`.
|
||
- `src/pages/auth/Login.vue` (2×), `src/pages/auth/Signup.vue` (2×), `src/pages/auth/CheckTheEmail.vue` (1×): `push({ name: "explore" })`.
|
||
- `src/pages/workouts/Feed.vue`: изменений не потребовалось (ссылок на dashboard нет).
|
||
- Verified: `yarn lint` exit 0, `yarn build` exit 0 (vue-tsc --noEmit 0 ошибок, vite `✓ built in 6.99s`; pre-existing >500kB chunk warning).
|
||
|
||
## TASK-HIDE-TRACK-T1 (2026-09-25)
|
||
|
||
- `../backend/tests/test_api_workout.py`: добавлен `test_workout_hide_start_end` (PATCH hide_start_end False→True→False + GET-верификация)
|
||
- Результат: `python -m pytest tests/test_api_workout.py -v` → **4 passed (5.13s)**
|
||
|