feat(simulations): resume session + RapportPage tolère rapport=null (FTD-21)
This commit is contained in:
parent
aaecc3f804
commit
ae8d8af1df
2 changed files with 48 additions and 5 deletions
|
|
@ -11,11 +11,28 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { apiFetch } from '@/shared/lib/api-client'
|
import { apiFetch } from '@/shared/lib/api-client'
|
||||||
|
import { getSimulationState } from '@/entities/production/api'
|
||||||
import type { CorrectEePayload, CorrectEoPayload, Report } from './types'
|
import type { CorrectEePayload, CorrectEoPayload, Report } from './types'
|
||||||
|
|
||||||
/** Récupère un rapport existant. Endpoint : `GET /simulations/:id`. */
|
/**
|
||||||
|
* Récupère un rapport existant. Endpoint : `GET /simulations/:id`.
|
||||||
|
*
|
||||||
|
* FTD-21 : depuis l'assouplissement de `getById` côté backend (tolère `rapport=null`
|
||||||
|
* pour permettre le resume), on unwrap le champ `rapport` du `SimulationState`.
|
||||||
|
* Si la simulation est encore en cours (`rapport === null`), on lève une erreur
|
||||||
|
* typée `REPORT_NOT_READY` que RapportPage catche pour rediriger vers /simulation/ee.
|
||||||
|
*/
|
||||||
export function getReport(id: string): Promise<Report> {
|
export function getReport(id: string): Promise<Report> {
|
||||||
return apiFetch<Report>(`/simulations/${id}`)
|
return getSimulationState(id).then((state) => {
|
||||||
|
if (state.rapport === null) {
|
||||||
|
throw {
|
||||||
|
error: true,
|
||||||
|
code: 'REPORT_NOT_READY',
|
||||||
|
message: 'Simulation en cours — rédaction pas encore corrigée.',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { ...state.rapport, simulation_id: state.simulation_id }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const CORRECTION_TIMEOUT_MS = 30_000
|
const CORRECTION_TIMEOUT_MS = 30_000
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
* Règle H : logique de floutage dans entities/report/lib.ts.
|
* Règle H : logique de floutage dans entities/report/lib.ts.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { useEffect } from 'react'
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
import { Link, useNavigate, useParams } from 'react-router-dom'
|
import { Link, useNavigate, useParams } from 'react-router-dom'
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
|
@ -23,6 +24,15 @@ import { Badge } from '@/shared/ui/Badge'
|
||||||
import { Button } from '@/shared/ui/Button'
|
import { Button } from '@/shared/ui/Button'
|
||||||
import type { Critere } from '@/entities/report/types'
|
import type { Critere } from '@/entities/report/types'
|
||||||
|
|
||||||
|
function isReportNotReady(err: unknown): boolean {
|
||||||
|
return (
|
||||||
|
typeof err === 'object' &&
|
||||||
|
err !== null &&
|
||||||
|
'code' in err &&
|
||||||
|
(err as { code: unknown }).code === 'REPORT_NOT_READY'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// ── Composants internes ──────────────────────────────────────────────────────
|
// ── Composants internes ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
function RapportSkeleton() {
|
function RapportSkeleton() {
|
||||||
|
|
@ -105,7 +115,16 @@ export function RapportPage() {
|
||||||
const { id = '' } = useParams<{ id: string }>()
|
const { id = '' } = useParams<{ id: string }>()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const { rapport, isLoading, isError } = useRapport(id)
|
const { rapport, isLoading, isError, error } = useRapport(id)
|
||||||
|
const isInProgress = isError && isReportNotReady(error)
|
||||||
|
|
||||||
|
// FTD-21 — si la simulation n'est pas encore corrigée, rediriger vers /simulation/ee.
|
||||||
|
// Le SimulationFlowProvider restaurera la session via localStorage si présent.
|
||||||
|
useEffect(() => {
|
||||||
|
if (isInProgress) {
|
||||||
|
navigate('/simulation/ee', { replace: true })
|
||||||
|
}
|
||||||
|
}, [isInProgress, navigate])
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: planData,
|
data: planData,
|
||||||
|
|
@ -137,8 +156,15 @@ export function RapportPage() {
|
||||||
{/* Loading */}
|
{/* Loading */}
|
||||||
{(isLoading || isPlanLoading) && <RapportSkeleton />}
|
{(isLoading || isPlanLoading) && <RapportSkeleton />}
|
||||||
|
|
||||||
{/* Erreur */}
|
{/* FTD-21 — simulation en cours : message discret avant redirection via useEffect */}
|
||||||
{(isError || isPlanError) && !isLoading && !isPlanLoading && (
|
{isInProgress && (
|
||||||
|
<p className="text-center text-sm text-ink-4" aria-live="polite">
|
||||||
|
Votre simulation est en cours.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Erreur (hors "en cours" déjà géré au-dessus) */}
|
||||||
|
{(isError || isPlanError) && !isInProgress && !isLoading && !isPlanLoading && (
|
||||||
<div
|
<div
|
||||||
role="alert"
|
role="alert"
|
||||||
className="space-y-3 rounded-lg border border-danger/30 bg-danger-bg px-4 py-6 text-center"
|
className="space-y-3 rounded-lg border border-danger/30 bg-danger-bg px-4 py-6 text-center"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue