# Cahier de recette — Lot L3 « Médias & listes »

**Périmètre livré** —
Back : PR [#170](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-BACK/pull/170) (EA-020 contrat de listes) ·
[#171](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-BACK/pull/171) (EA-022 export XLSX) ·
[#172](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-BACK/pull/172) (EA-021 préférences) ·
[#173](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-BACK/pull/173) (EA-035 positions de lecture) ·
[#174](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-BACK/pull/174) (EA-036 documents par support) ·
[#187](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-BACK/pull/187) (correctif CORS).
Front : PR [#29](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-FRONT/pull/29) (EA-019 DataTable) ·
[#30](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-FRONT/pull/30) (EA-023 export) ·
[#31](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-FRONT/pull/31) (EA-021 colonnes) ·
[#33](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-FRONT/pull/33) (EA-034 lecteur) ·
[#34](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-FRONT/pull/34) (EA-035 reprise) ·
[#35](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-FRONT/pull/35) (EA-040 complétion) ·
[#36](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-FRONT/pull/36) (EA-037 téléchargement) ·
[#37](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-FRONT/pull/37) (EA-026 retour).

**Déroulée le 09/08/2026 sur `staging.efektiv-academie-dev.com`**, par Claude sous session réelle
d'Enguerran (chaque profil ouvert par lui, aucun mot de passe manipulé), profil par profil.

**Résultat global : conforme.** Les onze critères fonctionnels sont validés. Cinq écarts trouvés,
dont un corrigé le jour même ; aucun n'est bloquant pour la mise en recette.

---

## S1 · Listes — contrat, tri, filtres (EA-020 #30, EA-019 #29)

- ✅ **S1.1** Tri d'une colonne en **trois temps** : croissant → décroissant → retour au tri par
  défaut. Vérifié sur « Nom d'utilisateur », écran Admin › Utilisateurs.
- ✅ **S1.2** Facettes **Rôle** et **Statut** cumulables : « Formateur » + « Actif » → 5 lignes,
  toutes conformes.
- ✅ **S1.3** Un tri non déclaré au schéma est **ignoré** (`sort[]=password` → tri par défaut),
  vérifié par l'API.
- ✅ **S1.4** `per_page` **borné** : une demande de 5 000 lignes est ramenée à 100.
- ✅ **S1.5** La recherche ne **relâche pas** le périmètre du rôle (testé sur le périmètre manager).

## S2 · Préférences de colonnes (EA-021 #31 / back #172)

- ✅ **S2.1** Menu « Colonnes » : poignées de glissement, cases à cocher, colonne **Action
  verrouillée** (non décochable), bouton Réinitialiser.
- ✅ **S2.2** Masquer « E-mail » → la colonne disparaît immédiatement de la table.
- ✅ **S2.3** **Persistance serveur** : après **rechargement complet de la page**, la colonne reste
  masquée. C'est le critère central — ce n'est pas du `localStorage`.
- ✅ **S2.4** « Réinitialiser » → la colonne revient, et l'état est persisté lui aussi.
- ✅ **S2.5** Cloisonnement : les préférences d'un utilisateur ne sont pas visibles d'un autre
  (vérifié par l'API, deux comptes).

## S3 · Export XLSX (EA-022 #171, EA-023 #36)

- ✅ **S3.1** Le fichier reflète **la vue courante** : export avec « Formateur + Actif » actifs →
  exactement les 5 lignes affichées.
- ✅ **S3.2** Fichier XLSX **réellement valide**, ouvert et inspecté : en-têtes FR
  (Prénom, Nom, Email, Rôle, Statut, Manager de rattachement, Créé le), ligne d'en-tête en gras.
- ✅ **S3.3** Le « Manager de rattachement » est résolu (nom complet, pas un identifiant).
- ✅ **S3.4** Le fichier n'est **pas paginé** (25 lignes exportées avec `per_page` à 20).
- ❌ **S3.5** → ✅ **corrigé le jour même** : le fichier arrivait sous le nom `utilisateurs.xlsx`
  au lieu de `utilisateurs-2026-08-09.xlsx`. Cause : `Content-Disposition` non exposé en CORS,
  donc invisible au JavaScript. Corrigé par [#187](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-BACK/pull/187),
  déployé et revérifié. **Invisible en testant l'API seule** : `curl` ignore le CORS.
- ⚠️ **S3.6** Le rôle est exporté en **slug technique** (`teacher`) quand l'écran affiche
  « Formateur ». Écart assumé jusqu'à EA-043 (#53), qui doit produire un référentiel unique.

## S4 · Lecteur unifié et tracking (EA-034 #33, EA-035 #34, EA-040 #35)

- ✅ **S4.1** Le lecteur YouTube démarre depuis la liste des leçons, sur la page apprenant **et**
  sur la page admin — même composant, deux écrans.
- ✅ **S4.2** **Le tracking se remplit enfin depuis l'interface** : `lesson_views` (leçon 485,
  12:53:42) et `media_progress` (0 s / **600 s**). La durée totale correspond exactement à la vidéo
  (10:00), ce qui prouve que `onReady` remonte bien la durée du lecteur.
- ✅ **S4.3** **Reprise à la seconde** — validée par Enguerran.

> ⚠️ **À retenir pour toute lecture d'indicateur** : avant L3, le front n'appelait **aucune** route
> de tracking. `lesson_views` et `media_progress` ne recevaient rien de l'interface. L'historique
> de mesure démarre donc à la mise en production de L3 — c'est ce qui justifie la mention
> « données disponibles depuis [date] » du cockpit (#61).

## S5 · Documents par support (EA-036 #174, EA-037 #36)

- ✅ **S5.1** Un document rattaché à un support précis n'apparaît **que** sur ce support.
- ✅ **S5.2** Le bouton de téléchargement est **présent uniquement** sur le support porteur d'un
  document, absent partout ailleurs.
- ✅ **S5.3** Le téléchargement passe par la **route authentifiée** (200, `application/pdf`),
  donc journalisé.
- ⚠️ **S5.4** Au premier rendu, le bouton s'affiche une fraction de seconde comme une **pastille
  blanche vide** (icône non encore peinte) avant d'afficher son icône. Cosmétique, à reprendre au
  passage sur le design.

## S6 · Navigation retour (EA-026 #37)

- ✅ **S6.1** Le retour est un vrai `<button>` nommé, donc atteignable au clavier.
- ⚠️ **S6.2** L'accès **direct par URL** à une page cours (`/courses/<slug>`) affiche une page vide :
  le slug est résolu depuis le `localStorage`, alimenté en naviguant depuis une liste.
  **Limite connue et tracée** — issue #48 (EA-038, résolution des slugs côté API), reportée en V2.
  Ce n'est pas une régression de L3.

---

## S7 · Cloisonnement par rôle — déroulé sur les six profils

Protocole appliqué à chacun : périmètre de la **liste**, périmètre de l'**export**, cohérence des
**compteurs** du tableau de bord.

| Profil | Rattachés | Liste | Export | Compteurs | Écran de liste |
|---|---|---|---|---|---|
| **Admin** | — | ✅ 96 | ✅ | ✅ | ✅ complet (écran pilote) |
| **Manager** (`manager`) | 2 (test) | ✅ 2 | ✅ 2 | ❌ **96 / 85** | ⚠️ sans facettes ni export |
| **Manager général** | 1 | ✅ 1 | ✅ 1 | ✅ 1 / 1 | ❌ absent |
| **Manager d'équipe** (`sub_manager`) | 4 | ✅ 4 | ✅ 4 | ✅ 4 / 1 | ❌ absent |
| **Directeur** | 1 | ✅ 1 | ✅ 1 | ✅ 1 / 1 | ❌ absent |
| **Formateur** | 0 | — 404 | — 404 | ✅ compteurs propres | ❌ aucun (ni route, ni écran) |
| **Apprenant** | — | s.o. | s.o. | s.o. | s.o. |

**Le cloisonnement des listes et des exports est correct sur tous les rôles.** Aucune fuite de
données constatée : un formateur reçoit 403 sur les routes des autres rôles, 404 sur les siennes
inexistantes.

### Écarts relevés au passage

- ❌ **Compteurs hors périmètre pour le seul rôle `manager`** → [#188](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-BACK/issues/188).
  Cause : `DashBoardController` énumère `sub_manager || director || manager_general` à **quatre
  endroits** et oublie `manager`. Correctif proposé : une méthode « ce rôle a-t-il un périmètre
  hiérarchique ? » plutôt que quatre énumérations — sinon le futur `manager_rh` sera oublié pareil.
- ⚠️ **Trois rôles managériaux sur quatre n'ont aucun écran de liste** (manager général, manager
  d'équipe, directeur). Les routes existent et fonctionnent, rien ne les expose. C'est ce
  qu'apportent EA-049/EA-052/EA-053 en L4.
- ⚠️ **Un manager ne peut pas exporter depuis l'interface** : la route répond, le bouton n'existe
  que sur l'écran admin. Renforce [#180](https://github.com/AAZTEKDEV/EFEKTIVACADEMIE-BACK/issues/180)
  (généralisation de la DataTable).
- ⚠️ **Le formateur n'a ni liste ni export**, alors que la matrice F11 lui accorde ses formations
  et ses résultats pédagogiques. Ce n'est pas une faille — rien ne fuit — mais un manque
  fonctionnel, couvert par EA-054 (#64), aujourd'hui en V2.
- ⚠️ **Onglets Pilotage non conformes** : « Mes cours », « Historique » et « Mes favoris »
  apparaissent encore côté Pilotage. Couvert par EA-056 (#68) en L4.

### Le rôle `manager` — vérifié en production

Le front affiche `manager` sous le libellé **« Manager RH »** et `sub_manager` sous **« Manager »**.
En production : **6 comptes `manager`**, **16 comptes `sub_manager`**.

Conséquence pour EA-043 (#53) : rétrograder `manager` reviendrait à rétrograder les six personnes
que l'interface appelle aujourd'hui « Manager RH ». Le détail et les décisions attendues sont dans
le CA de #53.

---

## Suites décidées

| Écart | Traitement |
|---|---|
| Nom de fichier d'export (CORS) | ✅ corrigé et déployé le 09/08 |
| Compteurs hors périmètre (#188) | entre L4 et L5, **avec #18 et #163** — même cause racine |
| Écrans de liste manquants | L4 (EA-049, EA-052, EA-053) |
| Export depuis les écrans managers (#180) | entre L4 et L5 |
| Onglets Pilotage (#68) | L4 |
| Deeplink par slug (#48) | V2 |
| Liste formateur (#64) | V2 |
