Skip to content

Livrable documentaire uniquement — Plan d’implémentation des parcours UX NEXORA

1. Objet du document

Ce document est le second plan d’implémentation de NEXORA. Il complète architecture/video-pipeline-implementation-plan.md et décrit les changements nécessaires pour rendre cohérents, fiables, accessibles et testables les parcours :

  • enseignant ;
  • administrateur et directeur ;
  • parent ;
  • sélection d’une classe, d’une séance et d’un élève ;
  • consultation du dashboard et de l’historique ;
  • lecture d’une vidéo avec détections ;
  • upload et suivi du traitement vidéo ;
  • rapports ;
  • suivi pédagogique ;
  • messagerie et demandes de rendez-vous ;
  • statistiques et assistant analytique ;
  • authentification, navigation, états d’erreur et responsive.

Le présent livrable est exclusivement documentaire. Sa création ne doit entraîner aucune modification du code, du schéma PostgreSQL, des dépendances, des données ou de la configuration de déploiement.

Il est écrit pour être remis à un exécuteur qui ne doit pas avoir à redécouvrir les décisions fonctionnelles. Avant chaque lot, l’exécuteur doit néanmoins relire le code courant et ajuster les numéros de migration si d’autres changements ont été intégrés entre-temps.

2. Résultat attendu

À la fin de l’implémentation :

  1. chaque action visible produit un résultat réel, un état de chargement ou une erreur actionnable ;
  2. un parent ne peut voir que ses enfants, leurs séances, leurs rapports publiés et les conversations auxquelles il appartient ;
  3. l’enseignant retrouve une séance et un élève après un rafraîchissement ou un retour navigateur ;
  4. la lecture vidéo utilise une sélection d’élèves compréhensible, des cadres temporellement exacts et un état courant synchronisé avec la vidéo ;
  5. un upload de plusieurs gigaoctets ne charge jamais le fichier entier en mémoire, peut être annulé et affiche une progression utile ;
  6. le suivi d’un élève possède un responsable, un statut, une échéance, une prochaine action et un historique minimal ;
  7. la messagerie constitue de vraies conversations bidirectionnelles et les demandes de rendez-vous sont persistées ;
  8. les rapports sont toujours générés dans un contexte explicitement choisi, relus, puis publiés ;
  9. les interfaces ont une langue, des libellés, des unités, des états vides et des erreurs cohérents ;
  10. tous les parcours essentiels fonctionnent au clavier et aux largeurs 360, 768, 1280 et 1440 px ;
  11. tout code ajouté ou modifié atteint 100 % de couverture en lignes, branches, fonctions et instructions, complété par des tests E2E et d’accessibilité pour ce que la couverture instrumentée ne prouve pas.

3. Discipline d’ingénierie obligatoire

Les règles suivantes sont des critères de revue, pas des recommandations :

  • Corriger le contrat et les autorisations serveur avant de modifier l’apparence.
  • Une donnée métier a une source de vérité unique.
  • Les URL représentent l’état navigable ; localStorage ne sert que de préférence secondaire et jamais d’autorité.
  • Un bouton sans effet, un catch silencieux, un faux succès ou une donnée codée en dur sont interdits.
  • Toute mutation est explicite, idempotente lorsque nécessaire, et retourne une erreur structurée.
  • Toute réponse HTTP est vérifiée avant lecture de son JSON.
  • Aucun nom d’état machine n’est affiché directement à l’utilisateur.
  • Ne pas introduire un framework d’état, une bibliothèque de design ou une couche générique si deux usages réels ne la justifient pas.
  • Ne pas déplacer du code sans bénéfice fonctionnel ou testable.
  • Ne pas utiliser any dans le nouveau code ; réduire les any rencontrés dans les fonctions effectivement modifiées.
  • Les actions destructrices précisent leur cible et leurs conséquences ; elles offrent Annuler et, lorsque possible, Rétablir.
  • Les erreurs techniques sont journalisées côté serveur avec un identifiant de corrélation ; l’interface reçoit un message sûr et une action de récupération.
  • Les tests doivent échouer avant la correction correspondante puis passer après.
  • Un lot n’est pas fusionné si pnpm check, pnpm lint, les tests unitaires, les tests de composants, les tests API et les scénarios E2E de ce lot ne passent pas.

4. Périmètre et non-objectifs

Inclus

  • Refonte progressive des parcours existants, sans réécriture totale de SvelteKit.
  • Corrections d’autorisation requises par l’expérience parent et la messagerie.
  • Migrations additives nécessaires au suivi et aux conversations.
  • Composants d’interface partagés seulement lorsqu’ils ont plusieurs consommateurs.
  • Instrumentation de test, accessibilité et observabilité UX.
  • Mise à jour de la documentation fonctionnelle après chaque lot.

Exclus

  • Changement de la taxonomie de scoring.
  • Entraînement de nouveaux modèles IA.
  • Refonte du pipeline bronze/silver/gold.
  • Application mobile native.
  • Temps réel par WebSocket lors de la première passe ; un polling borné et visible est suffisant.
  • Nouveau design de marque.
  • Traductions multilingues complètes. La première livraison utilise le français comme langue produit unique tout en préparant un catalogue de libellés.

5. État actuel qui motive le plan

Les problèmes ci-dessous doivent être transformés en tests de non-régression :

  • StudentTable.svelte affiche « 24 enrolled » en dur et expose un tri sans effet.
  • HistoryView.svelte marque la première ligne comme étant aujourd’hui, quelle que soit sa date, et imbrique une action Report dans une ligne cliquable.
  • Le dashboard conserve l’onglet dans un état local et non dans l’URL.
  • Les contrôles classe/séance sont affichés sur des vues qui n’en ont pas besoin.
  • Le panneau étudiant réserve de l’espace même lorsqu’aucun élève n’est sélectionné.
  • Le lecteur vidéo sépare la sélection des cadres de la sélection du graphique.
  • OverlayCanvas.svelte peut afficher plusieurs anciennes boîtes d’un même track et choisit un état sans le relier au temps courant.
  • le lecteur charge les chunks d’overlay en série à l’ouverture.
  • crypto.ts appelle file.arrayBuffer() pour le SHA-256, incompatible avec un fichier annoncé jusqu’à 20 Gio.
  • l’upload n’a ni annulation, ni débit, ni estimation, ni reprise intelligible.
  • l’administration contient neuf onglets plats et plusieurs implémentations de l’upload vidéo.
  • les rapports peuvent être générés pour la première séance trouvée sans confirmation explicite.
  • le suivi est une liste supprimable, sans statut ni prochaine action.
  • le filtre parent de /api/messages sélectionne les messages écrits par le parent, ce qui exclut les réponses de l’enseignant.
  • la sélection child/lesson du portail parent n’est pas strictement réduite aux liens du parent avant interrogation des données pédagogiques.
  • « Request a Meeting » utilise une alerte sans persistance.
  • les boutons Confirmer et Rejeter du chat analytique ont le même effet local.
  • la connexion affiche des comptes de démonstration sans garde d’environnement.
  • le produit mélange français, anglais, codes machine, /10, % et 0–100.

6. Principes UX et contrats transversaux

6.1 Langue, termes et unités

La première version cible le français :

Concept interne Libellé produit
post Séance
history Historique
stats Statistiques
messages Messages
followup Suivis
scheduled Planifiée
uploading Envoi en cours
processing Analyse en cours
completed / ready Prête
failed Échec du traitement
cancelled Annulée
deep Engagement soutenu
active Engagement actif
passive Engagement passif
disengaged Désengagement observé
withdrawal Retrait observé
unobserved Non observé

Règles :

  • Le score d’engagement est présenté sur 100 partout dans l’interface.
  • Les calculs internes peuvent rester sur 10, mais la conversion vit dans une seule fonction pure testée.
  • Une valeur manquante est « Non disponible », jamais 0 et jamais — sans aide.
  • Toutes les dates utilisent le fuseau configuré de l’établissement et Intl.DateTimeFormat('fr-FR').
  • « Retrait observé » doit être accompagné d’un contexte et ne doit jamais être présenté comme un diagnostic.

6.2 États de toute ressource distante

Chaque vue asynchrone modélise explicitement :

type AsyncState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'empty'; reason: string }
  | { status: 'error'; message: string; requestId?: string };

Ne pas créer un store global pour ce type. L’utiliser localement dans les vues qui en ont besoin. Une réponse antérieure peut rester visible pendant un rafraîchissement, mais elle doit être marquée comme telle.

6.3 Contrat d’erreur HTTP

Les endpoints modifiés retournent :

type ApiError = {
  error: {
    code: string;
    message: string;
    requestId: string;
    fieldErrors?: Record<string, string>;
    retryable: boolean;
  };
};

Le client ne montre jamais errorMessage, une stack, une clé R2, un identifiant Runpod ou une requête SQL. Il peut montrer requestId sous « Détails pour le support ».

6.4 Feedback des mutations

  • Le bouton déclencheur passe en attente et est le seul élément désactivé, sauf si la transaction impose de verrouiller tout le formulaire.
  • Une mutation réussie met d’abord à jour la donnée visible puis annonce le résultat.
  • Une erreur conserve les saisies.
  • Une suppression récupérable utilise un toast avec « Rétablir » pendant 8 secondes.
  • Une annulation de pipeline ou une publication de rapport utilise une confirmation décrivant précisément la cible.

