From 4712a3a16e0ab8f8b589c8773964418ab6dcdc26 Mon Sep 17 00:00:00 2001 From: Hermann_Kitio Date: Tue, 21 Apr 2026 02:56:02 +0300 Subject: [PATCH] refactor(simulations): supprimer SujetSelector + selectSujet orphelins (FTD-22) Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/TECH_DEBT.md | 2 + .../simulations/components/SujetSelector.tsx | 153 ------------------ .../hooks/__tests__/useSimulation.test.tsx | 46 +++++- .../simulations/hooks/useSimulation.ts | 9 -- 4 files changed, 40 insertions(+), 170 deletions(-) delete mode 100644 src/features/simulations/components/SujetSelector.tsx diff --git a/docs/TECH_DEBT.md b/docs/TECH_DEBT.md index 76d8faf..1595a6e 100644 --- a/docs/TECH_DEBT.md +++ b/docs/TECH_DEBT.md @@ -339,6 +339,7 @@ Vient du pattern `c.json(result, result.status)` où `result` contient déjà `s | FTD-11 | `@theme` Tailwind 4 non défini — palette et typographie absentes | 2026-04-18 | Résolu au Sprint 0.5 (design system). Palette Direction H complète (canvas/surface/ink/expria/deep/semantic) + typo Plus Jakarta Sans définis dans `src/index.css` via `@theme {}` et `.dark {}`. shadcn/ui remappé sur ces tokens. Règle L ajoutée dans `DEVELOPMENT_PRINCIPLES.md` pour garantir l'usage exclusif des tokens. | | 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`). | --- @@ -355,3 +356,4 @@ Vient du pattern `c.json(result, result.status)` où `result` contient déjà `s | 1.6 | 2026-04-20 | Ajout FTD-18 (SimulationForm shadcn Button — Sprint 0.5 bis D2) ; ajout FTD-19 (token --shadow-focus manquant — Sprint 0.5 bis D2) | | 1.7 | 2026-04-20 | Ajout FTD-20 🔴 (GET /simulations/:id manquant backend — bloque RapportPage Sprint 3 étape 15) | | 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) | diff --git a/src/features/simulations/components/SujetSelector.tsx b/src/features/simulations/components/SujetSelector.tsx deleted file mode 100644 index cba44ba..0000000 --- a/src/features/simulations/components/SujetSelector.tsx +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Écran de choix du sujet avant de démarrer la rédaction. - * - * Deux modes : - * - `choice` : propose "Sujet aléatoire" ou "Choisir dans la liste" - * - `list` : affiche les sujets actifs renvoyés par GET /sujets - * - * Règle H : aucune logique métier — le composant reçoit tache et handlers, - * et consomme `useSujets` pour la lecture du catalogue. - * Règle L : tokens Direction H uniquement (primitives Card / Button). - * - * Pour EO_T1, cet écran n'est pas rendu (flux direct SimulationForm) — - * cf. SimulationPage. - */ - -import { useState } from 'react' -import { ArrowLeft, Loader2, Shuffle, List } from 'lucide-react' -import { Button } from '@/shared/ui/Button' -import { Card } from '@/shared/ui/Card' -import type { SujetData, Tache } from '@/entities/production/types' -import { useSujets } from '../hooks/useSujets' - -interface Props { - tache: Tache - currentSujetId: string | null - onSelect: (sujet: SujetData) => void - onRandom: (sujets: SujetData[]) => void -} - -type ViewMode = 'choice' | 'list' - -export function SujetSelector({ tache, currentSujetId, onSelect, onRandom }: Props) { - const [mode, setMode] = useState('choice') - const { data: sujets, isLoading, isError, refetch } = useSujets(tache) - - if (mode === 'choice') { - return ( -
-
-

Choisir un sujet

-

- Lancez-vous avec un sujet aléatoire ou parcourez le catalogue. -

-
- - onRandom(sujets ?? [])} - > -
-
-
- - setMode('list')} - > -
-
-
-
- ) - } - - return ( -
-
- -

Liste des sujets

-
- - {isLoading && ( -
-
- )} - - {isError && ( -
-

- Impossible de charger la liste. Réessayez dans quelques instants. -

- -
- )} - - {sujets && sujets.length === 0 && ( -

- Aucun sujet disponible pour cette tâche pour le moment. -

- )} - - {sujets && sujets.length > 0 && ( -
    - {sujets.map((sujet) => { - const isCurrent = sujet.id === currentSujetId - return ( -
  • - onSelect(sujet)} - > -
    - {sujet.role && ( -
    - Rôle : {sujet.role} -
    - )} -

    {sujet.consigne}

    - {isCurrent && ( -
    Sujet actuel
    - )} -
    -
    -
  • - ) - })} -
- )} -
- ) -} diff --git a/src/features/simulations/hooks/__tests__/useSimulation.test.tsx b/src/features/simulations/hooks/__tests__/useSimulation.test.tsx index 95f00ee..14826dc 100644 --- a/src/features/simulations/hooks/__tests__/useSimulation.test.tsx +++ b/src/features/simulations/hooks/__tests__/useSimulation.test.tsx @@ -3,7 +3,7 @@ * * Transitions couvertes : * idle → choosing-subject (selectTask success, tâche avec catalogue) - * choosing-subject → task-selected (selectSujet) + * choosing-subject → task-selected (changeSubject + setStep depuis /sujets) * task-selected → correcting (submitText déclenché) * correcting → done (correctEe success) * correcting → task-selected (correctEe error) @@ -17,7 +17,7 @@ import { MemoryRouter } from 'react-router-dom' import React from 'react' import { describe, it, expect, vi, beforeEach } from 'vitest' import { useSimulation } from '../useSimulation' -import { SimulationFlowProvider } from '../../state/SimulationFlowProvider' +import { SimulationFlowProvider, useSimulationFlow } from '../../state/SimulationFlowProvider' import { createSimulation } from '@/entities/production/api' import { correctEe } from '@/entities/report/api' import type { Production } from '@/entities/production/types' @@ -142,11 +142,21 @@ describe('useSimulation — submitText', () => { let resolveCorrect!: (r: Report) => void mockCorrectEe.mockImplementation(() => new Promise(r => { resolveCorrect = r })) - const { result } = renderHook(() => useSimulation(), { wrapper: createWrapper() }) + const { result } = renderHook( + () => { + const sim = useSimulation() + const { setStep } = useSimulationFlow() + return { ...sim, setStep } + }, + { wrapper: createWrapper() }, + ) act(() => result.current.selectTask({ tache: 'EE_T1', mode: 'entrainement' })) await waitFor(() => expect(result.current.step).toBe('choosing-subject')) - act(() => result.current.selectSujet(mockSujet)) + act(() => { + result.current.changeSubject(mockSujet) + result.current.setStep('task-selected') + }) await waitFor(() => expect(result.current.step).toBe('task-selected')) act(() => result.current.submitText('Mon texte de production.')) @@ -161,11 +171,21 @@ describe('useSimulation — submitText', () => { mockCreateSimulation.mockResolvedValue(mockProduction) mockCorrectEe.mockRejectedValue({ code: 'SIMULATION_NOT_FOUND', message: 'Not found' }) - const { result } = renderHook(() => useSimulation(), { wrapper: createWrapper() }) + const { result } = renderHook( + () => { + const sim = useSimulation() + const { setStep } = useSimulationFlow() + return { ...sim, setStep } + }, + { wrapper: createWrapper() }, + ) act(() => result.current.selectTask({ tache: 'EE_T1', mode: 'entrainement' })) await waitFor(() => expect(result.current.step).toBe('choosing-subject')) - act(() => result.current.selectSujet(mockSujet)) + act(() => { + result.current.changeSubject(mockSujet) + result.current.setStep('task-selected') + }) await waitFor(() => expect(result.current.step).toBe('task-selected')) act(() => result.current.submitText('Mon texte.') @@ -188,11 +208,21 @@ describe('useSimulation — reset', () => { it('reset depuis task-selected remet step à idle et production à null', async () => { mockCreateSimulation.mockResolvedValue(mockProduction) - const { result } = renderHook(() => useSimulation(), { wrapper: createWrapper() }) + const { result } = renderHook( + () => { + const sim = useSimulation() + const { setStep } = useSimulationFlow() + return { ...sim, setStep } + }, + { wrapper: createWrapper() }, + ) act(() => result.current.selectTask({ tache: 'EE_T1', mode: 'entrainement' })) await waitFor(() => expect(result.current.step).toBe('choosing-subject')) - act(() => result.current.selectSujet(mockSujet)) + act(() => { + result.current.changeSubject(mockSujet) + result.current.setStep('task-selected') + }) await waitFor(() => expect(result.current.step).toBe('task-selected')) act(() => result.current.reset()) diff --git a/src/features/simulations/hooks/useSimulation.ts b/src/features/simulations/hooks/useSimulation.ts index 27a837c..4732915 100644 --- a/src/features/simulations/hooks/useSimulation.ts +++ b/src/features/simulations/hooks/useSimulation.ts @@ -11,19 +11,11 @@ import { useNavigate } from 'react-router-dom' import { useSimulationFlow } from '../state/SimulationFlowProvider' -import type { SujetData } from '@/entities/production/types' export function useSimulation() { const navigate = useNavigate() const flow = useSimulationFlow() - /** Sélectionne un sujet puis passe à la saisie (utilisé depuis /sujets). */ - function selectSujet(sujet: SujetData): void { - flow.changeSubject(sujet) - flow.setStep('task-selected') - navigate('/simulation/ee') - } - /** Retour à /sujets depuis SimulationForm (bouton "Changer de sujet"). */ function goToSubjectPicker(): void { flow.setStep('choosing-subject') @@ -42,7 +34,6 @@ export function useSimulation() { selectTask: flow.selectTask, submitText: flow.submitText, changeSubject: flow.changeSubject, - selectSujet, goToSubjectPicker, reset: flow.reset, }