feat(rapport/eo): support 5 critères × /4 — Phonologie (Sprint 4.8 frontend)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hermann_Kitio 2026-04-26 03:30:31 +03:00
parent 3ce91aaa7b
commit 04019f8348
11 changed files with 273 additions and 10 deletions

View file

@ -19,15 +19,17 @@ import type { Critere, ErreurCode } from '@/entities/report/types'
interface Props {
critere: Critere
erreursCodes: ErreurCode[]
/** Sprint 4.8 — échelle par critère : 4 (EO 5 critères) ou 5 (EE / EO legacy 4 critères). */
maxScore?: 4 | 5
}
export function CritereCard({ critere, erreursCodes }: Props) {
export function CritereCard({ critere, erreursCodes, maxScore = 5 }: Props) {
return (
<Card variant="default" className="space-y-3 p-4">
<div className="flex items-start justify-between gap-3">
<h3 className="text-sm font-semibold text-ink-primary">{critere.nom}</h3>
<Badge variant="nclc" className="shrink-0 tabular-nums">
{critere.score}/5
{critere.score}/{maxScore}
</Badge>
</div>

View file

@ -75,10 +75,12 @@ class FakeMediaRecorder {
}
static isTypeSupported(_t: string): boolean {
void _t
return true
}
start(_timeslice?: number) {
void _timeslice
this.state = 'recording'
}

View file

@ -149,7 +149,6 @@ export function useDeepgramLive(): UseDeepgramLiveResult {
// Conserver l'ancienne connexion ; nouvelle tentative à la prochaine échéance.
// FTD-XX : durcir avec une retry policy plus fine en Sprint 4c-2.
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [openConnection])
const connect = useCallback(async () => {

View file

@ -20,7 +20,12 @@ import { useEffect } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { Lock } from 'lucide-react'
import { usePlan } from '@/features/dashboard/hooks/usePlan'
import { isSectionVisible, groupErreursByCritere, critereCodeFromNom } from '@/entities/report/lib'
import {
isSectionVisible,
groupErreursByCritere,
critereCodeFromNom,
getMaxScorePerCritere,
} from '@/entities/report/lib'
import type { Report } from '@/entities/report/types'
import { useRapport } from '../hooks/useRapport'
import { useSimulation } from '../hooks/useSimulation'
@ -93,6 +98,7 @@ function RapportSkeleton() {
function CriteresSection({ rapport }: { rapport: Report }) {
const grouped = groupErreursByCritere(rapport.erreurs_codes)
const maxScore = getMaxScorePerCritere(rapport)
return (
<section aria-label="Détail par critère">
@ -101,7 +107,7 @@ function CriteresSection({ rapport }: { rapport: Report }) {
{rapport.criteres.map((c) => {
const code = critereCodeFromNom(c.nom)
const codes = code ? grouped[code] : []
return <CritereCard key={c.nom} critere={c} erreursCodes={codes} />
return <CritereCard key={c.nom} critere={c} erreursCodes={codes} maxScore={maxScore} />
})}
</div>
</section>

View file

@ -7,7 +7,6 @@
* - onSuccess : setPresentationT1 + navigate vers /simulation/eo/t1/presentation
*/
import React from 'react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, cleanup, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'