6.5 Accessibilité

  • Navigation complète au clavier.
  • Focus visible avec contraste AA.
  • Modales : role="dialog", aria-modal, titre référencé, focus initial, boucle de focus, fermeture par Échap, restitution du focus au déclencheur.
  • Les lignes interactives sont des liens ou boutons, jamais des div cliquables.
  • Les icônes seules ont un nom accessible.
  • Les statuts utilisent texte et icône en plus de la couleur.
  • Les mises à jour de progression utilisent une région aria-live="polite" sans annoncer chaque frame.
  • Respect de prefers-reduced-motion.
  • Cible tactile minimale de 44 × 44 px.

7. Parcours cibles détaillés

7.1 Enseignant — examiner une séance et agir

  1. L’enseignant arrive sur /dashboard?view=session.
  2. La dernière classe utilisée est proposée si elle est encore autorisée ; sinon la première classe autorisée est sélectionnée.
  3. Si aucune séance n’est choisie, une page vide utile explique comment en choisir une.
  4. L’enseignant ouvre le sélecteur, navigue par mois ou liste, puis choisit une séance.
  5. L’URL devient /dashboard?view=session&class=<id>&session=<id> sans rechargement complet.
  6. Le résumé de classe apparaît avec date, matière, statut des données et indicateurs.
  7. La liste affiche le nombre réel d’élèves, une recherche et un tri réellement actif.
  8. L’enseignant choisit un élève par clic ou clavier.
  9. Un drawer s’ouvre sans réduire la zone centrale sur petit écran ; l’URL reçoit student=<ref>.
  10. Le drawer présente état actuel, tendance, signaux, degré d’observation et actions.
  11. « Voir la vidéo » ouvre le lecteur au contexte temporel pertinent.
  12. « Créer un suivi » ouvre un formulaire prérempli avec séance et élève.
  13. Après sauvegarde, le suivi apparaît avec son statut et une action suivante.
  14. Retour navigateur restaure successivement l’élève, la séance puis la vue précédente.

7.2 Administrateur — déposer puis superviser une vidéo

  1. L’administrateur ouvre Administration > Cours et médias > Vidéos.
  2. Il filtre ou choisit une séance planifiée.
  3. L’interface affiche la galerie attendue : nombre d’élèves, photos valides, absents et avertissements non bloquants.
  4. Il sélectionne une vidéo et voit nom, taille, type et durée si disponible.
  5. Une validation client légère rejette un type ou une taille manifestement invalide ; la validation serveur reste l’autorité.
  6. L’upload direct R2 démarre avec étapes, octets envoyés, pourcentage, débit et ETA.
  7. « Annuler » demande confirmation, interrompt les requêtes et annule le multipart.
  8. Si le réseau tombe, les parties confirmées sont conservées et « Reprendre » tente les parties manquantes tant que l’upload serveur est encore valide.
  9. À la complétion, la ligne bascule automatiquement vers « Analyse en attente » puis les étapes du pipeline.
  10. Un traitement échoué affiche un résumé sûr, une action Réessayer et l’identifiant support. Réessayer crée une nouvelle tentative.
  11. Un traitement réussi affiche couverture, élèves reconnus/non reconnus, alertes et action « Ouvrir le lecteur ».

7.3 Lecture vidéo — filtrer et comprendre

  1. La modale s’ouvre avec tous les élèves sélectionnés.
  2. Le premier élève visible devient le focus du panneau de détail sans désélectionner les autres.
  3. Les cases contrôlent uniquement la visibilité des cadres.
  4. Un bouton ou clic explicite « Mettre en focus » choisit l’élève du graphique.
  5. Cliquer un cadre donne le focus à cet élève et conserve les autres cadres.
  6. « Tout sélectionner » et « Tout désélectionner » sont toujours disponibles.
  7. Le graphique affiche la position du playhead et l’état de l’élève au temps courant.
  8. Une boîte disparaît après la durée de tolérance prévue par le contrat overlay.
  9. Une donnée non observée est distinguée d’une donnée faible ou indisponible.
  10. Si les overlays échouent, la vidéo reste lisible et un bouton « Réessayer les détections » est proposé.

7.4 Suivi pédagogique — créer, traiter, résoudre

  1. Depuis un élève ou la vue Suivis, un membre du staff crée un suivi.
  2. Il choisit priorité, responsable, échéance facultative et prochaine action.
  3. Le suivi commence à open.
  4. Le responsable le passe à in_progress, ajoute une note et peut contacter le parent.
  5. Le suivi peut être filtré par responsable, statut, priorité, échéance et classe.
  6. Le passage à resolved exige une note de résolution courte.
  7. Une résolution peut être rouverte ; l’historique conserve acteur et date.
  8. Supprimer est réservé au nettoyage administratif. Le parcours normal utilise Résoudre.

7.5 Parent — comprendre puis échanger

  1. Le parent arrive sur son premier enfant lié ou retrouve le dernier enfant autorisé.
  2. Le serveur ne renvoie que les séances auxquelles cet enfant participe.
  3. La séance par défaut est la plus récente chronologiquement.
  4. La page explique en langage simple trois indicateurs maximum, leur tendance et la qualité d’observation.
  5. Le résumé n’est visible que s’il est publié et identifie sa date de publication.
  6. Le parent peut ouvrir la conversation relative à cet enfant.
  7. Les messages enseignant et parent sont visibles chronologiquement dans le même fil.
  8. Une demande de rendez-vous est enregistrée avec statut demandé, confirmé, refusé ou terminé.
  9. Les changements d’enfant et de séance utilisent la navigation SvelteKit et restent compatibles avec précédent/suivant du navigateur.

7.6 Rapport — générer, relire, publier

  1. Le staff choisit explicitement type, élève éventuel et séance.
  2. Un récapitulatif matière/date/élève est affiché avant génération.
  3. Le brouillon est généré sans publication automatique.
  4. Un écran de relecture affiche provenance, données manquantes et avertissements.
  5. La publication demande confirmation et enregistre l’auteur et la date.
  6. Le parent ne voit que la dernière version publiée qui lui est autorisée.
  7. Une nouvelle génération ne remplace jamais silencieusement la version publiée.

7.7 Statistiques — comprendre avant d’explorer

  1. La vue s’ouvre en mode Vue d’ensemble avec 4 à 6 indicateurs expliqués.
  2. Les filtres actifs sont visibles et supprimables individuellement.
  3. « Explorer » ouvre le constructeur BI existant.
  4. Ajouter, modifier et supprimer un widget produit un feedback et permet d’annuler.
  5. L’assistant propose un périmètre ; Confirmer exécute, Rejeter annule explicitement.
  6. Une erreur conserve la question de l’utilisateur et permet Réessayer.
  7. La réponse relie chaque conclusion aux éléments de preuve disponibles.

8. Architecture d’implémentation

8.1 État navigable

Introduire un parseur/sérialiseur pur pour :

type DashboardView = 'session' | 'history' | 'stats' | 'messages' | 'followups' | 'admin';

type DashboardLocation = {
  view: DashboardView;
  classId: number | null;
  sessionId: number | null;
  studentRef: string | null;
  adminSection: 'structure' | 'media' | 'publications' | 'advanced' | null;
  adminTab: string | null;
};

Fichier à créer :

  • web/src/lib/navigation/dashboard-location.ts

Le parseur rejette les nombres invalides et les valeurs inconnues. Les composants appellent goto avec replaceState pour une préférence non significative et pushState pour une vraie étape du parcours. Aucun composant enfant ne lit directement window.location.

8.2 Catalogue de libellés

Créer :

  • web/src/lib/i18n/fr.ts — objets constants et fonctions exhaustives pour statuts, états, erreurs connues et formats ;
  • web/src/lib/format.ts — score, date, durée, octets et noms complets ;
  • web/src/lib/types/domain.ts — types d’affichage partagés réellement utilisés.

Ne pas ajouter de bibliothèque i18n tant qu’une seconde langue n’est pas demandée. Le catalogue évite uniquement les chaînes contradictoires et prépare une future extraction.

8.3 Primitives UX minimales

Modifier :

  • web/src/lib/components/modals/Modal.svelte ;
  • web/src/lib/components/ui/Toast.svelte ;
  • web/src/routes/layout.css.

Créer seulement parce qu’au moins deux vues en ont besoin :

  • web/src/lib/components/ui/AsyncPanel.svelte — loading/empty/error/retry ;
  • web/src/lib/components/ui/ConfirmDialog.svelte — confirmation accessible ;
  • web/src/lib/components/ui/StatusBadge.svelte — statut texte + icône ;
  • web/src/lib/components/ui/Drawer.svelte — panneau étudiant et détails mobiles.

Ne pas créer Button, Card, Stack, Box ou un système de tokens complet. Garder les éléments HTML natifs tant qu’une primitive n’élimine pas une duplication réelle.

8.4 Couche client HTTP

Créer :

  • web/src/lib/http/api.ts

Elle ne doit faire que :

  • vérifier response.ok ;
  • parser les succès et ApiError ;
  • préserver AbortError ;
  • retourner une erreur typée ;
  • accepter un AbortSignal.

Elle ne contient ni cache global, ni logique métier, ni toast.

9. Lots d’implémentation ordonnés

Les lots doivent être réalisés dans l’ordre. Un lot peut être divisé en plusieurs PR, mais une PR doit rester verticale : contrat, interface et tests du même comportement.

Lot 0 — Baseline de test et contrats

