# Changelog — Expria Frontend Toutes les modifications notables du projet frontend sont documentées dans ce fichier. Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/). ## Convention Chaque entrée suit ce format : ``` ## [Unreleased] — YYYY-MM-DD — Session ### Added (nouveautés) - ... ### Changed (modifications) - ... ### Fixed (corrections) - ... ### Removed (suppressions) - ... ### Security (sécurité) - ... ``` --- ## [Unreleased] — 2026-04-24 — Sprint DA Charcoal — Reskin complet ### Changed - Remplacement intégral `src/index.css` par palette Charcoal (DESIGN_SYSTEM.md v2.0). Dark = thème par défaut, `.light` = override via `@custom-variant light`. - Sidebar navy `#0C1528` permanent (identique dark et light) avec tokens `--color-sidebar-*`. - Layout `AppLayout` : radial-gradient sur `
`, sidebar 230px, `max-w-[1100px]`. - Script anti-FOUC inline dans `index.html` (détection `prefers-color-scheme` + `localStorage`). - Renommage tokens Boréal→Charcoal sur ~45 composants (ink-1→ink-primary, expria→brand, line→border, deep→sidebar-bg, \*-bg→\*-soft, etc.). - Inversion `dark:` → baseline + `light:` sur 5 primitives shadcn (button, badge, input, dialog, avatar). - `DesignSystemPage` réécrite avec palette Charcoal complète. - `docs/adr/006-stack-versions-2026.md` mis à jour : tokens Charcoal, suppression `@variant dark` et `.dark {}`. ### Fixed - Logo Expria : wordmark forcé en `text-white` dans la sidebar (invisible en light mode sur fond navy). ### Notes - 59 fichiers modifiés, +1173/-727 lignes. - Tests : 122/122 verts. Typecheck : 0 erreur. - Timeout API intermittent observé (cold start Render) — préexistant, non lié au reskin. --- ## [Unreleased] — 2026-04-23 — Clean FTD-23 + FTD-24 ### Fixed - **FTD-23 résolu** : `useAutosave` ne fire plus après correction — `enabled` propagé avec `step !== 'done' && step !== 'correcting'` depuis `SimulationForm`. 2 tests de régression ajoutés. - **FTD-24 résolu** : polling automatique 3s dans `useRapport` quand `exercices_status` ou `modele_status === 'pending'`. Arrêt auto dès ready/error. Timeout 2 min avec message + bouton Réessayer dans `JobStatusFallback`. 5 tests ajoutés. ### Notes - Tests frontend : 122/122 verts (+7 vs baseline 115). - TECH_DEBT.md → v1.19. 10 FTD actives (cap 15). --- ## [Unreleased] — 2026-04-23 — FTD-28 — Semgrep CI + CI verte ### Added - Semgrep scan (`--severity=ERROR`) dans les CI frontend et backend (FTD-28). - Variables d'env factices dans CI frontend pour les tests. ### Fixed - 4 erreurs ESLint corrigées : split SimulationFlowProvider (react-refresh), hook conditionnel MonProfilPreparation, ref render useTimer, setState effect AppLayout. - Prettier format sur 7 fichiers. - CI frontend verte pour la première fois depuis le 18 avril. --- ## [Unreleased] — 2026-04-23 — FTD-27 — CI backend ### Added - `expria-backend/.github/workflows/ci.yml` — CI GitHub Actions (test + audit, Node 22). CI verte au premier run. - FTD-27 fermée dans TECH_DEBT.md (v1.17). --- ## [Unreleased] — 2026-04-23 — FTD-29 — Dependabot config ### Added - `.github/dependabot.yml` créé dans les 2 dépôts (npm, weekly, limit 10 PRs). - FTD-29 fermée dans TECH_DEBT.md (v1.16). --- ## [Unreleased] — 2026-04-23 — Réorg sécurité TECH_DEBT v1.15 ### Changed - `TECH_DEBT.md` v1.14 → v1.15 — réorganisation sécurité. - Gelées (backlog post-MVP) : FTD-06 (AudioWorklet), FTD-08 (Tests E2E), FTD-15 (option 'system' thème). - Ajoutées : FTD-27 🔴 (CI backend), FTD-28 🔴 (Semgrep CI), FTD-29 🟡 (Dependabot config). - GitHub : Dependabot alerts + security updates activés sur les deux dépôts (UI GitHub). --- ## [Unreleased] — 2026-04-23 — Triage FTD v1.14 ### Changed - `TECH_DEBT.md` v1.13 → v1.14 — triage dette technique : 17 → 15 FTD actives (cap respecté). - Fermées : FTD-04 (miroir docs, accepté ADR 004), FTD-05 (scaffold caduc, audit clean), FTD-20 (GET /simulations/:id livré Sprint 3.6a), FTD-22 (code orphelin /sujets, résolution complète). - Ajoutées : FTD-25 🟢 (ARCHITECTURE.md §3 désaligné), FTD-26 🟡 (cohabitation shared/ui vs shared/components/ui). --- ## [Unreleased] ### Added - Documentation initiale du projet (ARCHITECTURE, ONBOARDING, SECURITY, etc.) - 5 ADRs pour les décisions architecturales majeures - Code source de `src/entities/user/access.ts` et `lib.ts` avec tests ## [Unreleased] — 2026-04-22 — Sprint 3.5 — Clean post-Sprint 3 ### Changed - **FTD-17 résolu** : `PLAN_QUERY_KEY` centralisé dans `src/entities/user/query-keys.ts` (constantes pures, aucun import runtime). `usePlan` le ré-exporte ; `SimulationPage` et `RapportPage` remplacent leur `useQuery` inline par le hook `usePlan()` — déduplication totale de la clé et de la config `staleTime`. - **FTD-18 résolu** : `SimulationForm` migré de `@/shared/components/ui/button` (shadcn) vers la primitive canonique `@/shared/ui/Button`. Aucun variant à adapter (usage sans prop `variant`). - **FTD-19 résolu** : token `--shadow-focus` ajouté dans `@theme {}` (`0 0 0 3px rgba(27, 79, 216, 0.18)` — conforme `DESIGN_SYSTEM.md §2`) et dans `.dark {}` (recalculé sur la teinte expria dark). Migration de 5 occurrences `ring-2 ring-expria/20` → utility `shadow-focus` dans `Button`, `Card`, `SimulationForm` (×3), `SpecialCharsKeyboard`. - Factorisation `SimulationForm` : className dupliquée des deux boutons secondaires (« Suggestions d'idées » / « Changer de sujet ») extraite en const locale `secondaryActionBtn`. - `TECH_DEBT.md` → v1.11. 15 FTD actives (cap de 15 respecté). ### Notes - Timeouts DeepSeek intermittents observés pendant les tests manuels Groupe B + C — cause externe (API tierce), hors périmètre refactor Sprint 3.5. - B8 : comportement actuel diffère du spec `PARCOURS_UTILISATEURS.md §2 "Quota atteint"` — affichage d'une bannière inline au lieu du modal de blocage attendu. À corriger dans un sprint dédié (non inclus dans ce clean, qui n'introduit aucune nouvelle fonctionnalité). ## [Unreleased] — 2026-04-22 — Sprint 3.6c — Analyse patterns (Backend + Frontend) ### Added (backend) - `GET /users/patterns` — analyse des patterns récurrents pour utilisateur Premium. - Auth : `authMiddleware` + `planMiddleware('pattern_analysis')` (403 `PLAN_INSUFFICIENT` si Free/Standard). - < 5 productions corrigées → `200 { ready: false, minimum: 5, current: N }`. - ≥ 5 → `200 { ready: true, patterns, exercises, preparation_index, analyzed_productions, last_analysis }`. - `patternsController.aggregatePatterns` (pure) : agrège les `erreurs_codes` sur N productions, seuil 3/5, dédoublonnage intra-prod (un même code dans un rapport ne compte qu'une fois), codes `autre` distingués par description, tri par fréquence DESC. - `patternsController.computePreparationIndex` (pure) : 60 % score moyen normalisé + 20 % régularité (médiane des intervalles entre prod) + 20 % tendance (pente linéaire sur les 5 scores). Clamp `[0, 100]`, messages figés selon les seuils `<40` / `40-70` / `>70`. - `patternsController.list` — orchestre fetch productions + cache `pattern_analyses` + recompute + DeepSeek + INSERT. Stratégie d'invalidation : `MAX(productions.created_at) > lastAnalysis.created_at` → recompute, sinon cache hit. - `generatePatternExercices` dans `src/lib/deepseek.ts` — prompt système validé par Hermann avec format `{ consigne, exemple, correction, astuce }`, température 0.4, `AbortSignal.timeout(20_000)`, validation runtime des critères via `isValidCritere`. - Table `pattern_analyses` — migration `005_sprint_3_6c_pattern_analyses.sql` : UUID PK + FK cascade user_id + `productions_ids UUID[]` + patterns/exercises JSONB + preparation_index (CHECK `[0, 100]`) + preparation_message + analyzed_count + RLS SELECT par user_id + index `(user_id, created_at DESC)`. - 19 nouveaux tests (`patternsController.test.ts`) : 7 sur `aggregatePatterns`, 4 sur `computePreparationIndex`, 8 sur route (401, 403 free/standard, <5 prod, cache hit, cache miss + insert, no patterns, DeepSeek fail gracieux). **205 tests backend verts** (+19 vs baseline 186). ### Added (frontend) - Page `/progression` — route sous `AppLayout` + `ProtectedRoute`, remplace le placeholder `ComingSoon`. - `ProgressionPage` — orchestre `usePlan` + `usePatterns`, gate plan via `hasAccess('pattern_analysis')`. - `ProgressionPremium` — orchestrateur : si not-ready → `NotReadyState` ; sinon Hero indice + patterns + exercices long terme + footer « Analyse basée sur vos N dernières productions — il y a X ». - `PreparationIndexHero` — score /100 + jauge horizontale colorée (rouge <40 / ambre 40-70 / vert >70) + message. - `PatternsList` — liste des patterns avec libellé via nouveau `CRITERE_LABELS` + badge fréquence (3/5, 4/5, 5/5). - **`PatternExerciceCard`** — _nouveau composant lesson-style_, non interactif (contrairement à `ExerciceInteractive` du rapport individuel) : critère + diagnostic + consigne + bloc incorrect (barré rouge) côte à côte avec bloc correct (vert) + **encart astuce proéminent** (icône ampoule + fond warning). - `NotReadyState` — barre de progression N/5 + CTA `Démarrer une simulation`. - `BlurredProgression` — aperçu flouté pour Free/Standard + bouton upgrade Premium. - Section Dashboard Premium `MonProfilPreparation` — MetricCard indice (score + jauge compacte + message) + nombre d'erreurs récurrentes + CTA « Voir mon profil de préparation » vers `/progression`. Garde explicite `hasAccess('pattern_analysis')` → composant retourne `null` pour Free/Standard (pas rendu dans le DOM). - `usePatterns(plan)` — hook TanStack Query partagé entre `/progression` et dashboard ; clé `['users', 'patterns']`, `staleTime: 60s`, `enabled` conditionné par `hasAccess` pour éviter un 403 parasite. - `entities/patterns/types.ts` + `entities/patterns/api.ts` — types miroirs du backend (`Pattern`, `PatternExercice`, `PreparationIndex`, `PatternsReady`, `PatternsNotReady`) + `getPatterns()` avec timeout 25 s. - `CRITERE_LABELS` exporté depuis `entities/report/lib.ts` — miroir du backend pour affichage du libellé humain à partir du code taxonomie. - 13 nouveaux tests : 6 sur `ProgressionPremium` (not-ready, ready avec indice/patterns/exercices, footer, 0 pattern) + 7 sur `MonProfilPreparation` (gating Free/Standard, Premium ready/not-ready, loading, error, 0 pattern). **115 tests frontend verts** (+13 vs baseline 102). ### Notes - **Formule indice** arbitraire (60/20/20) — à affiner après observation prod si besoin. - **Dégradation gracieuse DeepSeek** : si `generatePatternExercices` throw, le backend persiste quand même l'analyse avec `exercises: []` et logue l'erreur. Le frontend affiche alors la liste des patterns sans section exercices (pas de message d'erreur explicite côté UI — l'utilisateur ne sait pas qu'il manque quelque chose). - **`ExerciceInteractive` NON réutilisé** pour les exercices long terme : les shapes et UX sont différents (lesson vs tentative). Deux composants distincts cohabitent. - **Migration SQL à exécuter manuellement** : `cd expria-backend && supabase db push` avant les tests end-to-end Premium. ## [Unreleased] — 2026-04-22 — Sprint 3.7 — Historique (Backend + Frontend) ### Added (backend) - `GET /simulations` — liste paginée des productions de l'utilisateur connecté. - Query params : `page` (défaut 1, entier ≥ 1), `limit` (défaut 20, entier entre 1 et 50). - Tri : `created_at DESC` côté Supabase. - Filtre : `user_id = profile.id` (double-protection avec RLS). - Projection : `id, tache, mode, score, nclc, nclc_cible, created_at` — champs lourds (`contenu`, `rapport`, `exercices`, `modele`) **exclus**. - Réponse : `{ data: ListItem[], pagination: { page, limit, total } }`. - Erreurs : `400 VALIDATION_ERROR` si `page`/`limit` invalide, `401 AUTH_REQUIRED` si JWT absent, `500 INTERNAL_ERROR` si DB down. - `simulationController.list(options, profile)` + interfaces `ListOptions`, `ListItem`, `ListResult`. - 12 nouveaux tests sur la route `GET /simulations` (186 tests backend verts, +12 vs baseline 174). ### Added (frontend) - Page `/historique` (route sous `AppLayout` + `ProtectedRoute`, remplace le placeholder `ComingSoon`). - `HistoriquePage` — orchestre `usePlan` + `useSimulationsList`, state local de pagination, gating plan Free via `hasAccess('dashboard')`. - `SimulationsList` — composant liste avec : - Empty state + CTA « Démarrer une simulation » → `/simulation/ee` - Loading skeleton (5 barres animées) - Error state (callout discret `border-l-danger`) - Aperçu flouté Free + bouton `variant="upgrade"` « Passer en Standard » - Pagination Précédent / Suivant (masquée si une seule page) - Affichage « Page X sur Y — Z simulations » - `SimulationListItem` — carte item : date relative, libellé de tâche (`formatTache`), score `/20`, `NCLC atteint / cible`, badges « Examen » et « En cours » (rapport non prêt). Clic → `/rapport/:id`. - `useSimulationsList(page, limit)` — hook TanStack Query, clé `['simulations', 'list', page, limit]`, `staleTime: 30s`, `placeholderData: keepPreviousData` pour éviter le flash de squelette au changement de page. - `listSimulations(page, limit)` dans `entities/production/api.ts` — wrap `apiFetch` + `URLSearchParams`. - Types `SimulationListItem` et `SimulationsListResponse` dans `entities/production/types.ts`. - `src/shared/lib/date.ts` — helper `formatRelativeDate(iso, now?)` basé sur `Intl.RelativeTimeFormat('fr', { numeric: 'auto' })`. Seuils : secondes → minutes → heures → jours → semaines → mois → années. Zéro dépendance. - 18 nouveaux tests frontend (7 `date.test.ts` + 11 `SimulationsList.test.tsx`). ### Notes - Les simulations avec `score === null` (en cours ou correction échouée) sont **affichées** avec un badge « En cours ». Clic → `/rapport/:id` — `RapportPage` gère le cas `REPORT_NOT_READY` (FTD-21) en redirigeant vers `/simulation/ee`. - `BlurredPreview` dupliqué localement dans `SimulationsList` (pattern équivalent à `BlurredSection` de `RapportPage`). À extraire en `shared/` si le pattern se répète dans un 3ᵉ endroit — pas fait dans ce sprint. - Pagination : Précédent/Suivant (MVP) retenu contre scroll infini. Le choix sera revu si l'historique dépasse 100 items en prod. - Tests frontend : **102/102 verts** (+18 vs baseline 84). ## [Unreleased] — 2026-04-22 — Sprint 3.6b — Qualité correction — Frontend ### Added - `NclcCibleSelector` (segmented control NCLC 9 / NCLC 10) dans `SimulationForm` — valeur propagée au payload `POST /corrections/ee` via `SimulationFlowProvider.submitText(texte, nclcCible)`. - Composants `rapport/` dans `features/simulations/components/` : - `ScoreHero` — score /20, jauge avec marqueur du seuil NCLC cible, écart vs objectif (« X points avant NCLC 9 »), badges NCLC atteint / cible. - `RevelationCards` — 3 colonnes : ce que le candidat croit / ce que le correcteur observe / conséquence. - `DiagnosticCallout` — callout « Ce qui freine votre progression ». - `CritereCard` — carte enrichie par critère (exemple / suggestion / astuce + badges codes taxonomie). - `ConseilNclcCallout` — plan d'action NCLC (objectif, écart, action prioritaire). - `ExerciceInteractive` — carte exercice avec zone texte, bouton Indice (révélé une fois), bouton « Voir la correction » (activé après saisie), explication. - `ProductionModeleSection` — texte final + notes pédagogiques + transformations original/amélioré + message encourageant. - `JobStatusFallback` — fallback pour `exercices_status` / `modele_status` en `'pending'` ou `'error'`. - Helpers dans `entities/report/lib.ts` : `groupErreursByCritere`, `ecartVsCible`, `critereCodeFromNom`. - Tests `ExerciceInteractive.test.tsx` (6 tests) — couvre état interne : révélation unique indice, activation bouton correction, affichage correction + explication. - FTD-24 🟡 dans `TECH_DEBT.md` — polling automatique pour exercices/modèle `pending` (refresh manuel en MVP). ### Changed - `entities/report/types.ts` — refonte complète alignée sur le backend Sprint 3.6a : `Report` remplace l'ancien (revelation, diagnostic, criteres enrichis, conseil_nclc, erreurs_codes top-level, exercices dynamiques, modele structuré, statuts pending/ready/error). Suppression de `feedback_court`, `erreurs[]`, `modele:string`, `idees[]` (obsolètes). - `entities/report/lib.ts` — `BlurableSection` réduite à `'criteres' | 'exercices' | 'modele'` : `revelation`, `diagnostic`, `conseil_nclc` deviennent visibles pour tous les plans conformément à PLANS_TARIFAIRES.md §2. - `entities/production/types.ts` — `SimulationState` étendu avec `nclc_cible`, `exercices`, `exercices_status`, `modele`, `modele_status` ; `SimulationRapport` aligné sur `CorrectionRapport` backend. - `entities/report/api.ts` — `getReport` recombine `SimulationState.rapport` + `exercices` + `modele` + statuts en un `Report` unifié pour `useRapport`. - `RapportPage.tsx` — réécriture complète : câble tous les nouveaux composants, branche le gating plan via `isSectionVisible`, affiche `JobStatusFallback` pour les jobs asynchrones. Résout l'écran blanc post-Sprint 3.6a. - `floutage.test.ts` réécrit (17 tests — matrice de visibilité + helpers lib). ### Fixed - **Race condition `modele_status`** (backend) : l'update principal de correction écrasait `modele_status='ready'` déjà posé par `runModeleJob` (lancé en parallèle option b). `correctionController.correctEE` ne touche plus aux colonnes `*_status` — pilotées exclusivement par les jobs asynchrones. - **Boucle infinie retour rapport → SimulationPage** : le useEffect sticky `step === 'done' → navigate('/rapport/:id')` renvoyait l'utilisateur sur le rapport à chaque tentative de retour vers `/simulation/ee`. Supprimé ; la navigation initiale vers `/rapport/:id` est déclenchée une seule fois dans `correctMutation.onSuccess` du provider. - **Boucle retour /sujets → SimulationPage** : même pattern sticky pour `step === 'choosing-subject' → navigate('/sujets')`. Supprimé ; navigation initiale vers `/sujets` déplacée dans `createMutation.onSuccess`. - **RapportPage hors SimulationFlowProvider** : la route `/rapport/:id` n'était pas sous `SimulationFlowLayout` — l'appel à `useSimulation()` depuis RapportPage throw. Route déplacée sous le layout, l'instance du provider est partagée avec `/simulation/ee` et `/sujets`. ### Added - Bouton « Nouvelle simulation » en bas de `RapportPage` qui `reset()` + `navigate('/simulation/ee')`. - `reset()` explicite dans le bouton « ← Retour » de `SujetsPage` avant la navigation, pour empêcher tout re-déclenchement de la garde sticky. ### Changed - Navigations post-mutation déplacées dans `onSuccess` du provider (pattern cohérent pour `createMutation` → `/sujets` et `correctMutation` → `/rapport/:id`). Plus de useEffect réactif aux changements de `step` côté SimulationPage. - `SujetsPage` : garde étendue de `!production` à `!production \|\| step === 'idle' \|\| step === 'done'` pour couvrir le cas post-rapport (évite le 400 VALIDATION_ERROR sur `PATCH /simulations/:id/sujet` d'une simulation déjà corrigée). - `RapportPage` breadcrumb : `` remplacé par `