strava-frontend/.roo/memory-bank/systemPatterns.md

3.9 KiB

System Patterns — Strava Frontend

Architecture (SPA layering)

src/
  main.ts            - app bootstrap, axios instances, providers
  App.vue            - root component
  router/            - vue-router config (lazy pages)
  layouts/           - AppLayout (authenticated shell), AuthLayout, RouterBypass
  pages/             - route components (workouts/, routes/, auth/, preferences/, admin/)
  components/        - reusable UI (navbar/, sidebar/, icons/, app-layout-navigation/)
  stores/            - Pinia stores (user-store, global-store)
  i18n/              - vue-i18n setup + locales/*.json
  services/          - axios helpers, vuestic-ui config (global-config, themes)
  scss/              - global styles, vuestic-sass, icon fonts

Dependency direction: pages -> components/stores/services. Pages own business logic; components are presentational.

API access pattern

  • Two Axios instances created in src/main.ts: axiosAuth (adds Authorization: Bearer <token> from localStorage on every request) and axiosPublic. Both have a shared response error interceptor: 401 -> localStorage.clear() + redirect to login route.
  • Instances are provided app-wide: app.provide('axiosAuth', ...) / app.provide('axiosPublic', ...). Components consume with inject('axiosAuth') as AxiosInstance.
  • Base URL is a hardcoded HOST constant in src/main.ts (https://cycle-rider.ru; localhost variant commented out). All backend paths are under /api/v0.
  • Auth check on startup: if localStorage.token exists, GET /api/v0/auth/check; on failure clear storage.

Data fetching pattern

  • No central API layer: components/composable .ts files (e.g. src/pages/workouts/components/GetWorkout.ts) call axiosAuth.get(url) directly and map the response into view state.
  • Workout detail data shape: { workout, results: [{ timestamp, longitude, latitude, elevation, power, heart_rate, speed }, ...] } — mapped into Chart.js datasets + map line coordinates.

State management

  • Pinia with options-style stores. useUserStore hydrates from localStorage keys user, profile, attachments on store init (no persistence plugin). useGlobalStore holds sidebar state.
  • Auth data lives in localStorage (keys: token, user, profile, attachments).

Routing

  • History-mode router (createWebHistory), all pages lazy-imported.
  • Two layouts: AppLayout (sidebar + navbar shell) for authenticated pages; AuthLayout for auth pages. Catch-all redirects to dashboard (= pages/workouts/Feed.vue).
  • No global auth guard — unauthenticated handling is done via the 401 interceptor.

Charts & maps

  • Chart.js via vue-chartjs (+ chartjs-adapter-moment, chartjs-plugin-zoom, chartjs-chart-geo for elevation). Chart building lives in src/pages/workouts/components/*.ts.
  • Yandex Maps via vue-yandex-maps (createYmaps with a hardcoded API key in main.ts). Route polyline drawn from workout results coordinates.

Styling

  • Vuestic UI (config in src/services/vuestic-ui/global-config.ts, themes in themes.ts, custom icons registered in icons-config/).
  • Tailwind CSS with Vuestic CSS variables as color tokens (--va-primary etc. in tailwind.config.js). Custom font-size tokens: tag, regularSmall/Medium/Large.
  • Global SCSS in src/scss/main.scss; icon fonts in src/scss/icon-fonts/.

i18n

  • vue-i18n in composition mode (legacy: false), locale and fallback = ru. Locales auto-loaded from src/i18n/locales/*.json via import.meta.glob + @intlify/unplugin-vue-i18n/vite plugin.

Error handling

  • Central axios interceptor logs and re-throws; 401 triggers logout redirect. Components typically use .then/.catch chains (callback style, not async/await) — keep the existing style when modifying.

Build / deploy

  • Vite build with vue-tsc --noEmit type-check in yarn build. Docker image + nginx.conf for static serving; serve -s ./dist for CI preview.