3.9 KiB
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(addsAuthorization: Bearer <token>fromlocalStorageon every request) andaxiosPublic. Both have a shared response error interceptor: 401 ->localStorage.clear()+ redirect tologinroute. - Instances are provided app-wide:
app.provide('axiosAuth', ...)/app.provide('axiosPublic', ...). Components consume withinject('axiosAuth') as AxiosInstance. - Base URL is a hardcoded
HOSTconstant insrc/main.ts(https://cycle-rider.ru; localhost variant commented out). All backend paths are under/api/v0. - Auth check on startup: if
localStorage.tokenexists, GET/api/v0/auth/check; on failure clear storage.
Data fetching pattern
- No central API layer: components/composable
.tsfiles (e.g.src/pages/workouts/components/GetWorkout.ts) callaxiosAuth.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.
useUserStorehydrates fromlocalStoragekeysuser,profile,attachmentson store init (no persistence plugin).useGlobalStoreholds 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;AuthLayoutfor auth pages. Catch-all redirects todashboard(=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-geofor elevation). Chart building lives insrc/pages/workouts/components/*.ts. - Yandex Maps via
vue-yandex-maps(createYmapswith a hardcoded API key inmain.ts). Route polyline drawn from workoutresultscoordinates.
Styling
- Vuestic UI (config in
src/services/vuestic-ui/global-config.ts, themes inthemes.ts, custom icons registered inicons-config/). - Tailwind CSS with Vuestic CSS variables as color tokens (
--va-primaryetc. intailwind.config.js). Custom font-size tokens:tag,regularSmall/Medium/Large. - Global SCSS in
src/scss/main.scss; icon fonts insrc/scss/icon-fonts/.
i18n
vue-i18nin composition mode (legacy: false), locale and fallback =ru. Locales auto-loaded fromsrc/i18n/locales/*.jsonviaimport.meta.glob+@intlify/unplugin-vue-i18n/viteplugin.
Error handling
- Central axios interceptor logs and re-throws; 401 triggers logout redirect. Components typically use
.then/.catchchains (callback style, not async/await) — keep the existing style when modifying.
Build / deploy
- Vite build with
vue-tsc --noEmittype-check inyarn build. Docker image +nginx.conffor static serving;serve -s ./distfor CI preview.