docs(frontend): met à jour l'état du frontend et le design système

30-frontend.md : le mode fixtures est inactif dans les deux environnements
(la doc affirmait l'inverse), services et pages manquants, widget alertes et
états de chargement, strict activé. 32-design-systeme-frontend.md : tokens
typographiques, ev-icon, app-alert-feed, classes de tableau.
This commit is contained in:
Johan LEROY
2026-09-21 14:35:30 +02:00
parent 7fd8d1ce30
commit f0ff953a5e
2 changed files with 50 additions and 24 deletions
+31 -23
View File
@@ -4,39 +4,47 @@ Application Angular 22, 100 % standalone, testée avec Vitest. Source dans `apps
## État actuel ## État actuel
Statut : `En cours`. L'application sert une première page métier, le tableau de bord, alimentée Statut : `En cours`. L'application sert le tableau de bord, la liste et le détail des sites, la
par des fixtures : les endpoints qu'elle appelle n'existent pas encore côté API. supervision des capteurs (admin) et le flux des alertes actives, tous branchés sur l'API réelle.
Ce qui est en place : Ce qui est en place :
- Bootstrap par `bootstrapApplication(App, appConfig)`, **aucun `NgModule`** dans le dépôt. - Bootstrap par `bootstrapApplication(App, appConfig)`, **aucun `NgModule`** dans le dépôt.
- `app.config.ts` fournit `provideBrowserGlobalErrorListeners()`, `provideRouter(routes)` et - `app.config.ts` fournit `provideBrowserGlobalErrorListeners()`, `provideRouter(routes)` et
`provideHttpClient(withInterceptors([mockApiInterceptor]))`. `provideHttpClient(withInterceptors([authInterceptor, mockApiInterceptor]))`.
- Une route `/dashboard` en composant différé, et une redirection depuis la racine. - Des routes en composants différés (`/dashboard`, `/sites`, `/sites/:siteId`,
- `core/services` porte `StatsService`, `AlertsService`, `PredictionsService`, `SitesService` et `/monitoring/sensors` réservée au rôle `admin`) et une redirection depuis la racine.
`AuthService`, `core/interceptors` l'intercepteur de fixtures et l'intercepteur d'authentification - `core/services` porte un service HTTP par domaine (`StatsService`, `AlertsService` avec ses
(jeton porteur, rafraîchissement sur 401), `core/guards` la garde de route `authGuard`, filtres `site_id` et `severity`, `PredictionsService`, `SitesService`, `ReadingsService`,
`features/dashboard` la page principale, `shared/components` la jauge de consommation et le `SensorsService`, `AuthService`), `core/interceptors` l'intercepteur de fixtures et l'intercepteur
graphique de charge par site, tous deux construits sur Chart.js. d'authentification (jeton porteur, rafraîchissement sur 401), `core/guards` la garde `authGuard`.
- `features/` porte une page par domaine. `shared/components` porte la jauge de consommation et
les graphiques Chart.js, le widget `app-alert-feed` (flux d'alertes filtrable par site et
sévérité, rafraîchi toutes les 60 s, première vue de l'application avec des états chargement /
vide / indisponible) et, dans `shared/models`, des types alignés sur les schémas Pydantic du
backend, plus les tables de présentation partagées (`alert-presentation.ts` : ton, libellé et
unité par sévérité, type et métrique).
- Une authentification complète côté interface : connexion, mot de passe oublié/réinitialisation, - Une authentification complète côté interface : connexion, mot de passe oublié/réinitialisation,
changement de mot de passe, garde de route sur `/dashboard` et `/sites`. Détail : changement de mot de passe, garde de route sur toute la zone authentifiée. Détail :
[31-contrat-authentification.md](31-contrat-authentification.md). [31-contrat-authentification.md](31-contrat-authentification.md).
- Un système de design partagé (`shared/components/ui/` : `ev-button`, `ev-card`, `ev-alert`, - Un système de design partagé (`shared/components/ui/` : `ev-button`, `ev-card`, `ev-alert`,
`ev-badge`, `ev-brand`, tokens CSS dans `styles/_tokens.scss`) que toute nouvelle page doit `ev-badge`, `ev-brand`, `ev-icon`, tokens CSS dans `styles/_tokens.scss`, classes globales de
réutiliser plutôt que redéfinir ses propres styles. Détail : formulaire, de navigation et de tableau) que toute nouvelle page doit réutiliser plutôt que
redéfinir ses propres styles. Détail :
[32-design-systeme-frontend.md](32-design-systeme-frontend.md). [32-design-systeme-frontend.md](32-design-systeme-frontend.md).
- L'état vit dans des signaux, sans bibliothèque dédiée. - L'état vit dans des signaux, sans bibliothèque dédiée.
- TypeScript en `"strict": true` ; `strictTemplates` n'est pas encore activé.
- Vitest via le builder `@angular/build:unit-test`, couverture activée. - Vitest via le builder `@angular/build:unit-test`, couverture activée.
- Prettier configuré, parser `angular` pour les gabarits HTML. - Prettier configuré, parser `angular` pour les gabarits HTML.
Ce qui n'existe pas encore : Ce qui n'existe pas encore :
- **`stats`/`alerts` restent sur fixtures.** `GET /api/v1/stats/summary` et `GET /api/v1/alerts` - **Le mode fixtures est inactif.** `useMockFixtures` vaut `false` dans `environment.ts` comme dans
sont servis par l'intercepteur de fixtures ; l'API expose bien ces routes désormais, mais rien `environment.development.ts` : `mockApiInterceptor` ne sert `/stats/summary` et `/alerts` que
ne bascule `useMockFixtures` à `false` en développement pour les consommer réellement. dans son propre spec. En développement, toutes les pages exigent un backend joignable et un jeton
`GET /api/v1/predictions` fait exception : jamais mocké, branché sur l'API réelle depuis cette valide.
PR (voir plus bas). - Un état de chargement généralisé : seul `app-alert-feed` en a un, les autres pages restent vides
- Aucun état de chargement : tant que la première réponse n'est pas arrivée, la page reste vide. tant que la première réponse n'est pas arrivée.
- Aucun lint : ESLint n'est pas installé. - Aucun lint : ESLint n'est pas installé.
## Arborescence ## Arborescence
@@ -87,11 +95,11 @@ sequenceDiagram
``` ```
`mockApiInterceptor` n'intercepte que `/stats/summary` et `/alerts`, et seulement si `mockApiInterceptor` n'intercepte que `/stats/summary` et `/alerts`, et seulement si
`environment.useMockFixtures` est vrai. Le drapeau est à `true` en développement, à `false` en `environment.useMockFixtures` est vrai. Le drapeau vaut `false` dans les deux fichiers
production : toute autre requête, et toutes les requêtes en production, suivent le chemin réel. d'environnement : en pratique toutes les requêtes suivent le chemin réel et l'intercepteur n'est
`/predictions` est volontairement exclu de cette liste (contrairement à `stats`/`alerts`) : il exercé que par son spec. `/predictions` et `/auth/*` ne sont de toute façon jamais mockés. En
suit toujours le chemin réel, comme `/auth/*` - en développement, ça veut dire qu'un jeton valide développement, un jeton valide et un backend joignable sont donc nécessaires pour que le tableau de
et un backend joignable sont nécessaires pour que la section prévisions du dashboard s'affiche. bord s'affiche.
En développement, `proxy.conf.json` redirige tout `/api` vers `http://localhost:8000`. C'est ce En développement, `proxy.conf.json` redirige tout `/api` vers `http://localhost:8000`. C'est ce
qui évite le CORS sur le poste, et c'est pourquoi `environment.development.ts` se contente d'un qui évite le CORS sur le poste, et c'est pourquoi `environment.development.ts` se contente d'un
@@ -20,8 +20,9 @@ seule fois dans `src/styles.scss`. Disponibles partout sans import supplémentai
| `--color-success` / `-bg`, `--color-warning` / `-bg` / `-text`, `--color-danger` / `-hover` / `-bg` / `-border`, `--color-critical` | États sémantiques (alertes, badges) | | `--color-success` / `-bg`, `--color-warning` / `-bg` / `-text`, `--color-danger` / `-hover` / `-bg` / `-border`, `--color-critical` | États sémantiques (alertes, badges) |
| `--color-text-inverse` | Texte sur fond coloré plein (boutons/badges) | | `--color-text-inverse` | Texte sur fond coloré plein (boutons/badges) |
| `--font-family` | Police unique de l'application | | `--font-family` | Police unique de l'application |
| `--font-size-xs` à `--font-size-2xl` | Échelle typographique (0.75rem à 2.25rem) : libellés, corps, titres, grands nombres |
| `--radius-sm`, `--radius-md`, `--radius-pill` | Rayons de bordure (input/bouton, carte, pastille) | | `--radius-sm`, `--radius-md`, `--radius-pill` | Rayons de bordure (input/bouton, carte, pastille) |
| `--shadow-card` | Ombre portée des cartes | | `--shadow-card`, `--shadow-card-hover` | Ombre portée des cartes, au repos et au survol |
| `--space-1` à `--space-5` | Échelle d'espacement (0.35rem à 2.5rem) | | `--space-1` à `--space-5` | Échelle d'espacement (0.35rem à 2.5rem) |
Les classes de formulaire partagées (`.form-label`, `.form-input`, `.form-select`, `.form-hint`) Les classes de formulaire partagées (`.form-label`, `.form-input`, `.form-select`, `.form-hint`)
@@ -84,6 +85,23 @@ dans le tableau `imports` du composant qui l'utilise.
```html ```html
<ev-brand /> <ev-brand />
``` ```
- **`<ev-icon>`** (`icon/`) : `name` (obligatoire : `spike` / `threshold` / `anomaly` / `outage` /
`sensor`, les types d'alerte du contrat) et `label` (facultatif). SVG inline en trait sur
`currentColor`, dimensionné par `font-size` comme `ev-brand`. Sans `label` l'icône est décorative
(`aria-hidden`) ; avec, elle porte `role="img"` et `aria-label`. Pas de bibliothèque d'icônes : la
CSP du reverse proxy (`script-src 'self'`) interdit les scripts tiers, pas le SVG inline.
```html
<ev-icon name="spike" label="Pic de consommation" />
```
- **`<app-alert-feed>`** (`shared/components/alert-feed/`) : widget métier plutôt qu'atome du kit,
mais réutilisable tel quel. Input `siteId` (facultatif : fige le site et masque son filtre). Il
porte ses filtres (`.form-select`), ses états et son rafraîchissement ; le parent ne fait que le
poser dans une section.
Les classes de tableau partagées sont dans `apps/frontend/src/styles/_tables.scss`, importées
globalement : `.ev-table-card` sur la `<ev-card>` qui enveloppe un tableau (padding nul),
`.ev-table` sur le `<table>`, `.ev-table__number` pour une cellule numérique en chiffres
tabulaires, `.ev-table__muted` pour une cellule sans valeur.
## Logo ## Logo