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 { 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). */ /** Crée une nouvelle simulation. Endpoint : `POST /simulations` (HTTP 201). */
export function createSimulation(payload: CreateSimulationPayload): Promise<Production> { export function createSimulation(payload: CreateSimulationPayload): Promise<Production> {
@ -21,6 +27,43 @@ export function getSimulation(id: string): Promise<Production> {
return apiFetch<Production>(`/simulations/${id}`) 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`. * 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 * 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). * 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 { export interface Production {
id: string id: string
@ -46,6 +50,8 @@ export interface Production {
mode: Mode mode: Mode
created_at: string created_at: string
sujet: SujetData | null sujet: SujetData | null
contenu?: string
sujet_id?: string
} }
/** Corps de la requête `POST /simulations`. */ /** Corps de la requête `POST /simulations`. */
@ -53,3 +59,40 @@ export interface CreateSimulationPayload {
tache: Tache tache: Tache
mode: Mode 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[]
}