fix(navigation): correctifs flux retour post-rapport et post-sujets (reset sticky useEffect)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hermann_Kitio 2026-04-22 20:15:53 +03:00
parent f51caa1b75
commit da4e465125
3 changed files with 30 additions and 27 deletions

View file

@ -54,17 +54,19 @@ export function AppRouter() {
<Route path="/" element={<Navigate to="/dashboard" replace />} /> <Route path="/" element={<Navigate to="/dashboard" replace />} />
<Route path="/dashboard" element={<DashboardPage />} /> <Route path="/dashboard" element={<DashboardPage />} />
{/* Simulation — /simulation/ee et /sujets partagent le SimulationFlowProvider. */} {/* Simulation /simulation/ee, /sujets et /rapport/:id partagent le
SimulationFlowProvider. L'instance est préservée entre ces routes
par React Router tant que le layout parent reste monté, ce qui
permet à RapportPage.reset() d'agir sur le même state que
SimulationPage (bouton « Nouvelle simulation » + breadcrumb). */}
<Route path="/simulation" element={<Navigate to="/simulation/ee" replace />} /> <Route path="/simulation" element={<Navigate to="/simulation/ee" replace />} />
<Route element={<SimulationFlowLayout />}> <Route element={<SimulationFlowLayout />}>
<Route path="/simulation/ee" element={<SimulationPage />} /> <Route path="/simulation/ee" element={<SimulationPage />} />
<Route path="/sujets" element={<SujetsPage />} /> <Route path="/sujets" element={<SujetsPage />} />
<Route path="/rapport/:id" element={<RapportPage />} />
</Route> </Route>
<Route path="/simulation/eo" element={<ComingSoon />} /> <Route path="/simulation/eo" element={<ComingSoon />} />
{/* Rapport */}
<Route path="/rapport/:id" element={<RapportPage />} />
{/* Autres sections — Sprint 4+ */} {/* Autres sections — Sprint 4+ */}
<Route path="/examen" element={<ComingSoon />} /> <Route path="/examen" element={<ComingSoon />} />
<Route path="/progression" element={<ComingSoon />} /> <Route path="/progression" element={<ComingSoon />} />

View file

@ -8,8 +8,6 @@
* Règle H : aucune logique métier tout est dans useSimulation() et les entités. * Règle H : aucune logique métier tout est dans useSimulation() et les entités.
*/ */
import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { usePlan } from '@/features/dashboard/hooks/usePlan' import { usePlan } from '@/features/dashboard/hooks/usePlan'
import { Button } from '@/shared/ui/Button' import { Button } from '@/shared/ui/Button'
import { useSimulation } from '../hooks/useSimulation' import { useSimulation } from '../hooks/useSimulation'
@ -30,8 +28,6 @@ function SimulationSkeleton() {
} }
export function SimulationPage() { export function SimulationPage() {
const navigate = useNavigate()
const { const {
data: planData, data: planData,
isLoading: isPlanLoading, isLoading: isPlanLoading,
@ -52,18 +48,12 @@ export function SimulationPage() {
reset, reset,
} = useSimulation() } = useSimulation()
// Redirige vers /sujets dès que la création aboutit pour une tâche avec catalogue. // Le reset sticky (step='done' ou 'choosing-subject' au retour) est déclenché
useEffect(() => { // explicitement par les callers qui ramènent vers /simulation/ee :
if (step === 'choosing-subject' && production) { // - RapportPage.goToSimulations : reset() avant navigate
navigate('/sujets') // - SujetsPage bouton « ← Retour » : reset() avant navigate
} // Un useEffect réactif ici annulerait les transitions légitimes de
}, [step, production, navigate]) // createMutation.onSuccess (idle → choosing-subject → navigate /sujets).
useEffect(() => {
if (step === 'done' && production) {
navigate(`/rapport/${production.id}`)
}
}, [step, production, navigate])
return ( return (
<main className="mx-auto max-w-2xl px-4 py-6"> <main className="mx-auto max-w-2xl px-4 py-6">

View file

@ -34,19 +34,24 @@ function SujetsSkeleton() {
export function SujetsPage() { export function SujetsPage() {
const navigate = useNavigate() const navigate = useNavigate()
const { production, changeSubject, setStep } = useSimulationFlow() const { step, production, changeSubject, setStep, reset } = useSimulationFlow()
// MVP : pas de production en contexte (refresh direct) → retour à /simulation/ee // Redirige vers /simulation/ee si :
// - production absente (refresh direct sur /sujets sans contexte)
// - step === 'idle' (état initial, pas de simulation en cours)
// - step === 'done' (simulation corrigée — /sujets ne doit pas patcher
// une simulation dont le rapport est déjà persisté — cf. FTD-23)
const shouldRedirect = !production || step === 'idle' || step === 'done'
useEffect(() => { useEffect(() => {
if (!production) navigate('/simulation/ee', { replace: true }) if (shouldRedirect) navigate('/simulation/ee', { replace: true })
}, [production, navigate]) }, [shouldRedirect, navigate])
const { data: sujets, isLoading, isError, refetch } = useSujets( const { data: sujets, isLoading, isError, refetch } = useSujets(
production?.tache ?? 'EE_T1', production?.tache ?? 'EE_T1',
!!production, !!production && !shouldRedirect,
) )
if (!production) return null if (shouldRedirect || !production) return null
function handleSelect(sujet: SujetData) { function handleSelect(sujet: SujetData) {
changeSubject(sujet) changeSubject(sujet)
@ -71,7 +76,13 @@ export function SujetsPage() {
<div className="mb-4 flex items-center gap-3"> <div className="mb-4 flex items-center gap-3">
<button <button
type="button" type="button"
onClick={() => navigate('/simulation/ee')} onClick={() => {
// « Retour » = annuler la simulation en cours et revenir au
// TaskSelector. reset() doit être appelé AVANT navigate pour que
// step retombe à 'idle' sans repasser par 'choosing-subject'.
reset()
navigate('/simulation/ee')
}}
className="text-sm text-ink-4 underline-offset-4 hover:text-ink-2 hover:underline" className="text-sm text-ink-4 underline-offset-4 hover:text-ink-2 hover:underline"
> >
Retour Retour