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 :
- chaque action visible produit un résultat réel, un état de chargement ou une erreur actionnable ;
- un parent ne peut voir que ses enfants, leurs séances, leurs rapports publiés et les conversations auxquelles il appartient ;
- l’enseignant retrouve une séance et un élève après un rafraîchissement ou un retour navigateur ;
- 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 ;
- un upload de plusieurs gigaoctets ne charge jamais le fichier entier en mémoire, peut être annulé et affiche une progression utile ;
- le suivi d’un élève possède un responsable, un statut, une échéance, une prochaine action et un historique minimal ;
- la messagerie constitue de vraies conversations bidirectionnelles et les demandes de rendez-vous sont persistées ;
- les rapports sont toujours générés dans un contexte explicitement choisi, relus, puis publiés ;
- les interfaces ont une langue, des libellés, des unités, des états vides et des erreurs cohérents ;
- tous les parcours essentiels fonctionnent au clavier et aux largeurs 360, 768, 1280 et 1440 px ;
- 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 ;
localStoragene sert que de préférence secondaire et jamais d’autorité. - Un bouton sans effet, un
catchsilencieux, 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
anydans le nouveau code ; réduire lesanyrencontrés dans les fonctions effectivement modifiées. - Les actions destructrices précisent leur cible et leurs conséquences ; elles
offrent
Annuleret, 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.svelteaffiche « 24 enrolled » en dur et expose un tri sans effet.HistoryView.sveltemarque 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.sveltepeut 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.tsappellefile.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/messagessélectionne les messages écrits par le parent, ce qui exclut les réponses de l’enseignant. - la sélection
child/lessondu 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,%et0–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
0et 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
divcliquables. - 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
- L’enseignant arrive sur
/dashboard?view=session. - 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.
- Si aucune séance n’est choisie, une page vide utile explique comment en choisir une.
- L’enseignant ouvre le sélecteur, navigue par mois ou liste, puis choisit une séance.
- L’URL devient
/dashboard?view=session&class=<id>&session=<id>sans rechargement complet. - Le résumé de classe apparaît avec date, matière, statut des données et indicateurs.
- La liste affiche le nombre réel d’élèves, une recherche et un tri réellement actif.
- L’enseignant choisit un élève par clic ou clavier.
- Un drawer s’ouvre sans réduire la zone centrale sur petit écran ; l’URL reçoit
student=<ref>. - Le drawer présente état actuel, tendance, signaux, degré d’observation et actions.
- « Voir la vidéo » ouvre le lecteur au contexte temporel pertinent.
- « Créer un suivi » ouvre un formulaire prérempli avec séance et élève.
- Après sauvegarde, le suivi apparaît avec son statut et une action suivante.
- 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
- L’administrateur ouvre Administration > Cours et médias > Vidéos.
- Il filtre ou choisit une séance planifiée.
- L’interface affiche la galerie attendue : nombre d’élèves, photos valides, absents et avertissements non bloquants.
- Il sélectionne une vidéo et voit nom, taille, type et durée si disponible.
- Une validation client légère rejette un type ou une taille manifestement invalide ; la validation serveur reste l’autorité.
- L’upload direct R2 démarre avec étapes, octets envoyés, pourcentage, débit et ETA.
- « Annuler » demande confirmation, interrompt les requêtes et annule le multipart.
- 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.
- À la complétion, la ligne bascule automatiquement vers « Analyse en attente » puis les étapes du pipeline.
- Un traitement échoué affiche un résumé sûr, une action Réessayer et l’identifiant support. Réessayer crée une nouvelle tentative.
- 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
- La modale s’ouvre avec tous les élèves sélectionnés.
- Le premier élève visible devient le focus du panneau de détail sans désélectionner les autres.
- Les cases contrôlent uniquement la visibilité des cadres.
- Un bouton ou clic explicite « Mettre en focus » choisit l’élève du graphique.
- Cliquer un cadre donne le focus à cet élève et conserve les autres cadres.
- « Tout sélectionner » et « Tout désélectionner » sont toujours disponibles.
- Le graphique affiche la position du playhead et l’état de l’élève au temps courant.
- Une boîte disparaît après la durée de tolérance prévue par le contrat overlay.
- Une donnée non observée est distinguée d’une donnée faible ou indisponible.
- 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
- Depuis un élève ou la vue Suivis, un membre du staff crée un suivi.
- Il choisit priorité, responsable, échéance facultative et prochaine action.
- Le suivi commence à
open. - Le responsable le passe à
in_progress, ajoute une note et peut contacter le parent. - Le suivi peut être filtré par responsable, statut, priorité, échéance et classe.
- Le passage à
resolvedexige une note de résolution courte. - Une résolution peut être rouverte ; l’historique conserve acteur et date.
- Supprimer est réservé au nettoyage administratif. Le parcours normal utilise Résoudre.
7.5 Parent — comprendre puis échanger
- Le parent arrive sur son premier enfant lié ou retrouve le dernier enfant autorisé.
- Le serveur ne renvoie que les séances auxquelles cet enfant participe.
- La séance par défaut est la plus récente chronologiquement.
- La page explique en langage simple trois indicateurs maximum, leur tendance et la qualité d’observation.
- Le résumé n’est visible que s’il est publié et identifie sa date de publication.
- Le parent peut ouvrir la conversation relative à cet enfant.
- Les messages enseignant et parent sont visibles chronologiquement dans le même fil.
- Une demande de rendez-vous est enregistrée avec statut demandé, confirmé, refusé ou terminé.
- 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
- Le staff choisit explicitement type, élève éventuel et séance.
- Un récapitulatif matière/date/élève est affiché avant génération.
- Le brouillon est généré sans publication automatique.
- Un écran de relecture affiche provenance, données manquantes et avertissements.
- La publication demande confirmation et enregistre l’auteur et la date.
- Le parent ne voit que la dernière version publiée qui lui est autorisée.
- Une nouvelle génération ne remplace jamais silencieusement la version publiée.
7.7 Statistiques — comprendre avant d’explorer
- La vue s’ouvre en mode Vue d’ensemble avec 4 à 6 indicateurs expliqués.
- Les filtres actifs sont visibles et supprimables individuellement.
- « Explorer » ouvre le constructeur BI existant.
- Ajouter, modifier et supprimer un widget produit un feedback et permet d’annuler.
- L’assistant propose un périmètre ; Confirmer exécute, Rejeter annule explicitement.
- Une erreur conserve la question de l’utilisateur et permet Réessayer.
- 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
- Exécuter et archiver les résultats de
pnpm check,pnpm lint,pnpm testetpnpm test:e2eavant modification. - Corriger uniquement les défauts de configuration qui empêchent la suite de partir :
playwright.config.tscontient actuellement deux propriétésworkers. - Ajouter les scripts
test:coverage,test:e2e:a11yettest:e2e:mobile. - Configurer des seuils de 100 % pour lignes, branches, fonctions et instructions des nouveaux modules purs, endpoints modifiés et composants inclus dans le lot.
- Ajouter un contrôle des fichiers modifiés afin qu’un fichier exécutable touché ne disparaisse pas du rapport de couverture.
- 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.
- Ajouter
data-testidseulement lorsqu’un rôle, un nom ou un libellé stable ne peut pas cibler correctement l’élément.
Fichiers à modifier
web/package.jsonweb/vitest.config.tsweb/vitest.components.config.tsweb/playwright.config.tsweb/e2e/hydration.tsdocker-compose.test.yml.github/workflows/*si une CI existe au moment de l’exécution
Fichiers à créer
web/tests/helpers/builders.tsweb/tests/helpers/http.tsweb/tests/helpers/accessibility.tsweb/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 :
idUUID primaire ;parent_user_idnon nul ;student_idFK versstudentsnon nul ;assigned_staff_idnullable ;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
- Écrire la migration additive et son rollback manuel documenté.
- Centraliser la vérification
parent → studentdans une fonction testable. - Remplacer le filtrage par auteur de
/api/messagespar un filtrage sur thread. - Exiger
threadIdou résoudre sans ambiguïté(parentUserId, studentId)à l’écriture. - Retourner les messages dans l’ordre chronologique ; paginer avec curseur stable
(createdAt, id). - Autoriser le parent à créer
messageetmeeting, jamaisfollowup. - Autoriser le staff à répondre seulement dans un thread qu’il peut consulter.
- Valider les transitions rendez-vous :
requested → confirmed|declined,confirmed → done|declined; aucune autre. - Dans le loader parent, valider
childdans les liens avant tout appel Gold. - Construire la liste des séances à partir de la présence réelle de l’enfant et la trier par date décroissante.
- 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
- Adapter
Messaging.svelteà une liste de threads avec dernier message et statut. - Ajouter loading, vide, erreur et retry.
- 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.
- Conserver le brouillon si l’envoi échoue.
- Remplacer l’alerte de
ParentReportModal.sveltepar la création persistée d’une demande de rendez-vous. - Afficher le statut, la prochaine étape et l’auteur de chaque changement.
- Ne jamais labelliser un message enseignant comme venant de « NEXORA ».
Fichiers à modifier
web/src/lib/server/db/schema.tsweb/src/routes/api/messages/+server.tsweb/src/routes/api/messages/students/+server.tsweb/src/routes/parent/+page.server.tsweb/src/routes/parent/+page.svelteweb/src/lib/components/messaging/Messaging.svelteweb/src/lib/components/views/MessagingBoard.svelteweb/src/lib/components/modals/ParentReportModal.svelteweb/scripts/seed_accounts.tsweb/scripts/seed_fake_data.ts
Fichiers à créer
web/drizzle/0002_ux_message_threads.sqlou prochain numéro libreweb/src/lib/server/messaging/access.tsweb/src/lib/server/messaging/service.tsweb/src/lib/server/messaging/types.tsweb/src/routes/api/messages/threads/+server.tsweb/src/routes/api/messages/threads/[threadId]/+server.tsweb/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
- Implémenter et tester
dashboard-location.ts. - 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.
- Remplacer
activeTablocal parviewdans l’URL. - Utiliser de vrais liens pour les vues et
aria-current="page". - Conserver
localStorageuniquement comme préférence de classe/séance si l’URL n’en fournit pas et si le serveur confirme l’accès. - Afficher
Controls.svelteseulement pour Séance et les sous-vues explicitement dépendantes d’une séance. - Sur mobile, remplacer les onglets par un bouton Menu ouvrant une navigation accessible ; ne pas créer de scroll horizontal caché.
- Transformer le panneau étudiant en drawer conditionnel fermé par défaut.
- À la fermeture du drawer, retirer
studentde l’URL et rendre le focus à la ligne. - En cas d’URL invalide, normaliser l’URL avec
replaceStateet montrer un message uniquement si l’utilisateur doit agir.
Fichiers à modifier
web/src/routes/dashboard/+page.server.tsweb/src/routes/dashboard/+page.svelteweb/src/lib/components/layout/Topbar.svelteweb/src/lib/components/layout/Controls.svelteweb/src/lib/components/dashboard/RightPanel.svelteweb/src/routes/layout.css
Fichiers à créer
web/src/lib/navigation/dashboard-location.tsweb/src/lib/navigation/dashboard-location.test.tsweb/src/lib/components/ui/Drawer.svelteweb/tests/unit/components/Topbar.test.tsweb/tests/unit/components/Drawer.test.tsweb/e2e/navigation.spec.tsweb/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
- Remplacer les tableaux
any[]du dashboard par des DTO nommés. - 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.
- Vérifier toutes les réponses HTTP et distinguer erreur des élèves, métriques, timeline et rapport.
- Ajouter skeleton initial, état vide sans séance, état séance en traitement et état sans données Gold.
- Corriger le nombre d’inscrits avec
students.lengthou le total serveur paginé. - 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.
- Ajouter recherche accent-insensible et résultat vide explicite.
- Transformer les lignes en boutons/liaisons accessibles ; Entrée et Espace ouvrent l’élève.
- Uniformiser score sur 100 et exposer la qualité d’observation.
- 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.
- Le statut de traitement provient des assets/jobs et non seulement de Gold.
- 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.tsweb/src/routes/dashboard/+page.svelteweb/src/lib/components/dashboard/MetricsRow.svelteweb/src/lib/components/dashboard/EngagementChart.svelteweb/src/lib/components/dashboard/StudentTable.svelteweb/src/lib/components/dashboard/Timeline.svelteweb/src/lib/components/dashboard/RightPanel.svelteweb/src/lib/components/modals/PlanningModal.svelteweb/src/lib/components/layout/Controls.svelteweb/src/routes/api/students/+server.tsweb/src/routes/api/metrics/+server.tsweb/src/routes/api/timeline/+server.tsweb/src/routes/api/sessions/+server.ts
Fichiers à créer
web/src/lib/dashboard/types.tsweb/src/lib/dashboard/load-session.tsweb/src/lib/dashboard/sort-students.tsweb/src/lib/dashboard/sort-students.test.tsweb/tests/unit/components/StudentTable.test.tsweb/tests/unit/components/PlanningModal.test.tsweb/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,
visibleStudentRefscontient tous les élèves de la séance ; focusedStudentRefest 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
- Déplacer la logique temporelle pure dans
video/overlay.ts. - Valider le manifeste et refuser une version inconnue avec erreur lisible.
- 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. - Indexer les samples par chunk et track. À chaque frame, faire une recherche bornée, pas un scan de tous les samples.
- Pour chaque track, rendre au plus une boîte : le dernier sample à
t <= currentTimedans la tolérance d’une seconde. - Chercher l’état dont
windowStartMs <= t < windowEndMs. Hors fenêtre : non observé. - Utiliser
requestVideoFrameCallbackquand disponible ; fallback borné. Annuler tous les callbacks au démontage et à la fermeture. - Dimensionner le canvas depuis le rectangle vidéo réellement rendu, tenir compte de
object-fit, du ratio source et du device pixel ratio. - Charger manifeste + états en parallèle, puis le chunk courant et le suivant. Annuler les fetchs à la fermeture ou au seek éloigné.
- Remplacer les deux sélections actuelles par
VideoSelection. - Ajouter Tout sélectionner, Tout désélectionner, recherche, nombre visible et focus.
- Cliquer un cadre donne le focus. La liste indique séparément Visible et En focus.
- Dessiner un playhead sur le graphique et actualiser son libellé au maximum quatre fois par seconde pour éviter le bruit accessible.
- Garder la vidéo utilisable si les overlays sont absents, invalides ou en erreur.
- Remplacer le nom de fichier technique du titre par matière, classe et date ; placer le nom original dans les détails.
- Appliquer le contrat complet de modale accessible.
Fichiers à modifier
web/src/lib/video/overlay.tsweb/src/lib/video/types.tsweb/src/lib/components/video/OverlayCanvas.svelteweb/src/lib/components/video/StudentVideoFilter.svelteweb/src/lib/components/modals/VideoPlayerModal.svelteweb/src/lib/components/modals/Modal.svelteweb/src/routes/dashboard/+page.svelteweb/src/routes/api/media/videos/[assetId]/overlay/manifest/+server.tsweb/src/routes/api/media/videos/[assetId]/overlay/states/+server.tsweb/src/routes/api/media/videos/[assetId]/overlay/chunks/[index]/+server.tsweb/src/routes/api/media/videos/[assetId]/playback/+server.ts
Fichiers à créer
web/src/lib/video/selection.tsweb/src/lib/video/selection.test.tsweb/src/lib/video/overlay.test.tsweb/tests/unit/components/StudentVideoFilter.test.tsweb/tests/unit/components/OverlayCanvas.test.tsweb/tests/unit/components/VideoPlayerModal.test.tsweb/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
AbortControllerau 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
- Supprimer l’usage de
file.arrayBuffer()sur la totalité du fichier. Ne pas calculer le SHA-256 intégral dans le thread principal. - 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.
- Extraire une machine d’état pure :
idle → validating → initiating → uploading → completing → processing → ready, avecpaused,cancelling,cancelled,failed. - Distinguer progression en octets de progression du pipeline.
- 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.
- Utiliser un
AbortControllerpar partie et un contrôleur global. - Conserver les ETags confirmés pour reprendre uniquement les parties manquantes.
- Après erreur, permettre de resélectionner le même fichier.
- Ajouter récapitulatif fichier et avertissement de galerie avant démarrage.
- Centraliser l’upload dans
VideoUpload.svelte; retirer l’implémentation multipart dupliquée dePlanningModal.svelteet l’ancien upload deAdminView.svelte. - 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
- Transformer
ProcessingStatus.svelteen résumé compact avec détail dépliable. - Mapper chaque étape machine vers un libellé français et une explication.
- Polling avec backoff, arrêt en état terminal, arrêt onglet caché et reprise visible.
- Retry et annulation affichent leur propre état, vérifient la réponse et ne rechargent pas la page.
- Afficher couverture et anomalies à la fin.
- 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.tsweb/src/lib/components/video/VideoUpload.svelteweb/src/lib/components/video/ProcessingStatus.svelteweb/src/lib/components/modals/PlanningModal.svelteweb/src/lib/components/views/AdminView.svelteweb/src/lib/video/types.tsweb/src/routes/api/admin/videos/+server.tsweb/src/routes/api/admin/videos/uploads/+server.tsweb/src/routes/api/admin/videos/uploads/[assetId]/parts/+server.tsweb/src/routes/api/admin/videos/uploads/[assetId]/complete/+server.tsweb/src/routes/api/admin/videos/uploads/[assetId]/+server.tsweb/src/routes/api/admin/videos/[assetId]/+server.tsweb/src/routes/api/admin/videos/[assetId]/cancel/+server.tsweb/src/routes/api/admin/videos/[assetId]/retry/+server.tsweb/src/lib/server/video/service.ts
Fichiers à créer
web/src/lib/video/upload-machine.tsweb/src/lib/video/upload-machine.test.tsweb/src/lib/video/upload-progress.tsweb/src/lib/video/upload-progress.test.tsweb/src/lib/workers/file-hash.worker.tsseulement si le hash client est retenuweb/tests/unit/components/VideoUpload.test.tsweb/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
- Découper
AdminView.sveltepar domaine. Le composant parent conserve uniquement la navigation et le chargement du sous-écran actif. - Encoder section et onglet dans l’URL.
- Charger uniquement les données nécessaires au sous-écran ; annuler un chargement lorsqu’on le quitte.
- Remplacer le
busyglobal par un état par mutation. - Ajouter recherche, pagination et état vide à chaque liste.
- Remplacer
confirm/alertpar les primitives accessibles.
Rapports
- Charger la liste dès l’ouverture de l’onglet.
- Présenter noms, matière, date, type, statut, auteur et date de publication.
- Exiger la sélection explicite de la séance et de l’élève si nécessaire.
- Ajouter écran de confirmation, génération de brouillon, prévisualisation et publication séparée.
- Enregistrer
publishedByIdet ne pas écraser le publié lors d’une régénération.
Galeries
- Afficher couverture
photos valides / élèves actifspar classe. - Afficher par élève : aucune photo, validation en attente, valide ou rejetée.
- Décrire les critères de qualité avant l’upload.
- Montrer la raison d’un rejet sans exposer de détail biométrique sensible.
- Détecter checksum dupliqué et demander confirmation avant de conserver un doublon.
Import CSV
- Fournir un modèle CSV téléchargeable avec version.
- Parser côté serveur, pas dans un grand textarea non structuré.
- Afficher aperçu des dix premières lignes et bilan valide/invalide.
- Ne rien écrire avant confirmation.
- Transactionner l’import et fournir un rapport d’erreurs par ligne téléchargeable.
Fichiers à modifier
web/src/lib/components/views/AdminView.svelteweb/src/lib/components/admin/AnalyticsSkillsTab.svelteweb/src/lib/server/db/schema.tsweb/src/routes/api/admin/classes/+server.tsweb/src/routes/api/admin/subjects/+server.tsweb/src/routes/api/admin/students/+server.tsweb/src/routes/api/admin/parent-students/+server.tsweb/src/routes/api/admin/sessions/+server.tsweb/src/routes/api/admin/photos/+server.tsweb/src/routes/api/admin/reports/+server.tsweb/src/routes/api/admin/reports/[id]/+server.tsweb/src/routes/api/admin/videos/+server.tsweb/src/routes/dashboard/+page.svelte
Fichiers à créer
web/src/lib/components/admin/AdminNavigation.svelteweb/src/lib/components/admin/StructureSection.svelteweb/src/lib/components/admin/MediaSection.svelteweb/src/lib/components/admin/ReportsSection.svelteweb/src/lib/components/admin/ReferenceGallerySection.svelteweb/src/lib/components/admin/CsvImportSection.svelteweb/src/lib/admin/types.tsweb/src/lib/server/imports/csv.tsweb/src/lib/server/imports/csv.test.tsweb/drizzle/0003_ux_report_publication.sqlou prochain numéro libreweb/e2e/admin-structure.spec.tsweb/e2e/admin-reports.spec.tsweb/e2e/admin-gallery.spec.tsweb/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_idFK nullable pendant backfill puis non nul pour les nouveaux suivis ;student_pkFK versstudents;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
- Migration additive et backfill par
video_name/student_id. - Service transactionnel de création et transitions.
- Valider les transitions
open → in_progress|resolved,in_progress → resolved|open,resolved → open. - Restreindre enseignant à ses suivis/élèves autorisés ; directeur/admin selon politique.
- Retourner directement nom, classe, séance et responsable depuis l’API ; supprimer la résolution fragile côté client.
- Refaire la vue en liste filtrable avec groupes En retard, Aujourd’hui, À venir et Résolus.
- Ajouter formulaire de création/édition avec erreurs champ par champ.
- Réinitialiser le formulaire à chaque nouvel élève ; aucune note ne peut fuiter d’un élève au suivant.
- Résoudre avec note, rouvrir, modifier échéance et réassigner selon permissions.
- Utiliser suppression uniquement pour admin, avec confirmation explicite.
Fichiers à modifier
web/src/lib/server/db/schema.tsweb/src/routes/api/followup/+server.tsweb/src/lib/components/views/FollowUpView.svelteweb/src/lib/components/modals/SavePlanModal.svelteweb/src/lib/components/dashboard/RightPanel.svelteweb/src/routes/dashboard/+page.svelteweb/src/routes/api/messages/+server.ts
Fichiers à créer
web/drizzle/0004_ux_followups.sqlou prochain numéro libreweb/src/lib/server/followups/service.tsweb/src/lib/server/followups/access.tsweb/src/lib/server/followups/types.tsweb/src/routes/api/followup/[id]/+server.tsweb/src/routes/api/followup/[id]/events/+server.tsweb/src/lib/components/followups/FollowUpForm.svelteweb/src/lib/components/followups/FollowUpDetails.svelteweb/src/lib/components/followups/FollowUpFilters.svelteweb/tests/unit/components/FollowUpForm.test.tsweb/tests/unit/components/FollowUpView.test.tsweb/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
- Trier côté serveur par date et ID décroissants.
- Ajouter pagination par curseur et filtres classe, matière, période et statut.
- Calculer « Aujourd’hui » à partir de la date dans le fuseau établissement.
- Séparer le lien de séance et le bouton Rapport ; stopper toute activation croisée.
- Ajouter états chargement/vide/erreur et une vue mobile en cartes.
- Ouvrir le rapport avec séance explicitement transmise.
Statistiques
- Ajouter un mode Vue d’ensemble par défaut et déplacer le BI sous Explorer.
- Réutiliser les mêmes unités et libellés que le dashboard.
- Rendre les filtres actifs visibles, fermables et effaçables globalement.
- Fermer les menus par Échap et clic extérieur, puis rendre le focus.
- Corriger la période personnalisée afin qu’un seul champ ne supprime pas l’autre.
- Rendre chaque action du panneau Fields réellement disponible ou la retirer.
- Ajouter undo lors de la suppression d’un widget.
- Montrer les erreurs de refresh, y compris
__refresh. - Demander confirmation avant Reset et expliquer que le dashboard local sera remplacé.
- Corriger la correspondance mesure/agrégation dans
VisualEditor.svelte.
Assistant analytique
- Modéliser la proposition de périmètre avec ID et état
proposed|accepted|rejected. - Confirmer envoie explicitement l’acceptation au serveur et déclenche la requête.
- Rejeter conserve la question et permet de la reformuler.
- Ne vider la zone de saisie qu’après acceptation locale de la requête ; restaurer en cas d’échec.
- Ajouter Réessayer, Copier et Arrêter si la requête est annulable.
- Auto-scroll uniquement si l’utilisateur est déjà proche du bas.
- Ne jamais rendre les blocs d’action internes dans le texte final.
Fichiers à modifier
web/src/routes/api/history/+server.tsweb/src/lib/components/views/HistoryView.svelteweb/src/lib/components/views/StatsView.svelteweb/src/lib/components/stats/StatsWorkspace.svelteweb/src/lib/components/stats/StatsFilters.svelteweb/src/lib/components/stats/FieldsPanel.svelteweb/src/lib/components/stats/AnalyticsCanvas.svelteweb/src/lib/components/stats/AnalyticsWidget.svelteweb/src/lib/components/stats/VisualEditor.svelteweb/src/lib/components/stats/ChatPanel.svelteweb/src/lib/components/stats/ScopeConfirmation.svelteweb/src/lib/analytics/local-canvas.tsweb/src/routes/api/stats/chat/+server.ts
Fichiers à créer
web/src/lib/history/types.tsweb/src/lib/history/date.tsweb/src/lib/history/date.test.tsweb/src/lib/components/stats/StatsOverview.svelteweb/tests/unit/components/HistoryView.test.tsweb/tests/unit/components/StatsFilters.test.tsweb/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
- Remplacer les changements via
window.location.hrefpargotoet URLs partageables. - Filtrer les séances au serveur avant sérialisation.
- Afficher matière lisible, date, enseignant si autorisé et statut de publication.
- Limiter le résumé initial à trois indicateurs avec tendance et explication.
- Afficher score sur 100 comme indicateur, accompagné de « Ce n’est pas une note ».
- Ajouter qualité d’observation et distinguer non observé d’un faible engagement.
- Remplacer « Withdrawal » par « Retrait observé » et une explication contextuelle.
- Afficher auteur/relecteur et date de publication du résumé.
- 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.
- Prévoir les états aucun enfant lié, aucune séance, aucun rapport et service indisponible.
- Rendre la messagerie et l’assistant secondaires par rapport au résumé pédagogique.
- Adapter topbar, sélecteurs, cercles et modales à 360 px.
Fichiers à modifier
web/src/routes/parent/+page.server.tsweb/src/routes/parent/+page.svelteweb/src/lib/components/parent/ParentAssistant.svelteweb/src/lib/components/messaging/Messaging.svelteweb/src/lib/components/modals/ParentReportModal.svelteweb/src/routes/api/parent/chat/+server.tsweb/src/routes/api/parent/reports/+server.tsweb/src/lib/server/reports.ts
Fichiers à créer
web/src/lib/parent/types.tsweb/src/lib/parent/presentation.tsweb/src/lib/parent/presentation.test.tsweb/tests/unit/components/ParentAssistant.test.tsweb/e2e/parent-security.spec.tsweb/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
- Masquer les comptes de démonstration sauf si une variable publique explicite de développement les active.
- Ne jamais préremplir un mot de passe en production.
- Ajouter
autocomplete="email"etautocomplete="current-password". - Ajouter afficher/masquer le mot de passe avec libellé accessible.
- Ajouter état pending, empêcher double soumission et conserver l’email après erreur.
- Ajouter le parcours mot de passe oublié seulement si le backend correspondant existe ; sinon ne pas afficher de lien factice.
- Remplacer les dernières chaînes anglaises/codes machine dans les fichiers touchés.
- Porter les petites tailles de texte importantes à un minimum lisible ; ne pas augmenter la densité pour conserver davantage de colonnes.
- Ajouter styles focus, reduced motion, contraste et cibles tactiles globaux.
- Auditer toutes les modales et tous les tableaux restants.
- Tester zoom 200 % sans perte d’action ni scroll bidimensionnel imposé.
Fichiers à modifier
web/src/routes/login/+page.svelteweb/src/routes/login/+page.server.tsweb/src/routes/layout.cssweb/src/lib/components/layout/Topbar.svelteweb/src/lib/components/modals/Modal.svelteweb/src/lib/components/ui/Toast.svelte- tous les composants touchés dans les lots précédents contenant encore un libellé brut
web/.env.exampledocs/docs/guide/accounts.md
Fichiers à créer
web/tests/unit/components/LoginPage.test.tsweb/e2e/accessibility.spec.tsweb/e2e/keyboard.spec.tsweb/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
- Mettre à jour les guides enseignant, admin et parent avec captures récentes.
- Mettre à jour les docs features pour refléter les véritables comportements.
- Documenter les migrations, backfills, métriques de succès et rollback.
- 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. - Les flags ont une date de suppression et ne dupliquent pas durablement la logique.
- Déployer d’abord sur données de démonstration, puis un petit groupe staff, puis tous les staff, enfin parents.
- 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.
- Supprimer ancien code et flags seulement après observation d’une période stable.
Fichiers à modifier
docs/docs/ux/flows.mddocs/docs/ux/teacher.mddocs/docs/ux/admin.mddocs/docs/ux/parent.mddocs/docs/features/dashboard.mddocs/docs/features/history.mddocs/docs/features/stats.mddocs/docs/features/messages.mddocs/docs/features/followup.mddocs/docs/features/admin.mddocs/docs/features/parent.mddocs/docs/guide/testing.mddocs/docs/guide/known-issues.mddocs/mkdocs.ymlweb/.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.tsweb/src/routes/dashboard/+page.svelteweb/src/routes/parent/+page.server.tsweb/src/routes/parent/+page.svelteweb/src/routes/login/+page.server.tsweb/src/routes/login/+page.svelteweb/src/routes/layout.css
Composants de structure et dashboard
web/src/lib/components/layout/Topbar.svelteweb/src/lib/components/layout/Controls.svelteweb/src/lib/components/dashboard/MetricsRow.svelteweb/src/lib/components/dashboard/EngagementChart.svelteweb/src/lib/components/dashboard/StudentTable.svelteweb/src/lib/components/dashboard/Timeline.svelteweb/src/lib/components/dashboard/RightPanel.svelte
Modales et primitives
web/src/lib/components/modals/Modal.svelteweb/src/lib/components/modals/PlanningModal.svelteweb/src/lib/components/modals/SavePlanModal.svelteweb/src/lib/components/modals/ParentReportModal.svelteweb/src/lib/components/modals/StudentReportModal.svelteweb/src/lib/components/modals/VideoPlayerModal.svelteweb/src/lib/components/ui/Toast.svelte
Vidéo
web/src/lib/crypto.tsweb/src/lib/video/overlay.tsweb/src/lib/video/types.tsweb/src/lib/components/video/OverlayCanvas.svelteweb/src/lib/components/video/StudentVideoFilter.svelteweb/src/lib/components/video/VideoUpload.svelteweb/src/lib/components/video/ProcessingStatus.svelte
Vues métier
web/src/lib/components/views/AdminView.svelteweb/src/lib/components/views/FollowUpView.svelteweb/src/lib/components/views/HistoryView.svelteweb/src/lib/components/views/MessagingBoard.svelteweb/src/lib/components/views/StatsView.svelteweb/src/lib/components/messaging/Messaging.svelteweb/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.tsweb/src/lib/server/reports.tsweb/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.jsonweb/vitest.config.tsweb/vitest.components.config.tsweb/playwright.config.tsdocker-compose.test.ymlweb/.env.example- les guides et features listés au lot 11
Fichiers principaux à créer
web/src/lib/navigation/dashboard-location.tsweb/src/lib/i18n/fr.tsweb/src/lib/format.tsweb/src/lib/http/api.tsweb/src/lib/types/domain.tsweb/src/lib/components/ui/AsyncPanel.svelteweb/src/lib/components/ui/ConfirmDialog.svelteweb/src/lib/components/ui/StatusBadge.svelteweb/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é
test: baseline, coverage gates and deterministic fixturesfix(parent): enforce child/session scope and thread authorizationfeat(messages): bidirectional threads and persistent meetingsfeat(navigation): URL-backed dashboard state and responsive shellfix(dashboard): truthful student list and cancellable loading statesfix(video): temporal overlay index and unified selection modelfeat(upload): cancellable resumable multipart UXrefactor(admin): route-backed information architecturefeat(reports): explicit context, review and publicationfeat(followups): owned actionable lifecyclefix(history): pagination, dates and independent report actionsfeat(stats): overview mode and truthful scope confirmationfeat(parent): contextual responsive presentationfix(a11y): keyboard, focus, reduced motion and localizationdocs(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 checkvert. - [ ]
pnpm lintvert. - [ ]
pnpm testvert. - [ ]
pnpm test:e2evert. - [ ] 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 :
- les sept parcours de la section 7 passent en E2E avec les rôles réels ;
- deux parents distincts ne peuvent jamais déduire ni lire les données de l’autre ;
- tous les boutons et liens exposés ont un effet réel et vérifié ;
- la sélection vidéo contrôle sans ambiguïté visibilité et focus ;
- bbox, état et graphique suivent le même temps vidéo ;
- un upload volumineux peut être annulé et repris sans lecture intégrale en mémoire ;
- aucun rapport parent n’est visible avant publication explicite ;
- tout suivi possède statut, responsable et prochaine action ;
- toute demande de rendez-vous est persistée et traçable ;
- précédent/suivant et rafraîchissement conservent un contexte autorisé ;
- aucun écran principal n’a de violation d’accessibilité critique ou sérieuse ;
- aucune page ne déborde horizontalement à 360 px ;
- le français et le score sur 100 sont cohérents dans les parcours livrés ;
- la couverture du code ajouté et modifié est de 100 % sur les quatre métriques ;
- 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:mobileajouté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: tablemessage_threads+messages.thread_id. server/messaging/{access,service}.ts./api/messagesre-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 :
childré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=) avecgotopush/replace ;localStorage= préférence secondaire. Controlsvisible seulement en vue Séance.RightPanel→ drawer conditionnel ;studentdans 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 ;checksumrendu 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_upsenrichi (status/priority/due_at/next_action/resolution/version + owner) ; tablefollow_up_events(historique immuable) — migration0003.- 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. FollowUpViewregroupé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-target44px.
Reste (PR suivantes)
- Rapports contextuels avec relecture obligatoire (
publishedById). - Historique paginé par curseur ; édition/réassignation des suivis en UI.
- Chat : scope
accepted/rejectedvéridique + Réessayer/Copier/Arrêter. - E2E par rôle + audit axe + test mobile final.