feat(production): types SimulationState + API autosave/updateSujet (FTD-21)

This commit is contained in:
Hermann_Kitio 2026-04-21 03:51:16 +03:00
parent 95711a7c44
commit d395a04193
2 changed files with 88 additions and 2 deletions

View file

@ -9,7 +9,13 @@
*/
import { apiFetch } from '@/shared/lib/api-client'
import type { CreateSimulationPayload, Production, SujetData, Tache } from './types'
import type {
CreateSimulationPayload,
Production,
SimulationState,
SujetData,
Tache,
} from './types'
/** Crée une nouvelle simulation. Endpoint : `POST /simulations` (HTTP 201). */
export function createSimulation(payload: CreateSimulationPayload): Promise<Production> {
@ -21,6 +27,43 @@ export function getSimulation(id: string): Promise<Production> {
return apiFetch<Production>(`/simulations/${id}`)
}
/**
* FTD-21 récupère l'état complet d'une simulation (contenu + sujet + rapport).
* Utilisé par `SimulationFlowProvider` pour restaurer une session depuis
* `localStorage.expria_simulation_id` au mount.
*
* Si `rapport === null` simulation en cours, restaurer `/simulation/ee`.
* Sinon simulation terminée, rediriger vers `/rapport/:id`.
*/
export function getSimulationState(id: string): Promise<SimulationState> {
return apiFetch<SimulationState>(`/simulations/${id}`)
}
/**
* FTD-21 autosave du contenu (debounce 30 s + beforeunload).
* Endpoint : `PATCH /simulations/:id/contenu`.
* Ne retourne rien : le client conserve déjà le texte localement.
*/
export async function autosaveContenu(id: string, contenu: string): Promise<void> {
await apiFetch<{ ok: true }>(`/simulations/${id}/contenu`, {
method: 'PATCH',
body: { contenu },
})
}
/**
* FTD-21 persiste un changement de sujet côté backend.
* Endpoint : `PATCH /simulations/:id/sujet`.
* Appelé depuis `SimulationFlowProvider.changeSubject` quand l'utilisateur
* choisit un autre sujet via `/sujets`.
*/
export async function updateSujet(id: string, sujetId: string): Promise<void> {
await apiFetch<{ sujet: SujetData }>(`/simulations/${id}/sujet`, {
method: 'PATCH',
body: { sujet_id: sujetId },
})
}
/**
* Mappe une Tache vers les paramètres de la route `GET /sujets`.
* Retourne `null` pour les tâches sans catalogue de sujets côté base

View file

@ -37,8 +37,12 @@ export interface SujetData {
}
/**
* Réponse du backend pour `POST /simulations` (HTTP 201) et `GET /simulations/:id`.
* Réponse du backend pour `POST /simulations` (HTTP 201).
* Format confirmé par l'audit backend 2026-04-17 (cf. ARCHITECTURE.md §5).
*
* FTD-21 : `contenu` et `sujet_id` sont persistés côté backend pour permettre
* la restauration de session. Ils ne sont pas retournés par `POST /simulations`
* mais peuvent être hydratés via `getSimulationState(id)` pour le resume.
*/
export interface Production {
id: string
@ -46,6 +50,8 @@ export interface Production {
mode: Mode
created_at: string
sujet: SujetData | null
contenu?: string
sujet_id?: string
}
/** Corps de la requête `POST /simulations`. */
@ -53,3 +59,40 @@ export interface CreateSimulationPayload {
tache: Tache
mode: Mode
}
/**
* Réponse du backend pour `GET /simulations/:id` (FTD-21).
*
* Contrairement au `Report` pur, cette réponse porte :
* - le contenu textuel en cours (pour restaurer la textarea)
* - le sujet joint (pour restaurer `SujetDisplay`)
* - le rapport si disponible (sinon `null` simulation en cours)
*
* Si `rapport === null`, le frontend restaure la session `/simulation/ee`.
* Sinon, il redirige vers `/rapport/:id`.
*/
export interface SimulationState {
simulation_id: string
tache: Tache
mode: Mode
created_at: string
contenu: string | null
sujet: SujetData | null
rapport: SimulationRapport | null
}
/**
* Rapport tel que stocké par le backend (sans `simulation_id` porté par SimulationState).
* Miroir de `EERapport` côté backend ; -exposé ici pour éviter l'import circulaire
* avec `entities/report/types.ts`.
*/
export interface SimulationRapport {
score: number
nclc: number
feedback_court: string
criteres: { nom: string; score: number; commentaire: string }[]
erreurs: string[]
modele: string
idees: string[]
exercices: string[]
}