From 477477b6a68a2da61d300e56f5127c0c7ffe0cb8 Mon Sep 17 00:00:00 2001 From: Hermann_Kitio Date: Tue, 21 Apr 2026 01:09:27 +0300 Subject: [PATCH] feat(simulations): hook useSujets + composant SujetSelector --- .../simulations/components/SujetSelector.tsx | 153 ++++++++++++++++++ src/features/simulations/hooks/useSujets.ts | 21 +++ 2 files changed, 174 insertions(+) create mode 100644 src/features/simulations/components/SujetSelector.tsx create mode 100644 src/features/simulations/hooks/useSujets.ts diff --git a/src/features/simulations/components/SujetSelector.tsx b/src/features/simulations/components/SujetSelector.tsx new file mode 100644 index 0000000..cba44ba --- /dev/null +++ b/src/features/simulations/components/SujetSelector.tsx @@ -0,0 +1,153 @@ +/** + * Écran de choix du sujet avant de démarrer la rédaction. + * + * Deux modes : + * - `choice` : propose "Sujet aléatoire" ou "Choisir dans la liste" + * - `list` : affiche les sujets actifs renvoyés par GET /sujets + * + * Règle H : aucune logique métier — le composant reçoit tache et handlers, + * et consomme `useSujets` pour la lecture du catalogue. + * Règle L : tokens Direction H uniquement (primitives Card / Button). + * + * Pour EO_T1, cet écran n'est pas rendu (flux direct SimulationForm) — + * cf. SimulationPage. + */ + +import { useState } from 'react' +import { ArrowLeft, Loader2, Shuffle, List } from 'lucide-react' +import { Button } from '@/shared/ui/Button' +import { Card } from '@/shared/ui/Card' +import type { SujetData, Tache } from '@/entities/production/types' +import { useSujets } from '../hooks/useSujets' + +interface Props { + tache: Tache + currentSujetId: string | null + onSelect: (sujet: SujetData) => void + onRandom: (sujets: SujetData[]) => void +} + +type ViewMode = 'choice' | 'list' + +export function SujetSelector({ tache, currentSujetId, onSelect, onRandom }: Props) { + const [mode, setMode] = useState('choice') + const { data: sujets, isLoading, isError, refetch } = useSujets(tache) + + if (mode === 'choice') { + return ( +
+
+

Choisir un sujet

+

+ Lancez-vous avec un sujet aléatoire ou parcourez le catalogue. +

+
+ + onRandom(sujets ?? [])} + > +
+
+
+ + setMode('list')} + > +
+
+
+
+ ) + } + + return ( +
+
+ +

Liste des sujets

+
+ + {isLoading && ( +
+
+ )} + + {isError && ( +
+

+ Impossible de charger la liste. Réessayez dans quelques instants. +

+ +
+ )} + + {sujets && sujets.length === 0 && ( +

+ Aucun sujet disponible pour cette tâche pour le moment. +

+ )} + + {sujets && sujets.length > 0 && ( +
    + {sujets.map((sujet) => { + const isCurrent = sujet.id === currentSujetId + return ( +
  • + onSelect(sujet)} + > +
    + {sujet.role && ( +
    + Rôle : {sujet.role} +
    + )} +

    {sujet.consigne}

    + {isCurrent && ( +
    Sujet actuel
    + )} +
    +
    +
  • + ) + })} +
+ )} +
+ ) +} diff --git a/src/features/simulations/hooks/useSujets.ts b/src/features/simulations/hooks/useSujets.ts new file mode 100644 index 0000000..aa13364 --- /dev/null +++ b/src/features/simulations/hooks/useSujets.ts @@ -0,0 +1,21 @@ +/** + * Hook de chargement des sujets disponibles pour une tâche. + * + * Règle H : aucune logique métier ici — mapping Tache → filtres DB dans + * `entities/production/api.ts` (getSujets). Le hook ne fait qu'encapsuler + * la requête TanStack Query avec un staleTime généreux (le catalogue de + * sujets change rarement). + */ + +import { useQuery } from '@tanstack/react-query' +import { getSujets } from '@/entities/production/api' +import type { SujetData, Tache } from '@/entities/production/types' + +export function useSujets(tache: Tache, enabled: boolean = true) { + return useQuery({ + queryKey: ['sujets', tache], + queryFn: () => getSujets(tache), + staleTime: 10 * 60 * 1000, + enabled, + }) +}