# 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 4200
- `npm run dev` — dev server with `APP_ENV=development` on port 4300, uses `environment.development.ts`
- `npm run local` — dev server with `APP_ENV=local` on port 4400, uses `environment.local.ts`
- `npm run serve` — alias for local config on port 4201
- `npm run build` — production build to `dist/adastra_angular/`
- `npm run watch` — incremental build in development configuration
- `npm run lint` — angular-eslint over `src/**/*.ts` and `src/**/*.html`
- `npm 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](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](src/app/app.routes.ts) from two arrays:
- [src/app/routes/auth.routes.ts](src/app/routes/auth.routes.ts) — `authGuard`-protected routes. Skydive admin sections (`aeronefs`, `canopies`, `dashboard`, `dropzones`, `jumps`) additionally require `adminGuard`.
- [src/app/routes/noauth.routes.ts](src/app/routes/noauth.routes.ts) — public pages and auth-entry routes (`login`, `register`) gated by `noauthGuard` (blocks entry when already authenticated).

Resolvers under [src/app/core/resolvers/](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/](src/app/core/interceptors/)):
1. `apiInterceptor` — prepends `environment.apiBaseUrl` to every request URL. **Consequence:** services call relative paths like `/cms/user`, never absolute URLs.
2. `tokenInterceptor` — attaches `Authorization: Token <jwt>` when a token exists.
3. `errorInterceptor` — unwraps `err.error` and rethrows.

`ApiService` ([api.service.ts](src/app/core/services/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](src/app/app.component.ts) renders only `FullComponent` ([src/app/components/shared/layout/full.component.ts](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](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/](src/app/core/services/skydive/), QCM exam data in `src/app/components/qcm/*.json` and `src/qcm-bpa.json`. Backend counterpart: `/api/skydive/*`.
- **Hero Wars** — analytics for a game guild, services in [src/app/core/services/herowars/](src/app/core/services/herowars/), weekly data snapshots in [src/files-data/](src/files-data/) (`Week_XX` folders and `hw-*.json` files are static data shipped as assets). Backend counterpart: `/api/herowars/*`.

### Path aliases

Configured in [tsconfig.json](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.
