expria-frontend/src/features/simulations/pages/SimulationPage.tsx
2026-04-22 20:15:53 +03:00

102 lines
3.1 KiB
TypeScript

/**
* Page de simulation Expression Écrite.
*
* Orchestre les 3 étapes du flux : sélection de tâche → saisie → rapport.
* Le choix du sujet est délégué à la page /sujets (refonte UX 2026-04-21).
*
* Règle D : quotas et permissions passent par canSimulate() — jamais de plan === '...'
* Règle H : aucune logique métier — tout est dans useSimulation() et les entités.
*/
import { usePlan } from '@/features/dashboard/hooks/usePlan'
import { Button } from '@/shared/ui/Button'
import { useSimulation } from '../hooks/useSimulation'
import { TaskSelector } from '../components/TaskSelector'
import { SimulationForm } from '../components/SimulationForm'
function SimulationSkeleton() {
return (
<div className="space-y-4" aria-busy="true" aria-label="Chargement…">
<div className="h-6 w-40 animate-pulse rounded bg-canvas-2" />
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
{Array.from({ length: 6 }).map((_, i) => (
<div key={i} className="h-24 animate-pulse rounded-lg bg-canvas-2" />
))}
</div>
</div>
)
}
export function SimulationPage() {
const {
data: planData,
isLoading: isPlanLoading,
isError: isPlanError,
refetch: refetchPlan,
} = usePlan()
const {
step,
production,
sujet,
isCreating,
isCorrecting,
correctError,
selectTask,
submitText,
goToSubjectPicker,
reset,
} = useSimulation()
// Le reset sticky (step='done' ou 'choosing-subject' au retour) est déclenché
// explicitement par les callers qui ramènent vers /simulation/ee :
// - RapportPage.goToSimulations : reset() avant navigate
// - SujetsPage bouton « ← Retour » : reset() avant navigate
// Un useEffect réactif ici annulerait les transitions légitimes de
// createMutation.onSuccess (idle → choosing-subject → navigate /sujets).
return (
<main className="mx-auto max-w-2xl px-4 py-6">
{isPlanLoading && <SimulationSkeleton />}
{isPlanError && (
<div className="space-y-3 text-center">
<p className="text-sm text-danger">
Impossible de charger vos informations. Réessayez dans quelques instants.
</p>
<Button variant="secondary" size="sm" onClick={() => refetchPlan()}>
Réessayer
</Button>
</div>
)}
{planData && step === 'idle' && (
<TaskSelector
type="EE"
plan={planData.plan}
simulationsUsed={planData.simulations_used}
isLoading={isCreating}
onSelect={selectTask}
/>
)}
{planData &&
(step === 'task-selected' || step === 'correcting') &&
production && (
<SimulationForm
tache={production.tache}
sujet={sujet}
plan={planData.plan}
simulationId={production.id}
initialContenu={production.contenu}
step={step}
isSubmitting={isCorrecting}
error={correctError}
onSubmit={submitText}
onBack={reset}
onChangeSujet={goToSubjectPicker}
/>
)}
</main>
)
}