expria-frontend/docs/CHANGELOG.md
Hermann_Kitio bc2a1174d1 docs: fermeture FTD-28 (Semgrep CI), TECH_DEBT v1.18, CI verte
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:26:52 +03:00

32 KiB
Raw Blame History

Changelog — Expria Frontend

Toutes les modifications notables du projet frontend sont documentées dans ce fichier.

Format basé sur Keep a Changelog.

Convention

Chaque entrée suit ce format :

## [Unreleased] — YYYY-MM-DD — Session <nom>

### Added (nouveautés)
- ...

### Changed (modifications)
- ...

### Fixed (corrections)
- ...

### Removed (suppressions)
- ...

### Security (sécurité)
- ...

[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).
  • PatternExerciceCardnouveau 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/:idRapportPage 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.tsBlurableSection réduite à 'criteres' | 'exercices' | 'modele' : revelation, diagnostic, conseil_nclc deviennent visibles pour tous les plans conformément à PLANS_TARIFAIRES.md §2.
  • entities/production/types.tsSimulationState étendu avec nclc_cible, exercices, exercices_status, modele, modele_status ; SimulationRapport aligné sur CorrectionRapport backend.
  • entities/report/api.tsgetReport 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 : <Link> remplacé par <button> qui reset() avant navigate.

Notes

  • Option β retenue : frontend aligné sur la structure backend réelle du Sprint 3.6a. Aucun aller-retour backend.
  • feedback_court supprimé de l'UI ; diagnostic remplace la section « Retour général ».
  • Polling automatique non implémenté (FTD-24) : refresh manuel de la page si exercices_status / modele_status = 'pending'.
  • Tests : 84/84 verts (+8 vs baseline 76).

[Unreleased] — 2026-04-22 — Sprint 3.6a — Qualité correction — Backend

Added (backend)

  • src/lib/taxonomieErreurs.ts : constantes des 63 codes TCF Canada + 4 codes autre par critère, validation runtime isValidCode / isValidCritere, et injection au prompt via buildTaxonomyPromptSection.
  • Prompts dynamiques dans src/lib/deepseek.ts : buildCorrectionPrompt (prompt maître avec nclc_cible 9 ou 10, sujet, documents T3), buildModelPrompt (production modèle cible NCLC 9 fixe), buildExercicesPrompt (3 exercices ciblés sur erreurs_codes + extraits exemple, format {difficulte, theme, diagnostic, consigne, extrait, indice, correction, explication}).
  • Post-traitement production modèle : wordCountTCF, stripModelAnnotations, truncateToMaxWords.
  • Route POST /corrections/ee accepte le paramètre nclc_cible (optionnel, défaut 9, valeurs acceptées : 9 ou 10 ; sinon 400 VALIDATION_ERROR).
  • Migration SQL supabase/migrations/004_sprint_3_6a_qualite_correction.sql — colonnes : revelation, diagnostic, conseil_nclc, erreurs_codes, exercices, modele, nclc_cible, exercices_status, modele_status + index GIN sur erreurs_codes (pour Sprint 3.6c).
  • controllers/__tests__/correctionController.test.ts (7 tests) : parallélisme, statuts ready/error, nclc_cible=10 propagé, simulation introuvable/autre user.
  • docs/TECH_DEBT.md TD-15 🟡 : jobs fire-and-forget peuvent rester pending si redémarrage process.

Changed (backend)

  • correctEE dans deepseek.ts — nouvelle signature correctEE(CorrectionInput) + nouvelle forme CorrectionRapport (revelation, diagnostic, criteres[{exemple,suggestion,astuce}], conseil_nclc, erreurs_codes). EERapport devient alias de CorrectionRapport.
  • correctionController.correctEE : lance 3 appels DeepSeek en parallèle ; await uniquement sur la correction pour répondre 200 ; modèle et exercices s'exécutent en fire-and-forget et mettent à jour {exercices,exercices_status} et {modele,modele_status} en base (pending → ready/error).
  • simulationController.getById retourne les nouveaux champs : nclc_cible, exercices, exercices_status, modele, modele_status en plus du rapport enrichi.
  • deepseek.test.ts réécrit — 25 tests (ancien pipeline supprimé, nouveaux tests sur correctEE/generateProductionModele/generateExercices/helpers + EO inchangé).

