strava-frontend/.roo/rules/common.md

9.7 KiB

Strava Frontend Developer Instructions

You must strictly follow these architectural, coding, and formatting rules for this project (Vue 3 SPA "cycle-rider"). All generated code and modifications must fully comply with the guidelines below.

🧠 Agent Behavior

  • Reasoning Language: All thoughts, analysis, and reasoning MUST be written in English to minimize token usage.
  • Communication: Communicate with the user in Russian unless asked otherwise.
  • Task Progress: Always track implementation progress with a todo list.

🏗 Project Structure (src/)

The SPA is organized as follows — keep the dependency direction pages -> components / stores / services:

  • src/main.ts — app bootstrap: axios instances, Pinia, router, i18n, Vuestic UI, Yandex Maps, optional GTM. Do NOT relocate providers; components rely on app.provide keys.
  • src/router/index.ts — single router config. All pages MUST be lazy-loaded via () => import(...). Routes are nested under AppLayout (authenticated shell) or AuthLayout (auth pages); catch-all redirects to dashboard.
  • src/pages/ — route-level components and page-private logic. One folder per domain area (workouts/, routes/, auth/, preferences/, admin/). Page-private helper modules live in a components/ subfolder of the page (e.g., pages/workouts/components/GetWorkout.ts).
  • src/components/ — reusable presentational components (navbar/, sidebar/, icons/, app-layout-navigation/). No direct API calls here; receive data via props.
  • src/stores/ — Pinia stores (options style via defineStore). useUserStore (user/profile data hydrated from localStorage), useGlobalStore (sidebar/UI state).
  • src/i18n/ — vue-i18n setup; locale files auto-discovered from src/i18n/locales/*.json (locale name = file name).
  • src/services/ — cross-cutting helpers: utils.ts (validators, sleep), vuestic-ui/ (global config, themes, icon registry).
  • src/scss/ — global styles; do not duplicate Vuestic theme tokens — use --va-* CSS variables.

🌐 API Access Pattern

  • Two Axios instances are created in src/main.ts and injected app-wide:
    • axiosAuth — request interceptor attaches Authorization: Bearer <localStorage.token>;
    • axiosPublic — no auth header.
  • Consume them with inject('axiosAuth') as AxiosInstance / inject('axiosPublic') as AxiosInstance. Do NOT create new axios instances in components.
  • All backend endpoints are under /api/v0 (base URL = HOST constant in main.ts).
  • A shared response interceptor handles 401 (clears localStorage, redirects to login). Do not add per-component 401 handling.
  • Auth data lives in localStorage keys: token, user, profile, attachments. Keep key names stable — useUserStore and main.ts depend on them.

📊 Charts & Maps

  • Charts: Chart.js 4 via vue-chartjs; adapters/plugins already installed (chartjs-adapter-moment, chartjs-plugin-zoom, chartjs-chart-geo for elevation). Chart-data building logic belongs in page-level .ts helper modules (e.g., pages/workouts/components/*), not in .vue templates.
  • Maps: Yandex Maps via vue-yandex-maps (registered in main.ts). Route polylines are built from workout results coordinates [longitude, latitude].
  • Workout detail API response shape: { workout, results: [{ timestamp, longitude, latitude, elevation, power, heart_rate, speed }, ...] }. Null-valued metrics (power/heart_rate/elevation) must be detected and their charts hidden, matching the existing pattern in GetWorkout.ts.

💾 State Management (Pinia)

  • Use options-style stores (defineStore("name", { state, actions })) consistent with existing stores.
  • Auth/profile state is hydrated from localStorage inside state() — there is no pinia-persist plugin. Do not add persistence of sensitive data beyond the existing keys.
  • UI/visual state (sidebar, theme) belongs in useGlobalStore.

🎨 Styling

  • Vuestic UI is the component library. Customize through src/services/vuestic-ui/global-config.ts and themes.ts, never by forking component internals.
  • Tailwind CSS utilities are available; color tokens MUST reference Vuestic CSS variables (var(--va-primary) etc.) as mapped in tailwind.config.js (primary, textPrimary, backgroundCardPrimary, ...). Do not hardcode hex colors that bypass the theme.
  • Font-size tokens: text-tag, text-regularSmall, text-regularMedium, text-regularLarge (defined in tailwind.config.js).
  • Global styles live in src/scss/main.scss; page-specific overrides go in the page's <style scoped>.
  • Custom Vuestic icons are registered in src/services/vuestic-ui/icons-config/.

🌍 i18n

  • vue-i18n composition mode (legacy: false); default and fallback locale is ru.
  • New translations: add the key to ALL locale files under src/i18n/locales/ (br, cn, es, gb, ir, ru) — missing keys in any locale break the UI for that locale.
  • In components use the useI18n() composable (t()).
  • User-facing strings must go through i18n; exception: validators in src/services/utils.ts currently hardcode Russian messages — keep the existing style when extending that file.

📘 Coding Conventions (TypeScript / Vue)

  • TypeScript strict: tsconfig.json has strict: true and the build runs vue-tsc --noEmit — type errors break the build. Mandatory type hints for all functions and props.
  • Modern types: use built-in types (Array, Record, | null / | undefined unions). Avoid any; when unavoidable (e.g., axios error handling), keep it local and typed via type guards (axios.isAxiosError).
  • Composition API: prefer <script setup lang="ts"> for new components; do not force-convert existing Options API components unless the task requires it.
  • Naming: PascalCase for components (WorkoutItem.vue), kebab-case for component usage in templates, camelCase for variables/functions, UPPER_SNAKE_CASE for constants.
  • Async style: existing pages use .then/.catch chains — match the surrounding style in a file rather than mixing in async/await.
  • No forbidden debugging: console.log / debugger statements are prohibited in committed code (the shared axios interceptor in main.ts is a known exception — do not add more).
  • No mutable module-level state in helper .ts modules (the current GetWorkout.ts pattern of module-level let is tech debt — do NOT copy it; return state from functions instead).
  • Absolute imports: use ../-relative or @/ paths consistently; the project uses relative imports from src/ — keep it relative within src.

🧩 Component Guidelines

  • Pages own data fetching and orchestration; presentational components receive props and emit events.
  • Reusable logic (fetch + transform) belongs in a page-level .ts module exporting pure functions (see GetWorkout.ts, LineWithLineChart.ts as the pattern to follow — but without the module-level let mutation).
  • Route meta and navigation: use named routes (router.push({ name: "login" })), never string paths for internal navigation.

📋 Page/Route Catalog (current)

Route name Path Page Notes
dashboard /dashboard pages/workouts/Feed.vue public workout feed, default landing
routes /routes pages/routes/Route.vue routes listing
list_workouts /workouts pages/workouts/WorkoutList.vue user's workouts
upload_workouts /workouts/upload pages/workouts/WorkoutUpload.vue FIT/GPX upload
workout_item /workouts/:id pages/workouts/WorkoutItem.vue private detail, charts + map
workout_public_item /public/workouts/:id pages/workouts/WorkoutPublicItem.vue public detail
preferences /preferences pages/preferences/Preferences.vue profile settings, modals
login / signup / logout / recover-password / recover-password-email /auth/* pages/auth/* AuthLayout pages
404 /404 pages/404.vue error page

⚠️ Environment & Secrets

  • The API HOST, Yandex Maps API key, and GTM config live in src/main.ts / import.meta.env (VITE_APP_GTM_ENABLED, VITE_APP_GTM_KEY). Do not hardcode new secrets; use import.meta.env.VITE_* for anything environment-specific.
  • Do not swap the production HOST unless the task explicitly requires local development.

🚀 Final Verification

When working in agent mode, always run yarn lint and yarn build as the final step before completing the task (build includes vue-tsc --noEmit type-check). There is NO test runner in this repo — do not invent test commands.