Étapes

  1. Exécuter et archiver les résultats de pnpm check, pnpm lint, pnpm test et pnpm test:e2e avant modification.
  2. Corriger uniquement les défauts de configuration qui empêchent la suite de partir : playwright.config.ts contient actuellement deux propriétés workers.
  3. Ajouter les scripts test:coverage, test:e2e:a11y et test:e2e:mobile.
  4. Configurer des seuils de 100 % pour lignes, branches, fonctions et instructions des nouveaux modules purs, endpoints modifiés et composants inclus dans le lot.
  5. Ajouter un contrôle des fichiers modifiés afin qu’un fichier exécutable touché ne disparaisse pas du rapport de couverture.
  6. Créer des builders de test explicites pour utilisateurs, classes, séances, élèves, vidéos, rapports et liens parents. Ils doivent produire des valeurs déterministes.
  7. Ajouter data-testid seulement lorsqu’un rôle, un nom ou un libellé stable ne peut pas cibler correctement l’élément.

Fichiers à modifier

  • web/package.json
  • web/vitest.config.ts
  • web/vitest.components.config.ts
  • web/playwright.config.ts
  • web/e2e/hydration.ts
  • docker-compose.test.yml
  • .github/workflows/* si une CI existe au moment de l’exécution

Fichiers à créer

  • web/tests/helpers/builders.ts
  • web/tests/helpers/http.ts
  • web/tests/helpers/accessibility.ts
  • web/scripts/verify-touched-coverage.mjs

Tests

  • Test unitaire du vérificateur de couverture avec fichier à 100 %, fichier incomplet, fichier absent et fichier exclu légitimement.
  • Test de fumée Playwright pour chaque rôle.
  • Test prouvant que les erreurs de console, d’hydratation et les requêtes 5xx non attendues font échouer un scénario E2E.

Sortie du lot

  • Baseline documentée.
  • Aucune baisse de couverture.
  • Les quatre commandes de validation peuvent être lancées en local et en CI.

Lot 1 — Autorisations parent, conversations et rendez-vous

Ce lot précède toute amélioration visuelle du portail parent.

Modèle de données

Créer une table message_threads :

  • id UUID primaire ;
  • parent_user_id non nul ;
  • student_id FK vers students non nul ;
  • assigned_staff_id nullable ;
  • last_message_at ;
  • created_at, updated_at ;
  • contrainte unique (parent_user_id, student_id) pour la première version.

Ajouter thread_id à messages, puis backfiller les messages parent existants. Les messages staff historiques sans destinataire parent déterminable restent dans une conversation legacy staff-only et ne sont jamais exposés à tous les parents liés à un élève. Ajouter updated_at et valider les transitions de statut par kind.

Étapes serveur

  1. Écrire la migration additive et son rollback manuel documenté.
  2. Centraliser la vérification parent → student dans une fonction testable.
  3. Remplacer le filtrage par auteur de /api/messages par un filtrage sur thread.
  4. Exiger threadId ou résoudre sans ambiguïté (parentUserId, studentId) à l’écriture.
  5. Retourner les messages dans l’ordre chronologique ; paginer avec curseur stable (createdAt, id).
  6. Autoriser le parent à créer message et meeting, jamais followup.
  7. Autoriser le staff à répondre seulement dans un thread qu’il peut consulter.
  8. Valider les transitions rendez-vous : requested → confirmed|declined, confirmed → done|declined ; aucune autre.
  9. Dans le loader parent, valider child dans les liens avant tout appel Gold.
  10. Construire la liste des séances à partir de la présence réelle de l’enfant et la trier par date décroissante.
  11. Une valeur URL interdite doit être remplacée par une valeur autorisée, sans fuite permettant de confirmer l’existence de l’autre élève.

Étapes interface

  1. Adapter Messaging.svelte à une liste de threads avec dernier message et statut.
  2. Ajouter loading, vide, erreur et retry.
  3. Afficher les messages du plus ancien au plus récent et faire défiler vers le dernier uniquement à l’ouverture ou après son propre envoi.
  4. Conserver le brouillon si l’envoi échoue.
  5. Remplacer l’alerte de ParentReportModal.svelte par la création persistée d’une demande de rendez-vous.
  6. Afficher le statut, la prochaine étape et l’auteur de chaque changement.
  7. Ne jamais labelliser un message enseignant comme venant de « NEXORA ».

Fichiers à modifier

  • web/src/lib/server/db/schema.ts
  • web/src/routes/api/messages/+server.ts
  • web/src/routes/api/messages/students/+server.ts
  • web/src/routes/parent/+page.server.ts
  • web/src/routes/parent/+page.svelte
  • web/src/lib/components/messaging/Messaging.svelte
  • web/src/lib/components/views/MessagingBoard.svelte
  • web/src/lib/components/modals/ParentReportModal.svelte
  • web/scripts/seed_accounts.ts
  • web/scripts/seed_fake_data.ts

Fichiers à créer

  • web/drizzle/0002_ux_message_threads.sql ou prochain numéro libre
  • web/src/lib/server/messaging/access.ts
  • web/src/lib/server/messaging/service.ts
  • web/src/lib/server/messaging/types.ts
  • web/src/routes/api/messages/threads/+server.ts
  • web/src/routes/api/messages/threads/[threadId]/+server.ts
  • web/src/routes/api/messages/threads/[threadId]/status/+server.ts

Tests unitaires et API — couverture de toutes les branches

  • Parent lié : liste son thread et voit messages parent + staff.
  • Parent non lié : 403 à la création, 404 neutre ou liste vide à la lecture selon le contrat choisi ; aucune fuite de nom.
  • Parent A ne lit pas le thread du parent B pour le même enfant.
  • Staff autorisé répond au bon thread.
  • Rôle anonyme, parent, teacher, director et admin sur chaque méthode.
  • student_id, threadId, contenu vide, kind inconnu et curseur invalide.
  • Toutes les transitions de rendez-vous valides et invalides.
  • Pagination sans doublon lorsque deux messages partagent le même timestamp.
  • URL parent avec enfant autorisé, interdit, absent et supprimé.
  • URL parent avec séance autorisée, autre séance, aucune séance et séance sans enfant.
  • Sélection de la séance la plus récente sur la date réelle.
  • Échec DB avant/après insert et absence de faux succès.

Tests de composants et E2E

  • Brouillon préservé après 500 puis envoyé après retry.
  • Réponse staff visible après reconnexion du parent.
  • Demande de rendez-vous créée, confirmée, refusée et terminée.
  • Navigation clavier du fil et annonce accessible du nouvel état.
  • Parent manipulant les query params : aucune donnée étrangère dans DOM, réseau ou réponse sérialisée.

Lot 2 — Shell, navigation URL et responsive

Étapes

  1. Implémenter et tester dashboard-location.ts.
  2. Au chargement, interpréter l’URL puis vérifier classe, séance et élève contre les données autorisées retournées par le serveur.
  3. Remplacer activeTab local par view dans l’URL.
  4. Utiliser de vrais liens pour les vues et aria-current="page".
  5. Conserver localStorage uniquement comme préférence de classe/séance si l’URL n’en fournit pas et si le serveur confirme l’accès.
  6. Afficher Controls.svelte seulement pour Séance et les sous-vues explicitement dépendantes d’une séance.
  7. Sur mobile, remplacer les onglets par un bouton Menu ouvrant une navigation accessible ; ne pas créer de scroll horizontal caché.
  8. Transformer le panneau étudiant en drawer conditionnel fermé par défaut.
  9. À la fermeture du drawer, retirer student de l’URL et rendre le focus à la ligne.
  10. En cas d’URL invalide, normaliser l’URL avec replaceState et montrer un message uniquement si l’utilisateur doit agir.

Fichiers à modifier

  • web/src/routes/dashboard/+page.server.ts
  • web/src/routes/dashboard/+page.svelte
  • web/src/lib/components/layout/Topbar.svelte
  • web/src/lib/components/layout/Controls.svelte
  • web/src/lib/components/dashboard/RightPanel.svelte
  • web/src/routes/layout.css

Fichiers à créer

  • web/src/lib/navigation/dashboard-location.ts
  • web/src/lib/navigation/dashboard-location.test.ts
  • web/src/lib/components/ui/Drawer.svelte
  • web/tests/unit/components/Topbar.test.ts
  • web/tests/unit/components/Drawer.test.ts
  • web/e2e/navigation.spec.ts
  • web/e2e/responsive-shell.spec.ts

Tests

  • Parse/sérialisation de chaque vue et combinaison de paramètres.
  • Valeur inconnue, nombre négatif, NaN, paramètre dupliqué et caractères encodés.
  • Précédent/suivant entre vue, séance et élève.
  • Rafraîchissement sur un deep link valide.
  • Deep link vers ressource interdite ou archivée.
  • Navigation admin absente pour les autres rôles.
  • Menu mobile au clavier, Échap, clic extérieur et restitution du focus.
  • Viewports 360×800, 768×1024, 1280×800 et 1440×900 sans débordement horizontal.

Lot 3 — Dashboard enseignant et sélection de séance

Étapes

  1. Remplacer les tableaux any[] du dashboard par des DTO nommés.
  2. Créer un chargement annulable : changer rapidement de séance annule les requêtes précédentes et une réponse obsolète ne peut écraser la nouvelle.
  3. Vérifier toutes les réponses HTTP et distinguer erreur des élèves, métriques, timeline et rapport.
  4. Ajouter skeleton initial, état vide sans séance, état séance en traitement et état sans données Gold.
  5. Corriger le nombre d’inscrits avec students.length ou le total serveur paginé.
  6. Implémenter les tris nom, engagement, état et drapeau. Le tri de drapeau doit définir un ordre stable et placer les égalités par nom.
  7. Ajouter recherche accent-insensible et résultat vide explicite.
  8. Transformer les lignes en boutons/liaisons accessibles ; Entrée et Espace ouvrent l’élève.
  9. Uniformiser score sur 100 et exposer la qualité d’observation.
  10. Modifier le sélecteur de planning :
    • un clic choisit clairement la séance ;
    • une séance unique peut être ouverte directement ;
    • le passage mois/jour conserve la date choisie ;
    • le hover n’est jamais nécessaire pour accéder à l’information ;
    • durée et période non sauvegardées sont soit persistées, soit retirées.
  11. Le statut de traitement provient des assets/jobs et non seulement de Gold.
  12. Le polling est lancé uniquement pour une séance active en traitement, suspendu lorsque l’onglet navigateur est caché, et nettoyé au démontage.

Fichiers à modifier

  • web/src/routes/dashboard/+page.server.ts
  • web/src/routes/dashboard/+page.svelte
  • web/src/lib/components/dashboard/MetricsRow.svelte
  • web/src/lib/components/dashboard/EngagementChart.svelte
  • web/src/lib/components/dashboard/StudentTable.svelte
  • web/src/lib/components/dashboard/Timeline.svelte
  • web/src/lib/components/dashboard/RightPanel.svelte
  • web/src/lib/components/modals/PlanningModal.svelte
  • web/src/lib/components/layout/Controls.svelte
  • web/src/routes/api/students/+server.ts
  • web/src/routes/api/metrics/+server.ts
  • web/src/routes/api/timeline/+server.ts
  • web/src/routes/api/sessions/+server.ts

Fichiers à créer

  • web/src/lib/dashboard/types.ts
  • web/src/lib/dashboard/load-session.ts
  • web/src/lib/dashboard/sort-students.ts
  • web/src/lib/dashboard/sort-students.test.ts
  • web/tests/unit/components/StudentTable.test.ts
  • web/tests/unit/components/PlanningModal.test.ts
  • web/tests/unit/components/RightPanel.test.ts

Tests

  • 0, 1, 24, 31 et plus de 100 élèves.
  • Chaque tri ascendant/descendant, stabilité et valeurs manquantes.
  • Recherche avec casse, accents, espace et aucun résultat.
  • Course entre deux sélections avec réponses réseau inversées.
  • Erreur indépendante de chaque ressource et retry correspondant.
  • Séance planifiée, upload, traitement, prête, échec et archivée.
  • Jour conservé au changement de vue calendrier.
  • Sélection à la souris, au clavier et retour du focus après drawer.
  • E2E enseignant complet : classe → séance → élève → vidéo → suivi → retour.

Lot 4 — Lecteur vidéo, sélection et overlays

Ce lot améliore l’interface du pipeline documenté dans le premier plan. Il ne modifie pas les modèles IA.

Modèle d’état client

type VideoSelection = {
  visibleStudentRefs: Set<string>;
  focusedStudentRef: string | null;
};

Invariants :

  • à chaque ouverture réussie, visibleStudentRefs contient tous les élèves de la séance ;
  • focusedStudentRef est nul si aucun élève n’est visible, sinon il appartient à la séance mais n’a pas besoin d’être visible ;
  • une mise à jour des données ne réinitialise pas la sélection existante ; seuls les élèves disparus sont retirés et les nouveaux sont ajoutés selon une règle explicite ;
  • les états sont indexés par fenêtre temporelle ; les bboxes par dernier sample de chaque track au temps courant.

Étapes

  1. Déplacer la logique temporelle pure dans video/overlay.ts.
  2. Valider le manifeste et refuser une version inconnue avec erreur lisible.
  3. Définir sans ambiguïté box: [x, y, width, height], normalisée 0..1. Ajouter un validateur qui rejette dimensions négatives et coordonnées hors bornes.
  4. Indexer les samples par chunk et track. À chaque frame, faire une recherche bornée, pas un scan de tous les samples.
  5. Pour chaque track, rendre au plus une boîte : le dernier sample à t <= currentTime dans la tolérance d’une seconde.
  6. Chercher l’état dont windowStartMs <= t < windowEndMs. Hors fenêtre : non observé.
  7. Utiliser requestVideoFrameCallback quand disponible ; fallback borné. Annuler tous les callbacks au démontage et à la fermeture.
  8. Dimensionner le canvas depuis le rectangle vidéo réellement rendu, tenir compte de object-fit, du ratio source et du device pixel ratio.
  9. Charger manifeste + états en parallèle, puis le chunk courant et le suivant. Annuler les fetchs à la fermeture ou au seek éloigné.
  10. Remplacer les deux sélections actuelles par VideoSelection.
  11. Ajouter Tout sélectionner, Tout désélectionner, recherche, nombre visible et focus.
  12. Cliquer un cadre donne le focus. La liste indique séparément Visible et En focus.
  13. Dessiner un playhead sur le graphique et actualiser son libellé au maximum quatre fois par seconde pour éviter le bruit accessible.
  14. Garder la vidéo utilisable si les overlays sont absents, invalides ou en erreur.
  15. Remplacer le nom de fichier technique du titre par matière, classe et date ; placer le nom original dans les détails.
  16. Appliquer le contrat complet de modale accessible.

Fichiers à modifier

  • web/src/lib/video/overlay.ts
  • web/src/lib/video/types.ts
  • web/src/lib/components/video/OverlayCanvas.svelte
  • web/src/lib/components/video/StudentVideoFilter.svelte
  • web/src/lib/components/modals/VideoPlayerModal.svelte
  • web/src/lib/components/modals/Modal.svelte
  • web/src/routes/dashboard/+page.svelte
  • web/src/routes/api/media/videos/[assetId]/overlay/manifest/+server.ts
  • web/src/routes/api/media/videos/[assetId]/overlay/states/+server.ts
  • web/src/routes/api/media/videos/[assetId]/overlay/chunks/[index]/+server.ts
  • web/src/routes/api/media/videos/[assetId]/playback/+server.ts

Fichiers à créer

  • web/src/lib/video/selection.ts
  • web/src/lib/video/selection.test.ts
  • web/src/lib/video/overlay.test.ts
  • web/tests/unit/components/StudentVideoFilter.test.ts
  • web/tests/unit/components/OverlayCanvas.test.ts
  • web/tests/unit/components/VideoPlayerModal.test.ts
  • web/e2e/video-player.spec.ts

Tests unitaires exhaustifs

  • Temps avant, au début, au milieu, à la fin exclusive et après une fenêtre.
  • Deux fenêtres adjacentes, trou temporel et chevauchement invalide.
  • Plusieurs samples du même track, plusieurs tracks du même élève et tracks inconnus.
  • Sample exactement à la limite de tolérance et juste après.
  • Seek arrière, seek avant, changement de chunk et chunk manquant.
  • Bbox valide, négative, débordante, nulle et format de version inconnu.
  • Ratios 16:9, 4:3, 9:16, letterbox et DPR 1/2.
  • Tous sélectionnés, aucun, recherche, focus visible, focus masqué et élève retiré.
  • Nettoyage de chaque callback et AbortController au démontage.

Tests E2E

  • Tous les cadres sont visibles à l’ouverture.
  • Décocher un élève masque uniquement son cadre.
  • Mettre un élève en focus change graphique et état, sans masquer les autres.
  • Cliquer une bbox sélectionne le bon focus.
  • Seek met à jour cadre, état et playhead.
  • Erreur overlay conserve la lecture et permet retry.
  • Fermeture Échap et focus restauré.
  • Mobile : vidéo, contrôles, liste et graphique restent utilisables sans débordement.

Lot 5 — Upload, traitement et administration des vidéos

Étapes upload

  1. Supprimer l’usage de file.arrayBuffer() sur la totalité du fichier. Ne pas calculer le SHA-256 intégral dans le thread principal.
  2. Choisir une stratégie unique : checksum multipart fourni par R2 si suffisant au contrat, ou worker Web de hash incrémental. Documenter la compatibilité navigateur.
  3. Extraire une machine d’état pure : idle → validating → initiating → uploading → completing → processing → ready, avec paused, cancelling, cancelled, failed.
  4. Distinguer progression en octets de progression du pipeline.
  5. Mesurer débit sur une fenêtre glissante et borner l’ETA ; ne pas afficher une fausse précision lorsque le débit est instable.
  6. Utiliser un AbortController par partie et un contrôleur global.
  7. Conserver les ETags confirmés pour reprendre uniquement les parties manquantes.
  8. Après erreur, permettre de resélectionner le même fichier.
  9. Ajouter récapitulatif fichier et avertissement de galerie avant démarrage.
  10. Centraliser l’upload dans VideoUpload.svelte; retirer l’implémentation multipart dupliquée de PlanningModal.svelte et l’ancien upload de AdminView.svelte.
  11. Ne jamais créer une séance vide sans informer l’utilisateur. Si séance puis upload échoue, proposer Réessayer l’upload ou Supprimer la séance planifiée.

Étapes traitement

  1. Transformer ProcessingStatus.svelte en résumé compact avec détail dépliable.
  2. Mapper chaque étape machine vers un libellé français et une explication.
  3. Polling avec backoff, arrêt en état terminal, arrêt onglet caché et reprise visible.
  4. Retry et annulation affichent leur propre état, vérifient la réponse et ne rechargent pas la page.
  5. Afficher couverture et anomalies à la fin.
  6. L’action Retry indique qu’elle crée une nouvelle tentative et conserve le dernier résultat actif éventuel.

Fichiers à modifier

  • web/src/lib/crypto.ts
  • web/src/lib/components/video/VideoUpload.svelte
  • web/src/lib/components/video/ProcessingStatus.svelte
  • web/src/lib/components/modals/PlanningModal.svelte
  • web/src/lib/components/views/AdminView.svelte
  • web/src/lib/video/types.ts
  • web/src/routes/api/admin/videos/+server.ts
  • web/src/routes/api/admin/videos/uploads/+server.ts
  • web/src/routes/api/admin/videos/uploads/[assetId]/parts/+server.ts
  • web/src/routes/api/admin/videos/uploads/[assetId]/complete/+server.ts
  • web/src/routes/api/admin/videos/uploads/[assetId]/+server.ts
  • web/src/routes/api/admin/videos/[assetId]/+server.ts
  • web/src/routes/api/admin/videos/[assetId]/cancel/+server.ts
  • web/src/routes/api/admin/videos/[assetId]/retry/+server.ts
  • web/src/lib/server/video/service.ts

Fichiers à créer

  • web/src/lib/video/upload-machine.ts
  • web/src/lib/video/upload-machine.test.ts
  • web/src/lib/video/upload-progress.ts
  • web/src/lib/video/upload-progress.test.ts
  • web/src/lib/workers/file-hash.worker.ts seulement si le hash client est retenu
  • web/tests/unit/components/VideoUpload.test.ts
  • web/tests/unit/components/ProcessingStatus.test.ts

Tests

  • Fichier 0 octet, trop grand, MIME refusé, extension trompeuse et fichier valide.
  • 1 partie, plusieurs parties, concurrence maximale et dernière partie plus petite.
  • Échec init, signature, PUT, complete et abort.
  • Retry avec backoff, épuisement, annulation pendant PUT et pendant complete.
  • Reprise sans renvoyer une partie confirmée.
  • Progression monotone en octets ; ETA absent lorsque non fiable.
  • Vérification qu’aucun appel à File.arrayBuffer() n’est fait sur le fichier complet.
  • Sélection du même fichier après erreur.
  • Échec upload après création de séance et choix explicite de récupération.
  • États pipeline et erreurs API sûres.
  • E2E avec interception réseau lente, coupure, reprise, annulation et succès.

Lot 6 — Architecture de l’administration, rapports, galeries et imports

Nouvelle arborescence

Administration
├── Structure
│   ├── Classes
│   ├── Matières
│   ├── Élèves
│   └── Liens parents
├── Cours et médias
│   ├── Planning
│   ├── Vidéos
│   ├── Galeries de référence
│   └── Import CSV
├── Publications
│   └── Rapports
└── Avancé
    └── Compétences analytiques

Étapes générales

  1. Découper AdminView.svelte par domaine. Le composant parent conserve uniquement la navigation et le chargement du sous-écran actif.
  2. Encoder section et onglet dans l’URL.
  3. Charger uniquement les données nécessaires au sous-écran ; annuler un chargement lorsqu’on le quitte.
  4. Remplacer le busy global par un état par mutation.
  5. Ajouter recherche, pagination et état vide à chaque liste.
  6. Remplacer confirm/alert par les primitives accessibles.

Rapports

  1. Charger la liste dès l’ouverture de l’onglet.
  2. Présenter noms, matière, date, type, statut, auteur et date de publication.
  3. Exiger la sélection explicite de la séance et de l’élève si nécessaire.
  4. Ajouter écran de confirmation, génération de brouillon, prévisualisation et publication séparée.
  5. Enregistrer publishedById et ne pas écraser le publié lors d’une régénération.

Galeries

  1. Afficher couverture photos valides / élèves actifs par classe.
  2. Afficher par élève : aucune photo, validation en attente, valide ou rejetée.
  3. Décrire les critères de qualité avant l’upload.
  4. Montrer la raison d’un rejet sans exposer de détail biométrique sensible.
  5. Détecter checksum dupliqué et demander confirmation avant de conserver un doublon.

Import CSV

  1. Fournir un modèle CSV téléchargeable avec version.
  2. Parser côté serveur, pas dans un grand textarea non structuré.
  3. Afficher aperçu des dix premières lignes et bilan valide/invalide.
  4. Ne rien écrire avant confirmation.
  5. Transactionner l’import et fournir un rapport d’erreurs par ligne téléchargeable.

Fichiers à modifier

  • web/src/lib/components/views/AdminView.svelte
  • web/src/lib/components/admin/AnalyticsSkillsTab.svelte
  • web/src/lib/server/db/schema.ts
  • web/src/routes/api/admin/classes/+server.ts
  • web/src/routes/api/admin/subjects/+server.ts
  • web/src/routes/api/admin/students/+server.ts
  • web/src/routes/api/admin/parent-students/+server.ts
  • web/src/routes/api/admin/sessions/+server.ts
  • web/src/routes/api/admin/photos/+server.ts
  • web/src/routes/api/admin/reports/+server.ts
  • web/src/routes/api/admin/reports/[id]/+server.ts
  • web/src/routes/api/admin/videos/+server.ts
  • web/src/routes/dashboard/+page.svelte

Fichiers à créer

  • web/src/lib/components/admin/AdminNavigation.svelte
  • web/src/lib/components/admin/StructureSection.svelte
  • web/src/lib/components/admin/MediaSection.svelte
  • web/src/lib/components/admin/ReportsSection.svelte
  • web/src/lib/components/admin/ReferenceGallerySection.svelte
  • web/src/lib/components/admin/CsvImportSection.svelte
  • web/src/lib/admin/types.ts
  • web/src/lib/server/imports/csv.ts
  • web/src/lib/server/imports/csv.test.ts
  • web/drizzle/0003_ux_report_publication.sql ou prochain numéro libre
  • web/e2e/admin-structure.spec.ts
  • web/e2e/admin-reports.spec.ts
  • web/e2e/admin-gallery.spec.ts
  • web/e2e/admin-import.spec.ts

Tests

  • Accès par rôle à chaque section et mutation.
  • Deep link et retour navigateur dans l’administration.
  • Une seule requête de chargement du domaine actif.
  • Rapports : aucun choix, séance explicite, élève explicite, erreur génération, prévisualisation, publication, nouvelle version et rapport parent publié.
  • Galerie : 0 photo, couverture partielle/complète, format rejeté, doublon, erreur R2.
  • CSV : BOM, accents, CRLF/LF, colonnes manquantes/supplémentaires, doublons, référence inconnue, 1 ligne, gros fichier, transaction annulée et rapport d’erreur.
  • Tablettes et mobiles : cartes ou tables scrollables avec en-têtes accessibles.

Lot 7 — Suivis pédagogiques

Modèle de données

Faire de follow_ups la source de vérité du travail pédagogique. La messagerie ne crée plus de nouveaux kind='followup'; les anciennes lignes restent lisibles pendant le backfill puis sont migrées ou marquées legacy.

Ajouter à follow_ups :

  • session_id FK nullable pendant backfill puis non nul pour les nouveaux suivis ;
  • student_pk FK vers students ;
  • owner_id ;
  • status: open|in_progress|resolved ;
  • priority: low|normal|high|urgent ;
  • due_at ;
  • next_action ;
  • resolution_note, resolved_at, resolved_by ;
  • updated_at ;
  • version entière pour verrouillage optimiste.

Créer follow_up_events avec suivi, acteur, ancien/nouveau statut, note et date.

Étapes

  1. Migration additive et backfill par video_name/student_id.
  2. Service transactionnel de création et transitions.
  3. Valider les transitions open → in_progress|resolved, in_progress → resolved|open, resolved → open.
  4. Restreindre enseignant à ses suivis/élèves autorisés ; directeur/admin selon politique.
  5. Retourner directement nom, classe, séance et responsable depuis l’API ; supprimer la résolution fragile côté client.
  6. Refaire la vue en liste filtrable avec groupes En retard, Aujourd’hui, À venir et Résolus.
  7. Ajouter formulaire de création/édition avec erreurs champ par champ.
  8. Réinitialiser le formulaire à chaque nouvel élève ; aucune note ne peut fuiter d’un élève au suivant.
  9. Résoudre avec note, rouvrir, modifier échéance et réassigner selon permissions.
  10. Utiliser suppression uniquement pour admin, avec confirmation explicite.

Fichiers à modifier

  • web/src/lib/server/db/schema.ts
  • web/src/routes/api/followup/+server.ts
  • web/src/lib/components/views/FollowUpView.svelte
  • web/src/lib/components/modals/SavePlanModal.svelte
  • web/src/lib/components/dashboard/RightPanel.svelte
  • web/src/routes/dashboard/+page.svelte
  • web/src/routes/api/messages/+server.ts

Fichiers à créer

  • web/drizzle/0004_ux_followups.sql ou prochain numéro libre
  • web/src/lib/server/followups/service.ts
  • web/src/lib/server/followups/access.ts
  • web/src/lib/server/followups/types.ts
  • web/src/routes/api/followup/[id]/+server.ts
  • web/src/routes/api/followup/[id]/events/+server.ts
  • web/src/lib/components/followups/FollowUpForm.svelte
  • web/src/lib/components/followups/FollowUpDetails.svelte
  • web/src/lib/components/followups/FollowUpFilters.svelte
  • web/tests/unit/components/FollowUpForm.test.ts
  • web/tests/unit/components/FollowUpView.test.ts
  • web/e2e/followup-workflow.spec.ts

Tests

  • Création minimale et complète ; élève/séance interdits ; échéance passée volontaire.
  • Toutes les transitions et transitions interdites.
  • Conflit de version lors de deux modifications.
  • Filtres combinés, tri d’échéance et état vide.
  • Retard calculé aux limites de date/fuseau.
  • Formulaire réinitialisé entre deux élèves.
  • Résolution avec note obligatoire et réouverture.
  • Historique immuable et acteur exact.
  • E2E complet enseignant puis directeur, y compris accès refusé.

Lot 8 — Historique, rapports contextuels et statistiques

Historique

  1. Trier côté serveur par date et ID décroissants.
  2. Ajouter pagination par curseur et filtres classe, matière, période et statut.
  3. Calculer « Aujourd’hui » à partir de la date dans le fuseau établissement.
  4. Séparer le lien de séance et le bouton Rapport ; stopper toute activation croisée.
  5. Ajouter états chargement/vide/erreur et une vue mobile en cartes.
  6. Ouvrir le rapport avec séance explicitement transmise.

Statistiques

  1. Ajouter un mode Vue d’ensemble par défaut et déplacer le BI sous Explorer.
  2. Réutiliser les mêmes unités et libellés que le dashboard.
  3. Rendre les filtres actifs visibles, fermables et effaçables globalement.
  4. Fermer les menus par Échap et clic extérieur, puis rendre le focus.
  5. Corriger la période personnalisée afin qu’un seul champ ne supprime pas l’autre.
  6. Rendre chaque action du panneau Fields réellement disponible ou la retirer.
  7. Ajouter undo lors de la suppression d’un widget.
  8. Montrer les erreurs de refresh, y compris __refresh.
  9. Demander confirmation avant Reset et expliquer que le dashboard local sera remplacé.
  10. Corriger la correspondance mesure/agrégation dans VisualEditor.svelte.

Assistant analytique

  1. Modéliser la proposition de périmètre avec ID et état proposed|accepted|rejected.
  2. Confirmer envoie explicitement l’acceptation au serveur et déclenche la requête.
  3. Rejeter conserve la question et permet de la reformuler.
  4. Ne vider la zone de saisie qu’après acceptation locale de la requête ; restaurer en cas d’échec.
  5. Ajouter Réessayer, Copier et Arrêter si la requête est annulable.
  6. Auto-scroll uniquement si l’utilisateur est déjà proche du bas.
  7. Ne jamais rendre les blocs d’action internes dans le texte final.

Fichiers à modifier

  • web/src/routes/api/history/+server.ts
  • web/src/lib/components/views/HistoryView.svelte
  • web/src/lib/components/views/StatsView.svelte
  • web/src/lib/components/stats/StatsWorkspace.svelte
  • web/src/lib/components/stats/StatsFilters.svelte
  • web/src/lib/components/stats/FieldsPanel.svelte
  • web/src/lib/components/stats/AnalyticsCanvas.svelte
  • web/src/lib/components/stats/AnalyticsWidget.svelte
  • web/src/lib/components/stats/VisualEditor.svelte
  • web/src/lib/components/stats/ChatPanel.svelte
  • web/src/lib/components/stats/ScopeConfirmation.svelte
  • web/src/lib/analytics/local-canvas.ts
  • web/src/routes/api/stats/chat/+server.ts

Fichiers à créer

  • web/src/lib/history/types.ts
  • web/src/lib/history/date.ts
  • web/src/lib/history/date.test.ts
  • web/src/lib/components/stats/StatsOverview.svelte
  • web/tests/unit/components/HistoryView.test.ts
  • web/tests/unit/components/StatsFilters.test.ts
  • web/tests/unit/components/ChatPanel.test.ts

Tests

  • Historique vide, une page, plusieurs pages, égalité de dates et changement de filtre.
  • Aujourd’hui hier/demain autour de minuit dans le fuseau choisi.
  • Bouton Rapport n’ouvre pas la séance ; ligne ouvre la bonne séance.
  • Mode Vue d’ensemble puis Explorer, conservé dans l’URL.
  • Filtres simples/combinés, personnalisés incomplets et Clear all.
  • Suppression widget, undo, expiration du undo et reset confirmé/annulé.
  • Mesures ajoutées/supprimées avec agrégations correctement alignées.
  • Scope accepté, rejeté, réponse en erreur, retry et abort.
  • Question conservée après erreur et contenu interne non rendu.
  • E2E historique et BI aux quatre largeurs de référence.

Lot 9 — Portail parent et rédaction responsable

Le contrôle d’accès du lot 1 est un prérequis.

Étapes

  1. Remplacer les changements via window.location.href par goto et URLs partageables.
  2. Filtrer les séances au serveur avant sérialisation.
  3. Afficher matière lisible, date, enseignant si autorisé et statut de publication.
  4. Limiter le résumé initial à trois indicateurs avec tendance et explication.
  5. Afficher score sur 100 comme indicateur, accompagné de « Ce n’est pas une note ».
  6. Ajouter qualité d’observation et distinguer non observé d’un faible engagement.
  7. Remplacer « Withdrawal » par « Retrait observé » et une explication contextuelle.
  8. Afficher auteur/relecteur et date de publication du résumé.
  9. Corriger le pied de page sur le traitement des données : ne pas affirmer qu’aucune vidéo n’est stockée si R2 est utilisé. Le texte final doit être validé par le responsable légal/produit.
  10. Prévoir les états aucun enfant lié, aucune séance, aucun rapport et service indisponible.
  11. Rendre la messagerie et l’assistant secondaires par rapport au résumé pédagogique.
  12. Adapter topbar, sélecteurs, cercles et modales à 360 px.

Fichiers à modifier

  • web/src/routes/parent/+page.server.ts
  • web/src/routes/parent/+page.svelte
  • web/src/lib/components/parent/ParentAssistant.svelte
  • web/src/lib/components/messaging/Messaging.svelte
  • web/src/lib/components/modals/ParentReportModal.svelte
  • web/src/routes/api/parent/chat/+server.ts
  • web/src/routes/api/parent/reports/+server.ts
  • web/src/lib/server/reports.ts

Fichiers à créer

  • web/src/lib/parent/types.ts
  • web/src/lib/parent/presentation.ts
  • web/src/lib/parent/presentation.test.ts
  • web/tests/unit/components/ParentAssistant.test.ts
  • web/e2e/parent-security.spec.ts
  • web/e2e/parent-responsive.spec.ts

Tests

  • 0, 1 et plusieurs enfants liés.
  • Enfant sans séance, sans métriques, non observé et avec rapport publié/non publié.
  • Tri chronologique et matières lisibles.
  • Chaque score/seuil, tendance positive/négative/stable et données insuffisantes.
  • Navigation précédent/suivant et rafraîchissement.
  • Aucune vidéo, overlay, rapport brouillon ou autre enfant dans la réponse parent.
  • Rédaction sans terme de diagnostic ni donnée technique.
  • Audit clavier et lecteur d’écran des sélecteurs et indicateurs.

Lot 10 — Connexion, cohérence globale et finition accessibilité

Étapes

  1. Masquer les comptes de démonstration sauf si une variable publique explicite de développement les active.
  2. Ne jamais préremplir un mot de passe en production.
  3. Ajouter autocomplete="email" et autocomplete="current-password".
  4. Ajouter afficher/masquer le mot de passe avec libellé accessible.
  5. Ajouter état pending, empêcher double soumission et conserver l’email après erreur.
  6. Ajouter le parcours mot de passe oublié seulement si le backend correspondant existe ; sinon ne pas afficher de lien factice.
  7. Remplacer les dernières chaînes anglaises/codes machine dans les fichiers touchés.
  8. Porter les petites tailles de texte importantes à un minimum lisible ; ne pas augmenter la densité pour conserver davantage de colonnes.
  9. Ajouter styles focus, reduced motion, contraste et cibles tactiles globaux.
  10. Auditer toutes les modales et tous les tableaux restants.
  11. Tester zoom 200 % sans perte d’action ni scroll bidimensionnel imposé.

Fichiers à modifier

  • web/src/routes/login/+page.svelte
  • web/src/routes/login/+page.server.ts
  • web/src/routes/layout.css
  • web/src/lib/components/layout/Topbar.svelte
  • web/src/lib/components/modals/Modal.svelte
  • web/src/lib/components/ui/Toast.svelte
  • tous les composants touchés dans les lots précédents contenant encore un libellé brut
  • web/.env.example
  • docs/docs/guide/accounts.md

Fichiers à créer

  • web/tests/unit/components/LoginPage.test.ts
  • web/e2e/accessibility.spec.ts
  • web/e2e/keyboard.spec.ts
  • web/e2e/visual-regression.spec.ts

Tests

  • Démo activée/désactivée selon environnement.
  • Succès, mauvais identifiants, erreur serveur, double clic et retour clavier.
  • Afficher/masquer sans perdre valeur ni focus.
  • Axe/outil équivalent : zéro violation critique/sérieuse sur login, dashboard, vidéo, admin, messages, suivis, statistiques et portail parent.
  • Parcours complet sans souris.
  • Zoom 200 %, reduced motion et contraste forcé.
  • Snapshots visuels aux quatre largeurs, avec données déterministes et régions animées masquées.

Lot 11 — Documentation, migration et déploiement progressif

Étapes

  1. Mettre à jour les guides enseignant, admin et parent avec captures récentes.
  2. Mettre à jour les docs features pour refléter les véritables comportements.
  3. Documenter les migrations, backfills, métriques de succès et rollback.
  4. Livrer les lots importants derrière des flags serveur simples si la coexistence est nécessaire : UX_URL_NAVIGATION, UX_MESSAGE_THREADS, UX_FOLLOWUPS_V2.
  5. Les flags ont une date de suppression et ne dupliquent pas durablement la logique.
  6. Déployer d’abord sur données de démonstration, puis un petit groupe staff, puis tous les staff, enfin parents.
  7. Avant ouverture parent, exécuter le test d’isolation avec au moins deux parents liés à des enfants différents et deux parents liés au même enfant.
  8. Supprimer ancien code et flags seulement après observation d’une période stable.

Fichiers à modifier

  • docs/docs/ux/flows.md
  • docs/docs/ux/teacher.md
  • docs/docs/ux/admin.md
  • docs/docs/ux/parent.md
  • docs/docs/features/dashboard.md
  • docs/docs/features/history.md
  • docs/docs/features/stats.md
  • docs/docs/features/messages.md
  • docs/docs/features/followup.md
  • docs/docs/features/admin.md
  • docs/docs/features/parent.md
  • docs/docs/guide/testing.md
  • docs/docs/guide/known-issues.md
  • docs/mkdocs.yml
  • web/.env.example

10. Inventaire consolidé des fichiers

Cet inventaire est volontairement explicite. « Modifier » signifie uniquement si le lot correspondant est exécuté ; l’exécuteur ne doit pas toucher mécaniquement tous les fichiers en une seule PR.

Fichiers existants à modifier

Shell et pages

  • web/src/routes/dashboard/+page.server.ts
  • web/src/routes/dashboard/+page.svelte
  • web/src/routes/parent/+page.server.ts
  • web/src/routes/parent/+page.svelte
  • web/src/routes/login/+page.server.ts
  • web/src/routes/login/+page.svelte
  • web/src/routes/layout.css

Composants de structure et dashboard

  • web/src/lib/components/layout/Topbar.svelte
  • web/src/lib/components/layout/Controls.svelte
  • web/src/lib/components/dashboard/MetricsRow.svelte
  • web/src/lib/components/dashboard/EngagementChart.svelte
  • web/src/lib/components/dashboard/StudentTable.svelte
  • web/src/lib/components/dashboard/Timeline.svelte
  • web/src/lib/components/dashboard/RightPanel.svelte

Modales et primitives

  • web/src/lib/components/modals/Modal.svelte
  • web/src/lib/components/modals/PlanningModal.svelte
  • web/src/lib/components/modals/SavePlanModal.svelte
  • web/src/lib/components/modals/ParentReportModal.svelte
  • web/src/lib/components/modals/StudentReportModal.svelte
  • web/src/lib/components/modals/VideoPlayerModal.svelte
  • web/src/lib/components/ui/Toast.svelte

Vidéo

  • web/src/lib/crypto.ts
  • web/src/lib/video/overlay.ts
  • web/src/lib/video/types.ts
  • web/src/lib/components/video/OverlayCanvas.svelte
  • web/src/lib/components/video/StudentVideoFilter.svelte
  • web/src/lib/components/video/VideoUpload.svelte
  • web/src/lib/components/video/ProcessingStatus.svelte

Vues métier

  • web/src/lib/components/views/AdminView.svelte
  • web/src/lib/components/views/FollowUpView.svelte
  • web/src/lib/components/views/HistoryView.svelte
  • web/src/lib/components/views/MessagingBoard.svelte
  • web/src/lib/components/views/StatsView.svelte
  • web/src/lib/components/messaging/Messaging.svelte
  • web/src/lib/components/parent/ParentAssistant.svelte
  • les composants de web/src/lib/components/stats/ cités dans le lot 8

Serveur et API

  • web/src/lib/server/db/schema.ts
  • web/src/lib/server/reports.ts
  • web/src/lib/server/video/service.ts
  • endpoints web/src/routes/api/messages/
  • endpoints web/src/routes/api/followup/
  • endpoints web/src/routes/api/history/
  • endpoints web/src/routes/api/admin/ cités dans les lots 5 et 6
  • endpoints web/src/routes/api/media/videos/
  • endpoints web/src/routes/api/parent/
  • endpoints web/src/routes/api/stats/
  • endpoints dashboard cités dans le lot 3

Outillage et documentation

  • web/package.json
  • web/vitest.config.ts
  • web/vitest.components.config.ts
  • web/playwright.config.ts
  • docker-compose.test.yml
  • web/.env.example
  • les guides et features listés au lot 11

Fichiers principaux à créer

  • web/src/lib/navigation/dashboard-location.ts
  • web/src/lib/i18n/fr.ts
  • web/src/lib/format.ts
  • web/src/lib/http/api.ts
  • web/src/lib/types/domain.ts
  • web/src/lib/components/ui/AsyncPanel.svelte
  • web/src/lib/components/ui/ConfirmDialog.svelte
  • web/src/lib/components/ui/StatusBadge.svelte
  • web/src/lib/components/ui/Drawer.svelte
  • modules et composants admin du lot 6
  • services messaging et followups des lots 1 et 7
  • types et fonctions pures dashboard/history/parent/video des lots correspondants
  • migrations SQL additives avec le prochain numéro Drizzle libre
  • tests unitaires, composants et E2E listés dans chaque lot

11. Stratégie de couverture complète

11.1 Définition de « 100 % »

Pour chaque fichier TypeScript/Svelte créé ou dont la logique exécutable est modifiée :

  • 100 % statements ;
  • 100 % branches ;
  • 100 % functions ;
  • 100 % lines.

Une ligne impossible à instrumenter ne peut être exclue qu’avec un commentaire local justifiant une contrainte de plateforme et un test E2E qui prouve le comportement. Les exclusions globales opportunistes sont interdites.

La couverture ne prouve pas :

  • le rendu CSS ;
  • la navigation lecteur d’écran ;
  • le responsive ;
  • la sécurité inter-utilisateurs ;
  • le comportement réel du navigateur lors d’un upload interrompu.

Ces dimensions sont donc obligatoirement couvertes par Playwright, audits d’accessibilité et tests API d’isolation.

11.2 Matrice minimale

Niveau Objet Preuve attendue
Unitaire parseurs URL, tri, formats, transitions, overlay, upload Toutes les entrées, limites et branches d’erreur
Composant états visuels et interactions DOM accessible, événements, focus, retry, conservation des saisies
API auth, validation, transactions Tous rôles, paramètres invalides, conflits et pannes simulées
Intégration DB migrations, backfills, contraintes Base vide, données legacy, rerun et rollback documenté
E2E parcours par rôle Résultat utilisateur et absence d’erreur console/réseau
Accessibilité pages et modales Axe + clavier + focus + zoom
Responsive quatre viewports Aucune action inaccessible ni débordement global
Sécurité parent/staff Isolation de tenant et absence de fuite dans payload/DOM
Performance vidéo/upload/listes budgets mesurés et absence de blocage main thread

11.3 Tests de mutation et tests négatifs

Pour les modules critiques access, transitions de statut, parseur URL et fonctions overlay, ajouter du mutation testing si l’outillage est acceptable. À défaut, faire une revue explicite prouvant que chaque inversion de condition importante fait échouer un test.

Les tests négatifs obligatoires incluent :

  • identifiant valide mais non autorisé ;
  • ressource supprimée entre chargement et mutation ;
  • double clic ou requête répétée ;
  • réponse lente arrivée dans le désordre ;
  • JSON invalide ;
  • 401, 403, 404, 409, 413, 422, 429 et 500 lorsque pertinents ;
  • réseau coupé puis restauré ;
  • date/fuseau à une limite de jour ;
  • liste vide et liste volumineuse ;
  • composant démonté pendant une requête.

11.4 Scénarios E2E canoniques

Enseignant

Connexion → séance → tri élève → détail → vidéo → focus → suivi → résolution

Vérifier URL, retour navigateur, conservation du contexte, clavier et feedback d’erreur.

Administrateur

Connexion → séance planifiée → galerie → upload interrompu → reprise → traitement → lecteur

Intercepter R2 et pipeline avec réponses déterministes ; un test séparé d’intégration peut utiliser MinIO ou le stockage de test réel.

Rapport

Séance explicite → élève explicite → brouillon → preview → publication → parent

Vérifier qu’une autre séance et un autre enfant n’apparaissent jamais.

Conversation

Parent envoie → enseignant répond → parent lit → parent demande rendez-vous → enseignant confirme

Vérifier ordre chronologique et persistance après reconnexion.

Statistiques

Vue d’ensemble → filtre → Explorer → widget → suppression/undo → question → scope rejeté puis accepté

Vérifier que Rejeter n’exécute aucune requête analytique.

12. Budgets de performance UX

Mesurer sur un ordinateur portable moyen avec réseau simulé « Fast 3G » pour les écrans de données et Wi-Fi instable simulé pour l’upload :

  • interaction visible après navigation : moins de 100 ms ;
  • skeleton ou feedback après action réseau : moins de 150 ms ;
  • aucune tâche principale supérieure à 50 ms due au hash ou aux overlays ;
  • recherche/tri de 500 élèves : moins de 50 ms ;
  • ouverture de modale vidéo avant disponibilité overlay : moins de 500 ms hors temps de chargement média ;
  • rendu overlay : cible 30 fps, aucun scan linéaire de l’historique complet par frame ;
  • progression upload actualisée au maximum 4 fois/s ;
  • polling traitement : aucun chevauchement et backoff en erreur ;
  • aucune fuite de callback, timer ou requête après navigation.

Ajouter des tests ou profils reproductibles. Ne pas transformer ces budgets en tests CI flaky au milliseconde près ; utiliser bornes larges et profilage manuel archivé pour le canvas et les gros fichiers.

13. Observabilité et métriques de succès

Journaliser côté serveur, sans données sensibles :

  • échec/succès d’upload, octets, durée et tentative ;
  • transitions pipeline ;
  • génération/publication de rapport ;
  • création/résolution de suivi ;
  • création/changement de statut de rendez-vous ;
  • erreurs API avec requestId.

Ne pas journaliser contenu des messages, prompts parent, noms complets, bboxes ou détails biométriques.

Mesures produit recommandées :

  • taux de sélection réussie d’une séance ;
  • temps médian séance → élève → action ;
  • taux d’upload terminé/repris/annulé ;
  • taux d’ouverture du lecteur après traitement ;
  • taux de suivi ayant une prochaine action et un responsable ;
  • délai médian de réponse aux messages et rendez-vous ;
  • taux de rapports relus puis publiés ;
  • erreurs par parcours et abandons après erreur.

La collecte de métriques produit doit faire l’objet d’une validation confidentialité. Elle ne bloque pas les corrections fonctionnelles et de sécurité.

14. Ordre des PR recommandé

  1. test: baseline, coverage gates and deterministic fixtures
  2. fix(parent): enforce child/session scope and thread authorization
  3. feat(messages): bidirectional threads and persistent meetings
  4. feat(navigation): URL-backed dashboard state and responsive shell
  5. fix(dashboard): truthful student list and cancellable loading states
  6. fix(video): temporal overlay index and unified selection model
  7. feat(upload): cancellable resumable multipart UX
  8. refactor(admin): route-backed information architecture
  9. feat(reports): explicit context, review and publication
  10. feat(followups): owned actionable lifecycle
  11. fix(history): pagination, dates and independent report actions
  12. feat(stats): overview mode and truthful scope confirmation
  13. feat(parent): contextual responsive presentation
  14. fix(a11y): keyboard, focus, reduced motion and localization
  15. docs(ux): update journeys, screenshots and operator guidance

Chaque PR doit rester réversible et ne pas mélanger une migration de données avec une refonte visuelle sans tests de compatibilité.

15. Checklist de Definition of Done par lot

  • [ ] Contrat fonctionnel et autorisations écrits avant le composant.
  • [ ] États nominal, chargement, vide, erreur, retry et interdit traités.
  • [ ] Aucune action visible sans implémentation.
  • [ ] Aucune chaîne machine ou erreur brute affichée.
  • [ ] URL et retour navigateur vérifiés si le contexte est navigable.
  • [ ] Navigation clavier, focus et nom accessible vérifiés.
  • [ ] Viewports 360, 768, 1280 et 1440 testés.
  • [ ] Saisie conservée après erreur.
  • [ ] Timers, observers, callbacks et requêtes nettoyés.
  • [ ] Autorisations testées avec tous les rôles pertinents.
  • [ ] 100 % lignes/branches/fonctions/instructions sur tout code touché.
  • [ ] Scénario E2E du lot vert sans erreur console inattendue.
  • [ ] pnpm check vert.
  • [ ] pnpm lint vert.
  • [ ] pnpm test vert.
  • [ ] pnpm test:e2e vert.
  • [ ] Migration testée sur base vide et copie de données legacy si applicable.
  • [ ] Documentation et captures mises à jour quand le comportement est livré.
  • [ ] Ancien code et flags retirés dès que la compatibilité n’est plus requise.

16. Critères de réception finaux

La refonte UX est considérée terminée uniquement si :

  1. les sept parcours de la section 7 passent en E2E avec les rôles réels ;
  2. deux parents distincts ne peuvent jamais déduire ni lire les données de l’autre ;
  3. tous les boutons et liens exposés ont un effet réel et vérifié ;
  4. la sélection vidéo contrôle sans ambiguïté visibilité et focus ;
  5. bbox, état et graphique suivent le même temps vidéo ;
  6. un upload volumineux peut être annulé et repris sans lecture intégrale en mémoire ;
  7. aucun rapport parent n’est visible avant publication explicite ;
  8. tout suivi possède statut, responsable et prochaine action ;
  9. toute demande de rendez-vous est persistée et traçable ;
  10. précédent/suivant et rafraîchissement conservent un contexte autorisé ;
  11. aucun écran principal n’a de violation d’accessibilité critique ou sérieuse ;
  12. aucune page ne déborde horizontalement à 360 px ;
  13. le français et le score sur 100 sont cohérents dans les parcours livrés ;
  14. la couverture du code ajouté et modifié est de 100 % sur les quatre métriques ;
  15. la documentation décrit le produit réellement déployé, pas un comportement prévu.

17. Statut d'implémentation (revue UI/UX livrée)

État réel au commit courant. Non-objectif : cette refonte est progressive ; les lots non encore exécutés restent décrits ci-dessus et seront livrés en PR.

Lot 0 — Baseline et contrats ✅

  • playwright.config.ts : une seule propriété workers (la duplication cassait la config).
  • Scripts test:coverage, test:e2e:a11y, test:e2e:mobile ajoutés.
  • tests/helpers/builders.ts, tests/helpers/http.ts.
  • Contrats techniques : http/api.ts (vérifie response.ok, parse ApiError, préserve AbortError), i18n/fr.ts (catalogue français), format.ts (score /100, date fr-FR, durée, octets), types/domain.ts (DTO + AsyncState).

Lot 1 — Autorisations parent, conversations, rendez-vous ✅

  • Migration 0002_ux_message_threads.sql : table message_threads + messages.thread_id.
  • server/messaging/{access,service}.ts.
  • /api/messages re-filtré par thread (fix du bug : le parent ne doit pas être filtré par auteur), lien parent→étudiant vérifié avant écriture, rendez-vous persistés.
  • Loader parent : child réduit strictement aux liens du parent, séances filtrées par présence réelle de l'enfant (lessonsForStudent), tri décroissant par date.

Lot 2 — Shell, navigation URL, responsive ✅

  • navigation/dashboard-location.ts : parseur/sérialiseur pur (rejette ids invalides, vues inconnues, paramètres dupliqués) + tests.
  • Le dashboard est piloté par l'URL (?view=session&class=&session=&student=) avec goto push/replace ; localStorage = préférence secondaire.
  • Controls visible seulement en vue Séance.
  • RightPanel → drawer conditionnel ; student dans l'URL, retiré à la fermeture.
  • Primitives : Drawer, AsyncPanel, ConfirmDialog, StatusBadge.

Lot 3 — Dashboard enseignant ✅

  • StudentTable : tri réel (nom/état/engagement/alerte), recherche accent-insensible, comptage réel (plus de « 24 enrolled » en dur), lignes boutons accessibles.
  • Logique pure sort-students.ts + tests.

Lot 4 — Lecteur vidéo ✅

  • VideoSelection (visible + focus) avec règles explicites.
  • OverlayCanvas : états temporels (stateAtTime), une bbox par track (fenêtre courante
  • suivante, pas de scan intégral), letterbox + DPR, requestVideoFrameCallback, nettoyage.
  • Libellés français via i18n/fr.ts.

Lot 5 — Upload résumable ✅

  • upload-machine.ts (état nommé), upload-progress.ts (débit/ETA bornés), tests.
  • crypto.ts : plus jamais d'arrayBuffer() sur le fichier complet (null pour > 128 Mo → le serveur valide via ETags R2 multipart + HeadObject ; checksum rendu optionnel).

Lot 6 — Architecture admin ✅ (structure)

  • AdminView regroupée en 4 sections françaises : Structure, Cours et médias, Publications, Avancé (9 onglets réorganisés).

Lot 7 — Suivis pédagogiques ✅

  • follow_ups enrichi (status/priority/due_at/next_action/resolution/version + owner) ; table follow_up_events (historique immuable) — migration 0003.
  • Service transactionnel followups/service.ts (transitions validées, optimiste), access.ts.
  • API [id] (transition/résolution) + [id]/events ; GET avec filtres statut/priorité/propriétaire.
  • FollowUpView regroupée (En retard / Aujourd'hui / À venir / Résolus), résolution avec note obligatoire. Tests (8).

Lot 8 — Historique & Stats ✅

  • History : « Aujourd'hui » calculé sur fuseau établissement (history/date.ts + tests), bouton Rapport séparé (stopPropagation).
  • Stats : mode Vue d'ensemble par défaut (KPI expliqués), toggle Explorer, filtres actifs visibles/effaçables.

Lot 9 — Portail parent ✅

  • parent/presentation.ts (+ tests) : score sur 100, « Retrait observé », qualité d'observation, « ce n'est pas une note ».
  • Page parent : français, cercles null-safe, responsive 360 px vérifié (aucun débordement).

Lot 10 — Connexion & accessibilité ✅

  • Comptes démo masqués hors dev (NODE_ENV/NEXORA_SHOW_DEMO_ACCOUNTS), pas de préremplissage du mot de passe en prod.
  • autocomplete email/current-password, afficher/masquer.
  • layout.css : focus-visible, reduced-motion, .touch-target 44px.

Reste (PR suivantes)

  • Rapports contextuels avec relecture obligatoire (publishedById).
  • Historique paginé par curseur ; édition/réassignation des suivis en UI.
  • Chat : scope accepted/rejected véridique + Réessayer/Copier/Arrêter.
  • E2E par rôle + audit axe + test mobile final.