feat(simulations): router /sujets + SimulationFlowProvider wiring + useSimulation refacto
This commit is contained in:
parent
782439b309
commit
a6f95c2093
4 changed files with 87 additions and 116 deletions
|
|
@ -1,119 +1,49 @@
|
|||
/**
|
||||
* Hook d'orchestration du flux simulation EE.
|
||||
* Hook d'orchestration du flux simulation EE — consommateur de SimulationFlowProvider.
|
||||
*
|
||||
* Séquence : createSimulation (POST /simulations)
|
||||
* → [choosing-subject] (sauf EO_T1 — sujet fixe)
|
||||
* → correctEe (POST /corrections/ee, timeout 30 s)
|
||||
* Depuis la refonte /sujets (Option A), l'état vit dans le Provider pour survivre
|
||||
* aux navigations entre /simulation/ee et /sujets. Ce hook ajoute la navigation
|
||||
* vers /sujets après création d'une simulation pour une tâche avec catalogue.
|
||||
*
|
||||
* State machine :
|
||||
* 'idle' → sélection de tâche disponible
|
||||
* 'choosing-subject' → écran SujetSelector (hors EO_T1)
|
||||
* 'task-selected' → formulaire de saisie visible
|
||||
* 'correcting' → correction en cours (30 s max)
|
||||
* 'done' → rapport disponible dans `report`
|
||||
*
|
||||
* Règle H : aucune logique métier ici — les gardes de quota et de plan
|
||||
* sont dans TaskSelector (UX) et dans le backend (autorité).
|
||||
* Règle H : aucune logique métier — les gardes de quota et de plan sont dans
|
||||
* TaskSelector (UX) et dans le backend (autorité).
|
||||
*/
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { createSimulation } from '@/entities/production/api'
|
||||
import { correctEe } from '@/entities/report/api'
|
||||
import type {
|
||||
CreateSimulationPayload,
|
||||
Production,
|
||||
SujetData,
|
||||
Tache,
|
||||
} from '@/entities/production/types'
|
||||
import type { Report } from '@/entities/report/types'
|
||||
import type { ApiError } from '@/shared/types/api'
|
||||
|
||||
export type SimulationStep =
|
||||
| 'idle'
|
||||
| 'choosing-subject'
|
||||
| 'task-selected'
|
||||
| 'correcting'
|
||||
| 'done'
|
||||
|
||||
/** Tâches qui ne passent pas par l'écran de choix de sujet. */
|
||||
const TACHES_SANS_CATALOGUE: Tache[] = ['EO_T1']
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useSimulationFlow } from '../state/SimulationFlowProvider'
|
||||
import type { SujetData } from '@/entities/production/types'
|
||||
|
||||
export function useSimulation() {
|
||||
const [step, setStep] = useState<SimulationStep>('idle')
|
||||
const [production, setProduction] = useState<Production | null>(null)
|
||||
const navigate = useNavigate()
|
||||
const flow = useSimulationFlow()
|
||||
|
||||
const createMutation = useMutation({
|
||||
mutationFn: createSimulation,
|
||||
onSuccess: (data) => {
|
||||
setProduction(data)
|
||||
setStep(TACHES_SANS_CATALOGUE.includes(data.tache) ? 'task-selected' : 'choosing-subject')
|
||||
},
|
||||
})
|
||||
|
||||
const correctMutation = useMutation({
|
||||
mutationFn: correctEe,
|
||||
onMutate: () => setStep('correcting'),
|
||||
onSuccess: () => setStep('done'),
|
||||
onError: () => setStep('task-selected'),
|
||||
})
|
||||
|
||||
function selectTask(payload: CreateSimulationPayload): void {
|
||||
createMutation.mutate(payload)
|
||||
}
|
||||
|
||||
function submitText(texte: string): void {
|
||||
if (!production) return
|
||||
correctMutation.mutate({ simulationId: production.id, contenu: texte, tache: production.tache })
|
||||
}
|
||||
|
||||
/** Remplace le sujet courant sans toucher à l'étape. */
|
||||
function changeSubject(sujet: SujetData): void {
|
||||
setProduction((p) => (p ? { ...p, sujet } : p))
|
||||
}
|
||||
|
||||
/** Choix manuel : remplace le sujet et passe à la saisie. */
|
||||
/** Sélectionne un sujet puis passe à la saisie (utilisé depuis /sujets). */
|
||||
function selectSujet(sujet: SujetData): void {
|
||||
changeSubject(sujet)
|
||||
setStep('task-selected')
|
||||
flow.changeSubject(sujet)
|
||||
flow.setStep('task-selected')
|
||||
navigate('/simulation/ee')
|
||||
}
|
||||
|
||||
/** Choix aléatoire côté client à partir d'une liste pré-chargée. */
|
||||
function selectRandom(sujets: SujetData[]): void {
|
||||
if (sujets.length > 0) {
|
||||
const pick = sujets[Math.floor(Math.random() * sujets.length)]
|
||||
changeSubject(pick)
|
||||
}
|
||||
setStep('task-selected')
|
||||
}
|
||||
|
||||
/** Retour à l'écran SujetSelector depuis SimulationForm. */
|
||||
function backToSubject(): void {
|
||||
setStep('choosing-subject')
|
||||
}
|
||||
|
||||
function reset(): void {
|
||||
setStep('idle')
|
||||
setProduction(null)
|
||||
createMutation.reset()
|
||||
correctMutation.reset()
|
||||
/** Retour à /sujets depuis SimulationForm (bouton "Changer de sujet"). */
|
||||
function goToSubjectPicker(): void {
|
||||
flow.setStep('choosing-subject')
|
||||
navigate('/sujets')
|
||||
}
|
||||
|
||||
return {
|
||||
step,
|
||||
production,
|
||||
sujet: production?.sujet ?? null,
|
||||
report: (correctMutation.data ?? null) as Report | null,
|
||||
isCreating: createMutation.isPending,
|
||||
isCorrecting: correctMutation.isPending,
|
||||
createError: createMutation.error as ApiError | null,
|
||||
correctError: correctMutation.error as ApiError | null,
|
||||
selectTask,
|
||||
submitText,
|
||||
changeSubject,
|
||||
step: flow.step,
|
||||
production: flow.production,
|
||||
sujet: flow.sujet,
|
||||
report: flow.report,
|
||||
isCreating: flow.isCreating,
|
||||
isCorrecting: flow.isCorrecting,
|
||||
createError: flow.createError,
|
||||
correctError: flow.correctError,
|
||||
selectTask: flow.selectTask,
|
||||
submitText: flow.submitText,
|
||||
changeSubject: flow.changeSubject,
|
||||
selectSujet,
|
||||
selectRandom,
|
||||
backToSubject,
|
||||
reset,
|
||||
goToSubjectPicker,
|
||||
reset: flow.reset,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue