diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0352038..854f38b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -29,13 +29,40 @@ Chaque entrée suit ce format : --- +## [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). @@ -44,10 +71,12 @@ Chaque entrée suit ce format : ## [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. @@ -57,6 +86,7 @@ Chaque entrée suit ce format : ## [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). @@ -65,6 +95,7 @@ Chaque entrée suit ce format : ## [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). @@ -73,6 +104,7 @@ Chaque entrée suit ce format : ## [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). @@ -83,6 +115,7 @@ Chaque entrée suit ce format : ## [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). @@ -92,14 +125,15 @@ Chaque entrée suit ce format : ## [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`. @@ -107,13 +141,14 @@ Chaque entrée suit ce format : - `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 }`. @@ -126,12 +161,13 @@ Chaque entrée suit ce format : - 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). +- **`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). @@ -141,15 +177,16 @@ Chaque entrée suit ce format : - 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. @@ -161,6 +198,7 @@ Chaque entrée suit ce format : - 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 : @@ -178,15 +216,16 @@ Chaque entrée suit ce format : - 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. @@ -202,6 +241,7 @@ Chaque entrée suit ce format : - 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. @@ -210,30 +250,34 @@ Chaque entrée suit ce format : - `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 ` +``` + +**Bouton secondaire :** +```tsx + +``` + +**Badge sémantique :** +```tsx + + {children} + +``` + ### Règles d'implémentation - Chaque primitive **accepte `className`** en plus de ses props typées, pour overrides ponctuels. - Chaque primitive **expose ses props via un type exporté** (`ButtonProps`, `CardProps`, etc.). - Aucune primitive ne contient de logique métier ou d'appel API. Elles reçoivent tout par props. -- Les icônes sont passées par une prop `icon` acceptant un `ReactNode`, jamais par nom de string. +- Les icônes sont importées de `lucide-react` et passées comme composant, jamais par nom de string. --- -## 5. Layout dashboard — spécification +## 6. Layout principal — `AppLayout` -Les primitives ci-dessus s'assemblent dans `src/features/dashboard/` et `src/pages/dashboard/`. - -### Structure sémantique - -``` - - (≥ 1024px) -
- (greeting + plan) -
- (NCLC estimé + progression) - (simulations restantes) - (dernier score) -
- @@ -72,19 +78,16 @@ export function BottomNav() { {/* Bottom nav bar */} {/* Footer — ThemeToggle */} -
+
- Thème + Thème
diff --git a/src/app/router.tsx b/src/app/router.tsx index 50e7233..00c0d86 100644 --- a/src/app/router.tsx +++ b/src/app/router.tsx @@ -20,8 +20,8 @@ const DesignSystemPage = import.meta.env.DEV function ComingSoon() { return (
-

Page en cours de développement

-

Disponible dans une prochaine version.

+

Page en cours de développement

+

Disponible dans une prochaine version.

) } @@ -83,7 +83,7 @@ export function AppRouter() { Loading…
}> + Loading…}> } diff --git a/src/features/auth/components/ProtectedRoute.tsx b/src/features/auth/components/ProtectedRoute.tsx index b1081e5..0e793cd 100644 --- a/src/features/auth/components/ProtectedRoute.tsx +++ b/src/features/auth/components/ProtectedRoute.tsx @@ -24,7 +24,7 @@ export function ProtectedRoute({ children }: ProtectedRouteProps) { if (isLoading) { return (
-
-

Se connecter

-

Accédez à votre espace Expria.

+
+

Se connecter

+

Accédez à votre espace Expria.

{error && (
{error}
@@ -121,9 +121,9 @@ export function LoginPage() { -

+

Pas encore de compte ?{' '} - + Créer un compte

diff --git a/src/features/auth/pages/RegisterPage.tsx b/src/features/auth/pages/RegisterPage.tsx index e78a08a..99c0b34 100644 --- a/src/features/auth/pages/RegisterPage.tsx +++ b/src/features/auth/pages/RegisterPage.tsx @@ -84,20 +84,20 @@ export function RegisterPage() { return (
-
-

Créer un compte

-

Commencez votre préparation TCF Canada.

+
+

Créer un compte

+

Commencez votre préparation TCF Canada.

{successMessage ? ( <>
{successMessage}
-

- +

+ Retour à la connexion

@@ -107,7 +107,7 @@ export function RegisterPage() { {formError && (
{formError}
@@ -185,9 +185,9 @@ export function RegisterPage() { -

+

Déjà un compte ?{' '} - + Se connecter

diff --git a/src/features/dashboard/components/MonProfilPreparation.tsx b/src/features/dashboard/components/MonProfilPreparation.tsx index 221b213..7ca440c 100644 --- a/src/features/dashboard/components/MonProfilPreparation.tsx +++ b/src/features/dashboard/components/MonProfilPreparation.tsx @@ -44,10 +44,10 @@ export function MonProfilPreparation({ plan }: Props) { if (isLoading || isError || !data) { return ( -

+

Mon profil de préparation

-

+

{isError ? 'Profil temporairement indisponible.' : 'Chargement…'}

@@ -58,14 +58,14 @@ export function MonProfilPreparation({ plan }: Props) { const remaining = Math.max(0, data.minimum - data.current) return ( -

+

Mon profil de préparation

-

+

Encore {remaining}{' '} {remaining > 1 ? 'simulations' : 'simulation'} pour débloquer votre profil.

-

+

{data.current}/{data.minimum} simulations corrigées

@@ -80,27 +80,27 @@ export function MonProfilPreparation({ plan }: Props) {
-

+

Indice de préparation

-

+

{data.preparation_index.score} - /100 + /100

-

+

{data.preparation_index.message}

-
+
-

+

{patternsCount === 0 ? 'Aucune erreur récurrente identifiée — continuez !' : `${patternsCount} ${patternsCount > 1 ? 'erreurs récurrentes identifiées' : 'erreur récurrente identifiée'}.`} diff --git a/src/features/dashboard/components/PaywallBanner.tsx b/src/features/dashboard/components/PaywallBanner.tsx index 10acfd7..e853e46 100644 --- a/src/features/dashboard/components/PaywallBanner.tsx +++ b/src/features/dashboard/components/PaywallBanner.tsx @@ -9,9 +9,9 @@ import { Button } from '@/shared/components/ui/button' export function PaywallBanner() { return ( -

-

Passez à Standard pour débloquer :

-