Rôle
diff --git a/src/features/simulations/components/TaskSelector.tsx b/src/features/simulations/components/TaskSelector.tsx
index 24c8adb..0387b46 100644
--- a/src/features/simulations/components/TaskSelector.tsx
+++ b/src/features/simulations/components/TaskSelector.tsx
@@ -1,14 +1,15 @@
/**
- * Sélecteur de tâche pour lancer une simulation Expression Écrite.
+ * Sélecteur de tâche pour lancer une simulation.
*
- * Affiche les 3 tâches EE : T1, T2, T3 (sélectionnables si quota OK).
- * Les tâches Expression Orale seront sur /simulation/eo (Sprint EO).
+ * Filtre les cartes selon `type` :
+ * - 'EE' → 3 tâches EE (T1/T2/T3) sélectionnables si quota OK
+ * - 'EO' → EO_T1 (Entretien) + EO_T3 (Point de vue) + EO_T2 Live verrouillé (Premium)
*
* Règle D : le quota est vérifié via canSimulate(), jamais if (plan === 'free').
* Règle H : aucune logique métier — uniquement appel de canSimulate() et affichage.
*/
-import { Loader2 } from 'lucide-react'
+import { Lock, Loader2 } from 'lucide-react'
import { canSimulate } from '@/entities/user/lib'
import { cn } from '@/shared/lib/utils'
import { Card } from '@/shared/ui/Card'
@@ -16,7 +17,10 @@ import { Badge } from '@/shared/ui/Badge'
import type { Plan } from '@/entities/user/lib'
import type { CreateSimulationPayload, Tache } from '@/entities/production/types'
+export type TaskKind = 'EE' | 'EO'
+
interface Props {
+ type: TaskKind
plan: Plan
simulationsUsed: number
isLoading: boolean
@@ -24,20 +28,29 @@ interface Props {
}
interface TaskCard {
- tache: Tache
+ key: string
+ tache: Tache | null // null = carte verrouillée (EO_T2 Live)
label: string
sublabel: string
+ lockLabel?: string
}
-const TASK_CARDS: readonly TaskCard[] = [
- { tache: 'EE_T1', label: 'Expression Écrite', sublabel: 'Tâche 1' },
- { tache: 'EE_T2', label: 'Expression Écrite', sublabel: 'Tâche 2' },
- { tache: 'EE_T3', label: 'Expression Écrite', sublabel: 'Tâche 3' },
+const EE_CARDS: readonly TaskCard[] = [
+ { key: 'EE_T1', tache: 'EE_T1', label: 'Expression Écrite', sublabel: 'Tâche 1' },
+ { key: 'EE_T2', tache: 'EE_T2', label: 'Expression Écrite', sublabel: 'Tâche 2' },
+ { key: 'EE_T3', tache: 'EE_T3', label: 'Expression Écrite', sublabel: 'Tâche 3' },
]
-export function TaskSelector({ plan, simulationsUsed, isLoading, onSelect }: Props) {
+const EO_CARDS: readonly TaskCard[] = [
+ { key: 'EO_T1', tache: 'EO_T1', label: 'Expression Orale', sublabel: 'Entretien' },
+ { key: 'EO_T3', tache: 'EO_T3', label: 'Expression Orale', sublabel: 'Point de vue' },
+ { key: 'EO_T2_LIVE', tache: null, label: 'Expression Orale', sublabel: 'Tâche 2 — Live', lockLabel: 'Exclusivité Premium' },
+]
+
+export function TaskSelector({ type, plan, simulationsUsed, isLoading, onSelect }: Props) {
const simulationCheck = canSimulate(plan, simulationsUsed)
const quotaBlocked = !simulationCheck.allowed
+ const cards = type === 'EE' ? EE_CARDS : EO_CARDS
return (
@@ -62,31 +75,40 @@ export function TaskSelector({ plan, simulationsUsed, isLoading, onSelect }: Pro
)}
- {TASK_CARDS.map((card) => {
- const abbrev = card.tache.split('_')[0]
+ {cards.map((card) => {
+ const locked = card.tache === null || quotaBlocked
+ const abbrev = card.tache ? card.tache.split('_')[0] : 'EO'
- if (quotaBlocked) {
+ if (locked) {
return (
+ {card.tache === null && (
+
+ )}
{card.label}
{card.sublabel}
+ {card.lockLabel && (
+ {card.lockLabel}
+ )}
)
}
return (
{
- if (!isLoading) onSelect({ tache: card.tache, mode: 'entrainement' })
+ if (!isLoading && card.tache) {
+ onSelect({ tache: card.tache, mode: 'entrainement' })
+ }
}}
>
diff --git a/src/features/simulations/pages/SimulationPage.tsx b/src/features/simulations/pages/SimulationPage.tsx
index c60fc9c..e9f6bff 100644
--- a/src/features/simulations/pages/SimulationPage.tsx
+++ b/src/features/simulations/pages/SimulationPage.tsx
@@ -1,8 +1,8 @@
/**
* Page de simulation Expression Écrite.
*
- * Orchestre les 3 étapes du flux : sélection de tâche → saisie du texte → rapport.
- * Le choix du sujet est désormais délégué à la page /sujets (refonte UX 2026-04-21).
+ * Orchestre les 3 étapes du flux : sélection de tâche → saisie → rapport.
+ * Le choix du sujet est délégué à la page /sujets (refonte UX 2026-04-21).
*
* Règle D : quotas et permissions passent par canSimulate() — jamais de plan === '...'
* Règle H : aucune logique métier — tout est dans useSimulation() et les entités.
@@ -17,7 +17,6 @@ import { useQuery } from '@tanstack/react-query'
import { getPlanStatus } from '@/entities/user/api'
import { Button } from '@/shared/ui/Button'
import { useSimulation } from '../hooks/useSimulation'
-import { useSujets } from '../hooks/useSujets'
import { TaskSelector } from '../components/TaskSelector'
import { SimulationForm } from '../components/SimulationForm'
@@ -57,16 +56,10 @@ export function SimulationPage() {
correctError,
selectTask,
submitText,
- changeSubject,
+ goToSubjectPicker,
reset,
} = useSimulation()
- // Catalogue passé à SimulationForm (dropdown hérité — refacto étape 3).
- const { data: sujets, isLoading: isLoadingSujets } = useSujets(
- production?.tache ?? 'EE_T1',
- !!production,
- )
-
// Redirige vers /sujets dès que la création aboutit pour une tâche avec catalogue.
useEffect(() => {
if (step === 'choosing-subject' && production) {
@@ -97,6 +90,7 @@ export function SimulationPage() {
{planData && step === 'idle' && (
)}