241 lines
No EOL
10 KiB
Markdown
241 lines
No EOL
10 KiB
Markdown
# 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 <nom>
|
|
|
|
### Added (nouveautés)
|
|
- ...
|
|
|
|
### Changed (modifications)
|
|
- ...
|
|
|
|
### Fixed (corrections)
|
|
- ...
|
|
|
|
### Removed (suppressions)
|
|
- ...
|
|
|
|
### Security (sécurité)
|
|
- ...
|
|
```
|
|
|
|
---
|
|
|
|
## [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
|
|
|
|
|
|
## 2026-04-21 — Tâche G5 — Suggestions d'idées DeepSeek
|
|
|
|
### Ajouté
|
|
- **Backend** — `POST /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
|
|
- **Frontend** — `getIdees(consigne, contenu)` dans
|
|
`entities/report/api.ts` (POST `/sujets/idees`,
|
|
timeoutMs 15 000)
|
|
- Hook `useIdees` — `useMutation` 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.ts` — `Critere.note` → `Critere.score`, `Report.exercices: Exercice[]` → `Report.exercices: string[]`, JSDoc ajusté
|
|
- `src/features/simulations/pages/RapportPage.tsx` — import `Exercice` retiré, `critere.note` → `critere.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_modele`→`modele`, `suggestions_idees`→`idees`, 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é) |