Notes

  • Option A retenue : backend renvoie uniquement la nouvelle forme. Frontend (Sprint 3.6b) casse tant que non livré — livraison groupée sans déploiement intermédiaire.
  • Prompt exercices rédigé côté backend (option b), basé sur les codes taxonomie + extraits exemple des critères. Format aligné sur captures d'écran demandées.
  • Migration SQL à exécuter manuellement via supabase db push — Hermann avant le premier test end-to-end.
  • Tests backend : 173/173 verts (+18 vs baseline de 155).

[Unreleased] — 2026-04-22 — Planification Sprint 3.6a/3.6b/3.6c

Added

  • Sprints 3.6a (backend prompts + taxonomie), 3.6b (frontend rapport enrichi), 3.6c (analyse patterns Premium) ajoutés à la ROADMAP entre Sprint 3.5 et Sprint 4.
  • TAXONOMIE_ERREURS.md — 63 codes d'erreurs TCF Canada sur 4 critères + 4 codes « autre » + procédure d'enrichissement.

2026-04-21 — FTD-21 — Persistance session /simulation/ee

Added

  • useAutosave(simulationId, contenu, enabled) : autosave debounce 30 s + flush sur beforeunload, dedup par dernier contenu sauvegardé (6 tests).
  • SimulationFlowProvider hydrate la session au montage depuis localStorage (expria_simulation_id) → GET /simulations/:id → restaure step='task-selected' + production + sujet si rapport=null ; nettoie la clé sinon (3 tests resume).
  • Types SimulationState, SimulationRapport + API getSimulationState, autosaveContenu, updateSujet dans entities/production.
  • Indicateur "Sauvegardé à HH:MM" sous la textarea SimulationForm (text-xs, aria-live="polite").

Changed

  • getReport délègue désormais à getSimulationState et lève REPORT_NOT_READY si rapport=null. RapportPage catche cette erreur et redirige vers /simulation/ee avec message discret "Votre simulation est en cours.".
  • SimulationForm accepte simulationId, initialContenu, step et persiste expria_simulation_id dans localStorage tant que la simulation est active ; nettoie la clé quand step='done'.
  • changeSubject persiste le changement côté backend via PATCH /simulations/:id/sujet (best-effort, silencieux si échec).

Security

  • localStorage ne stocke que simulation_id (UUID non-sensible) — conforme SECURITY.md §2.6.

Notes

  • FTD-21 reste ouvert pour /simulation/eo (Sprint 4) et /examen (Sprint 7).

2026-04-21 — Tâche G5 — Suggestions d'idées DeepSeek

Ajouté

  • BackendPOST /sujets/idees : génère 5 suggestions d'idées via DeepSeek pour aider l'étudiant à prolonger sa rédaction (prompt coach TCF Canada, temperature 0.5, timeout 15 s via AbortSignal, JSON strict { idees: string[] })
  • generateIdees(consigne, contenu) dans src/lib/deepseek.ts (validation tableau non vide)
  • 5 tests route POST /sujets/idees : 401 sans auth, 400 sujet_consigne manquant, 400 contenu < 30 mots, 200 succès avec idees[], 500 DeepSeek throw
  • FrontendgetIdees(consigne, contenu) dans entities/report/api.ts (POST /sujets/idees, timeoutMs 15 000)
  • Hook useIdeesuseMutation exposant { idees, isLoading, error, fetchIdees, reset }
  • Composant IdeesSuggestions — modal shadcn Dialog avec liste à puces, états loading/erreur/succès, reset() automatique à la fermeture
  • Bouton "Suggestions d'idées" (icône Lightbulb) dans SimulationForm à côté de "Changer de sujet"
  • Prop plan: Plan ajouté à SimulationForm (wiring planData.plan depuis SimulationPage)

Règles d'accès

  • Règle D respectée : hasAccess(plan, 'tips') obligatoire
  • Plan Free : bouton visible mais désactivé avec tooltip "Disponible en Standard" (tips=false pour Free)
  • Standard + Premium : bouton actif dès 30 mots écrits
  • Désactivé également si !sujet, isSubmitting, ou idees.isLoading

Tests

  • Backend — Typecheck : 0 erreur, Vitest : 144/144 passés (+5 tests POST /sujets/idees)
  • Frontend — Typecheck : 0 erreur, Vitest : 67/67 passés
  • Test manuel : validé avec compte Standard (bouton actif à 30+ mots, modal affiche 5 idées) et Free (bouton verrouillé avec tooltip)

