expria-frontend/src/entities/production/api.ts
Hermann_Kitio 99617f117c style: prettier format
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:17:16 +03:00

109 lines
3.9 KiB
TypeScript

/**
* Appels API du domaine `production`.
*
* Toutes les requêtes passent par `apiFetch` (Règle J / Règle F).
* - `POST /simulations` : timeout 5s (défaut), retry désactivé (POST non-idempotent).
* - `GET /simulations/:id` : timeout 5s, retry activé (GET idempotent).
*
* Erreurs notables : `QUOTA_REACHED` (Free 5/5), `PLAN_INSUFFICIENT` (exam_mode).
*/
import { apiFetch } from '@/shared/lib/api-client'
import type {
CreateSimulationPayload,
Production,
SimulationState,
SimulationsListResponse,
SujetData,
Tache,
} from './types'
/** Crée une nouvelle simulation. Endpoint : `POST /simulations` (HTTP 201). */
export function createSimulation(payload: CreateSimulationPayload): Promise<Production> {
return apiFetch<Production>('/simulations', { method: 'POST', body: payload })
}
/** Récupère une simulation existante. Endpoint : `GET /simulations/:id`. */
export function getSimulation(id: string): Promise<Production> {
return apiFetch<Production>(`/simulations/${id}`)
}
/**
* Sprint 3.7 — liste paginée des simulations de l'utilisateur connecté.
* Endpoint : `GET /simulations?page=X&limit=Y`. Tri `created_at DESC` côté backend.
* Champs lourds exclus (contenu, rapport, exercices, modele) — cf. SimulationListItem.
*/
export function listSimulations(page: number, limit: number): Promise<SimulationsListResponse> {
const qs = new URLSearchParams({ page: String(page), limit: String(limit) })
return apiFetch<SimulationsListResponse>(`/simulations?${qs.toString()}`)
}
/**
* 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
* (EO_T1 : sujet fixe connu, EO_T2_LIVE : interaction sans sujet).
*/
function mapTacheToSujetParams(tache: Tache): { mode: 'EE' | 'EO'; tacheNumber: 1 | 2 | 3 } | null {
switch (tache) {
case 'EE_T1':
return { mode: 'EE', tacheNumber: 1 }
case 'EE_T2':
return { mode: 'EE', tacheNumber: 2 }
case 'EE_T3':
return { mode: 'EE', tacheNumber: 3 }
case 'EO_T3':
return { mode: 'EO', tacheNumber: 3 }
case 'EO_T1':
return null
}
}
/**
* Récupère la liste des sujets actifs disponibles pour une tâche.
* Endpoint : `GET /sujets?mode=XX&tache=N`.
*
* Retourne `[]` immédiatement pour les tâches sans catalogue (EO_T1).
*/
export function getSujets(tache: Tache): Promise<SujetData[]> {
const params = mapTacheToSujetParams(tache)
if (!params) return Promise.resolve([])
const qs = new URLSearchParams({ mode: params.mode, tache: String(params.tacheNumber) })
return apiFetch<{ sujets: SujetData[] }>(`/sujets?${qs.toString()}`).then((r) => r.sujets)
}