# 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 ` 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.