From 18f92098cba9fe5502bf4b63b487dad9cfb9ea98 Mon Sep 17 00:00:00 2001 From: Hermann_Kitio Date: Wed, 22 Apr 2026 14:03:46 +0300 Subject: [PATCH] =?UTF-8?q?refactor(simulation-ee):=20Sprint=203.5=20clean?= =?UTF-8?q?=20=E2=80=94=20FTD-17/18/19=20r=C3=A9solus,=20factorisation=20S?= =?UTF-8?q?imulationForm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/TECH_DEBT.md | 45 +++---------------- src/entities/user/query-keys.ts | 13 ++++++ src/features/dashboard/hooks/usePlan.ts | 3 +- .../simulations/components/SimulationForm.tsx | 11 +++-- .../components/SpecialCharsKeyboard.tsx | 2 +- src/features/simulations/hooks/useRapport.ts | 1 - .../simulations/pages/RapportPage.tsx | 9 +--- .../simulations/pages/SimulationPage.tsx | 12 +---- src/index.css | 2 + src/shared/ui/Button.tsx | 2 +- src/shared/ui/Card.tsx | 2 +- 11 files changed, 36 insertions(+), 66 deletions(-) create mode 100644 src/entities/user/query-keys.ts diff --git a/docs/TECH_DEBT.md b/docs/TECH_DEBT.md index 91a0d11..b12a265 100644 --- a/docs/TECH_DEBT.md +++ b/docs/TECH_DEBT.md @@ -179,46 +179,7 @@ Vient du pattern `c.json(result, result.status)` où `result` contient déjà `s --- -### FTD-17 — Clé `['plan']` dupliquée entre features -**Priorité :** 🟢 Mineur -**Statut :** Ouvert — accepté par design (Sprint 3) -**Estimation de session :** 30 min -**Description :** La queryKey TanStack Query `['plan']` est écrite littéralement dans deux endroits : `features/dashboard/hooks/usePlan.ts` (via `PLAN_QUERY_KEY`) et `features/simulations/pages/SimulationPage.tsx` (inline). TanStack Query partage bien le cache par clé identique, donc la déduplication fonctionne en pratique. Mais toute faute de frappe dans l'une des occurrences briserait silencieusement la mise en cache partagée. - -**À faire :** -- Déplacer `PLAN_QUERY_KEY` vers `src/entities/user/api.ts` (ou un nouveau `src/entities/user/query-keys.ts`) -- Importer `PLAN_QUERY_KEY` dans `usePlan.ts` et `SimulationPage.tsx` depuis ce fichier partagé -- Supprimer la constante locale dans `usePlan.ts` - -**Condition de résolution :** avant l'ajout d'une troisième feature qui consomme `['plan']` (T2 Live page, rapport page). - ---- - -### FTD-18 — SimulationForm utilise encore shadcn Button -**Priorité :** 🟢 Mineur -**Statut :** Ouvert -**Estimation de session :** 30 min -**Description :** `src/features/simulations/components/SimulationForm.tsx` importe `Button` depuis `@/shared/components/ui/button` (shadcn). Depuis Sprint 0.5 bis, la primitive canonique est `@/shared/ui/Button`. SimulationForm a été volontairement exclu de l'étape D1 pour respecter la règle max 3 fichiers par étape. - -**À faire :** -- Remplacer l'import shadcn par `@/shared/ui/Button` -- Adapter `variant` si nécessaire (`"outline"` → `"secondary"`) - -**Condition de résolution :** session dédiée — à grouper avec d'autres migrations shadcn → shared/ui si elles surviennent. - ---- - -### FTD-19 — Token `--shadow-focus` absent de `src/index.css` -**Priorité :** 🟢 Mineur -**Statut :** Ouvert -**Estimation de session :** 15 min -**Description :** `DESIGN_SYSTEM.md` spécifie un token `--shadow-focus` pour les états focus des composants interactifs. Ce token n'a jamais été ajouté à `src/index.css`. En attendant, le focus est rendu via `ring-2 ring-expria/20` (token Direction H valide), utilisé dans `Button`, `Card`, `MobileHeader`. Le rendu visuel est correct mais ne correspond pas au token documenté. - -**À faire :** -- Ajouter `--shadow-focus` dans `@theme {}` de `src/index.css` (valeur à aligner avec `DESIGN_SYSTEM.md`) -- Remplacer `ring-2 ring-expria/20` par `shadow-focus` dans les composants concernés - -**Condition de résolution :** session design system dédiée (peut être groupée avec FTD-15 ou une refonte de tokens). +> FTD-17, FTD-18, FTD-19 résolus au Sprint 3.5 (2026-04-22) — voir §5 Historique des résolutions. --- @@ -366,6 +327,9 @@ Frontend : | FTD-13 | Incompatibilité Vitest 3 / Vite 8 (conflit de types `Plugin` entre le Vite 8 top-level avec Rolldown et le Vite 7 pinné de Vitest 3.2.4 ; `npm run build` cassé) | 2026-04-17 | Résolu par upgrade Vitest `3.2.4 → 4.1.4` (et `@vitest/coverage-v8` idem) à l'étape 12-bis du Sprint 0. Vitest 4.x supporte nativement Vite 8 Rolldown. Correctif complémentaire : script `typecheck` passé de `tsc --noEmit -p tsconfig.app.json` à `tsc -b --noEmit` pour couvrir aussi `tsconfig.node.json` (d'où `vite.config.ts`) et éviter qu'un bug similaire échappe à la CI. | | FTD-16 | `VITE_MAINTENANCE_MODE` non lu dans le code — la variable d'env était dans `env.ts` mais jamais consommée | 2026-04-18 | Résolu au Sprint 1 étape 6. Ajout de `isMaintenanceMode` dans `src/shared/config/env.ts` et garde dans `src/app/main.tsx` : `isMaintenanceMode ? : `. `MaintenancePage` est statique (aucun provider requis), tokens Direction H exclusivement. | | FTD-22 | Code orphelin suite à la refonte UX `/sujets` (2026-04-21) — composant `SujetSelector` et helper `selectSujet` plus référencés après bascule dropdown → page dédiée | 2026-04-21 | Résolu **partiellement**. Supprimé : `src/features/simulations/components/SujetSelector.tsx` + helper `selectSujet` de `useSimulation.ts` (les tests `useSimulation.test.tsx` adaptés en utilisant `changeSubject` + `setStep('task-selected')` via `useSimulationFlow`). **Conservés intentionnellement** : le step `'choosing-subject'` (utilisé par `SimulationFlowProvider.selectTask` pour les tâches avec catalogue et par `SimulationPage` pour naviguer vers `/sujets`) et le helper `goToSubjectPicker` (bouton "Changer de sujet" dans `SimulationForm`). | +| FTD-17 | Clé `['plan']` dupliquée entre features (`usePlan`, `SimulationPage`, `RapportPage`) | 2026-04-22 | Résolu au Sprint 3.5. Création de `src/entities/user/query-keys.ts` (constantes pures, aucun import runtime) exportant `PLAN_QUERY_KEY = ['plan'] as const`. `features/dashboard/hooks/usePlan.ts` l'importe et le re-exporte pour conserver la rétro-compatibilité de l'import `PLAN_QUERY_KEY`. `SimulationPage.tsx` et `RapportPage.tsx` remplacent leur `useQuery` inline par le hook `usePlan()` — dédup totale de la clé et de la config staleTime. | +| FTD-18 | SimulationForm utilise encore le shadcn Button au lieu de la primitive `@/shared/ui/Button` | 2026-04-22 | Résolu au Sprint 3.5. Remplacement de l'import `@/shared/components/ui/button` par `@/shared/ui/Button` dans `SimulationForm.tsx`. Aucun variant à adapter (usage du Button sans prop `variant` → `primary` par défaut dans les deux implémentations). Les 7 autres consommateurs shadcn (`Login/RegisterPage`, `PaywallBanner`, `DesignSystemPage`, `ThemeToggle`, `dialog.tsx`) restent hors scope de cette FTD. | +| FTD-19 | Token `--shadow-focus` absent de `src/index.css` | 2026-04-22 | Résolu au Sprint 3.5. Ajout de `--shadow-focus: 0 0 0 3px rgba(27, 79, 216, 0.18)` dans `@theme {}` (valeur conforme à `DESIGN_SYSTEM.md §2`) et `--shadow-focus: 0 0 0 3px rgba(91, 127, 255, 0.32)` dans `.dark {}` (recalculé sur la teinte expria dark `#5B7FFF`). Tailwind 4 génère automatiquement l'utility `shadow-focus`. Migration de 5 occurrences `ring-2 ring-expria/20` → `shadow-focus` dans `Button.tsx`, `Card.tsx`, `SimulationForm.tsx` (×3), `SpecialCharsKeyboard.tsx`. Factorisation bonus : className dupliquée des boutons secondaires de `SimulationForm` extraite en const `secondaryActionBtn`. | --- @@ -384,3 +348,4 @@ Frontend : | 1.8 | 2026-04-20 | Ajout FTD-21 🔴 (persistance session simulation — prod + sujet perdus au refresh, session dédiée après G1-G5) | | 1.9 | 2026-04-21 | FTD-22 résolu partiellement (nettoyage code orphelin refonte `/sujets` — `SujetSelector` + `selectSujet` supprimés ; `choosing-subject` + `goToSubjectPicker` conservés) | | 1.10 | 2026-04-21 | FTD-21 résolu partiellement pour `/simulation/ee` (autosave 30 s + `beforeunload` + reprise via `localStorage` + `PATCH /:id/contenu` + `PATCH /:id/sujet` + `getById` tolère `rapport=null`) ; EO + examen restent ouverts | +| 1.11 | 2026-04-22 | Sprint 3.5 Clean — FTD-17, FTD-18, FTD-19 résolus. 15 FTD actives restantes (cap de 15 respecté) | diff --git a/src/entities/user/query-keys.ts b/src/entities/user/query-keys.ts new file mode 100644 index 0000000..9267797 --- /dev/null +++ b/src/entities/user/query-keys.ts @@ -0,0 +1,13 @@ +/** + * Clés TanStack Query partagées pour le domaine `user`. + * + * Source unique — importée par `features/dashboard/hooks/usePlan`, + * `features/simulations/pages/SimulationPage`, `features/simulations/pages/RapportPage`, + * et tout futur consommateur du statut de plan. Une clé locale inline briserait + * silencieusement le cache partagé de TanStack Query à la moindre faute de frappe. + * + * Ce module ne contient que des constantes pures — aucun import React, TanStack + * ou autre dépendance runtime. + */ + +export const PLAN_QUERY_KEY = ['plan'] as const diff --git a/src/features/dashboard/hooks/usePlan.ts b/src/features/dashboard/hooks/usePlan.ts index 4bd5cee..4a221ca 100644 --- a/src/features/dashboard/hooks/usePlan.ts +++ b/src/features/dashboard/hooks/usePlan.ts @@ -12,8 +12,9 @@ import { useQuery } from '@tanstack/react-query' import { getPlanStatus } from '@/entities/user/api' +import { PLAN_QUERY_KEY } from '@/entities/user/query-keys' -export const PLAN_QUERY_KEY = ['plan'] as const +export { PLAN_QUERY_KEY } export function usePlan() { return useQuery({ diff --git a/src/features/simulations/components/SimulationForm.tsx b/src/features/simulations/components/SimulationForm.tsx index 6c35084..03ac25c 100644 --- a/src/features/simulations/components/SimulationForm.tsx +++ b/src/features/simulations/components/SimulationForm.tsx @@ -13,7 +13,7 @@ import { useEffect, useRef, useState, type FormEvent } from 'react' import { Clock, Lightbulb, Loader2, Shuffle } from 'lucide-react' import { z } from 'zod' -import { Button } from '@/shared/components/ui/button' +import { Button } from '@/shared/ui/Button' import { formatTache } from '@/entities/production/lib' import { hasAccess, type Plan } from '@/entities/user/lib' import type { SujetData, Tache } from '@/entities/production/types' @@ -32,6 +32,9 @@ import { IdeesSuggestions } from './IdeesSuggestions' const MIN_WORDS_IDEES = 30 const LS_SIMULATION_ID_KEY = 'expria_simulation_id' +const secondaryActionBtn = + 'inline-flex items-center gap-1.5 rounded-md border border-line bg-surface px-3 py-1.5 text-sm text-ink-2 transition-colors hover:border-expria hover:text-expria focus:border-expria focus:outline-none focus:shadow-focus disabled:cursor-not-allowed disabled:opacity-50' + const textSchema = z.object({ texte: z .string() @@ -212,7 +215,7 @@ export function SimulationForm({ onClick={handleIdeesClick} disabled={ideesDisabled} title={ideesTitle} - className="inline-flex items-center gap-1.5 rounded-md border border-line bg-surface px-3 py-1.5 text-sm text-ink-2 transition-colors hover:border-expria hover:text-expria focus:border-expria focus:outline-none focus:ring-2 focus:ring-expria/20 disabled:cursor-not-allowed disabled:opacity-50" + className={secondaryActionBtn} aria-label="Obtenir des suggestions d'idées" >