"use client"; // Cahier d'erreurs : filtres par statut, cartes dépliables (question, votre réponse, // correction, explication), changement de statut et suppression. import { useCallback, useEffect, useMemo, useState } from "react"; import { CheckCircle2, ChevronDown, NotebookPen, Trash2 } from "lucide-react"; import { Badge, Card, EmptyState, Skeleton, Tabs, cn } from "@/components/ui"; import { Markdown } from "@/components/chat/markdown"; import { ErrorBanner, fetchJson, fmtDate } from "./shared"; type ErrorEntry = { id: number; course_code: string; concept_id: number | null; concept_name: string | null; question: string; given_answer: string; correction: string; explanation: string; source: string; status: "a-revoir" | "comprise" | "maitrisee"; created_at: string; }; const STATUS_META: Record = { "a-revoir": { label: "À revoir", tone: "red" }, comprise: { label: "Comprise", tone: "amber" }, maitrisee: { label: "Maîtrisée", tone: "green" }, }; const SOURCE_LABELS: Record = { quiz: "Quiz", exam: "Examen blanc", chat: "Chat", correction: "Correction" }; export function ErrorsApp({ course }: { course: string }) { const upper = course.toUpperCase(); const [entries, setEntries] = useState(null); const [filter, setFilter] = useState("toutes"); const [open, setOpen] = useState>({}); const [error, setError] = useState(null); const load = useCallback(() => { setError(null); fetchJson<{ errors: ErrorEntry[] }>(`/api/learning/errors?course=${upper}`) .then((d) => setEntries(d.errors)) .catch((e) => { setEntries([]); setError(e instanceof Error ? e.message : "Erreur de chargement."); }); }, [upper]); useEffect(() => { load(); }, [load]); async function patch(id: number, body: { status?: ErrorEntry["status"]; remove?: boolean }) { setError(null); try { const res = await fetch("/api/learning/errors", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id, ...body }), }); if (!res.ok) { const d = await res.json().catch(() => ({})); throw new Error((d as { error?: string }).error ?? `Erreur ${res.status}`); } setEntries((es) => es ? body.remove ? es.filter((e) => e.id !== id) : es.map((e) => (e.id === id && body.status ? { ...e, status: body.status } : e)) : es ); } catch (e) { setError(e instanceof Error ? e.message : "Erreur d'enregistrement."); } } const filtered = useMemo(() => { if (!entries) return []; if (filter === "toutes") return entries; return entries.filter((e) => e.status === filter); }, [entries, filter]); const counts = useMemo(() => { const c = { toutes: entries?.length ?? 0, "a-revoir": 0, comprise: 0, maitrisee: 0 }; for (const e of entries ?? []) c[e.status]++; return c; }, [entries]); return (

Cahier d'erreurs

Vos erreurs de quiz et d'examens, à retravailler jusqu'à les maîtriser — la forme de révision la plus rentable.

{error && } {entries === null ? (
) : filtered.length === 0 ? ( : } title={filter === "toutes" ? "Aucune erreur enregistrée" : "Rien dans ce filtre"} description={ filter === "toutes" ? "Vos réponses incorrectes aux quiz et examens blancs apparaîtront ici automatiquement." : "Changez de filtre pour voir les autres entrées." } /> ) : (
{filtered.map((e) => { const meta = STATUS_META[e.status]; const isOpen = !!open[e.id]; return ( {isOpen && (

Votre réponse

{e.given_answer || "—"}

Correction

{e.explanation && (

Explication

)}
{(Object.keys(STATUS_META) as ErrorEntry["status"][]).map((s) => ( ))}
)}
); })}
)}
); }