Files
EME_APP/review.md
2026-07-24 16:02:20 +02:00

531 lines
48 KiB
Markdown

# review.md — Journal des décisions EME
> Ce fichier trace chaque étape réalisée et chaque choix technique, avec sa justification.
> Il est mis à jour au fil de l'avancement. Lecture du plus ancien (haut) au plus récent (bas).
>
> Règles métier -> `docs/regles-metier/regles-gestion.md` · Contexte -> `CONTEXT.md` · Plan -> `TODO.md`
---
## Conventions et choix transverses
Ces décisions s'appliquent à tout le projet, sauf mention contraire.
| Décision | Pourquoi |
|---|---|
| Statuts métier en `String` côté Prisma (pas d'enum natif) | SQL Server ne supporte pas les enums Prisma. Les valeurs sont validées côté TypeScript via `src/models/enums.ts`. |
| Timestamps automatiques : `createdAt @default(now())`, `updatedAt @updatedAt` | Évite d'avoir à fournir ces valeurs manuellement ; `updatedAt` est géré par Prisma au runtime. |
| `actif Boolean @default(true)` | Une entité créée est active par défaut. |
| `@unique` sur les identifiants fonctionnels | Intégrité : un identifiant en double casse l'authentification / l'unicité métier. |
| Champs nullable selon les cardinalités `0..1` du diagramme de classes | Un champ non connu à la création (ex. date de retour) ou optionnel doit être `NULL`, sinon le workflow métier est bloqué. |
| `onDelete: NoAction, onUpdate: NoAction` sur les données de traçabilité (Emprunt, Checklist, ChecklistElement, Anomalie, Historique) | Ces données ne doivent jamais être supprimées/altérées automatiquement en cascade. Toute suppression sera explicite côté service. Évite aussi l'erreur SQL Server "multiple cascade paths". |
| ERD tenu à jour en miroir du schéma (`docs/conceptions/uml/ERD.md`) | La source de vérité doit refléter le code réel (nullable, `UK`, etc.). |
| Workflow Git : `develop` comme branche d'intégration + branches dédiées `feat/...`, `fix/...`, `docs/...` | `master` reste figé ; chaque changement logique est isolé sur une branche puis mergé dans `develop`, conformément à `CLAUDE.md`. |
| Process avant chaque migration : `prisma format` -> `prisma validate` -> `npm run build` -> migration | Détecter toute erreur de schéma/typage avant de toucher la base. |
| DTO nommés `XxxResponse` (sortie) et `XxxRequest` (entrée) | Le sens est explicite dans le nom. Sortie = mapping (`toXxxResponse`), entrée = validation. |
| Confirmation responsable uniquement en cas d'écart | Les checklists sont préremplies. Sans modification, le départ ou le retour est automatique ; une différence déclarée reste provisoire jusqu'à la décision du responsable. |
| Un seul emprunt bloquant par catégorie et par étudiant | La catégorie reste bloquée jusqu'à la clôture du retour, sans limite calendaire après clôture. |
---
## État actuel synthétique
Cette section résume l'état courant du projet. Le journal chronologique plus bas conserve
l'historique complet, y compris des étapes devenues obsolètes après branchement API.
| Bloc | État | Commentaire |
|---|---|---|
| Infrastructure | Terminé | Docker Compose SQL Server + Adminer, monorepo, backend Node/TS, frontend Flutter Web. |
| Base de données | Terminé pour la V1 | Schéma Prisma 16 entités, migrations, seeds et fixtures de démonstration. |
| API étudiant | À adapter au nouveau workflow | Les parcours automatiques restent valides ; le retour doit être prérempli depuis le départ et les seules différences doivent être mises en attente. |
| Frontend étudiant | À adapter au nouveau workflow | Interface premium conservée ; préremplissage et écran d'attente à ajouter lorsqu'une valeur est modifiée. |
| Authentification | Prête pour Azure, démo active | Double mode sécurisé : `x-user-email` seulement en développement, MSAL/OIDC + Bearer JWT en mode Azure. Validation réelle en attente des identifiants Entra ID. |
| API responsable | Partielle pour le nouveau workflow | Supervision existante ; confirmation des écarts de départ et de retour à ajouter avec alertes et transitions atomiques. |
| Frontend responsable | Partiel pour le nouveau workflow | Dashboard existant ; file des écarts, contrôle des checklists et décisions à ajouter. |
| Tests automatisés | Non démarré | Tests backend/frontend/E2E à ajouter ; tests runtime manuels effectués. |
| Documentation | Partielle | README principal et documents de conception présents ; OpenAPI, guides utilisateur et captures restent à produire. |
## Commandes validées
Commandes utilisées et validées dans l'environnement de développement local :
```bash
docker compose up -d
cd eme-backend
npm run dev
npm run build
npm run lint
npm run seed
npm run fixtures
cd ../eme-frontend
npm run build:auth
flutter run -d web-server --web-port 5000
C:\flutter\flutter\bin\cache\dart-sdk\bin\dart.exe analyze
```
Notes :
- le backend écoute sur `http://localhost:3000` ;
- le frontend web de démo écoute sur `http://localhost:5000` ;
- Adminer est disponible sur `http://localhost:8081` ;
- le wrapper `flutter` peut rester bloqué dans l'environnement Codex ; l'analyse directe avec l'exécutable Dart fonctionne et ne signale aucune erreur ;
- la compilation Web complète a été validée avec l'exécutable Dart du SDK Flutter et produit `build/web`.
## Scénario de démo historique
> Ce scénario valide toujours les parcours automatiques sans écart. Les branches avec modification de checklist et confirmation responsable restent à implémenter et à tester.
Scénario étudiant validé avec SQL Server Docker et backend compilé :
1. Afficher le catalogue des matériels disponibles du campus.
2. Ouvrir le détail d'un matériel et vérifier les accessoires.
3. Créer un emprunt avec checklist de départ.
4. Vérifier que l'emprunt apparaît dans `mes-emprunts`.
5. Restituer conforme : l'emprunt passe `CLOTURE` et le matériel redevient disponible.
6. Restituer non conforme : l'emprunt passe `RETOUR_NON_CONFORME` et le matériel sort du catalogue disponible.
Scénario responsable validé avec SQL Server Docker et backend compilé :
1. Karim (`RESPONSABLE`) accède au dashboard, aux emprunts, au stock, aux anomalies, aux notifications et à l'historique de son campus.
2. Lucas (`ETUDIANT`) reçoit une réponse `403` sur les routes responsable.
3. Une anomalie peut avancer dans le cycle `DETECTEE -> EN_COURS_TRAITEMENT -> RESOLUE -> CLOTUREE`.
4. Chaque transition enregistre le responsable et crée une entrée d'historique.
5. L'API génère l'export CSV de l'historique et l'interface Flutter déclenche son téléchargement.
### Note outillage — migrations en environnement non-interactif
`prisma migrate dev` se bloque quand il doit demander une confirmation (ex. ajout de contrainte `@unique` sur table existante) car le terminal est non-interactif.
Contournement utilisé : générer le SQL avec `prisma migrate diff --from-config-datasource --to-schema ... --script`, écrire le fichier de migration, puis appliquer avec `prisma migrate deploy`. Résultat strictement identique à `migrate dev`, sans perte de données.
En terminal interactif (VS Code), `migrate dev` fonctionne normalement (taper `y`).
---
## Journal chronologique
### Étape 0 — Audit initial
- Constat : Block 0 (bootstrap) terminé mais non coché ; schéma à 4/16 entités ; aucune migration ; aucun commit Git.
- Décision : compléter le schéma par petits blocs (et non d'un coup) pour valider/committer au fur et à mesure.
### Étape 1 — Configuration des migrations Prisma
- Correction `DATABASE_URL` (mot de passe désaligné avec `DB_PASSWORD`).
- Découverte : Prisma 7 interdit `url` dans `schema.prisma` -> la connexion vit dans `prisma.config.ts`. Ligne `url` retirée du datasource.
### Étape 2 — Modèles de référence (Campus, Role, Utilisateur, CategorieMateriel)
- Defaults + timestamps auto ajoutés sur Campus, Utilisateur, CategorieMateriel (Role n'a pas de timestamps -> non modifié).
- `@unique` sur `Role.code`, `Utilisateur.microsoftId`, `Utilisateur.email`.
- `Utilisateur.classe` -> nullable : un `RESPONSABLE` n'a pas de classe (seuls les étudiants en ont).
- Ajout dans l'ERD des annotations `UK` (sur `code`, `microsoft_id`, `email`) et `nullable` (sur `classe`).
- Choix laissé de côté : tailles `@db.NVarChar(n)` (colonnes restées en `NVARCHAR(1000)` par défaut). À reconsidérer plus tard.
### Étape 3 — Bloc A : Infrastructure (SallePret, PosteEmprunt) — commit `d026b75`
- `SallePret` rattachée à `Campus` (FK `campus_id`) ; `PosteEmprunt` rattaché à `SallePret` (FK `salle_pret_id`).
### Étape 4 — Bloc B : CarteEtudiante — commit `dfa73cd`
- Rattachée à `Utilisateur`.
- Dette notée : `qr_code` devrait être `@unique` (sert à l'identification, RG02) — non posé pour rester fidèle à l'ERD.
### Étape 5 — Bloc C : Matériel (Materiel, Accessoire, MaterielAccessoire) — commit `0774ee3`
- Table de liaison `MaterielAccessoire` entre `Materiel` et `Accessoire`, sans colonnes `created_at`/`updated_at`.
- Dette notée : `@@unique([materielId, accessoireId])` et `Materiel.statut @default("DISPONIBLE")`.
### Étape 6 — Bloc D : Transactions (Emprunt, Checklist, ChecklistElement) — commit `6908a2b`
- Champs de retour/optionnels nullable : `dateRetourReelle`, `modeIdentificationRetour`, `commentaireDepart`, `commentaireRetour`, `Checklist.commentaire`, `ChecklistElement.commentaire`.
- Pourquoi : RG12 crée l'emprunt au départ ; les infos de retour n'existent pas encore. En `NOT NULL`, la création serait impossible.
- `ChecklistElement.accessoireId` rendu nullable : un élément de checklist peut être un libellé libre, sans accessoire catalogué associé (cardinalité 0..1).
- Les 9 FK en `NoAction` : traçabilité + évitement des cascade paths.
- Annotation `nullable` ajoutée dans l'ERD sur les champs de retour/commentaires d'`Emprunt` et `accessoire_id`/`commentaire` de checklist.
### Étape 7 — Stratégie Git : branche `develop`
- Création de `develop` depuis `master`. Désormais tous les commits vont sur `develop` ; `master` figé au Bloc C.
### Étape 8 — Bloc E : Suivi (Anomalie, Notification, Historique) — commit `0fc9f9e`
- Schéma 16/16 complet.
- Nullable selon `0..1` : `Anomalie.traiteeParId` (null tant que non pris en charge), `observation`, `dateResolution` ; `Notification.anomalieId` ; les 5 FK optionnelles d'`Historique` (un événement n'a pas toujours d'emprunt/matériel/etc.).
- Double relation `Anomalie` vers `Utilisateur` nommée : `@relation("AnomalieEtudiant")` (étudiant concerné) et `@relation("AnomalieResponsable")` (responsable qui traite).
- Defaults : `Anomalie.detecteeAutomatiquement @default(true)`, `Notification.lu @default(false)`, `Notification.dateCreation @default(now())`.
- Les 12 FK en `NoAction` (traçabilité).
- `Notification` et `Historique` créées sans colonne `updatedAt` (seulement `date_creation`/`created_at`).
- Annotation `nullable` ajoutée dans l'ERD sur `traitee_par_id`, `observation`, `date_resolution`, `anomalie_id` et les 5 FK optionnelles d'`Historique`.
### Étape 9 — Seeds de référence — commit `2c3a5db`
- Fichier `prisma/seed.ts` autonome (adapter MSSQL, lecture directe des variables DB), lancé via `npm run seed` (script ajouté au `package.json`).
- Choix `npm run seed` (ts-node) plutôt que `prisma db seed` : l'API de configuration du seed en Prisma 7 (`prisma.config.ts`) n'étant pas confirmée, un script direct est plus fiable.
- Données : 2 rôles (`ETUDIANT`, `RESPONSABLE`), 1 campus (`Campus Saint-Christophe`, Cergy, 95800, adresse provisoire), 1 salle (`Salle de prêt principale`), 1 poste (`POSTE-01`), 6 catégories (Ordinateur portable, Vidéoprojecteur, Tablette, Caméra, Périphérique audio, Câble / Adaptateur).
- Idempotence : `upsert` sur `Role.code` ; `findFirst` avant `create` pour les entités sans clé unique. Relance vérifiée (aucun doublon).
- Incident : le client Prisma était obsolète (ne connaissait que les 4 modèles de référence). Régénéré avec `npx prisma generate` avant le seed. Leçon : après des migrations, régénérer/vérifier le client avant d'exécuter du code qui l'utilise.
### Étape 10 — Fixtures de test
- Fichier `prisma/fixtures.ts` + script `npm run fixtures` (séparé du seed : données de dev/test, non installées en production).
- Non destructif : détection via un email témoin (`marie.dupont@ensitech.eu`) ; si présent, le script s'arrête (cohérent avec le principe "pas de suppression automatique").
- Contenu : 4 utilisateurs (3 étudiants + 1 responsable), 3 cartes, 6 matériels (statuts variés), 3 accessoires + liaisons, 4 emprunts couvrant `EN_COURS` / `EN_RETARD` / `CLOTURE` / `RETOUR_NON_CONFORME`, 6 checklists, 1 anomalie + 1 notification, 2 entrées d'historique.
- Idempotence vérifiée (2e exécution = skip).
#### Contexte métier clarifié — structure ENSUP (impacte la modélisation)
- ENSUP est un groupe **multi-campus** (Saint-Christophe/Cergy, Saint-Quentin, Nantes, Marseille...).
- Chaque campus héberge **deux écoles** : ENSUP (filières généralistes) et Ensitech (informatique). Bâtiments et salles **partagés**.
- La distinction d'école se fait par le **domaine email** : `ensitech.eu` (info) et `ensup.eu` (reste). La validation de domaine (RG, à venir) doit accepter les **deux**.
- Le modèle n'a pas d'entité "École" : seule `Campus` existe. Un campus = un lieu physique partagé par les deux écoles (et non une école).
- MVP : on démarre avec le **seul campus Saint-Christophe (Cergy)** ; les autres campus viendront ensuite.
### Étape 11 — Block 1 : middlewares backend
- Structure en couches : `utils/logger.ts`, `errors/app-error.ts`, `middlewares/` (request-logger, not-found, error-handler).
- Gestionnaire d'erreurs global : une `AppError` renvoie son statut + message ; toute autre erreur devient une 500 (stack exposée uniquement en `development`) et est tracée.
- Logger de requêtes (méthode, URL, statut, durée) + logger applicatif horodaté.
- CORS restreint à `env.frontendUrl` (était ouvert à toutes les origines). `SIGTERM` géré en plus de `SIGINT` (arrêt propre du container Docker).
- Express 5 propage nativement les erreurs des handlers async : pas de wrapper `asyncHandler` nécessaire.
- Vérifié au runtime : `/health` 200, route inconnue 404 en JSON, requêtes journalisées.
### Étape 12 — Block 1 : ESLint + Prettier
- ESLint 10 (flat config `eslint.config.mjs`) + typescript-eslint 8 ; Prettier 3 (`.prettierrc.json`).
- Règles de qualité strictes encodées : `no-explicit-any`, `no-non-null-assertion`, `explicit-module-boundary-types`, `no-unused-vars` (ignore le préfixe `_`), `prefer-const`. `eslint-config-prettier` désactive les règles en conflit avec Prettier.
- Prettier : quotes simples, point-virgule, 100 colonnes, 2 espaces, trailing commas.
- Scripts : `lint`, `lint:fix`, `format`, `format:check`.
- État : lint vert sur tout le code existant ; formatage appliqué (3 fichiers) ; build OK.
### Étape 13 — Correctif tooling : type-check des scripts Prisma
- Problème : `prisma/seed.ts` et `fixtures.ts` étaient hors du `include` du tsconfig ; l'IDE ne chargeait pas les types Node (`process` non reconnu, 2 erreurs).
- Correctif : `tsconfig.json` élargi à `src` + `prisma` en `noEmit` (IDE et `tsc --noEmit`) ; nouveau `tsconfig.build.json` dédié à la compilation `src` -> `dist` (`npm run build`).
- Bénéfice : les scripts Prisma sont désormais type-checkés (angle mort comblé).
### Étape 14 — Block 4 : architecture en couches + 1er endpoint (catalogue matériel)
- Mise en place de l'architecture en couches : `routes/` -> `controllers/` -> `services/` -> `repositories/` (un dossier par couche).
- Auth simulée temporaire : middleware `current-user` qui résout l'utilisateur via l'en-tête `x-user-email` et pose `req.user` (typé via `types/authenticated-user.ts` + augmentation `Express.Request`). À remplacer par la validation JWT au Block 3.
- Endpoint `GET /api/materiels` (catalogue) : applique RG10 (matériels `DISPONIBLE` du campus de l'étudiant), filtres `categorieId` et `q` (recherche nom/marque/modèle/référence).
- Format de réponse standardisé : `{ "data": ... }` (cohérent avec `{ "error": ... }`).
- Vérifié au runtime : 401 sans en-tête / utilisateur inconnu, 200 avec les disponibles du campus, filtres OK, 400 sur `categorieId` invalide.
### Étape 15 — Block 4 : endpoint "mes emprunts en cours"
- Endpoint `GET /api/mes-emprunts` : retourne les emprunts non restitués (`EN_COURS`, `EN_RETARD`) de l'utilisateur courant, triés par date de retour prévue, avec le matériel et sa catégorie inclus.
- RG15 : filtrage par `utilisateurId` (chacun ne voit que ses propres emprunts).
- Réutilise l'architecture en couches et l'auth simulée déjà en place.
- Vérifié au runtime : Marie 1 (`EN_COURS`, son `CLOTURE` exclu), Lucas 1 (`EN_RETARD`), Sofia 0 (son `RETOUR_NON_CONFORME` exclu), 401 sans en-tête.
### Étape 16 — Block 4 : profil et identification
- `GET /api/auth/me` (protégé) : renvoie le profil complet de l'utilisateur courant (role + campus).
- `GET /api/auth/carte/:qr` (public) : identification par QR code (RG02/RG03) ; contrôles carte active, non expirée et compte actif ; renvoie le profil.
- Refactor du routage : `currentUser` appliqué par sous-routeur (au lieu d'un middleware global) pour laisser la route d'identification publique.
- Vérifié au runtime : `/me` 200 (profil Marie) et 401 sans en-tête ; `/carte` QR valide 200 sans en-tête (route publique), QR inconnu 404.
### Étape 17 — DTO de sortie sur les endpoints existants
- Dossier `src/dtos/` : par ressource, une interface `XxxResponse` + un mapper pur `toXxxResponse` (utilisateur, matériel, emprunt).
- Mapping effectué dans le controller (couche présentation) ; les services continuent de renvoyer les entités (réutilisables).
- Champs internes désormais masqués : `microsoftId`, timestamps, FK brutes, `numeroInventaire`/`numeroSerie`.
- Vérifié au runtime sur `/me`, `/materiels`, `/mes-emprunts`.
### Étape 18 — Durcissement du .gitignore (sécurité)
- Trou comblé : `**/.env` ne couvrait pas les variantes (`.env.local`, `.env.production`...). Ajout de `**/.env.*` avec exception `!**/.env.example`.
- Préventif : clés/certificats (`*.pem`, `*.key`, `*.crt`, `*.cert`, `*.pfx`, `*.p12`) pour Azure AD/TLS à venir ; dumps de base (`*.bak`, `*.dump`) ; `**/coverage/` ; `*.tsbuildinfo`.
- Vérifié : aucun secret n'était déjà suivi ; les vrais `.env` restent ignorés ; `.env.example` (placeholders) reste versionné.
### Étape 19 — Block 4 : création d'emprunt (POST /api/emprunts)
- Écriture transactionnelle : réservation atomique du matériel (`updateMany where statut=DISPONIBLE`), création de l'emprunt (`EN_COURS`), de la checklist de départ et de ses éléments. Rollback global si une étape échoue.
- Règles : RG07 (matériel disponible), RG10 (matériel du campus de l'étudiant), RG11 (checklist obligatoire non vide), RG12 (matériel -> `EMPRUNTE`, horodatage).
- Validation d'entrée manuelle (`CreerEmpruntRequest`, `parseCreerEmpruntRequest`) : aucune dépendance ajoutée. Durée d'emprunt par défaut : +14 jours.
- Contrôle du poste (existe + même campus, RG05). Routage : `POST /api/emprunts` et `GET /api/mes-emprunts` sur des routeurs séparés.
- Vérifié au runtime : 401 sans auth, 400 checklist vide, 404 matériel inconnu, 201 création (matériel -> `EMPRUNTE`, +14j), 409 doublon (réservation atomique).
### Étape 20 — Block 4 : restitution + anomalie automatique (POST /api/emprunts/:id/restitution)
- Comparaison automatique départ/retour (RG17) : un élément présent au départ mais absent/détérioré au retour rend la restitution non conforme (RG20). Appariement par `accessoireId` sinon par `nomElement`.
- Restitution atomique : checklist de retour + mise à jour emprunt/matériel + (si non conforme) anomalie `DETECTEE` + notifications.
- RG15 (propriétaire), statut restituable, RG18 (conforme -> `CLOTURE` + matériel `DISPONIBLE`), RG19 (non conforme -> `RETOUR_NON_CONFORME` + matériel `DETERIORE`/`NON_CONFORME`).
- RG24 : notification à tous les responsables (`RESPONSABLE`) du campus de l'emprunt.
- Vérifié au runtime : 404/403/409 ; restitution conforme (CLOTURE + DISPONIBLE) ; non conforme (RETOUR_NON_CONFORME + NON_CONFORME + anomalie + notification à Karim).
- **Block 4 (API Étudiant) terminé : 6/6 endpoints.**
### Étape 21 — Frontend : setup Flutter Web + écran d'accueil ENSUP
- Projet Flutter Web créé (`eme-frontend/`), `google_fonts` ajouté (Darker Grotesque pour les titres, Titillium Web pour le corps).
- Thème ENSUP (`theme/ensup_colors.dart`, `theme/ensup_theme.dart`) : couleurs de la charte graphique.
- Composants réutilisables : `EnsupTopBar`, `ProfileCard`, `ActionCard`.
- Écran d'accueil (`screens/home_screen.dart`) fidèle à la maquette : topbar bleu foncé, carte profil, deux cartes Emprunter/Restituer. Données statiques (pas encore branché à l'API).
- Convention `CLAUDE.md` respectée : guillemets doubles en Dart (règle lint `prefer_single_quotes` désactivée en conséquence).
- `flutter analyze` : aucun problème. App lancée dans Chrome (http://localhost:5000).
### Étape 22 — Suppression du PDF de charte graphique
- PDF de charte ENSUP retiré du dépôt (redondant : la maquette validée applique la charte, et les couleurs/typos sont dans `CONTEXT.md` §7).
- Références mises à jour dans `CLAUDE.md` et `CONTEXT.md` pour pointer vers la maquette.
- Le PDF reste récupérable dans l'historique Git si besoin (logo officiel source).
### Étape 23 — Frontend : écran d'identification (écran de démarrage)
- Correction : l'écran de démarrage est l'identification (`s-login` de la maquette), pas l'accueil étudiant. Logo, "Bienvenue sur EME", deux options (scanner carte / compte ENSUP Microsoft 365), bouton accès responsable.
- `EnsupTopBar` rendue réutilisable (utilisateur optionnel, marque paramétrable) ; nouveau widget `IdentificationOption` ; coins ENSUP aussi sur cet écran.
- Navigation identification -> accueil étudiant (`HomeScreen`).
### Étape 24 — Frontend : écran catalogue matériel
- Écran catalogue (`s-catalogue`) : recherche **fonctionnelle** + filtres par catégorie (chips) + liste de matériels avec statut (Disponible/Emprunté) et état vide.
- Nouveaux composants : `MaterielCard`, `StatusPill`, modèle `MaterielItem`. Données statiques (4 matériels de la maquette).
- Navigation : Accueil (Emprunter) -> Catalogue ; bouton retour vers l'accueil. « Sélectionner » -> placeholder (détail à venir).
### Étape 25 — Frontend : écran détail matériel
- Écran détail (`s-detail`) : caractéristiques (référence, catégorie, marque, modèle, état, campus, statut) + accessoires du kit (tags) + aperçu + boutons « Démarrer l'emprunt » / « Retour au catalogue ».
- Modèle `MaterielItem` enrichi (marque, modèle, état, accessoires) ; données statiques mises à jour.
- Navigation : Catalogue (Sélectionner) -> Détail ; « Démarrer l'emprunt » -> placeholder (checklist à venir).
> Note historique : les étapes 21 à 25 décrivent l'état initial statique du frontend.
> Elles ont été remplacées fonctionnellement par les branchements API des étapes 26 à 32.
---
## Dette technique en attente
À traiter ensemble plus tard (avec mise à jour ERD en miroir) :
| Dette | Statut | Impact V1 |
|---|---|---|
| `CarteEtudiante.qr_code` -> `@unique` | À faire avec migration + ERD | Moyen : sécurise l'identification QR. |
| `MaterielAccessoire` -> `@@unique([materielId, accessoireId])` | À faire avec migration + ERD | Moyen : évite les doublons d'accessoires. |
| `Materiel.statut` -> `@default("DISPONIBLE")` | À faire avec migration + ERD | Faible : le code fournit déjà le statut explicitement. |
| Tailles de colonnes `@db.NVarChar(n)` | À cadrer | Faible V1, important pour qualité BDD. |
| Swagger/OpenAPI | À faire | Moyen : utile pour tester/documenter l'API. |
| Validation réelle Azure AD | En attente des identifiants Entra ID | Fort : le code OIDC/MSAL/JWT est prêt, mais une connexion Microsoft réelle doit encore être testée sur le tenant ENSUP. |
| Blocage d'une catégorie jusqu'à la clôture du retour | Décidé, à implémenter | Fort : contrôle à appliquer atomiquement lors de toute demande d'emprunt. |
| Confirmation responsable des écarts déclarés | Décidée, à implémenter | Fort : nécessite des statuts conditionnels, alertes, endpoints et écrans de décision. |
---
### Étape 26 — Connexion frontend ↔ API (catalogue)
- Package `http` ajouté. Couche `services/api_client.dart` : URL de base, en-tête d'auth simulée (`x-user-email: lucas.martin@ensitech.eu`), gestion des erreurs (serveur injoignable, message `{error:{message}}`).
- `services/materiel_service.dart` (`getCatalogue`) + `MaterielItem.fromJson` (icône déduite de la catégorie, champs absents complétés).
- Écran catalogue branché via `FutureBuilder` : chargement / données / erreur + bouton Réessayer. Filtres de catégorie déduits des vraies données.
- CORS backend assoupli en développement (`origin: true` si `NODE_ENV=development`), strict en production.
- Testé en réel côté backend : Docker SQL Server OK, backend compilé OK, `/health`, `/api/auth/me`, `/api/materiels` OK, 401 sans `x-user-email` OK.
### Étape 27 — Connexion frontend ↔ API (détail matériel)
- Correctif runtime backend : `npm run dev` lance maintenant `ts-node --files`, sinon l'augmentation `Express.Request.user` n'était pas chargée par `ts-node` malgré un build TypeScript vert.
- Endpoint `GET /api/materiels/:id` ajouté : détail filtré par campus utilisateur, avec catégorie, campus et accessoires du kit.
- Frontend : `MaterielItem` porte maintenant l'`id`, `MaterielService.getDetail(id)` consomme le nouvel endpoint, et le catalogue charge le détail réel avant de naviguer vers l'écran détail.
- Testé en réel côté backend : `/api/materiels/1` renvoie le PC avec accessoires (`Chargeur`, `Souris`) ; `/api/materiels/abc` renvoie 400.
- Limite environnement : `flutter analyze`, `flutter analyze --no-pub` et `dart format` restent bloqués jusqu'au timeout dans le sandbox. À relancer dans un terminal Flutter local.
### Étape 28 — Connexion frontend ↔ API (création d'emprunt)
- Branche dédiée `feat/frontend-emprunt-api` créée après merge de `feat/catalogue-detail-api` dans `develop`.
- `ApiClient.post` ajouté et `EmpruntService.creerEmprunt` consomme `POST /api/emprunts` avec l'auth simulée.
- Checklist de départ branchée sur l'API : construction du payload (`materielId`, `posteEmpruntId`, `modeIdentification`, commentaire, éléments), état de chargement, affichage d'erreur via snackbar.
- Un matériel sans accessoire génère un élément de checklist portant le nom du matériel, afin de respecter RG11 côté backend (checklist obligatoire non vide).
- Confirmation alimentée par la réponse API (`id`, `dateEmprunt`, matériel renvoyé).
- Testé sans mutation de données : `POST /api/emprunts` avec checklist vide renvoie bien 400 `La checklist de depart est obligatoire (RG11)`.
### Étape 29 — Données de démonstration catalogue
- Branche dédiée `feat/demo-catalogue-fixtures` créée après merge de `feat/frontend-emprunt-api` dans `develop`.
- `prisma/fixtures.ts` enrichi avec un catalogue de démonstration idempotent : si les fixtures de base existent déjà, le script assure seulement les matériels de catalogue manquants.
- 8 matériels disponibles ajoutés : Lenovo ThinkPad, MacBook Air, Surface Pro, vidéoprojecteur Epson, caméra Canon, casque Jabra, kit câbles HDMI/USB-C, enceinte JBL.
- Accessoires associés assurés sans doublon logique par recherche de nom/référence avant création.
- Vérifié en réel : `npm run fixtures` OK ; `GET /api/materiels` pour Lucas renvoie 10 matériels disponibles.
### Étape 30 — Connexion frontend ↔ API (restitution)
- Branche dédiée `feat/frontend-restitution-api` créée après merge de `feat/demo-catalogue-fixtures` dans `develop`.
- `EmpruntItem.fromJson` ajouté pour mapper `GET /api/mes-emprunts`.
- `EmpruntService.getMesEmprunts()` et `EmpruntService.restituerEmprunt()` ajoutés.
- Écran de sélection restitution branché sur les vrais emprunts en cours, avec chargement, erreur, état vide et chargement du détail matériel avant la checklist.
- Checklist retour branchée sur `POST /api/emprunts/:id/restitution`, avec commentaire optionnel, état de chargement et erreurs via snackbar.
- Le résultat affiché se base sur le statut renvoyé par le backend (`CLOTURE` => conforme, sinon anomalie), afin de rester cohérent avec la comparaison serveur.
- Vérifié sans mutation : `GET /api/mes-emprunts` renvoie les emprunts de Lucas ; restitution d'un identifiant inexistant renvoie 404.
### Étape 31 — Stabilisation parcours étudiant complet
- Branche dédiée `feat/student-flow-stabilization` créée après merge de `feat/frontend-restitution-api` dans `develop`.
- Docker SQL Server relancé puis tests runtime faits avec le backend compilé.
- Flux conforme validé en réel : catalogue -> détail -> création emprunt -> apparition dans `mes-emprunts` -> restitution conforme -> statut `CLOTURE` -> matériel de nouveau visible dans le catalogue.
- Flux non conforme validé en réel : création emprunt -> restitution avec élément absent -> statut `RETOUR_NON_CONFORME` -> matériel retiré du catalogue disponible.
- Aucun correctif code nécessaire après ces tests ; backend `build` et `lint` restent verts.
### Étape 32 — Polish V1 étudiant
- Branche dédiée `feat/v1-student-polish` créée après merge de `feat/student-flow-stabilization` dans `develop`.
- Identité simulée et campus MVP centralisés dans `lib/demo_identity.dart` tant que l'auth Azure AD n'est pas branchée.
- Libellés UI harmonisés sur le contexte réel du MVP : `Saint-Christophe · Cergy` / `Ensitech Cergy` au lieu de `Paris · Ensitech`.
- Commentaire obsolète du modèle `MaterielItem` corrigé : les données viennent maintenant de l'API.
- `TODO.md` mis à jour : les parcours emprunt et restitution étudiant sont maintenant branchés API.
### Étape 33 — Clarification du journal de décisions
- Branche dédiée `docs/review-current-state` créée depuis `develop`.
- Ajout d'une synthèse d'état courant pour éviter de devoir relire tout l'historique.
- Correction de la convention Git : `develop` est la branche d'intégration, les travaux passent par branches dédiées.
- Ajout des commandes validées et du scénario de démo étudiant testé.
- Dette technique reformatée en tableau avec statut et impact V1.
- Clarification des étapes frontend statiques historiques : elles sont conservées pour mémoire mais remplacées par les branchements API ultérieurs.
### Étape 34 — API responsable : dashboard
- Branche dédiée `feat/responsable-dashboard-api` créée depuis `develop`.
- Endpoint `GET /api/responsable/dashboard` ajouté, protégé par rôle `RESPONSABLE`.
- Périmètre campus appliqué via `user.campusId` (RG27).
- KPIs exposés : matériels par statut, emprunts par statut, anomalies par statut, notifications non lues.
- Activité récente exposée depuis l'historique, avec utilisateur, matériel et emprunt associé.
- Vérifié au runtime : Karim (`RESPONSABLE`) obtient 200 avec KPIs ; Lucas (`ETUDIANT`) obtient 403.
### Étape 35 — API responsable : consultation des emprunts
- Branche dédiée `feat/responsable-emprunts-api` créée après merge du dashboard responsable dans `develop`.
- Endpoint `GET /api/responsable/emprunts` ajouté, protégé par rôle `RESPONSABLE`.
- Filtrage par campus du responsable appliqué systématiquement (RG27).
- Filtre optionnel `statut` ajouté pour RG28 : `EN_COURS`, `EN_RETARD`, `CLOTURE`, `RETOUR_NON_CONFORME`, `ANNULE`.
- Réponse enrichie avec l'étudiant, le matériel, la catégorie, les dates et le statut.
- Vérifié au runtime : Karim obtient les emprunts du campus, `statut=EN_COURS` filtre correctement, statut invalide renvoie 400, Lucas obtient 403.
### Étape 36 — API responsable : consultation du stock
- Branche dédiée `feat/responsable-stock-api` créée après merge de la consultation des emprunts responsable dans `develop`.
- Endpoint `GET /api/responsable/materiels` ajouté, protégé par rôle `RESPONSABLE`.
- Filtrage par campus du responsable appliqué systématiquement (RG27/RG29).
- Filtres optionnels ajoutés : `statut`, `categorieId`, `q` (nom, marque, modèle, référence).
- Contrairement au catalogue étudiant, cet endpoint retourne tout le stock actif du campus, quel que soit le statut.
- Réponse enrichie avec catégorie et accessoires attendus.
- Vérifié au runtime : Karim obtient tout le stock du campus, `statut=DISPONIBLE` et `q=MacBook` filtrent correctement, statut invalide renvoie 400, Lucas obtient 403.
### Étape 37 — API responsable : consultation des anomalies
- Branche dédiée `feat/responsable-anomalies-api` créée après merge de la consultation du stock responsable dans `develop`.
- Endpoint `GET /api/responsable/anomalies` ajouté, protégé par rôle `RESPONSABLE`.
- Filtrage par campus du responsable appliqué systématiquement via l'emprunt lié à l'anomalie (RG27).
- Filtres optionnels ajoutés : `statut` (`DETECTEE`, `EN_COURS_TRAITEMENT`, `RESOLUE`, `CLOTUREE`) et `type`.
- Réponse enrichie avec anomalie, emprunt, étudiant concerné, matériel, catégorie et responsable de traitement si renseigné.
- Limite volontaire : consultation uniquement ; le cycle de traitement/résolution reste à développer.
### Étape 38 — API responsable : notifications
- Branche dédiée `feat/responsable-notifications-api` créée après merge de la consultation des anomalies responsable dans `develop`.
- Endpoint `GET /api/responsable/notifications` ajouté, protégé par rôle `RESPONSABLE`.
- Filtre optionnel `lu=true|false` ajouté pour séparer les notifications lues et non lues.
- Endpoint `PATCH /api/responsable/notifications/:id/lu` ajouté pour marquer une notification du responsable connecté comme lue.
- Endpoint `PATCH /api/responsable/notifications/lu-toutes` ajouté pour marquer toutes les notifications non lues du responsable connecté comme lues.
- Réponse enrichie avec l'anomalie liée et le matériel concerné lorsque la notification pointe vers une anomalie.
### Étape 39 — API responsable : historique et export
- Branche dédiée `feat/responsable-historique-api` créée après merge des notifications responsable dans `develop`.
- Endpoint `GET /api/responsable/historique` ajouté, protégé par rôle `RESPONSABLE`.
- Filtrage par campus du responsable appliqué systématiquement (RG27).
- Filtres optionnels ajoutés : `action`, `utilisateurId`, `materielId`, `empruntId`, `dateDebut`, `dateFin`.
- Endpoint `GET /api/responsable/historique/export.csv` ajouté avec les mêmes filtres.
- Export CSV généré côté API avec les colonnes principales : date, action, description, utilisateur, email, matériel, emprunt.
### Étape 40 — API responsable : cycle de statut des anomalies
- Branche dédiée `feat/responsable-anomalies-cycle-api` créée après merge de l'historique responsable dans `develop`.
- Endpoint `PATCH /api/responsable/anomalies/:id/statut` ajouté, protégé par rôle `RESPONSABLE`.
- Filtrage par campus appliqué avant toute modification pour empêcher le traitement d'une anomalie hors campus (RG27).
- Transitions autorisées : `DETECTEE -> EN_COURS_TRAITEMENT -> RESOLUE -> CLOTUREE`.
- Le responsable connecté est enregistré dans `traiteeParId`; une observation optionnelle peut être conservée.
- Une entrée d'historique `TRAITEMENT_ANOMALIE` est créée à chaque transition.
### Étape 41 — Frontend responsable : premier branchement API
- Branche dédiée `feat/responsable-frontend-api` créée pour isoler le travail frontend responsable.
- Le bouton "Accès responsable matériel" de l'écran d'identification ouvre maintenant un écran responsable.
- Client API étendu pour accepter l'e-mail simulé du responsable (`karim.benali@ensup.eu`) et les requêtes `PATCH`.
- Service frontend responsable ajouté pour consommer dashboard, emprunts, stock, anomalies, notifications et historique.
- Écran responsable ajouté avec onglets : dashboard, emprunts, stock, anomalies, notifications, historique.
- Les anomalies peuvent être avancées au prochain statut autorisé via l'API.
- Vérification statique effectuée avec l'exécutable Dart direct : `dart analyze` OK. Le wrapper `flutter` reste instable dans cette session et bloque au lancement web automatisé.
### Étape 42 — Frontend responsable : polish opérationnel
- Branche dédiée `feat/responsable-frontend-polish` créée après merge du premier branchement responsable.
- Statuts responsables remplacés par des libellés métier lisibles et des badges colorés.
- Recherche ajoutée sur les onglets emprunts, stock, anomalies, notifications et historique.
- Les lignes de listes affichent désormais une méta-information utile : identifiant emprunt, marque/modèle, date de détection, date de notification ou auteur historique.
- Action "Tout marquer lu" ajoutée dans l'onglet notifications.
- Action export CSV rendue visible dans l'onglet historique avec indication de l'endpoint disponible.
- Vérification statique : `dart analyze` OK.
### Étape 43 — Synchronisation de l'état courant
- Synthèse mise en cohérence avec les étapes 34 à 42 : API et frontend responsable désormais fonctionnels pour la V1.
- Documentation corrigée pour refléter l'existence du README principal.
- Limites restantes explicitées : authentification Azure AD, téléchargement CSV frontend, tests automatisés, OpenAPI et guides utilisateur.
- Commandes de vérification actualisées : build et lint backend verts, analyse Dart directe sans erreur.
### Étape 44 — Refonte premium de l'espace responsable
- Branche dédiée `style/responsable-premium-dashboard` créée depuis `develop`.
- L'espace responsable adopte une structure de supervision distincte du parcours étudiant : barre supérieure métier, navigation latérale sur ordinateur et navigation compacte sur petit écran.
- Dashboard enrichi avec disponibilités, emprunts en cours, retards, anomalies actives, notifications non lues, activité récente et taux de disponibilité du parc.
- Accès directs ajoutés depuis les points d'attention vers les vues emprunts, stock, anomalies et notifications.
- Les listes métier partagent désormais une hiérarchie visuelle, une recherche et des lignes responsives cohérentes avec la charte ENSUP.
- Aucun changement backend ni métier ; rendu validé manuellement dans le navigateur et `dart analyze` sans erreur.
### Étape 45 — Téléchargement CSV de l'historique responsable
- Branche dédiée `feat/responsable-historique-export` créée depuis `develop`.
- Client HTTP étendu pour récupérer un fichier binaire avec son type de contenu et son nom.
- Le bouton `Export CSV` appelle désormais l'endpoint responsable avec l'identité simulée de Karim et déclenche le téléchargement navigateur.
- Un état de chargement bloque les doubles clics et les erreurs API sont affichées dans l'interface.
- Endpoint vérifié au runtime : réponse `200`, type `text/csv`, nom `historique-responsable.csv` et contenu non vide.
- Vérification statique : `dart analyze` sans erreur.
### Étape 46 — Refonte premium de l'espace étudiant
- L'ordre actuel du parcours est confirmé : identification, puis choix `Emprunter` ou `Restituer`.
- Branche dédiée `style/student-premium-workspace` créée depuis `develop`.
- Charte et maquette conservées : couleurs ENSUP, typographies, barre supérieure, coins de marque et structure des écrans.
- Barre supérieure rendue responsive, avec priorité donnée au titre et à l'identité sur les petites largeurs.
- Accueil rééquilibré avec profil identifié, hiérarchie renforcée et cartes d'action adaptatives avec états de survol.
- Catalogue, détail matériel et sélection de restitution enrichis sans modifier leurs données ni leurs actions.
- Détail et checklists passent automatiquement de deux colonnes à une colonne sur écran étroit ; tableau de contrôle et lignes de matériel évitent les débordements.
- Écrans de confirmation et de résultat conservés fonctionnellement et harmonisés via les composants partagés.
- Vérification statique : `dart analyze` sans erreur.
### Étape 47 — Sécurisation SSO Microsoft Entra ID
- Branche dédiée `feat/block-3-azure-sso` créée depuis `develop`.
- Backend configuré avec deux modes : `demo` en développement et `azure` pour une authentification réelle ; le mode démo est explicitement interdit en production.
- Middleware d'authentification refactoré vers un service dédié. En mode Azure, `x-user-email` est ignoré et un Bearer Token Microsoft est obligatoire.
- Validation JWT ajoutée : signature RSA via les clés JWKS Microsoft, algorithme `RS256`, issuer, audience, tenant, scope `access_as_user`, identifiant `oid` et domaines `ensup.eu` / `ensitech.eu`.
- L'utilisateur, son rôle et son campus restent résolus depuis `eme_db`. Un étudiant obtient `403` sur la supervision, tandis que le responsable de démonstration obtient `200`.
- Frontend doté d'un double mode de session. La simulation sélectionne Lucas ou Karim sans pouvoir être utilisée en mode Azure.
- Bibliothèque officielle `@azure/msal-browser` intégrée via un bundle local construit avec esbuild et un pont typé `dart:js_interop`.
- Flux OpenID Connect préparé avec connexion Microsoft, acquisition silencieuse de l'Access Token, appel de `/api/auth/me`, navigation selon le rôle local, restauration après actualisation et déconnexion MSAL.
- Profil authentifié typé et affiché dans les espaces étudiant et responsable : nom, initiales, classe, rôle et campus ne sont plus figés sur les identités de démonstration.
- Vérifications réussies : build et lint backend, analyse Dart, compilation Flutter Web complète et contrôles runtime des rôles en mode démo.
- Limite actuelle : faute d'identifiants Entra ID, le flux Microsoft réel n'a pas encore pu être exécuté. Le QR sécurisé reste un sous-bloc séparé à développer après le SSO.
- Commits : `313bb1c`, `530bd52`, `ef31e2a`, `dc208fa`, `36224f3`.
### Étape 48 — Changement métier : confirmation responsable des écarts
- Décision corrigée après précision métier : le responsable ne valide pas systématiquement chaque départ et chaque retour.
- Au départ, la checklist est préremplie avec l'état de référence, normalement tous les éléments présents. Sans modification, l'emprunt démarre automatiquement.
- Si l'étudiant modifie une valeur au départ, le matériel est réservé, le responsable est alerté et l'état déclaré reste provisoire jusqu'à sa confirmation.
- Au retour, la checklist est automatiquement préremplie avec l'état de départ réellement validé.
- Sans modification au retour, la restitution est clôturée automatiquement.
- Dès que l'étudiant tente de modifier une valeur au retour, le responsable est alerté. Aucun changement de l'état officiel du matériel n'est appliqué avant sa décision.
- La confirmation du responsable authentifie le changement et crée l'anomalie ; un refus conserve l'état précédemment validé.
- Un étudiant ne peut avoir qu'un emprunt bloquant d'une même catégorie. La catégorie est débloquée après la clôture automatique ou la décision du responsable sur un retour modifié.
- Chaque tentative de modification, décision, anomalie et changement d'état officiel est historisé.
- `CLAUDE.md`, `CONTEXT.md`, les règles métier, `TODO.md`, le README et la maquette textuelle sont synchronisés avant toute modification du code.
- Un diagramme Mermaid devient la source de vérité du parcours ; le PNG contradictoire est conservé uniquement comme historique.
### Étape 49 — Messages applicatifs visibles
- Les `SnackBar` Flutter noir et blanc sont remplacés par un composant global affiché en haut de l'écran.
- Quatre niveaux visuels sont distingués : information, succès, alerte et erreur.
- Chaque message dispose d'une icône, d'un titre, d'un texte explicite, d'une fermeture manuelle et d'une disparition automatique.
- Le composant est utilisé dans les parcours étudiant, la connexion et l'espace responsable.
- Vérification réussie avec `dart analyze`.
- Commit : `4ee593c`.
### Étape 50 — Tableau des emprunts responsable
- La liste des emprunts responsable devient un tableau opérationnel sur ordinateur avec une colonne par information : numéro, matériel, étudiant, dates et statut.
- Les en-têtes permettent de trier par matériel, étudiant, date d'emprunt, retour prévu, retour réel et statut.
- Des filtres serveur sont ajoutés pour l'année, le statut et la recherche par matériel, référence ou étudiant.
- Le tableau devient une liste compacte sur petit écran afin d'éviter tout débordement horizontal.
- Les menus déroulants contraignent les libellés longs afin d'éviter les débordements Flutter.
- Les requêtes restent filtrées par campus côté API.
- Vérifications réussies : build et lint backend, analyse Dart, tris et recherches testés sur SQL Server.
### Étape 51 — Tableau du stock matériel responsable
- La liste du stock devient un tableau opérationnel sur ordinateur et conserve une vue compacte sur petit écran.
- Les colonnes présentent la référence, le matériel, la catégorie, la marque et le modèle, l'état général et le statut.
- La recherche et les filtres serveur permettent de cibler un statut ou une catégorie du campus.
- Le tri serveur accepte le matériel, la référence, la catégorie, la marque, l'état général et le statut, dans les deux ordres.
- Les menus de filtres restent sur une ligne et tronquent les libellés trop longs pour prévenir tout débordement.
- Vérifications réussies : build et lint backend, analyse Dart, tris et filtres combinés testés sur SQL Server.
### Étape 52 — Tableau des anomalies responsable
- La liste des anomalies devient un tableau opérationnel sur ordinateur et une liste compacte sur petit écran.
- Les colonnes présentent la date de détection, le type et la description, le matériel, l'étudiant, le statut et l'action suivante.
- La recherche serveur couvre la description, le type, le matériel, la référence et l'identité de l'étudiant.
- Les anomalies peuvent être filtrées par statut et type, puis triées par date, type, matériel, étudiant ou statut.
- Le changement de statut existant reste disponible dans chaque ligne et conserve les transitions métier autorisées.
- Vérifications réussies : build et lint backend, analyse Dart, tris et filtres combinés testés sur SQL Server.
### Étape 53 — Tableau et export filtré de l'historique responsable
- L'historique devient un tableau d'audit sur ordinateur et une liste compacte sur petit écran.
- Les colonnes présentent la date et l'heure, l'action et sa description, l'utilisateur, le matériel, l'emprunt et le point d'opération.
- La recherche serveur couvre l'action, la description, l'utilisateur et le matériel.
- Les événements peuvent être filtrés par action et période, puis triés par date, action, utilisateur, matériel ou emprunt.
- L'export CSV reprend désormais les filtres, la période et l'ordre actifs dans l'interface.
- Vérifications réussies : build et lint backend, analyse Dart, filtre combiné sur 22 événements et export CSV filtré testés sur SQL Server.
### Étape 54 — Correction du rafraîchissement après décision sur un écart
- La décision du responsable était correctement enregistrée en base, mais l'interface affichait ensuite l'erreur Flutter `setState() callback argument returned a Future`.
- La méthode `_rafraichir()` utilisait une fonction fléchée dont l'affectation de `_charger()` retournait accidentellement un `Future` depuis `setState()`.
- Le message de succès était déclenché, puis l'exception du rafraîchissement était interceptée par le même `catch`, qui affichait un faux message d'erreur à sa place.
- Le callback de `setState()` utilise maintenant un bloc synchrone qui affecte le nouveau chargement sans retourner le `Future`.
- La confirmation reste enregistrée, le message de succès reste visible et les données de supervision sont rechargées normalement.
- Vérifications réussies : `dart analyze` sans erreur et serveur Flutter opérationnel sur le port 5000.
---
*Dernière mise à jour : 2026-07-24 — Rafraîchissement après décision responsable corrigé.*