- Rename hwclan.service.ts → hw-clan.service.ts, hwmember.service.ts → hw-member.service.ts (Angular Style Guide kebab-case) - Replace _apiVersion (/v1, /v2) with _apiDomain (/skydive, /cms, /herowars) across all feature services - Update herowars barrel to reference renamed files - Update CLAUDE.md to reflect corrected path examples
6.0 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
npm start— dev server (production config) on port 4200npm run dev— dev server withAPP_ENV=developmenton port 4300, usesenvironment.development.tsnpm run local— dev server withAPP_ENV=localon port 4400, usesenvironment.local.tsnpm run serve— alias for local config on port 4201npm run build— production build todist/adastra_angular/npm run watch— incremental build in development configurationnpm run lint— angular-eslint oversrc/**/*.tsandsrc/**/*.htmlnpm test— Karma + Jasmine (single spec:ng test --include src/path/to/file.spec.ts)
Node engines: ^18.19.1 || ^20.11.1. Angular 18, TypeScript 5.5, strict mode with strictTemplates.
Environments
src/environments/environment.ts is the default (production). environment.development.ts and environment.local.ts are swapped in via fileReplacements in angular.json for their respective configurations. environment.example.ts documents the expected shape (apiBaseUrl, apiKey, useApiKey, refreshInterval, googleMapApiKey). Never commit real keys to the development/local files — the production environment.ts currently has real keys checked in; be mindful.
Architecture
Standalone Angular 18 app (no NgModules) wired up in src/app/app.config.ts. It provides the router with withViewTransitions(), three HTTP interceptors, charts, French locale (fr-FR), and an APP_INITIALIZER that eagerly fetches the current user when a JWT is present in localStorage.
Routing split
Routes are composed in src/app/app.routes.ts from two arrays:
- src/app/routes/auth.routes.ts —
authGuard-protected routes. Skydive admin sections (aeronefs,canopies,dashboard,dropzones,jumps) additionally requireadminGuard. - src/app/routes/noauth.routes.ts — public pages and auth-entry routes (
login,register) gated bynoauthGuard(blocks entry when already authenticated).
Resolvers under src/app/core/resolvers/ pre-fetch data for each route — new route components should generally follow this pattern rather than fetching in ngOnInit.
HTTP pipeline
Three interceptors chained in order (src/app/core/interceptors/):
apiInterceptor— prependsenvironment.apiBaseUrlto every request URL. Consequence: services call relative paths like/cms/user, never absolute URLs.tokenInterceptor— attachesAuthorization: Token <jwt>when a token exists.errorInterceptor— unwrapserr.errorand rethrows.
ApiService (api.service.ts) is a thin HttpClient wrapper; most feature services call it with a versioned prefix (e.g., _apiDomain = '/skydive' or /cms). The API version is per-service, not global.
Auth flow
JwtService stores the token in localStorage['jwtToken']. UserService exposes currentUser (BehaviorSubject) and isAuthenticated (ReplaySubject) observables consumed by guards and the layout. The APP_INITIALIZER in app.config.ts calls userService.getCurrentUser() if a token exists, so guards can rely on isAuthenticated being populated on first navigation. Admin role check is currentUser.role === 'Admin'.
Layout
AppComponent renders only FullComponent (src/app/components/shared/layout/full.component.ts), which owns the Material sidenav + toolbar shell and swaps menu items between MENUITEMS and MENUITEMSADMIN defined in src/app/components/shared/menu-items.ts based on role. Route changes render inside its <router-outlet>.
Feature areas
The app spans four mostly-independent feature domains, mirroring the backend's route grouping:
- E-commerce —
products,product/:slug,products/:category(public + auth variants). Categories are baked into menu items, not dynamic. Backend counterpart:/api/ecommerce/*. - CMS —
pages,page/:slug,home, profiles, articles. Backend counterpart:/api/cms/*. - Skydive —
skydive/*routes, services in src/app/core/services/skydive/, QCM exam data insrc/app/components/qcm/*.jsonandsrc/qcm-bpa.json. Backend counterpart:/api/skydive/*. - Hero Wars — analytics for a game guild, services in src/app/core/services/herowars/, weekly data snapshots in src/files-data/ (
Week_XXfolders andhw-*.jsonfiles are static data shipped as assets). Backend counterpart:/api/herowars/*.
Path aliases
Configured in tsconfig.json and used everywhere — prefer them over relative paths:
@components, @services, @models, @guards, @interceptors, @resolvers, @viewmodels, @interfaces, @constants, @core/*, @environments/*, @data/* (→ src/files-data/*), @assets/*, @styles/*.
Each alias points at an index.ts barrel — when adding a new component/service/model, also export it from the appropriate barrel or it won't be importable via the alias.
Styling
SCSS with includePaths: ["src/styles"] so imports like @use 'variables' resolve. Global Chartist styles are registered in angular.json build options. Material 18 with French date locale and DD/MM/YYYY format configured in AppComponent's providers.
Related service
The backend API lives in a sibling repo at /Users/julien/Sites/adastra_api (Express + Sequelize, additional working directory). Routes are grouped under src/routes/api/ into the same four domains as the frontend: skydive, cms, ecommerce, herowars. The v1, v2, v3 folders alongside them are legacy backups slated for removal — ignore them. When a task spans both repos (e.g., adding an endpoint + consumer), check there as well.