2026-04-21 — Tâche G4 + Refonte page /sujets + Fix quota simulations

Ajouté

  • Tâche G4 — choix du sujet avec dropdown intégré et bouton aléatoire dans SimulationForm (hook useSujets, composant SujetSelector, getSujets() sur GET /sujets?mode=&tache=)
  • Refonte UX /sujets (Option A) — page dédiée avec grille de cartes SujetCard (responsive 1/2/3 colonnes), état partagé via SimulationFlowProvider pour survivre aux navigations entre /simulation/ee et /sujets. MVP : refresh sur /sujets redirige vers /simulation/ee.
  • Bouton "Changer de sujet" dans SimulationForm — retour à /sujets via goToSubjectPicker
  • Prop type: 'EE' | 'EO' sur TaskSelector (EO_CARDS réservé usage futur — non routé, /simulation/eo reste ComingSoon jusqu'au Sprint EO)

Modifié

  • useSimulation refacto en consommateur de SimulationFlowProvider (source de vérité déplacée hors du hook)
  • SujetDisplay redevient présentationnel (dropdown retiré)
  • TaskSelector : retrait des cartes EO de la page Expression Écrite (affiche uniquement EE T1/T2/T3)

Corrigé

  • Quota simulations (backend — commit ecb478e, expria-backend) : incrément simulations_used déplacé de simulationController.create() vers correctionController.correctEE/EO (Option B). Une simulation créée mais jamais corrigée ne consomme plus le quota utilisateur.

Supprimé

  • SujetSelector.tsx — orphelin après refonte /sujets
  • Helper selectSujet de useSimulation — orphelin
  • FTD-22 tracée résolue partiellement (step 'choosing-subject'
    • goToSubjectPicker conservés intentionnellement)

Tests

  • Typecheck : 0 erreur
  • Vitest : 67/67 passés
  • Test manuel : flux complet EE T1 avec choix de sujet (carte + aléatoire + changement de sujet) validé

2026-04-21 — Tâches G2+G3 — Clavier + Minuteur

Ajouté

  • Composant SpecialCharsKeyboard — 30 caractères spéciaux français en flex-wrap, sticky au scroll
  • Bloc "Temps restant" sticky avec TimerDisplay MM:SS (critique < 2min : rouge + pulse, expiré : rouge bold)
  • Composant WordCountBar — barre de progression colorée (orange < cible, vert dans cible, rouge > cible)
  • Hook useTimer avec 7 tests unitaires
  • Config par tâche dans simulationConfig.ts (EE T1: 10min/60-120 mots, T2: 20min/120-150, T3: 30min/120-180)
  • Auto-submit à l'expiration si ≥ 30 mots
  • Bouton "Soumettre ma production" (était "Envoyer")
  • Textarea auto-resize sans scroll interne

Changed

  • Compteur de caractères remplacé par WordCountBar
  • Bouton soumission bloqué si < 30 mots

Tests

  • Typecheck : 0 erreur
  • Vitest : 66/66 passés (+7 tests useTimer)
  • Test manuel : minuteur + clavier validés sur mobile et desktop

2026-04-21 — Tâche G1 — Affichage de la consigne

Ajouté

  • Interface SujetData dans entities/production/types.ts
  • Production enrichie avec sujet: SujetData | null
  • Composant SujetDisplay — affiche consigne, rôle, contexte, doc1, doc2 selon le sujet retourné
  • useSimulation expose sujet dans son retour
  • SimulationForm intègre SujetDisplay au-dessus de la textarea
  • FTD-21 tracée (persistance session simulation)

Tests

  • Typecheck : 0 erreur
  • Vitest : 59/59 passés
  • Test manuel : consigne affichée sur /simulation/ee

2026-04-20 — Audit frontend ↔ backend — alignement types Report

Modifié

  • src/entities/report/types.tsCritere.noteCritere.score, Report.exercices: Exercice[]Report.exercices: string[], JSDoc ajusté
  • src/features/simulations/pages/RapportPage.tsx — import Exercice retiré, critere.notecritere.score, ExerciceCard refactoré pour consommer une string rendue en Markdown, clé d'itération par index

Supprimé

  • Interface Exercice { titre, contenu } de entities/report/types.ts — remplacée par string[] pour coller au contrat backend

Contexte (backend associé, expria-backend)

Quatre commits côté backend finalisent l'alignement du contrat Report :

  • feat(corrections): renommages production_modelemodele, suggestions_ideesidees, ajout feedback_court + prompts DeepSeek mis à jour + validations runtime
  • feat(corrections): réponse enrichie avec simulation_id côté correctionController
  • feat(simulations): nouvelle route GET /simulations/:id (auth owner, gestion SIMULATION_NOT_FOUND/AUTH_REQUIRED/REPORT_NOT_READY) + 4 tests
  • feat(simulations): sujet aléatoire (table sujets) retourné avec chaque production créée (EO_T2_LIVE exclu, non bloquant si aucun sujet actif)

Tests

  • Typecheck : 0 erreur
  • Vitest : 59/59 passés

À faire (hors scope — session frontend dédiée ultérieurement)

  • Ajouter sujet: SujetData | null dans entities/production/types.ts
  • Consommer le sujet retourné dans SimulationPage (affichage consigne + docs)
  • Consommer feedback_court dans RapportPage (rendu toujours visible — cf. PLANS_TARIFAIRES §2 — déjà supporté par le type Report, reste à brancher dans l'UI si ce n'est pas déjà le cas)

2026-04-20 — Sprint 0.5 bis — AppLayout + primitives UI + refonte visuelle

Ajouté

  • src/app/AppLayout.tsx — layout applicatif desktop/mobile (sidebar fixe 240px, drawer mobile, BottomNav)
  • src/app/Sidebar.tsx — navigation latérale avec verrouillage hasAccess() (Progression, Examen blanc, Historique)
  • src/app/MobileHeader.tsx — header mobile sticky (Logo, ThemeToggle, bouton menu hamburger)
  • src/app/BottomNav.tsx — navigation mobile fixe (4 items, bottom sheet "Simuler", tap target min 44px)
  • src/shared/ui/Button.tsx — primitive Button (variants: primary/secondary/ghost/upgrade ; sizes: sm/md/lg ; loading Loader2)
  • src/shared/ui/Card.tsx — primitive Card (variants: default/raised/interactive ; rendu <button> si onClick fourni)
  • src/shared/ui/Badge.tsx — primitive Badge (variants: plan/nclc/neutral ; couleur selon planValue pour variant plan)

Modifié

  • src/app/router.tsx — layout routes via PrivateLayout (ProtectedRoute + AppLayout + Outlet) ; ComingSoon inline ; redirect /simulation/simulation/ee
  • src/features/simulations/components/TaskSelector.tsx — refonte avec Card interactive / Card default opacity-60, Badge "EE"/"EO", eyebrow tracking-widest, icône verrou
  • src/features/simulations/pages/SimulationPage.tsx — suppression header interne (Logo + ThemeToggle) ; root <main> ; Button migré vers @/shared/ui/Button variant="secondary"
  • src/features/dashboard/pages/DashboardPage.tsx — suppression header interne ; Button variant="primary" avec navigate('/simulation/ee') ; Badge variant="plan" planValue={data.plan} ; tout migré vers @/shared/ui/

Documentation

  • docs/TECH_DEBT.md v1.6 — ajout FTD-18 (SimulationForm migration Button), FTD-19 (token --shadow-focus manquant)

Tests

  • Typecheck : 0 erreur
  • Vitest : 59/59 passés
  • Tests manuels : à valider par Hermann

2026-04-19 — Sprint 1 / Étape 6 — Maintenance mode + outillage sécurité

Ajouté

  • Page de maintenance statique (src/app/MaintenancePage.tsx) — logo + message, tokens Direction H, zéro dépendance
  • Guard VITE_MAINTENANCE_MODE dans main.tsx — si true, aucun provider ne se monte, aucun appel réseau
  • Variable VITE_MAINTENANCE_MODE dans env.ts (optionnelle, défaut false)
  • Hook PreToolUse Claude Code (security-check.sh) — 9 patterns SECURITY.md §2
  • Hook Stop Claude Code (check-file-size.sh) — alerte fichiers > 200 lignes
  • MCP server Semgrep enregistré dans Claude Code

Documentation

  • ARCHITECTURE.md §7 — ajout VITE_MAINTENANCE_MODE dans la liste des variables
  • TECH_DEBT.md — FTD-16 résolu (maintenance mode implémenté)

Tests

  • Typecheck : 0 erreur
  • Vitest : 37/37 passés
  • Test manuel : maintenance mode vérifié (page affichée, aucun appel réseau, routing bloqué)