feat(progression): page /progression + section Dashboard Premium — patterns, exercices long terme, indice de préparation (Sprint 3.6c)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hermann_Kitio 2026-04-22 23:12:23 +03:00
parent a752029c19
commit a60c298605
18 changed files with 1005 additions and 7 deletions

View file

@ -0,0 +1,23 @@
/**
* Appels API du domaine `patterns`.
*
* Endpoint unique : `GET /users/patterns`.
* - Plan non-Premium 403 PLAN_INSUFFICIENT (géré côté hook via `enabled`).
* - < 5 productions corrigées 200 { ready: false, minimum, current }.
* - 5 productions 200 { ready: true, patterns, exercises, preparation_index, ... }.
*
* Timeout par défaut : 10 s (largement suffisant le backend retourne sur cache
* sauf recompute + DeepSeek qui peut prendre jusqu'à 20 s côté serveur, mais
* reste sous le timeout HTTP côté proxy). Retry activé par défaut sur GET.
*/
import { apiFetch } from '@/shared/lib/api-client'
import type { PatternsResponse } from './types'
const PATTERNS_TIMEOUT_MS = 25_000
export function getPatterns(): Promise<PatternsResponse> {
return apiFetch<PatternsResponse>('/users/patterns', {
timeoutMs: PATTERNS_TIMEOUT_MS,
})
}

View file

@ -0,0 +1,52 @@
/**
* Types publics du domaine `patterns` Sprint 3.6c.
*
* Miroir de la réponse backend `GET /users/patterns` (cf. expria-backend
* Sprint 3.6c, commit c48ae8d). Feature Premium uniquement le gating se
* fait via `hasAccess(plan, 'pattern_analysis')` côté frontend ; la route
* backend renvoie 403 `PLAN_INSUFFICIENT` si plan non Premium (fallback
* défensif).
*/
import type { CritereCode } from '@/entities/report/types'
export interface Pattern {
code: string
critere: CritereCode
frequency: number // 3, 4 ou 5 (seuil d'agrégation : ≥ 3)
description: string | null // non-null uniquement pour code === 'autre'
}
export interface PatternExercice {
code: string
critere: CritereCode
diagnostic: string
exercice: {
consigne: string
exemple: string // phrase incorrecte générique (pas du candidat)
correction: string // version correcte
astuce: string // procédé mnémotechnique / réflexe de relecture
}
}
export interface PreparationIndex {
score: number // 0-100 entier
message: string // interprétation textuelle fixée par le backend
}
export interface PatternsReady {
ready: true
patterns: Pattern[]
exercises: PatternExercice[]
preparation_index: PreparationIndex
analyzed_productions: number
last_analysis: string // ISO timestamp
}
export interface PatternsNotReady {
ready: false
minimum: number // toujours 5 côté backend actuel
current: number // nb de productions corrigées déjà réalisées
}
export type PatternsResponse = PatternsReady | PatternsNotReady

View file

@ -87,3 +87,15 @@ export function ecartVsCible(score: number, nclcCible: number): {
}
export type { Critere }
/**
* Libellés officiels des 4 critères TCF Canada miroir de backend
* `src/lib/taxonomieErreurs.ts` CRITERE_LABELS. Utilisé par les listes de
* patterns et tout affichage nécessitant le libellé humain à partir du code.
*/
export const CRITERE_LABELS: Record<CritereCode, string> = {
adequation_tache: 'Adéquation à la tâche et au registre',
coherence_cohesion: 'Cohérence et cohésion du discours',
competence_lexicale: 'Compétence lexicale',
competence_grammaticale: 'Compétence grammaticale',
}