From 997f39bd3396a386cac34f8472674189d868f0b0 Mon Sep 17 00:00:00 2001 From: Hermann_Kitio Date: Mon, 20 Apr 2026 00:08:34 +0300 Subject: [PATCH] =?UTF-8?q?feat(simulations):=20useSimulation=20hook=20+?= =?UTF-8?q?=20TaskSelector=20+=20SimulationForm=20+=20SimulationPage=20+?= =?UTF-8?q?=20route=20(Sprint=203=20=C3=A9tape=2014)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- docs/TECH_DEBT.md | 16 ++ src/app/router.tsx | 9 + .../simulations/components/SimulationForm.tsx | 139 +++++++++++++++ .../simulations/components/TaskSelector.tsx | 113 +++++++++++++ .../hooks/__tests__/useSimulation.test.tsx | 160 ++++++++++++++++++ .../simulations/hooks/useSimulation.ts | 74 ++++++++ .../simulations/pages/SimulationPage.tsx | 110 ++++++++++++ 7 files changed, 621 insertions(+) create mode 100644 src/features/simulations/components/SimulationForm.tsx create mode 100644 src/features/simulations/components/TaskSelector.tsx create mode 100644 src/features/simulations/hooks/__tests__/useSimulation.test.tsx create mode 100644 src/features/simulations/hooks/useSimulation.ts create mode 100644 src/features/simulations/pages/SimulationPage.tsx diff --git a/docs/TECH_DEBT.md b/docs/TECH_DEBT.md index 9b40017..c0c2a1e 100644 --- a/docs/TECH_DEBT.md +++ b/docs/TECH_DEBT.md @@ -161,6 +161,21 @@ 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). + +--- + ## 3. Fonctionnalités reportées ### FTD-06 — AudioWorklet au lieu de ScriptProcessorNode (T2 Live) @@ -270,3 +285,4 @@ Vient du pattern `c.json(result, result.status)` où `result` contient déjà `s | 1.2 | 2026-04-17 | Ajout FTD-13 résolu (incompatibilité Vitest 3 / Vite 8) suite à l'étape 12-bis du Sprint 0 | | 1.3 | 2026-04-18 | FTD-11 résolu (design system Sprint 0.5) ; ajout FTD-14 (anti-FOUC), FTD-15 (option 'system' thème) | | 1.4 | 2026-04-18 | FTD-16 résolu (VITE_MAINTENANCE_MODE implémenté — Sprint 1 étape 6) | +| 1.5 | 2026-04-19 | Ajout FTD-17 (clé ['plan'] dupliquée entre features — Sprint 3 étape 14) | diff --git a/src/app/router.tsx b/src/app/router.tsx index 2654f7f..28de9a8 100644 --- a/src/app/router.tsx +++ b/src/app/router.tsx @@ -5,6 +5,7 @@ import { LoginPage } from '@/features/auth/pages/LoginPage' import { RegisterPage } from '@/features/auth/pages/RegisterPage' import { ProtectedRoute } from '@/features/auth/components/ProtectedRoute' import { DashboardPage } from '@/features/dashboard/pages/DashboardPage' +import { SimulationPage } from '@/features/simulations/pages/SimulationPage' const DesignSystemPage = import.meta.env.DEV ? React.lazy(() => import('@/features/design-system/DesignSystemPage')) @@ -24,6 +25,14 @@ export function AppRouter() { } /> + + + + } + /> {import.meta.env.DEV && ( void + onBack: () => void +} + +export function SimulationForm({ tache, isSubmitting, error, onSubmit, onBack }: Props) { + const [texte, setTexte] = useState('') + const [fieldError, setFieldError] = useState(null) + + function handleSubmit(e: FormEvent) { + e.preventDefault() + setFieldError(null) + + const parsed = textSchema.safeParse({ texte }) + if (!parsed.success) { + setFieldError(parsed.error.flatten().fieldErrors.texte?.[0] ?? null) + return + } + + onSubmit(parsed.data.texte) + } + + const apiError = mapCorrectError(error) + const charCount = texte.length + + return ( +
+
+ +

{formatTache(tache)}

+
+ + {apiError && ( +
+ {apiError} +
+ )} + +
+
+ +